服務調用和客戶端負載均衡
在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();
}
}