Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
130 lines
4.0 KiB
C#
130 lines
4.0 KiB
C#
using Microsoft.EntityFrameworkCore;
|
||
using Microsoft.Extensions.Options;
|
||
using Serilog;
|
||
using Yarp.ReverseProxy.Configuration;
|
||
using Yarp.ReverseProxy.LoadBalancing;
|
||
using YarpGateway.Config;
|
||
using YarpGateway.Data;
|
||
using YarpGateway.DynamicProxy;
|
||
using YarpGateway.LoadBalancing;
|
||
using YarpGateway.Middleware;
|
||
using YarpGateway.Services;
|
||
using StackExchange.Redis;
|
||
using Fengling.ServiceDiscovery.Extensions;
|
||
using Fengling.ServiceDiscovery.Kubernetes.Extensions;
|
||
|
||
var builder = WebApplication.CreateBuilder(args);
|
||
|
||
builder.Host.UseSerilog(
|
||
(context, services, configuration) =>
|
||
configuration
|
||
.ReadFrom.Configuration(context.Configuration)
|
||
.ReadFrom.Services(services)
|
||
.Enrich.FromLogContext()
|
||
);
|
||
|
||
builder.Services.Configure<JwtConfig>(builder.Configuration.GetSection("Jwt"));
|
||
builder.Services.Configure<RedisConfig>(builder.Configuration.GetSection("Redis"));
|
||
builder.Services.AddSingleton(sp => sp.GetRequiredService<IOptions<RedisConfig>>().Value);
|
||
|
||
builder.Services.AddDbContextFactory<GatewayDbContext>(options =>
|
||
options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection"))
|
||
);
|
||
|
||
builder.Services.AddSingleton<DatabaseRouteConfigProvider>();
|
||
builder.Services.AddSingleton<DatabaseClusterConfigProvider>();
|
||
builder.Services.AddSingleton<IRouteCache, RouteCache>();
|
||
|
||
builder.Services.AddSingleton<IRedisConnectionManager, RedisConnectionManager>();
|
||
builder.Services.AddSingleton<IConnectionMultiplexer>(sp =>
|
||
{
|
||
var config = sp.GetRequiredService<RedisConfig>();
|
||
var connectionOptions = ConfigurationOptions.Parse(config.ConnectionString);
|
||
connectionOptions.AbortOnConnectFail = false;
|
||
connectionOptions.ConnectRetry = 3;
|
||
connectionOptions.ConnectTimeout = 5000;
|
||
connectionOptions.SyncTimeout = 3000;
|
||
connectionOptions.DefaultDatabase = config.Database;
|
||
|
||
var connection = ConnectionMultiplexer.Connect(connectionOptions);
|
||
connection.ConnectionFailed += (sender, e) =>
|
||
{
|
||
Serilog.Log.Error(e.Exception, "Redis connection failed");
|
||
};
|
||
connection.ConnectionRestored += (sender, e) =>
|
||
{
|
||
Serilog.Log.Information("Redis connection restored");
|
||
};
|
||
|
||
return connection;
|
||
});
|
||
|
||
builder.Services.AddSingleton<ILoadBalancingPolicy, DistributedWeightedRoundRobinPolicy>();
|
||
|
||
builder.Services.AddSingleton<DynamicProxyConfigProvider>();
|
||
builder.Services.AddSingleton<IProxyConfigProvider>(sp => sp.GetRequiredService<DynamicProxyConfigProvider>());
|
||
|
||
builder.Services.AddHostedService<PgSqlConfigChangeListener>();
|
||
|
||
// 添加 Kubernetes 服务发现
|
||
builder.Services.AddKubernetesServiceDiscovery(options =>
|
||
{
|
||
options.LabelSelector = "app.kubernetes.io/managed-by=yarp";
|
||
options.UseInClusterConfig = false; // 本地调试设为 false,生产环境设为 true
|
||
});
|
||
|
||
builder.Services.AddServiceDiscovery();
|
||
|
||
var corsSettings = builder.Configuration.GetSection("Cors");
|
||
builder.Services.AddCors(options =>
|
||
{
|
||
var allowAnyOrigin = corsSettings.GetValue<bool>("AllowAnyOrigin");
|
||
var allowedOrigins = corsSettings.GetSection("AllowedOrigins").Get<string[]>() ?? Array.Empty<string>();
|
||
|
||
options.AddPolicy("AllowFrontend", policy =>
|
||
{
|
||
if (allowAnyOrigin)
|
||
{
|
||
policy.AllowAnyOrigin();
|
||
}
|
||
else
|
||
{
|
||
policy.WithOrigins(allowedOrigins);
|
||
}
|
||
|
||
policy.AllowAnyHeader()
|
||
.AllowAnyMethod()
|
||
.AllowCredentials();
|
||
});
|
||
});
|
||
|
||
builder.Services.AddControllers();
|
||
builder.Services.AddHttpForwarder();
|
||
builder.Services.AddRouting();
|
||
builder.Services.AddReverseProxy();
|
||
|
||
var app = builder.Build();
|
||
|
||
app.UseCors("AllowFrontend");
|
||
app.UseMiddleware<JwtTransformMiddleware>();
|
||
app.UseMiddleware<TenantRoutingMiddleware>();
|
||
|
||
app.MapControllers();
|
||
app.MapReverseProxy();
|
||
|
||
await app.Services.GetRequiredService<IRouteCache>().InitializeAsync();
|
||
|
||
try
|
||
{
|
||
Log.Information("Starting YARP Gateway");
|
||
app.Run();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Log.Fatal(ex, "Application terminated unexpectedly");
|
||
}
|
||
finally
|
||
{
|
||
Log.CloseAndFlush();
|
||
}
|