Spring cloud 服務調用和負載均衡機制

服務調用和客戶端負載均衡

在Spring cloud中採用Http REST風格調用服務,用 RestTemplate 或者 WebClient 來完成遠程服務調用。

服務往往是多實例部署的,調用時只需選擇其中一個實例進行調用即可,因此在調用方需做負載均衡處理,任何一款rpc調用框架都要考慮負載均衡的問題。

RestTemplate集成負載均衡使用方法

用RestTemplate調用http服務的寫法通常是:

Order o = resteTemplate.getForObject("http://xxx/order/100", Order.class);

由於微服務往往是多實例的,所以url參數不應該直接寫死爲“ip:port”的形式,在Spring cloud中, url中的服務地址可以直接指定爲服務的名稱,如下形式:

Order o = resteTemplate.getForObject("http://order-svc/order/100", Order.class);

Spring cloud框架背後會利用服務發現機制(即DiscoveryClient)解析“order-svc”這個服務名字, “order-svc”這個服務背後有多臺實例,最終只需要其中一個實例的ip即可,這裏就需要負載均衡處理。

spring-cloud-commons模塊中對客戶端負載均衡處理能力進行了抽象,只需要在項目中引入一款具體的 負載均衡實現產品即可,使用Spring cloud官方的spring-cloud-starter-loadbalancer就可以, 使用Ribbon也可以,需要哪一種就引入對應的模塊即可。

配置RestTemplate

Spring cloud自動裝配機制不會配置RestTemplate實例,需要用戶手工定義,通過 @LoadBalanced 註解對其進行負載均衡增強。

@SpringBootApplication
public class Application {
  @Bean
  @LoadBalanced
  public RestTemplate restTemplate() {
      return new RestTeamplate();
  }
}

使用 WebClient 也需要定義:

@Bean
@LoadBalanced
public WebClient.Builder webClientBuilder() {
    return new WebClient.Builder();
}

通過這樣聲明,程序啓動後通過 RestTemplate 發起調用時,不需要指定目標IP地址,只需要使用目標微服務名稱即可調用, 框架會自動執行負載均衡處理:

Order order = resteTemplate.getForObject("http://order-svc/order/100", Order.class);

Spring cloud負載均衡抽象

RestTemplate 底層要支持負載均衡處理,是通過攔截器實現的,RestTemplate 本身是支持攔截器註冊的。

LoadBalancerClient

Spring cloud客戶端負載均衡的抽象接口是 LoadBalancerClient,通過其 choose(serviceId) 方法得到 一個具體的服務實例進行調用,正是在 choose 方法中要實現負載均衡邏輯。

具體的負載均衡實現技術需要實現這個接口,例如 spring-cloud-loadbalancer 提供的 BlockingLoadBalancerClient, Ribbon是另一款負載均衡實現產品,它也有自己對應的實現者。

ReactiveLoadBalancer

ReactiveLoadBalancer 組件封裝具體的負載均衡策略,如:隨機、輪詢、一致性hash等。

LoadBalancerClient 通常把負載均衡策略處理委託給一個 ReactiveLoadBalancer 執行,例如 spring-cloud-loadbalancer模塊中提供兩種策略實現:

  • RoundRobinLoadBalancer 輪詢策略(默認)
  • RandomLoadBalancer 隨機策略

Ribbon提供了更多的負載均衡策略。

如果我們要實現自己的負載均衡策略,也需要實現一個 ReactiveLoadBalancer,通常選擇實現它的子接口 ReactorServiceInstanceLoadBalancer,示例:

public class HashLoadBalancer implements ReactorServiceInstanceLoadBalancer {
    public Mono<Response<ServiceInstance>> choose(Request request) {
        //實現方式可以參考RoundRobinLoadBalancer實現
    }
}

LoadBalancerInterceptor攔截器

那麼 LoadBalancerClient 是如何參與到 RestTemplate 的執行邏輯中的呢?

答案是攔截器,RestTemplate對象內部支持攔截器註冊機制,參考它的 setInterceptors(interceptors) 方法實現, 攔截器對象即 ClientHttpRequestInterceptor 接口的實現者,spring-cloud-common模塊中提供一個攔截器接口的 實現LoadBalancerInterceptor,它內部持有一個 LoadBalanceClient 對象,如下代碼:

public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
    private LoadBalancerClient loadBalancer;
    private LoadBalancerRequestFactory requestFactory;
    // ......
}

最後,在RestTemplate對象上調用 setInterceptors(Arrays.asList(new LoadBalancerInterceptor(loadBalancerClient))) 方法就可以把負載均衡攔截器註冊到 RestTemplate 對象中。

自動裝配

@LoadBalanced 註解使得Spring cloud會自動裝配一個LoadBalancerInterceptor攔截器,並註冊到 restTemplate對象的攔截器鏈中,如果使用spring-cloud-loadbalancer負載均衡實現,可以從LoadBalancerAutoConfiguration 自動配置類開始跟蹤其原理。

自定義負載均衡策略

ReactiveLoadBalancer接口定義的負載均衡策略,Spring cloud默認是輪詢策略,我們也可以實現這個接口 擴展自己的負載均衡策略。

public class MyGreyLoadBalancer extends ReactorServiceInstanceLoadBalancer {
    
}

public class CustomLoadBalancerConfiguration {
  @Bean
  ReactorLoadBalancer<ServiceInstance> myGreyLoadBalancer(Environment environment,
                                                          LoadBalancerClientFactory loadBalancerClientFactory) {
    String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
    return new MyGreyLoadBalancer(
            loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), 
            name);
  }
}

然後指定使用自己的策略:

@Configuration
@LoadBalancerClient(value = "order-svc", configuration=CustomLoadBalancerConfiguration.class)
public class Application {
    @Bean
    @LoadBalaced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章