# YARP 网关外部集成文档 ## 1. PostgreSQL 数据库集成 ### 概述 PostgreSQL 作为主数据库,存储网关配置数据,包括租户、路由、服务实例等信息。 ### 连接配置 **配置位置**: `src/appsettings.json` ```json { "ConnectionStrings": { "DefaultConnection": "Host=81.68.223.70;Port=15432;Database=fengling_gateway;Username=movingsam;Password=***" } } ``` ### DbContext 配置 **文件**: `src/Data/GatewayDbContext.cs` ```csharp // 注册 DbContext 工厂 builder.Services.AddDbContextFactory(options => options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")) ); ``` ### 数据模型 | 实体 | 表名 | 用途 | |------|------|------| | `GwTenant` | Tenants | 租户信息 | | `GwTenantRoute` | TenantRoutes | 租户路由配置 | | `GwServiceInstance` | ServiceInstances | 服务实例(集群节点) | | `GwPendingServiceDiscovery` | PendingServiceDiscoveries | K8s 待处理服务发现 | ### 配置变更通知机制 **文件**: `src/Config/ConfigNotifyChannel.cs` 使用 PostgreSQL `LISTEN/NOTIFY` 机制实现配置变更实时通知: ```csharp // 发送通知(在 DbContext.SaveChangesAsync 中触发) await using var cmd = new NpgsqlCommand($"NOTIFY {ConfigNotifyChannel.GatewayConfigChanged}", connection); // 监听通知(在 PgSqlConfigChangeListener 中) cmd.CommandText = $"LISTEN {ConfigNotifyChannel.GatewayConfigChanged}"; ``` **监听服务**: `src/Services/PgSqlConfigChangeListener.cs` - 监听 PostgreSQL NOTIFY 通道 - 检测配置版本变更 - 触发路由/集群配置热更新 - 提供 5 分钟兜底轮询机制 --- ## 2. Redis 集成 ### 概述 Redis 用于分布式锁、路由缓存同步,确保多实例网关的配置一致性。 ### 连接配置 **配置位置**: `src/Config/RedisConfig.cs` ```csharp public class RedisConfig { public string ConnectionString { get; set; } = "81.68.223.70:16379,password=***"; public int Database { get; set; } = 0; public string InstanceName { get; set; } = "YarpGateway"; } ``` ### 连接管理器 **文件**: `src/Services/RedisConnectionManager.cs` ```csharp // 注册 Redis 连接 builder.Services.AddSingleton(sp => { var config = sp.GetRequiredService(); var connectionOptions = ConfigurationOptions.Parse(config.ConnectionString); connectionOptions.AbortOnConnectFail = false; connectionOptions.ConnectRetry = 3; connectionOptions.ConnectTimeout = 5000; connectionOptions.SyncTimeout = 3000; connectionOptions.DefaultDatabase = config.Database; return ConnectionMultiplexer.Connect(connectionOptions); }); ``` ### 分布式锁实现 **接口**: `IRedisConnectionManager` ```csharp public interface IRedisConnectionManager { IConnectionMultiplexer GetConnection(); Task AcquireLockAsync(string key, TimeSpan? expiry = null); Task ExecuteInLockAsync(string key, Func> func, TimeSpan? expiry = null); } ``` **锁机制特性**: - 基于键值对的分布式锁 - 自动过期时间(默认 10 秒) - 指数退避重试策略 - Lua 脚本安全释放锁 --- ## 3. Kubernetes 服务发现集成 ### 概述 通过自定义的 Fengling.ServiceDiscovery 包实现 Kubernetes 服务自动发现,将 K8s Service 自动注册为网关后端服务。 ### 配置 **文件**: `src/Program.cs` ```csharp // 添加 Kubernetes 服务发现 var useInClusterConfig = builder.Configuration.GetValue("ServiceDiscovery:UseInClusterConfig", true); builder.Services.AddKubernetesServiceDiscovery(options => { options.LabelSelector = "app.kubernetes.io/managed-by=yarp"; options.UseInClusterConfig = useInClusterConfig; }); builder.Services.AddServiceDiscovery(); ``` ### 依赖包 | 包名 | 用途 | |------|------| | `Fengling.ServiceDiscovery.Core` | 服务发现核心接口 | | `Fengling.ServiceDiscovery.Kubernetes` | Kubernetes 实现 | | `Fengling.ServiceDiscovery.Static` | 静态配置实现 | ### 后台同步服务 **文件**: `src/Services/KubernetesPendingSyncService.cs` ```csharp public class KubernetesPendingSyncService : BackgroundService { private readonly TimeSpan _syncInterval = TimeSpan.FromSeconds(30); private readonly TimeSpan _staleThreshold = TimeSpan.FromHours(24); // 同步 K8s 服务到数据库待处理表 } ``` **同步逻辑**: 1. 每 30 秒从 K8s API 获取服务列表 2. 对比数据库中的待处理服务记录 3. 新增/更新/清理过期服务 4. 标记不再存在的 K8s 服务 ### 待处理服务数据模型 **文件**: `src/Models/GwPendingServiceDiscovery.cs` ```csharp public class GwPendingServiceDiscovery { public string K8sServiceName { get; set; } // K8s Service 名称 public string K8sNamespace { get; set; } // K8s 命名空间 public string K8sClusterIP { get; set; } // ClusterIP public string DiscoveredPorts { get; set; } // JSON 序列化的端口列表 public string Labels { get; set; } // K8s 标签 public string AssignedClusterId { get; set; } // 分配的集群 ID public int Status { get; set; } // 状态 } ``` --- ## 4. JWT 认证集成 ### 概述 网关解析 JWT Token,提取租户和用户信息,转换为下游服务可用的 HTTP 头。 ### 配置 **文件**: `src/Config/JwtConfig.cs` ```csharp public class JwtConfig { public string Authority { get; set; } = string.Empty; // 认证服务器地址 public string Audience { get; set; } = string.Empty; // 受众 public bool ValidateIssuer { get; set; } = true; // 验证签发者 public bool ValidateAudience { get; set; } = true; // 验证受众 } ``` **配置示例** (`src/appsettings.json`): ```json { "Jwt": { "Authority": "https://your-auth-server.com", "Audience": "fengling-gateway", "ValidateIssuer": true, "ValidateAudience": true } } ``` ### JWT 转换中间件 **文件**: `src/Middleware/JwtTransformMiddleware.cs` ```csharp public async Task InvokeAsync(HttpContext context) { var authHeader = context.Request.Headers["Authorization"].FirstOrDefault(); if (!string.IsNullOrEmpty(authHeader) && authHeader.StartsWith("Bearer ")) { var token = authHeader.Substring("Bearer ".Length).Trim(); var jwtToken = jwtHandler.ReadJwtToken(token); // 提取声明并转换为 HTTP 头 var tenantId = jwtToken.Claims.FirstOrDefault(c => c.Type == "tenant")?.Value; var userId = jwtToken.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value; context.Request.Headers["X-Tenant-Id"] = tenantId; context.Request.Headers["X-User-Id"] = userId; context.Request.Headers["X-User-Name"] = userName; context.Request.Headers["X-Roles"] = string.Join(",", roles); } await _next(context); } ``` ### JWT 声明到 HTTP 头映射 | JWT 声明类型 | HTTP 头 | 说明 | |--------------|---------|------| | `tenant` | `X-Tenant-Id` | 租户标识 | | `ClaimTypes.NameIdentifier` | `X-User-Id` | 用户 ID | | `ClaimTypes.Name` | `X-User-Name` | 用户名 | | `ClaimTypes.Role` | `X-Roles` | 角色列表(逗号分隔) | --- ## 5. 外部 API 和服务连接 ### CORS 配置 **文件**: `src/appsettings.json` ```json { "Cors": { "AllowedOrigins": [ "http://localhost:5173", "http://127.0.0.1:5173", "http://localhost:5174" ], "AllowAnyOrigin": false } } ``` ### 健康检查端点 **文件**: `src/Program.cs` ```csharp app.MapGet("/health", () => Results.Ok(new { status = "healthy", timestamp = DateTime.UtcNow })); ``` ### 下游服务健康检查 **文件**: `src/Config/DatabaseClusterConfigProvider.cs` ```csharp HealthCheck = new HealthCheckConfig { Active = new ActiveHealthCheckConfig { Enabled = true, Interval = TimeSpan.FromSeconds(30), Timeout = TimeSpan.FromSeconds(5), Path = "/health" } } ``` ### 动态代理配置 **文件**: `src/DynamicProxy/DynamicProxyConfigProvider.cs` 实现 `IProxyConfigProvider` 接口,从数据库动态加载路由和集群配置: ```csharp public class DynamicProxyConfigProvider : IProxyConfigProvider { public IProxyConfig GetConfig() => _config; public void UpdateConfig() { var routes = _routeProvider.GetRoutes(); var clusters = _clusterProvider.GetClusters(); _config = new InMemoryProxyConfig(routes, clusters, ...); } } ``` --- ## 6. 集成架构图 ``` ┌─────────────────────────────────────────────────────────────┐ │ 客户端请求 │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ YARP Gateway │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ 中间件管道 │ │ │ │ CORS → JWT转换 → 租户路由 → Controllers → Proxy │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────────────────────┐ │ │ │RouteCache│ │ConfigProv│ │LoadBalancingPolicy │ │ │ └────┬─────┘ └────┬─────┘ └──────────────────────────┘ │ └───────┼─────────────┼────────────────────────────────────────┘ │ │ ┌───────────────┼─────────────┼───────────────┐ │ │ │ │ ▼ ▼ ▼ ▼ ┌───────────────┐ ┌───────────┐ ┌───────────┐ ┌───────────────────┐ │ PostgreSQL │ │ Redis │ │ K8s │ │ Auth Server │ │ │ │ │ │ API │ │ (JWT) │ │ - 租户配置 │ │ - 分布式锁│ │ │ │ │ │ - 路由配置 │ │ - 缓存 │ │ - Service │ │ - Token 签发 │ │ - 服务实例 │ │ │ │ - Pod │ │ - 声明信息 │ │ - NOTIFY机制 │ │ │ │ │ │ │ └───────────────┘ └───────────┘ └───────────┘ └───────────────────┘ │ │ LISTEN/NOTIFY ▼ ┌───────────────────────────────────────────────────────┐ │ 配置变更监听器 │ │ PgSqlConfigChangeListener + FallbackPolling │ └───────────────────────────────────────────────────────┘ ``` --- ## 7. 配置热更新流程 ``` 数据库配置变更 │ ▼ DbContext.SaveChangesAsync() │ ▼ NOTIFY gateway_config_changed │ ▼ PgSqlConfigChangeListener.OnNotification() │ ▼ RouteCache.ReloadAsync() │ ▼ DynamicProxyConfigProvider.UpdateConfig() │ ▼ YARP 配置生效(无需重启) ``` **兜底机制**: 每 5 分钟检查版本号,防止 NOTIFY 丢失导致配置不一致。