ocelot熔断必须绑定httpclientfactory,因其依赖httpclient实例承载polly熔断器的状态(closed/open/halfopen)和滑动窗口计数;若跳过该工厂手动创建httpclient,会导致状态丢失、并发竞争与半开洪峰等雪崩风险。

直接在 Ocelot 中配限流和熔断,必须通过 Polly 策略注入到 HttpClient 生命周期里;手写中间件或每次请求 new 一个策略,会导致状态丢失、并发竞争、半开洪峰等线上雪崩风险。
为什么 Ocelot 的熔断必须绑定 HttpClientFactory
Ocelot 本身不内置熔断状态机,它依赖下游 HttpClient 实例承载 CircuitBreakerPolicy 的状态(Closed/Open/HalfOpen)和滑动窗口计数。若跳过 IHttpClientFactory,比如在 HttpRequestBuilderMiddleware 里临时 new HttpClient 并附加策略,会丢失连接复用、策略实例隔离、生命周期管理——所有熔断统计变成“每次请求重置”,等于没开。
正确路径是:Ocelot → 命名 HttpClient → AddPolicyHandler 注入 CircuitBreakerAsync 或 PolicyWrap。
- 必须用
services.AddHttpClient<iorderservice>("order-api")</iorderservice>定义命名客户端 - 策略注册必须在
AddPolicyHandler链中,不能在业务层调用ExecuteAsync -
CircuitBreakerAsync必须传exceptionsAllowedBeforeBreaking和durationOfBreak,缺一不可
HandleResult 和 Handle 必须分开处理 HTTP 状态码与异常
HTTP 500 是 HttpRequestException,但 429、503、404 是合法响应体 + 错误码,Handle<httprequestexception>()</httprequestexception> 捕不到。若只配异常处理,熔断器永远不触发——这是最常踩的坑。
正确做法是显式拆开:
- 用
Handle<httprequestexception>()</httprequestexception>捕网络层失败(DNS、连接超时、服务无响应) - 用
OrResult<httpresponsemessage>(r => r.StatusCode == HttpStatusCode.TooManyRequests)</httpresponsemessage>捕 429 限流响应 - 用
OrResult<httpresponsemessage>(r => !r.IsSuccessStatusCode)</httpresponsemessage>捕所有非 2xx,但注意别把 404 当故障——要按业务判断
示例片段:
Policy<httpresponsemessage>.Handle<httprequestexception>()
.OrResult(r => r.StatusCode is HttpStatusCode.ServiceUnavailable or HttpStatusCode.GatewayTimeout)
.CircuitBreakerAsync(
exceptionsAllowedBeforeBreaking: 5,
durationOfBreak: TimeSpan.FromMinutes(1))</httprequestexception></httpresponsemessage>
限流(Rate Limiting)不是 Polly 职责,Ocelot 自带且必须前置
Polly 没有原生限流策略(Bulkhead 是并发隔离,不是 QPS 限流)。Ocelot 的限流靠 RateLimitOptions + Redis 或内存存储,配置在 ocelot.json 的 GlobalConfiguration 或路由级 RateLimitOptions 中。
关键点:
- 限流必须在熔断之前生效——否则被限流的请求仍会进熔断统计,导致误熔断
- Ocelot 默认使用内存限流器(
aspnetcore-rate-limiter),生产环境务必切 Redis 后端 - 配置项如
ClientWhitelist、EnableRateLimiting、Period(如1m)必须全小写,大小写敏感 - 限流拒绝响应是 429,需确保熔断策略里已包含
OrResult(r => r.StatusCode == HttpStatusCode.TooManyRequests)
PolicyWrap 组合策略顺序不能颠倒
常见需求是「先超时,再重试,最后熔断」,但 Policy.Wrap 执行顺序是**从外到内**:最外层策略最先拦截,最内层最后执行。错误写法:Policy.Wrap(retry, circuitBreaker) 会让重试逻辑在熔断打开时仍被执行(违反熔断语义)。
正确组合顺序:
- 外层:熔断(
CircuitBreakerAsync)→ 拦截所有请求,Open 状态直接返回 - 中层:重试(
WaitAndRetryAsync)→ 只在熔断 Closed 状态下生效 - 内层:超时(
TimeoutAsync)→ 作为最终兜底,防止单次请求卡死
示例:
var policyWrap = Policy.WrapAsync(
circuitBreakerPolicy,
retryPolicy,
timeoutPolicy);
注意:timeoutPolicy 必须是 TimeoutPolicy<httpresponsemessage></httpresponsemessage> 类型,且 onTimeout 回调里不能 throw 异常(会绕过熔断),应返回 fallback 响应。
复杂点在于:熔断的 durationOfBreak 和重试的退避时间必须错开,避免半开窗口被重试洪峰打垮;fallback 响应体若含缓存头,需确认是否被 HttpClient 缓存污染后续请求——这些细节线上出问题时很难定位。











