Silky微服务框架在线文档Silky微服务框架在线文档
首页
文档
配置
源码解析
博文
github
gitee
首页
文档
配置
源码解析
博文
github
gitee
  • 启动时

    • 主机的构建
    • 服务引擎
    • 模块
    • 服务与服务条目的解析
    • Silky服务主机
    • 依赖注入约定
    • RPC 服务代理
  • 运行时

    • 终结点与路由
    • 执行器与调度
    • 本地执行器与服务端过滤器
    • 远程执行器与 RPC 调用
    • RPC 服务端消息处理
    • 服务治理
    • 缓存拦截器
    • 分布式事务(TCC)
    • HTTP 网关请求管道
    • 过滤器执行管道
    • Polly 弹性策略管道
    • 端点健康监控

概述

在 silky 微服务集群中,当服务 A 需要调用服务 B 的接口时,服务 A 内部并不存在服务 B 接口的实现类。这意味着无法直接将服务 B 的接口注入到服务 A 的业务类中使用。

silky 通过 Castle.DynamicProxy(动态代理)解决这一问题:框架在启动时自动为所有远程服务接口(没有本地实现类的 [ServiceRoute] 接口)生成一个运行时代理对象,并将该代理注册到 IoC 容器中。注入方使用接口进行依赖注入时,实际得到的是该代理对象,调用接口方法时由代理内部通过 RPC 完成远程调用。


代理的识别

框架通过 ServiceHelper.FindServiceProxyTypes() 找到需要生成代理的接口类型——即所有没有本地实现类的服务接口:

public static IEnumerable<Type> FindServiceProxyTypes(ITypeFinder typeFinder)
{
    // FindAllServiceTypes 返回 (Type, IsLocal) 元组
    // 过滤出 IsLocal = false 的接口,即没有本地实现的远程服务接口
    var proxyTypes = FindAllServiceTypes(typeFinder)
        .Where(p => !p.Item2)   // Item2 = IsLocal
        .Select(p => p.Item1);  // Item1 = 接口类型
    return proxyTypes;
}

以订单微服务为例,假设 IAccountAppService 定义在 account.contract 包中,订单微服务依赖了该包但没有实现 IAccountAppService,那么 IAccountAppService 就会被识别为需要生成代理的远程服务接口。


代理注册

RpcProxyCollectionExtensions.AddRpcProxy() 在 RpcProxyModule.ConfigureServices() 阶段完成代理的创建与注册:

public static IServiceCollection AddRpcProxy(this IServiceCollection services)
{
    var serviceProxyTypes =
        ServiceHelper.FindServiceProxyTypes(EngineContext.Current.TypeFinder);

    foreach (var serviceServiceType in serviceProxyTypes)
    {
        AddAServiceClientProxy(services, serviceServiceType);
    }
    return services;
}

private static void AddAServiceClientProxy(this IServiceCollection services, Type type)
{
    // 代理拦截器类型:RpcClientProxyInterceptor(Castle 拦截器适配器)
    var rpcProxyInterceptorType = typeof(RpcClientProxyInterceptor);
    services.AddTransient(rpcProxyInterceptorType);

    // 将 Castle 拦截器包装为 Autofac/DI 可识别的类型
    var rpcInterceptorAdapterType =
        typeof(SilkyAsyncDeterminationInterceptor<>).MakeGenericType(rpcProxyInterceptorType);

    // 使用 Castle.DynamicProxy 创建接口代理并注册到 DI
    services.AddTransient(
        type,   // 注册的接口类型,例如 IAccountAppService
        serviceProvider => ProxyGeneratorInstance
            .CreateInterfaceProxyWithoutTarget(   // 不需要真实实现目标
                type,
                (IInterceptor)serviceProvider.GetRequiredService(rpcInterceptorAdapterType)
            )
    );
}

每个远程服务接口都被注册为**瞬时(Transient)**依赖。代理对象在注入时创建,不持有状态。


代理拦截器(RpcClientProxyInterceptor)

RpcClientProxyInterceptor 是代理的核心,当业务代码调用代理对象的任意方法时,Castle 拦截器会转入 InterceptAsync() 方法执行:

public class RpcClientProxyInterceptor : SilkyInterceptor, ITransientDependency
{
    private readonly IIdGenerator _idGenerator;
    private readonly IServiceEntryLocator _serviceEntryLocator;
    private readonly IServiceKeyExecutor _serviceKeyExecutor;
    private readonly IExecutor _executor;  // 统一执行器(本地/远程决策)

    public override async Task InterceptAsync(ISilkyMethodInvocation invocation)
    {
        // 1. 根据方法信息生成 ServiceEntryId,定位对应的 ServiceEntry
        var serviceEntryId = _idGenerator.GetDefaultServiceEntryId(invocation.Method);
        var serviceEntry = _serviceEntryLocator.GetServiceEntryById(serviceEntryId);

        if (serviceEntry == null)
        {
            throw new NotFindServiceEntryException(
                $"Could not find service entry with id '{serviceEntryId}'.");
        }

        try
        {
            if (invocation.Method.ReturnType == typeof(void)
                || invocation.Method.ReturnType.GenericTypeArguments.IsNullOrEmpty())
            {
                // 无返回值方法
                await _executor.Execute(serviceEntry, invocation.Arguments, _serviceKeyExecutor.ServiceKey);
            }
            else
            {
                // 有返回值方法:将返回值写回 ReturnValue 属性
                invocation.ReturnValue =
                    await _executor.Execute(serviceEntry, invocation.Arguments, _serviceKeyExecutor.ServiceKey);
            }
        }
        catch (Exception)
        {
            // 执行失败时,如果配置了 Fallback,走 Fallback(继续执行调用链)
            if (serviceEntry.FallbackMethodExecutor != null && serviceEntry.FallbackProvider != null)
            {
                await invocation.ProceedAsync();
                return;
            }
            throw;
        }
    }
}

拦截流程:

业务代码调用 IAccountAppService.GetByIdAsync(id)
    │
    ▼(Castle 拦截)
RpcClientProxyInterceptor.InterceptAsync()
    │  1. 生成 ServiceEntryId
    │  2. 通过 ServiceEntryLocator 查找 ServiceEntry
    │  3. 调用 IExecutor.Execute()
    ▼
DefaultExecutor.Execute()
    │  serviceEntry.IsLocal == false
    ▼(远程分支)
RemoteExecutor(Polly 策略 → LoadBalance → DotNetty RPC)
    │
    ▼
目标微服务实例处理并返回结果
    │
    ▼
invocation.ReturnValue = result(写回调用方)

完整调用示意

// 订单微服务中注入账户服务接口(无本地实现,实际注入的是代理对象)
public class OrderService : IOrderService, IScopedDependency
{
    private readonly IAccountAppService _accountService;

    // 构造函数注入,得到的是动态代理对象
    public OrderService(IAccountAppService accountService)
    {
        _accountService = accountService;
    }

    public async Task<OrderDto> CreateAsync(CreateOrderInput input)
    {
        // 这行代码看似是普通的本地调用,实际上触发了 Castle 拦截
        // 最终通过 DotNetty RPC 发送到账户微服务实例执行
        var account = await _accountService.GetByIdAsync(input.AccountId);

        // ... 业务逻辑
    }
}

代理模块依赖

RpcProxyModule 依赖 RpcModule 和 CastleModule:

  • RpcModule:提供 IServiceEntryLocator、IExecutor 等 RPC 核心组件
  • CastleModule:提供 Castle DynamicProxy 的 Autofac 适配层(SilkyAsyncDeterminationInterceptor)

包含 RpcProxyModule 的启动模块(DefaultGeneralHostModule、DefaultWebHostModule 等)在应用启动时自动完成代理注册,无需开发者显式配置。


ServiceKey — 多实现路由

当远程服务有多个实现(通过 [ServiceKey] 标注),调用方可通过 IServiceKeyExecutor 指定目标实现:

// 在 HTTP 请求头中传入 ServiceKey
// ServiceKey: v2

// 或在代码中手动指定(适用于服务内部调用)
using (serviceKeyExecutor.Change("v2"))
{
    var result = await _accountService.GetByIdAsync(id);
}

RpcClientProxyInterceptor 中的 _serviceKeyExecutor.ServiceKey 会读取当前 AsyncLocal 上下文中的 ServiceKey,并在 RpcContext 中透传到服务端,由服务端根据 ServiceKey 选择对应的实现类执行。


与直接使用 HTTP 客户端的区别

对比项silky RPC 代理HttpClient
协议DotNetty TCP(二进制帧)HTTP/1.1
性能更低延迟,长连接复用相对较高延迟
服务发现框架自动,基于注册中心需手动配置或引入服务发现组件
调用方式强类型接口注入,透明调用手动构造请求,需处理序列化
治理能力内置超时/重试/熔断/降级需手动集成 Polly
适用场景微服务内部通信对接外部 HTTP API
编辑当前页
Prev
依赖注入约定