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

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

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

概述

服务治理(Service Governance) 是 silky 框架保障微服务可靠性的核心机制。框架通过 Polly 弹性库在客户端(调用方)和服务端(提供方)两侧分别实现多层策略保护,主要包括:

  • 超时控制:防止慢接口拖垮调用方
  • 重试机制:对暂时性故障自动重试
  • 熔断器:防止雪崩效应
  • 降级(Fallback):熔断或超时后返回兜底结果
  • 负载均衡:分流策略决定请求路由到哪个实例
  • 并发保护:限制单实例最大并发处理数

这些治理参数均通过 GovernanceOptions 统一配置,支持全局默认值、接口级覆盖和方法级覆盖三层配置。


GovernanceOptions — 治理参数

GovernanceOptions 是所有服务治理参数的数据模型,挂载在 ServiceEntry 和 ServiceEntryDescriptor 上:

参数名类型默认值说明
ShuntStrategyShuntStrategyPolling负载均衡策略
TimeoutMillSecondsint5000调用超时时间(毫秒)
EnableCachingInterceptorbooltrue是否启用缓存拦截
EnableCircuitBreakerbooltrue是否启用熔断
ExceptionsAllowedBeforeBreakingint3触发熔断前允许的最大异常次数
BreakerSecondsint60熔断持续时间(秒)
RetryTimesint3最大重试次数
RetryIntervalMillSecondsint50重试间隔(毫秒)
MaxConcurrentHandlingCountint50服务端最大并发处理数(设为 0 表示不限制)
AddressFuseSleepDurationSecondsint60端点被标识为不健康后的熔断恢复等待时间(秒)
UnHealthAddressTimesAllowedBeforeRemovingint3端点被标识为不健康多少次后从服务列表移除

注意:ProhibitExtranet(禁止外网访问)不在 GovernanceOptions 中,只能通过 [Governance] 特性或 [ProhibitExtranet] 特性在接口/方法上单独设置,无法在 appsettings 的 Governance: 配置节内全局配置。


三层配置优先级

治理参数按以下优先级生效(高优先级覆盖低优先级):

方法级([Governance] 特性)
    > 接口级([Governance] 特性,继承到所有方法)
        > 全局默认值(appsettings 中 Governance 配置节)

全局默认值(配置文件)

# appsettings.yml
Governance:
  TimeoutMillSeconds: 10000    # 全局超时 10s
  RetryTimes: 0                # 全局关闭重试
  EnableCircuitBreaker: false  # 全局关闭熔断

接口/方法级覆盖(特性)

[ServiceRoute]
[Governance(TimeoutMillSeconds = 3000, EnableCircuitBreaker = false)]  // 接口级:覆盖全局
public interface IOrderAppService
{
    // 方法级:覆盖接口级
    [Governance(TimeoutMillSeconds = 500, ShuntStrategy = ShuntStrategy.HashAlgorithm)]
    Task<OrderDto> GetByIdAsync(long id);

    // 使用接口级设置(3000ms 超时)
    Task<OrderDto> CreateAsync(CreateOrderInput input);
}

ReConfiguration 逻辑

ServiceEntry 在构造时会调用 ReConfiguration(governanceProvider) 方法,按照优先级将特性中的参数覆盖到 GovernanceOptions 上:

private void ReConfiguration(IGovernanceProvider governanceProvider)
{
    if (governanceProvider == null) return;

    // 如果方法上标注了 [Governance] 特性,用特性参数覆盖全局配置
    if (governanceProvider.TimeoutMillSeconds > 0)
        GovernanceOptions.TimeoutMillSeconds = governanceProvider.TimeoutMillSeconds;

    if (governanceProvider.ShuntStrategy != ShuntStrategy.Polling)
        GovernanceOptions.ShuntStrategy = governanceProvider.ShuntStrategy;

    // ... 其他参数类似
}

超时控制

客户端超时

由 DefaultTimeoutInvokePolicyProvider 创建 Polly TimeoutPolicy:

Policy.TimeoutAsync(
    TimeSpan.FromMilliseconds(serviceEntryDescriptor.GovernanceOptions.TimeoutMillSeconds),
    TimeoutStrategy.Optimistic  // 协作式取消(通过 CancellationToken)
);
  • TimeoutStrategy.Optimistic:通过传递 CancellationToken 协作取消,性能较好但需要被调用方响应取消信号
  • 超时触发后,抛出 Polly.Timeout.TimeoutRejectedException,该异常会被计入重试和熔断统计

服务端超时

服务端同样有超时策略(IHandlePolicyProvider),防止业务实现方法无限运行占用服务端线程资源。


重试机制

重试策略构建

由 InvokeFailoverPolicyProviderBase 系列实现,内置两种重试场景:

场景 1:普通故障重试(DefaultRetryInvokePolicyProvider)

Policy<object?>
    .Handle<Exception>(ex =>
    {
        // 只对非业务异常进行重试
        return !ex.IsFriendlyException()
            && !ex.IsNotFindServiceError()
            && !ex.IsFrameworkException();
    })
    .WaitAndRetryAsync(
        retryCount: governanceOptions.RetryTimes,
        sleepDurationProvider: (retryAttempt, context) =>
            TimeSpan.FromMilliseconds(governanceOptions.RetryIntervalMillSeconds),
        onRetry: (outcome, timespan, retryAttempt, context) =>
        {
            Logger.LogWarning("Retry {retryAttempt} for {serviceEntryId}...", retryAttempt, serviceEntryId);
        }
    );

场景 2:服务端并发溢出故障转移(OverflowServerHandleFailoverPolicyProvider)

当服务端返回 OverflowMaxServerHandleException(并发超限)时,客户端自动切换到另一个健康实例重试:

Policy<object?>
    .Handle<OverflowMaxServerHandleException>()
    .RetryAsync(
        retryCount: availableEndpointCount - 1, // 最多尝试所有可用实例
        onRetry: (outcome, retryAttempt, context) =>
        {
            // 将当前失败的端点加入"本次调用排除列表"
            excludeEndpoints.Add(context[PollyContextNames.SelectedEndpoint]);
        }
    );

重试的注意事项

Warning

重试只对幂等操作安全。对于写操作(如创建订单),应将 RetryTimes = 0 关闭重试,或通过业务层实现幂等控制,否则重试会导致数据重复。


熔断器(Circuit Breaker)

工作原理

熔断器是一个状态机,有三个状态:

         N次失败超过阈值
Closed ─────────────────▶ Open(拒绝所有请求)
  ▲                           │
  │    BreakerSeconds 过后     │
  │                           ▼
  └─────── Half-Open(放行一个探测请求)
                   │ 成功
                   │ 失败 → Open
状态说明
Closed(关闭)正常状态,请求正常通过
Open(打开)熔断状态,所有请求立即返回 CircuitBreakerException,不发起 RPC
Half-Open(半开)熔断恢复探测,放行一个请求测试服务是否恢复

熔断器策略实现

Policy
    .Handle<Exception>(ex =>
    {
        // 只有「真正的」异常才触发熔断
        // 友好异常、找不到服务、框架异常、服务端异常 → 不触发熔断
        var isNotCircuitBreakerException =
            ex.IsFriendlyException()      // UserFriendlyException
            || ex.IsNotFindServiceError() // 未找到服务
            || ex.IsFrameworkException()  // 框架内部异常
            || ex.IsNotImplemented()      // 未实现
            || ex.IsServerException();    // 服务端异常

        return !isNotCircuitBreakerException;
    })
    .CircuitBreakerAsync(
        exceptionsAllowedBeforeBreaking: governanceOptions.ExceptionsAllowedBeforeBreaking,
        durationOfBreak: TimeSpan.FromSeconds(governanceOptions.BreakerSeconds),
        onBreak: (ex, timespan) => { /* 熔断开启事件 */ },
        onReset: () => { /* 熔断恢复事件 */ }
    );

策略缓存:熔断器策略以 serviceEntryId 为 key 缓存(ConcurrentDictionary),同一服务条目在整个应用生命周期共享同一个熔断器实例,因此熔断状态是持久有效的(不随每次请求重建策略而重置)。

禁用熔断

对于某些明确不需要熔断保护的服务条目(如查询接口),可以关闭熔断:

[Governance(EnableCircuitBreaker = false)]
Task<OrderDto> GetByIdAsync(long id);

降级(Fallback)

当服务调用最终失败(超过重试次数,或熔断器打开)后,框架会尝试调用开发者预先定义的 Fallback 方法,返回兜底结果。

定义 Fallback

// 1. 在接口方法上标注 [Fallback],指定 Fallback 类型
[ServiceRoute]
public interface IOrderAppService
{
    [Fallback(typeof(OrderFallback))]
    Task<OrderDto> GetByIdAsync(long id);
}

// 2. 实现 Fallback 类(必须注册到 IoC 容器)
public class OrderFallback : IScopedDependency
{
    // Fallback 方法签名必须与原方法一致
    public Task<OrderDto> GetByIdAsync(long id)
    {
        // 返回缓存结果、默认值或空对象
        return Task.FromResult(new OrderDto { Id = id, Status = "Unknown" });
    }
}

Fallback 触发条件

Policy<object>.Handle<Exception>(ex =>
{
    // 只对"需要 Fallback"的异常触发降级
    // 实现了 INotNeedFallback 的异常不降级(如业务友好异常)
    return !(ex is INotNeedFallback);
})
.FallbackAsync(
    fallbackAction: async (ctx, token) =>
        await _fallbackInvoker.Invoke(serviceEntry, parameters)
);

Fallback 与熔断的组合

Polly 策略通过 WrapAsync 嵌套,执行顺序从外到内:

FallbackPolicy(最外层:兜底)
    └── CircuitBreakerPolicy(熔断保护)
            └── RetryPolicy(重试)
                    └── TimeoutPolicy(超时,最内层)
                            └── 实际 RPC 调用

当内层策略全部失败后,依次向外传播,直到 FallbackPolicy 捕获并调用降级方法。


服务端并发保护

并发数限制

服务端通过 MaxConcurrentHandlingCount 限制单个服务条目的最大并发处理数:

// DefaultServerMessageReceivedHandler 中
if (getServerInstanceHandleInfo.AllowMaxConcurrentCount > 0
    && getServerInstanceHandleInfo.ConcurrentCount > getServerInstanceHandleInfo.AllowMaxConcurrentCount)
{
    throw new OverflowMaxServerHandleException(
        $"Exceeds the maximum allowable processing concurrency. " +
        $"Current: {getServerInstanceHandleInfo.ConcurrentCount}");
}

客户端收到 OverflowMaxServerHandleException 后,由 OverflowServerHandleFailoverPolicyProvider 将请求故障转移到另一个健康实例,而不是计入普通错误重试次数。

服务端自保护熔断(FuseProtection)

当 FuseProtection = true 时,服务端在 BreakerSeconds 内发生 ExceptionsAllowedBeforeBreaking 次未知异常后,触发服务端熔断,临时拒绝该服务条目的所有请求:

Governance:
  FuseProtection: true
  ExceptionsAllowedBeforeBreaking: 5
  FuseSleepDurationSeconds: 30

与客户端熔断的区别:

  • 客户端熔断:在调用方,阻止请求发出,快速失败
  • 服务端熔断:在提供方,阻止请求进入业务执行,保护数据库、文件系统等下游资源

禁止外网访问(ProhibitExtranet)

部分服务条目只允许内部 RPC 调用,不应通过网关的 HTTP 接口对外暴露。通过 ProhibitExtranet = true 实现:

[Governance(ProhibitExtranet = true)]
Task<string> GetInternalSecretAsync();

或使用专用特性:

[ProhibitExtranet]
Task<string> GetInternalSecretAsync();

网关在路由匹配时,如果请求来自外部(非 RPC 内部调用),且目标服务条目设置了 ProhibitExtranet = true,则返回 403 Forbidden。


全局治理配置示例

Governance:
  ShuntStrategy: Polling          # 负载均衡策略(Polling/Random/HashAlgorithm)
  TimeoutMillSeconds: 5000        # 超时 5 秒
  RetryTimes: 3                   # 重试 3 次
  RetryIntervalMillSeconds: 50    # 重试间隔 50ms
  EnableCircuitBreaker: true      # 启用熔断
  ExceptionsAllowedBeforeBreaking: 3  # 3 次异常触发熔断
  BreakerSeconds: 60              # 熔断 60 秒
  EnableCachingInterceptor: true  # 启用缓存拦截
  FuseProtection: false           # 关闭服务端自保护熔断
  MaxConcurrentHandlingCount: 0   # 不限制并发数

治理能力全景图

调用方(客户端)                                  提供方(服务端)

 ┌─────────────────────────────────────┐      ┌─────────────────────────────────┐
 │  服务条目调用                          │      │  接收 RPC 请求                    │
 │                                     │      │                                 │
 │  Fallback Policy(兜底降级)           │      │  FuseProtection(服务端熔断)      │
 │      └── CircuitBreaker(客户端熔断)  │      │                                 │
 │              └── Retry(重试)        │ TCP  │  MaxConcurrentCount(并发保护)    │
 │                      └── Timeout    │─────▶│                                 │
 │                          (超时)     │      │  业务方法执行                      │
 │                                     │      │  (LocalInvoker + 过滤器管道)     │
 │  EndpointSelector(负载均衡)         │      │                                 │
 │  (Polling/Random/Hash/Appoint)     │      │  异常分类处理                      │
 │                                     │      │  (友好异常/验证异常/未知异常)       │
 └─────────────────────────────────────┘      └─────────────────────────────────┘
编辑当前页
Prev
RPC 服务端消息处理
Next
缓存拦截器