SpringCloud 负载均衡与服务调用-探索

前言

在Spring Cloud Commons中提供了大量的与服务治理相关的抽象接口,其中包括DiscoveryClient用于获取注册中心上的服务对应的所有实例。在使用过程中会出现一个问题,在注册中心返回的实例列表中,应该选择哪个实例?服务注册和发现spring文档

LoadBalancerClient

SpringCloud定义了客户端负载均衡接口LoadBalancerClient,客户端负载均衡表示这个操作是在客户端进行的,客户端获取了所有的实例列表,并且根据算法选择其中一个实例。
有了负载均衡特性之后,在开发过程中无须通过DiscoveryClient获取ServiceInstance列表,而是直接进行服务调用,因为SpringCloud在底层屏蔽了负载均衡的逻辑。
如下代码介绍了LoadBalancerClient接口的一个简单用法,LoadBalancerClient相当于负载均衡组件:

List 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 execute(String serviceId, LoadBalancerRequest request) throws IOException;

     T execute(String serviceId, ServiceInstance serviceInstance,
            LoadBalancerRequest 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是新一代客户端负载均衡实现,如使用需要引入依赖


    org.springframework.cloud
    spring-cloud-starter-loadbalancer

@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构造器需要LoadBalancerClientLoadBalancerRequestFactory,前者根据负载均衡请求和服务名做真正的服务调用,后者构造负载均衡请求

你可能感兴趣的:(SpringCloud 负载均衡与服务调用-探索)