- Create MigrationTool console app for exporting DB config to K8s YAML - Support dry-run mode and validation - Add Npgsql and YamlDotNet dependencies
10 KiB
10 KiB
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | key_links | |||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 06-gateway-plugin-research | 02 | execute | 1 |
|
true |
|
|
|
计划 02:YARP 插件集成
将插件系统集成到 YARP 反向代理管道,实现 Transform 方式的请求/响应处理,以及通过 Metadata 驱动的插件启用机制。目的: 实现 PLUG-03 - 插件隔离与生命周期管理,完成网关插件化。 产出: 可工作的 YARP 插件集成系统,含单元测试。
<execution_context> @/Users/mac/.config/opencode/get-shit-done/workflows/execute-plan.md @/Users/mac/.config/opencode/get-shit-done/templates/summary.md </execution_context>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/006-gateway-plugin-research/006-RESEARCH.md @src/Fengling.Gateway.Plugin.Abstractions/IGatewayPlugin.cs @src/yarpgateway/Plugins/PluginHost.cs @src/yarpgateway/Program.cs ## 现有插件接口// Fengling.Gateway.Plugin.Abstractions
public interface IGatewayPlugin
{
string Name { get; }
string Version { get; }
string? Description { get; }
Task OnLoadAsync();
Task OnUnloadAsync();
}
public interface IRequestPlugin : IGatewayPlugin
{
Task<HttpContext?> OnRequestAsync(HttpContext context);
Task<HttpContext?> OnRouteMatchedAsync(HttpContext context, RouteConfig route);
Task<HttpContext?> OnForwardingAsync(HttpContext context, HttpRequestMessage request);
}
public interface IResponsePlugin : IGatewayPlugin
{
Task OnBackendResponseAsync(HttpContext context, HttpResponseMessage response);
Task OnResponseFinalizingAsync(HttpContext context);
}
public interface IRouteTransformPlugin : IGatewayPlugin
{
Task<RouteConfig> TransformRouteAsync(RouteConfig original, HttpContext context);
}
YARP Transform 接口
// YARP Transform
public interface IRequestTransform
{
Task ApplyAsync(RequestTransformContext context);
}
public interface IResponseTransform
{
Task ApplyAsync(ResponseTransformContext context);
}
public interface IClusterDestinationsTransform
{
Task ApplyAsync(ClusterDestinationsContext context);
}
Task 1: 创建 PluginTransformProvider
src/yarpgateway/Plugins/PluginTransformProvider.cs,
tests/YarpGateway.Tests/Unit/Plugins/PluginTransformProviderTests.cs
- Test 1: 从路由 Metadata 发现启用的插件
- Test 2: 按 PluginOrder 排序
- Test 3: 创建 RequestTransform
- Test 4: 创建 ResponseTransform
创建 YARP Transform 提供者:
-
创建
PluginTransformProvider.cs:- 实现
IProxyConfigFilter或IDynamicRouteConfigProvider - 扫描路由 Metadata 中的 "Plugins" 键
- 从 PluginHost 获取已加载的插件
- 按 "PluginOrder" 排序
- 实现
-
核心逻辑:
public class PluginTransformProvider : IProxyConfigFilter
{
private readonly PluginHost _pluginHost;
public async Task ApplyTransformAsync(HttpContext context, RouteConfig route)
{
var pluginIds = route.Metadata?["Plugins"]?.Split(',');
if (pluginIds == null) return;
var order = route.Metadata?["PluginOrder"]?.Split(',') ?? pluginIds;
var orderedPlugins = order.Select(id => pluginIds.IndexOf(id))
.Select(i => _pluginHost.GetPlugins().ElementAt(i));
foreach (var plugin in orderedPlugins.OfType<IRequestPlugin>())
{
await plugin.OnRouteMatchedAsync(context, route);
}
}
}
- 创建 Request/Response Transform 类:
PluginRequestTransform- 调用 IRequestPluginPluginResponseTransform- 调用 IResponsePluginPluginRouteTransform- 调用 IRouteTransformPlugin dotnet test tests/YarpGateway.Tests --filter "FullyQualifiedName~PluginTransformProviderTests" --no-build- PluginTransformProvider 存在
- Transform 测试通过
- 创建
DestinationSelector.cs:- 实现 IClusterDestinationsTransform
- 在路由匹配后、负载均衡前执行
- 可以根据 HttpContext 修改可用目标列表
public class DestinationSelector : IClusterDestinationsTransform
{
private readonly PluginHost _pluginHost;
public async Task ApplyAsync(ClusterDestinationsContext context)
{
var httpContext = context.HttpContext;
// 获取路由上启用的插件
var routeConfig = httpContext.GetRouteConfig();
var pluginIds = routeConfig?.Metadata?["Plugins"]?.Split(',');
if (pluginIds == null) return;
foreach (var pluginId in pluginIds)
{
var plugin = _pluginHost.GetPlugin(pluginId);
if (plugin is IRequestPlugin requestPlugin)
{
// 让插件过滤/修改目标列表
var availableDestinations = context.Destinations.ToList();
// 插件逻辑可以修改 availableDestinations
context.Destinations = availableDestinations;
}
}
}
}
- 注册到 YARP:
builder.Services.AddSingleton<IClusterDestinationsTransform, DestinationSelector>();
dotnet test tests/YarpGateway.Tests --filter "FullyQualifiedName~DestinationSelectorTests" --no-build
- DestinationSelector 存在
- 目标选择逻辑工作正常
Task 3: 创建 PluginConfigWatcher (Console DB 通知)
src/yarpgateway/Plugins/PluginConfigWatcher.cs,
tests/YarpGateway.Tests/Unit/Plugins/PluginConfigWatcherTests.cs
- Test 1: 监听配置变更通知
- Test 2: 触发插件重载
- Test 3: 处理通知失败
创建配置监听器,监听 Console DB 通知:
- 创建
PluginConfigWatcher.cs:- 实现
IHostedService或使用现有的PgSqlConfigChangeListener - 监听插件配置变更频道(如
plugin_config_changed) - 触发 PluginHost 重载
- 实现
public class PluginConfigWatcher : BackgroundService
{
private readonly PluginHost _pluginHost;
private readonly NpgsqlConnection _connection;
private const string Channel = "plugin_config_changed";
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await _connection.OpenAsync(stoppingToken);
await _connection.ListenAsync(Channel, stoppingToken);
await foreach (var notification in _connection.Notifications(stoppingToken))
{
// 解析通知,获取需要重载的插件
var payload = JsonSerializer.Deserialize<PluginConfigPayload>(notification.Payload);
await _pluginHost.ReloadAsync(payload.PluginId);
}
}
}
- 注册到 DI:
builder.Services.AddHostedService<PluginConfigWatcher>();
dotnet test tests/YarpGateway.Tests --filter "FullyQualifiedName~PluginConfigWatcherTests" --no-build
- PluginConfigWatcher 存在
- 监听器正确响应通知
Task 4: 更新 Program.cs 集成插件系统
src/yarpgateway/Program.cs
- Test 1: 插件系统在启动时初始化
- Test 2: Transform 被正确应用
更新 Program.cs 集成插件系统:
- 注册插件服务:
// 插件目录
var pluginDirectory = configuration.GetValue<string>("Plugin:Directory") ?? "plugins";
// 插件主机
builder.Services.AddSingleton(sp => new PluginHost(pluginDirectory));
// Transform 提供者
builder.Services.AddSingleton<IProxyConfigFilter, PluginTransformProvider>();
// 目标选择器
builder.Services.AddSingleton<IClusterDestinationsTransform, DestinationSelector>();
// 配置监听器
builder.Services.AddHostedService<PluginConfigWatcher>();
- 初始化插件:
var app = builder.Build();
// 启动时加载插件
var pluginHost = app.Services.GetRequiredService<PluginHost>();
await pluginHost.LoadAllAsync();
- 配置 YARP 使用 Transform:
builder.Services.AddReverseProxy()
.AddTransforms<PluginTransformProvider>();
dotnet build src/yarpgateway/YarpGateway.csproj
- Program.cs 正确集成插件系统
- 构建通过
1. dotnet build src/yarpgateway/YarpGateway.csproj 无错误
2. dotnet test tests/YarpGateway.Tests --filter "FullyQualifiedName~Plugin" 通过
3. 插件可通过 Metadata 启用
4. Transform 正确应用到请求/响应
<success_criteria>
- 插件通过 YARP Transform 管道处理请求
- 目标选择在路由匹配后执行
- 插件通过 Metadata 启用
- Console DB 通知可触发插件重载
- 所有单元测试通过 </success_criteria>