Skip to content

客户端负载均衡

概述

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

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

一览

机制注解解析后的 URL负载均衡来源
服务 ID@CoApi(serviceId = "svc")http://svcCoApi.kt
LB 协议@CoApi(baseUrl = "lb://svc")http://svcCoApi.kt
注解@CoApi @LoadBalancedLoadBalanced.kt
属性coapi.clients.<name>.load-balanced=true按属性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 创建时,AbstractHttpClientFactoryBean.loadBalanced() 应用优先级顺序:

mermaid
sequenceDiagram
    autonumber
    participant FB as AbstractHttpClientFactoryBean
    participant Props as ClientProperties
    participant Def as CoApiDefinition

    FB->>Props: getLoadBalancedFromProperties(name)
    alt Properties has load-balanced value
        Props-->>FB: non-null Boolean
        FB->>FB: return true
    else Properties has baseUrl
        FB->>Props: getBaseUrlFromProperties(name)
        Props-->>FB: non-blank URL
        FB->>FB: return false (direct URL overrides)
    else No properties override
        FB->>Def: definition.loadBalanced
        FB->>FB: return annotation-determined value
    end

优先级AbstractHttpClientFactoryBean.kt:42-56):

优先级来源效果
1(最高)coapi.clients.<name>.load-balanced覆盖为 true
2coapi.clients.<name>.base-url(非空)强制非负载均衡
3(最低)@CoApi / @LoadBalanced 注解注解的默认值

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: loadBalanced() → true
    FB->>Builder: customize(definition, builder)
    Builder->>Builder: builder.filters { ... }
    Builder->>Builder: check: any existing LoadBalancedExchangeFilterFunction?
    alt Already present
        Builder->>Builder: skip
    else Not present
        Builder->>CTX: getBean(LoadBalancedExchangeFilterFunction)
        CTX-->>LB: filter function
        Builder->>Builder: filters.add(LB)
    end

LoadBalancedWebClientBuilderCustomizer 内部类(WebClientFactoryBean.kt:34-43)在添加前检查重复项,确保幂等性。

RestClient 负载均衡

对于同步堆栈,RestClientFactoryBean 添加 LoadBalancerInterceptor

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

    FB->>FB: loadBalanced() → true
    FB->>Builder: customize(definition, builder)
    Builder->>Builder: builder.requestInterceptors { ... }
    Builder->>Builder: check: any existing load balancer interceptor?
    alt Already present
        Builder->>Builder: skip
    else Not present
        Builder->>CTX: getBean(LoadBalancerInterceptor)
        CTX-->>LB: interceptor
        Builder->>Builder: interceptors.add(LB)
    end

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

除了负载均衡外,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

AbstractWebClientFactoryBean.kt 中的过滤器解析从 ApplicationContext 解析 bean 名称和类型。

服务发现配置

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 中启用了负载均衡serviceIdlb://@LoadBalanced 或属性

相关页面

参考资料

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