前言
在Spring Cloud Commons中提供了大量的与服务治理相关的抽象接口,其中包括
DiscoveryClient
用于获取注册中心上的服务对应的所有实例。在使用过程中会出现一个问题,在注册中心返回的实例列表中,应该选择哪个实例?服务注册和发现spring文档
LoadBalancerClient
SpringCloud定义了客户端负载均衡接口LoadBalancerClient
,客户端负载均衡表示这个操作是在客户端进行的,客户端获取了所有的实例列表,并且根据算法选择其中一个实例。
有了负载均衡特性之后,在开发过程中无须通过DiscoveryClient
获取ServiceInstance
列表,而是直接进行服务调用,因为SpringCloud在底层屏蔽了负载均衡的逻辑。
如下代码介绍了LoadBalancerClient接口的一个简单用法,LoadBalancerClient相当于负载均衡组件:
List<ServiceInstance> serviceInstances = discoveryClient.getInstances("ORDER-SERVER");
LoadBalancerClient client = new LoadBalancerClient(serviceInstances);
ServiceInstance serviceInstance = client.choose("ORDER-SERVER");
String result = new RestTemplate().getForObject(
"http://"+serviceInstance.getHost()+":"+serviceInstance.getPort()+"/hello",
String.class
);
LoadBalancerClient
(客户端负载均衡器)接口的源码如下
public interface LoadBalancerClient extends ServiceInstanceChooser {
<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;
<T> T execute(String serviceId, ServiceInstance serviceInstance,
LoadBalancerRequest<T> request) throws IOException;
URI reconstructURI(ServiceInstance instance, URI original);
}
-
execute()
根据ServiceInstance对指定服务执行请求 -
URI reconstructURI(ServiceInstance instance, URI original)
根据具有逻辑服务名的URL生成具有实际意义可连接的URL,如http://myservice/path/to/service => http://host:port/path/to/service
LoadBalancerClient接口继承ServiceInstanceChooser
(服务实例选择器),源码如下
public interface ServiceInstanceChooser {
ServiceInstance choose(String serviceId);
}
-
ServiceInstance choose(String serviceId)
根据传入的服务名serviceId得到一个对应服务的实例。
LoadBalancerClient实现
LoadBalancerClient有多种实现:
- RibbonLoadBalancerClient
基于Netflix Ribbon的LoadBalancerClient实现 - BlockingLoadBalancerClient
基于Spring Cloud LoadBalancer的LoadBalancerClient实现
SpringCloudLoadBalancer是新一代客户端负载均衡实现,如使用需要引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
@LoadBalanced
使用Spring Cloud可以直接基于服务名进行服务调用。这是因为Spring Cloud扩展了RestTemplate,只需要在定义RestTemplate Bean时加上@LoadBalanced注解,就可以基于服务名进行调用
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
restTemplate.getForObject("http://ORDER-SERVER/hello", String.class);
Spring Cloud Common提供了LoadBalancerAutoConfiguration
这个自动配置类,该类配置了拦截器,所有被@LoadBalanced
注解修饰的RestTemplate
都会被添加一个拦截器LoadBalancerInterceptor
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
private LoadBalancerClient loadBalancer;
private LoadBalancerRequestFactory requestFactory;
public LoadBalancerInterceptor(LoadBalancerClient loadBalancer,
LoadBalancerRequestFactory requestFactory) {
this.loadBalancer = loadBalancer;
this.requestFactory = requestFactory;
}
public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
// for backwards compatibility
this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
}
@Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
final ClientHttpRequestExecution execution) throws IOException {
final URI originalUri = request.getURI();
String serviceName = originalUri.getHost();
Assert.state(serviceName != null,
"Request URI does not contain a valid hostname: " + originalUri);
return this.loadBalancer.execute(serviceName,
this.requestFactory.createRequest(request, body, execution));
}
}
LoadBalancerInterceptor
构造器需要LoadBalancerClient
和LoadBalancerRequestFactory
,前者根据负载均衡请求和服务名做真正的服务调用,后者构造负载均衡请求
网友评论