fengling-gateway/.planning/phases/006-gateway-plugin-research/006-02-PLAN.md
movingsam 52eba07097 feat: add MigrationTool for gateway config migration (IMPL-7)
- Create MigrationTool console app for exporting DB config to K8s YAML
- Support dry-run mode and validation
- Add Npgsql and YamlDotNet dependencies
2026-03-08 00:35:04 +08:00

10 KiB
Raw Blame History

phase plan type wave depends_on files_modified autonomous requirements must_haves key_links
06-gateway-plugin-research 02 execute 1
006-01
true
PLUG-03
truths artifacts
插件通过路由 Metadata 启用
请求 Transform 轻量处理请求
目标选择在路由匹配后执行
插件按配置顺序执行
path provides
src/yarpgateway/Plugins/PluginTransformProvider.cs YARP Transform 提供者
path provides
src/yarpgateway/Plugins/YarpPluginMiddleware.cs 插件管道集成
path provides
src/yarpgateway/Plugins/PluginConfigWatcher.cs Console DB 通知监听
path provides
tests/YarpGateway.Tests/Unit/Plugins/YarpIntegrationTests.cs 集成测试
from to via
PluginTransformProvider PluginHost 获取已加载插件
from to via
YarpPluginMiddleware PluginTransformProvider 应用 Transform
from to via
PluginConfigWatcher PluginHost 触发重载

计划 02YARP 插件集成

将插件系统集成到 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 提供者:
  1. 创建 PluginTransformProvider.cs

    • 实现 IProxyConfigFilterIDynamicRouteConfigProvider
    • 扫描路由 Metadata 中的 "Plugins" 键
    • 从 PluginHost 获取已加载的插件
    • 按 "PluginOrder" 排序
  2. 核心逻辑:

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);
        }
    }
}
  1. 创建 Request/Response Transform 类:
    • PluginRequestTransform - 调用 IRequestPlugin
    • PluginResponseTransform - 调用 IResponsePlugin
    • PluginRouteTransform - 调用 IRouteTransformPlugin dotnet test tests/YarpGateway.Tests --filter "FullyQualifiedName~PluginTransformProviderTests" --no-build
    • PluginTransformProvider 存在
    • Transform 测试通过
Task 2: 创建目标选择器 (DestinationSelector) src/yarpgateway/Plugins/DestinationSelector.cs, tests/YarpGateway.Tests/Unit/Plugins/DestinationSelectorTests.cs - Test 1: OnRouteMatchedAsync 选择目标 - Test 2: 根据上下文修改目标列表 - Test 3: 特殊租户路由到特殊目标 创建目标选择器,用于 OnRouteMatchedAsync 阶段:
  1. 创建 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;
            }
        }
    }
}
  1. 注册到 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 通知:
  1. 创建 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);
        }
    }
}
  1. 注册到 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 集成插件系统:
  1. 注册插件服务:
// 插件目录
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>();
  1. 初始化插件:
var app = builder.Build();

// 启动时加载插件
var pluginHost = app.Services.GetRequiredService<PluginHost>();
await pluginHost.LoadAllAsync();
  1. 配置 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>
完成后创建 `.planning/phases/006-gateway-plugin-research/006-02-SUMMARY.md`