概述
silky 框架的 RPC 过滤器管道设计参考了 ASP.NET Core MVC 的过滤器系统,通过状态机驱动各类型过滤器按固定顺序执行。框架在服务端(LocalInvoker)和客户端(RemoteInvoker)分别维护独立的过滤器管道。
本文聚焦于过滤器管道的运行时执行机制,说明各组件如何协作、过滤器的执行顺序和短路规则。
过滤器类型体系
服务端过滤器接口
| 接口 | 执行时机 | 说明 |
|---|---|---|
IServerAuthorizationFilter / IAsyncServerAuthorizationFilter | 最早,Action 执行之前 | 鉴权过滤器,可通过设置 Result 短路整个管道 |
IServerExceptionFilter / IAsyncServerExceptionFilter | 异常发生后 | 捕获并处理 Action 或 ResultFilter 抛出的异常 |
IServerFilter / IAsyncServerFilter | Action 执行前后 | 通用 Action 过滤器,可在执行前短路 |
IServerResultFilter / IAsyncServerResultFilter | Result 写出前后 | 可修改或替换执行结果 |
IAlwaysRunServerResultFilter / IAsyncAlwaysRunServerResultFilter | 无论是否短路,始终执行 | 用于清理等保证执行的操作 |
客户端过滤器接口
与服务端对称,前缀替换为 Client:
| 接口 | 说明 |
|---|---|
IClientFilter / IAsyncClientFilter | 客户端 Action 过滤器 |
IClientExceptionFilter / IAsyncClientExceptionFilter | 客户端异常过滤器 |
IClientResultFilter / IAsyncClientResultFilter | 客户端结果过滤器 |
IAlwaysRunClientResultFilter | 始终执行的客户端结果过滤器 |
自定义过滤器的方式
继承 ServerFilterAttribute 或 ClientFilterAttribute(均实现了对应的同步 + 异步接口),重写所需方法:
[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Method,
AllowMultiple = true, Inherited = true)]
public class TimingServerFilter : ServerFilterAttribute
{
private Stopwatch _sw;
public override void OnActionExecuting(ServerInvokeExecutingContext context)
{
_sw = Stopwatch.StartNew();
}
public override void OnActionExecuted(ServerInvokeExecutedContext context)
{
_sw.Stop();
// context.ServiceEntry.Id 可获取服务条目标识
Console.WriteLine($"Elapsed: {_sw.ElapsedMilliseconds}ms");
}
}
过滤器解析:从 ServiceEntry 到执行列表
FilterDescriptor 的生成
在 ServiceEntry 构造时,框架从接口和方法上收集所有 IFilterMetadata 特性,按以下规则构建 FilterDescriptor 列表:
- 接口级过滤器:
FilterScope.Global(实际为接口级范围) - 方法级过滤器:
FilterScope.Action - 通过
IOrderedFilter.Order值决定同类过滤器内的执行顺序(值越小越先执行)
DefaultFilterProvider
DefaultFilterProvider(ISingletonDependency)在每次调用时将 FilterDescriptor 实例化为可执行的 FilterItem:
public class DefaultFilterProvider : IFilterProvider
{
public void ProviderFilters(List<FilterItem> filterItems)
{
foreach (var filterItem in filterItems)
{
var filter = filterItem.Descriptor.Filter;
if (filter is IServerFilterFactory factory)
{
// 工厂型过滤器:每次从 DI 容器创建新实例
filterItem.Filter = factory.CreateInstance(serviceProvider);
filterItem.IsReusable = factory.IsReusable;
}
else
{
// 特性型过滤器:直接复用特性实例
filterItem.Filter = filter;
filterItem.IsReusable = true;
}
}
}
}
服务端过滤器管道执行顺序
LocalInvokerBase 通过状态机(State 枚举驱动的 while 循环)串联各过滤器阶段。以下是完整的执行顺序:
InvokeBegin
│
▼
① AuthorizationFilter(鉴权)
│ 设置 Result → AuthorizationShortCircuit → ⑤ AlwaysRunResultFilter
│ 通过 ↓
▼
② ExceptionFilter 注册(包裹后续所有阶段)
│
▼
③ ActionFilter.OnActionExecuting
│ 设置 Result → 短路,跳过 ActionInside → ④ ActionFilter.OnActionExecuted
│ 通过 ↓
▼
ActionInside(调用实际服务方法:ObjectMethodExecutor.Execute)
│
▼
③' ActionFilter.OnActionExecuted(逆序执行)
│ 如有异常 → ② ExceptionFilter 处理
▼
④ ResultFilter.OnResultExecuting
▼
WriteResult(将 _result 写回 ServiceEntryContext)
▼
④' ResultFilter.OnResultExecuted(逆序执行)
▼
⑤ AlwaysRunResultFilter(无论是否短路,始终执行)
▼
InvokeEnd(完成)
FilterCursor 工作原理
FilterCursor 是轻量的游标结构体,在 _filters 数组上按类型逐步推进:
public FilterCursorItem<TFilter?, TFilterAsync?> GetNextFilter<TFilter, TFilterAsync>()
{
while (_index < _filters.Length)
{
var filter = _filters[_index] as TFilter;
var filterAsync = _filters[_index] as TFilterAsync;
_index += 1;
if (filter != null || filterAsync != null)
return new FilterCursorItem<TFilter?, TFilterAsync?>(filter, filterAsync);
}
return default;
}
每个阶段开始时调用 _cursor.Reset() 重置游标,确保遍历同一过滤器列表的不同类型切面。
短路规则
- Authorization 短路:任一
IServerAuthorizationFilter设置了context.Result,管道跳过 ExceptionFilter 和 ActionFilter,直接进入AlwaysRunResultFilter - Action 短路:
OnActionExecuting中设置了context.Result,跳过实际方法执行,直接进入OnActionExecuted(Canceled = true) - 异步过滤器短路:
IAsyncServerFilter.OnActionExecutionAsync若未调用next(),则_serviceEntryInvokeExecutedContext.Canceled = true
客户端过滤器管道
RemoteInvoker(继承 RemoteInvokerBase)对称地实现客户端过滤器管道。执行顺序与服务端一致,主要差异:
- ActionInside 对应发送 RPC 消息(
ITransportClient.SendAsync),而非调用本地方法 - 使用
ClientInvokeExecutingContext/ClientInvokeExecutedContext替代服务端上下文 - 客户端鉴权过滤器用于在发出调用前进行权限检查
// 客户端过滤器示例:在 RPC 调用前追加自定义请求头
public class AppendTraceHeaderClientFilter : ClientFilterAttribute
{
public override void OnActionExecuting(ClientInvokeExecutingContext context)
{
RpcContext.Context.SetInvokeAttachment("X-Custom-Trace", Guid.NewGuid().ToString());
}
}
过滤器注册方式汇总
| 注册方式 | 作用范围 | 示例 |
|---|---|---|
| 特性标注在接口上 | 接口下所有服务条目 | [TimingServerFilter] 标注 IOrderAppService |
| 特性标注在方法上 | 单个服务条目 | [TimingServerFilter] 标注 GetByIdAsync |
实现 IServerFilterFactory | 可通过 DI 容器注入依赖 | 工厂类创建带依赖的过滤器实例 |
注意:过滤器特性均设置了
AllowMultiple = true,同一目标可标注多个同类过滤器;通过Order属性控制同类过滤器的执行顺序(值小的先执行)。
