fengling-gateway/.planning/codebase/INTEGRATIONS.md
2026-02-28 15:44:16 +08:00

374 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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<GatewayDbContext>(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<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;
return ConnectionMultiplexer.Connect(connectionOptions);
});
```
### 分布式锁实现
**接口**: `IRedisConnectionManager`
```csharp
public interface IRedisConnectionManager
{
IConnectionMultiplexer GetConnection();
Task<IDisposable> AcquireLockAsync(string key, TimeSpan? expiry = null);
Task<T> ExecuteInLockAsync<T>(string key, Func<Task<T>> func, TimeSpan? expiry = null);
}
```
**锁机制特性**:
- 基于键值对的分布式锁
- 自动过期时间(默认 10 秒)
- 指数退避重试策略
- Lua 脚本安全释放锁
---
## 3. Kubernetes 服务发现集成
### 概述
通过自定义的 Fengling.ServiceDiscovery 包实现 Kubernetes 服务自动发现,将 K8s Service 自动注册为网关后端服务。
### 配置
**文件**: `src/Program.cs`
```csharp
// 添加 Kubernetes 服务发现
var useInClusterConfig = builder.Configuration.GetValue<bool>("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 丢失导致配置不一致。