Skip to content

客户端负载均衡 ​

概述 ​

在微服务架构中,服务需要调用其他服务而无需硬编码主机名。CoApi 与 Spring Cloud LoadBalancer 集成,提供客户端负载均衡:HTTP 客户端本身选择调用哪个服务实例。这消除了对外部负载均衡器的需求,并让应用程序直接控制实例选择、重试和断路。

CoApi 提供了三种选择负载均衡的方式,都解析为相同的机制:LoadBalancedExchangeFilterFunction(响应式)或 BlockingLoadBalancerInterceptor(同步)被添加到 HTTP 客户端的过滤器/拦截器链中。

一览 ​

机制注解解析后的 URL负载均衡来源
服务 ID@CoApi(serviceId = "svc")http://svc是CoApi.kt
LB 协议@CoApi(baseUrl = "lb://svc")http://svc是CoApi.kt
注解@CoApi @LoadBalanced空是LoadBalanced.kt
属性coapi.clients.<name>.load-balanced=true/false按属性true→是,false→否CoApiProperties.kt
直接 URL@CoApi(baseUrl = "http://...")按指定否CoApi.kt

URL 解析流程 ​

当 toCoApiDefinition() 解析注解时,它会解析基础 URL 并确定负载均衡:

mermaid
flowchart TD
    A["@CoApi annotation"] --> B{baseUrl is not blank?}
    B -->|Yes| C["Resolve ${} placeholders"]
    B -->|No| D{serviceId is not blank?}
    D -->|Yes| E["lb:// + serviceId"]
    D -->|No| F[Empty string]
    C --> G{Starts with lb:// ?}
    E --> G
    G -->|Yes| H["Strip lb:// → http://<br>loadBalanced = true"]
    G -->|No| I["Use URL as-is<br>loadBalanced = false"]
    F --> J{"@LoadBalanced present?"}
    J -->|Yes| K["loadBalanced = true"]
    J -->|No| L["loadBalanced = false"]

    H --> M[CoApiDefinition]
    I --> M
    K --> M
    L --> M

CoApiDefinition.kt:70-97 中的解析逻辑:

输入解析后的 URLloadBalanced
@CoApi(baseUrl = "lb://order-service")http://order-servicetrue
@CoApi(serviceId = "order-service")http://order-servicetrue
@CoApi @LoadBalanced"" (空)true
@CoApi(baseUrl = "\${github.url}")解析后的值false

运行时负载均衡决策 ​

创建客户端时,工厂 Bean 先解析出生效的定义:ClientProperties.resolve(definition) 通过 CoApiDefinition.withOverrides() 这一条规则,应用 coapi.clients.<name>.* 的覆盖配置:

mermaid
sequenceDiagram
    autonumber
    participant FB as WebClientFactoryBean / RestClientFactoryBean
    participant Props as ClientProperties
    participant Def as CoApiDefinition

    FB->>Props: resolve(definition)
    Props->>Def: withOverrides(base-url, load-balanced)
    alt 配置了 load-balanced
        Def-->>FB: 配置值(true/false)
    else 配置了 base-url
        Def-->>FB: 仅 lb:// URL 为 true(改写为 http://)
    else 无覆盖配置
        Def-->>FB: 注解决定的值
    end

优先级(CoApiDefinition.kt:156):

优先级来源效果
1(最高)coapi.clients.<name>.load-balanced覆盖为配置值(true 启用,false 禁用)
2coapi.clients.<name>.base-url(非空)仅 lb:// URL 启用负载均衡(自 v2.3.0 起);普通 http(s):// URL 则禁用
3(最低)@CoApi / @LoadBalanced 注解注解决定的默认值

lb:// 前缀匹配不区分大小写(LB:// 同样有效,自 v2.3.0 起)。如果某客户端被判定为负载均衡、但 classpath 上没有 Spring Cloud LoadBalancer,创建客户端时会抛出说明原因和解决办法的异常(自 v3.0.0 起),而不是 NoClassDefFoundError。

INFO

自 v2.1.1 起,显式配置 load-balanced: false 会被正确尊重并禁用负载均衡——在 v2.1.1 之前,任何配置值(包括 false)都会被当作 true。配置键中的 <name> 是 @CoApi 的 name 属性;未设置时为接口的简单类名。

WebClient 负载均衡 ​

对于响应式堆栈,WebClientFactoryBean 添加 LoadBalancedExchangeFilterFunction:

mermaid
sequenceDiagram
    autonumber
    participant FB as WebClientFactoryBean
    participant CTX as ApplicationContext
    participant Builder as WebClient.Builder
    participant LB as LoadBalancedExchangeFilterFunction

    FB->>FB: effectiveDefinition().loadBalanced → true
    FB->>Builder: LoadBalancedWebClientBuilderCustomizer.customize(definition, builder)
    Builder->>Builder: builder.filters { ... }
    Builder->>Builder: check: any existing LB filter (LoadBalanced or Deferring)?
    alt Already present
        Builder->>Builder: skip
    else Not present
        Builder->>CTX: getBean(LoadBalancedExchangeFilterFunction)
        CTX-->>LB: filter function
        Builder->>Builder: filters.add(LB)
    end

内部的 LoadBalancedWebClientBuilderCustomizer(LoadBalancedWebClientBuilderCustomizer.kt:36)只作用于负载均衡的客户端,在添加前检查重复项,确保幂等性。该检查同时识别 LoadBalancedExchangeFilterFunction 与 Spring Cloud 的 DeferringLoadBalancerExchangeFilterFunction(@LoadBalanced WebClient.Builder 上的默认装配),因此已带负载均衡能力的 Builder 不会被重复装配。

RestClient 负载均衡 ​

对于同步堆栈,RestClientFactoryBean 添加 BlockingLoadBalancerInterceptor:

mermaid
sequenceDiagram
    autonumber
    participant FB as RestClientFactoryBean
    participant CTX as ApplicationContext
    participant Builder as RestClient.Builder
    participant LB as BlockingLoadBalancerInterceptor

    FB->>FB: effectiveDefinition().loadBalanced → true
    FB->>Builder: LoadBalancedRestClientBuilderCustomizer.customize(definition, builder)
    Builder->>Builder: builder.requestInterceptors { ... }
    Builder->>Builder: check: any existing LB interceptor (Blocking or Deferring)?
    alt Already present
        Builder->>Builder: skip
    else Not present
        Builder->>CTX: getBean(BlockingLoadBalancerInterceptor)
        CTX-->>LB: interceptor
        Builder->>Builder: interceptors.add(LB)
    end

拦截器按 BlockingLoadBalancerInterceptor 接口解析,而非具体的 LoadBalancerInterceptor 类,因此无论 Spring Cloud 注册的是普通拦截器还是 RetryLoadBalancerInterceptor(启用重试时)都能正确工作。与响应式侧一致,去重检查也识别 Spring Cloud 安装在 @LoadBalanced RestClient.Builder 上的 DeferringLoadBalancerInterceptor。

每个客户端的过滤器和拦截器配置 ​

除了负载均衡外,CoApi 还支持通过 YAML 属性配置每个客户端的过滤器/拦截器链:

yaml
coapi:
  clients:
    ServiceApiClientUseFilterBeanName:
      reactive:
        filter:
          names:
            - loadBalancerExchangeFilterFunction
    ServiceApiClientUseFilterType:
      reactive:
        filter:
          types:
            - org.springframework.cloud.client.loadbalancer.reactive.LoadBalancedExchangeFilterFunction
属性类型适用于来源
coapi.clients.<name>.reactive.filter.namesBean 名称WebClient(响应式)CoApiProperties.kt
coapi.clients.<name>.reactive.filter.types类类型WebClient(响应式)CoApiProperties.kt
coapi.clients.<name>.sync.interceptor.namesBean 名称RestClient(同步)CoApiProperties.kt
coapi.clients.<name>.sync.interceptor.types类类型RestClient(同步)CoApiProperties.kt

工厂 Bean 通过 AbstractHttpClientFactoryBean.kt 从 ApplicationContext 解析这些引用——先按名称、再按类型。过滤器来自 ReactiveClientProperties,拦截器来自 SyncClientProperties(两者都由 CoApiProperties 实现)。

服务发现配置 ​

CoApi 与任何 Spring Cloud DiscoveryClient 配合工作。开发环境的简单内存配置:

yaml
spring:
  cloud:
    discovery:
      client:
        simple:
          instances:
            github-service:
              - host: api.github.com
                secure: true
                port: 443
            provider-service:
              - host: localhost
                port: 8010

需求 ​

需求如何实现
类路径上有 spring-cloud-starter-loadbalancerGradle/Maven 依赖
服务实例已注册Spring Cloud DiscoveryClient 或 SimpleDiscoveryClient
CoApi 中启用了负载均衡serviceId、lb://、@LoadBalanced 或属性

相关页面 ​

参考资料 ​

  1. CoApi.kt — api/src/main/kotlin/me/ahoo/coapi/api/CoApi.kt
  2. LoadBalanced.kt — api/src/main/kotlin/me/ahoo/coapi/api/LoadBalanced.kt
  3. CoApiDefinition.kt — spring/src/main/kotlin/me/ahoo/coapi/spring/CoApiDefinition.kt
  4. ClientProperties.kt — spring/src/main/kotlin/me/ahoo/coapi/spring/client/ClientProperties.kt
  5. WebClientFactoryBean.kt — spring/src/main/kotlin/me/ahoo/coapi/spring/client/reactive/WebClientFactoryBean.kt
  6. RestClientFactoryBean.kt — spring/src/main/kotlin/me/ahoo/coapi/spring/client/sync/RestClientFactoryBean.kt
  7. CoApiProperties.kt — spring-boot-starter/src/main/kotlin/.../CoApiProperties.kt
  8. consumer application.yaml — example/example-consumer-server/src/main/resources/application.yaml