SpringCloud客户端负载均衡——Ribbon

发布时间:2020-08-08 16:17:22 作者:ckxllf
来源:ITPUB博客 阅读:248

  Ribbon——A ribbon is a long, narrow piece of cloth that you use for tying things together or as a decoration.

  Ribbon是一个工具类框架,不需要独立部署。

  负载均衡设备/负载均衡软件模块都会维护一个可用的服务清单,通过心跳检测来剔除故障节点,保证清单中都是可用节点。

  客户端负载均衡,由客户端节点维护要访问的服务清单,服务清单来自于注册中心。

  如前所示,使用客户端负载均衡调用分两步:

  1. 服务提供者注册到服务中心。

  2. 服务消费者通过标有@LoadBalanced注解的RestTemplate进行服务调用。

  在service-consumer服务中,通过调用RestTemplate的getForEntity方法,GET调用hello-service的/hello接口。

  RestTemplate

  GET

  RestTemplate有两类GET实现:getForEntity和getForObject。

  getForEntity()有三个重载实现,均返回ResponseEntity,

  // url为请求地址,responseType为响应体body的类型,uriVariables为url参数

  // uriVariables配合url中的占位符进行动态传参,如:

  // entity = getForEntity("http://user-serivce/user?name={1}", String.class, "John");,将John传给参数name

  public ResponseEntity getForEntity(String url, Class responseType, Object... uriVariables) throws RestClientException {

  RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); // new AcceptHeaderRequestCallback(responseType)

  ResponseExtractor> responseExtractor = responseEntityExtractor(responseType); // new ResponseEntityResponseExtractor<>(responseType)

  return nonNull(execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables));

  }

  // uriVariables为Map类型,key需要与url中的占位符对应,如:

  // params.put("myname", "John");

  // entity = getForEntity("http://user-service/user?name={myname}", String.class, params); 将key为myname对应的value——John传给name

  public ResponseEntity getForEntity(String url, Class responseType, Map uriVariables);

  // 使用URI对象代替url和uriVariables

  public ResponseEntity getForEntity(URI url, Class responseType);

  // 使用:

  ResponseEntity entity = restTemplate.getForEntity(url, String.class, "John");

  String body = entity.getBody();

  ResponseEntity entity = restTemplate.getForEntity(url, User.class, "John");

  User body = entity.getBody();

  getForObject()也有三个重载实现,传入execute方法的不是ResponseExtractor,而是HttpMessageConverterExtractor,返回的则是对象类型,三个重载和getForEntity的三个重载关系类似:

  public T getForObject(String url, Class responseType, Object... uriVariables) throws RestClientException {

  RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); // new AcceptHeaderRequestCallback(responseType)

  HttpMessageConverterExtractor responseExtractor = new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);

  return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);

  }

  public T getForObject(String url, Class responseType, Map uriVariables);

  public T getForObject(URI url, Class responseType);

  // 使用:

  String result = restTemplate.getForObject(url, String.class, "John");

  User user = restTempleate.getForObject(url, User.class, "John");

  // 使用getForObject可以省略从response中获取body的步骤

  POST

  RestTemplate有三类POST实现:postForEntity和postForObject,postForLocation。

  postForEntity()有三个重载实现,均返回ResponseEntity,

  // 相较于getForEntity,新增参数Object request,reqeust如果是HttpEntity对象,RestTemplate将其当作完整的http请求对象处理,request中包含了header和body的内容。如果request是普通对象,RestTemplate将其转换为HttpEntity来处理,request作为body。

  // if (request instanceof HttpEntity){this.requestEntity = (HttpEntity) request; }

  // else if (requestBody != null) { this.requestEntity = new HttpEntity<>(request); }

  // else { this.requestEntity = HttpEntity.EMPTY; }

  // 使用:

  // User user = new User("didi", 30);

  // entity = postForEntity("http://user-serivce/user", user, String.class, "John");,将John传给参数name

  public ResponseEntity postForEntity(String url, Object request, Class responseType, Object... uriVariables) throws RestClientException {

  RequestCallback requestCallback = httpEntityCallback(request, responseType); // new HttpEntityRequestCallback(requestBody, responseType)

  ResponseExtractor> responseExtractor = responseEntityExtractor(responseType); // new ResponseEntityResonseExtracor<>(responseType)

  return nonNull(execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables));

  }

  public ResponseEntity postForEntity(String url, Object request, Class responseType, Map uriVariables);

  public ResponseEntity postForEntity(URI url, Object request, Class responseType);

  postForObject()也有三个重载实现,传入execute方法的不是ResponseExtractor,而是HttpMessageConverterExtractor,返回的则是对象类型,三个重载和postForEntity的三个重载关系类似:

  public T postForObject(String url, Object request, Class responseType, Object... uriVariables) throws RestClientException {

  RequestCallback requestCallback = httpEntityCallback(request, responseType);

  HttpMessageConverterExtractor responseExtractor = new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);

  return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);

  }

  public T postForObject(String url, Object request, Class responseType, Map uriVariables);

  public T postForObject(URI url, Object request, Class responseType);

  postForLocation()用于发送post请求,返回新资源的URI,有三个重载实现,均返回URI对象,

  public URI postForLocation(String url, Object request, Object... uriVariables) throws RestClientException {

  RequestCallback requestCallback = httpEntityCallback(request); // new HttpEntityRequestCallback(request, null)

  HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, headersExtractor(), uriVariables); // new HeadersExtractor()

  return (headers != null ? headers.getLocation() : null);

  }

  public URI postForLocation(String url, Object request, Map uriVariables);

  public URI postForLocation(URI url, Object ruquest);

  execute

  RestTemplate中,不同的请求方式,最终会调用到execute的三个重载实现上来

  // ------------------------------------

  public T execute(String url, HttpMethod method, RequestCallback requestCallback, ResponseExtractor responseExtractor, Object... uriVariables) {

  URI expanded = getUriTemplateHandler().expand(url, uriVariables);

  return doExecute(expanded, method, requestCallback, responseExtractor);

  }

  public T execute(String url, HttpMethod method, RequestCallback requestCallback, ResponseExtractor responseExtractor, Map uriVariables);

  public T execute(URI url, HttpMethod method, RequestCallback requestCallback, ResponseExtractor responseExtractor);

  execute()的三个重载实现,都会调用doExecute()方法,去执行请求

  protected T doExecute(URI url, HttpMethod method, RequestCallback requestCallback, ResponseExtractor responseExtractor) throws RestClientException {

  ClientHttpResponse response = null;

  ClientHttpRequest request = createRequest(url, method);

  if (requestCallback != null) { requestCallback.doWithRequest(request); }

  response = request.execute();// 此处执行前会被拦截

  handleResponse(url, method, response);

  return (responseExtractor != null ? responseExtractor.extractData(response) : null);

  }

  doExecute()方法接收的参数中,有RequestCallback和ResponseExtractor。

  RequestCallback

  AcceptHeaderRequestCallback implements RequestCallback // AcceptHeaderRequestCallback用于GET请求

  HttpEntityRequestCallback extends AcceptHeaderRequestCallback // HttpEntityRequestCallback 用于POST、PUT等请求

  ResponseEntity

  // ResponseEntity扩展自HttpEntity,增加了http的status(http请求状态码)

  package org.springframework.http;

  public class ResponseEntity extends HttpEntity {

  private final Object status; // status为int或HttpStatus类型

  // getter/setter...

  }

  // HttpEntity表示http的request或response的entity,包含headers(http请求的头信息)和body(http请求的请求体)

  package org.springframework.http;

  public class HttpEntity {

  private final HttpHeaders headers;

  private final T body;

  // getter/setter...

  }

  @LoadBalanced

  在服务消费者中,给RestTemplate添加了@LoadBalanced注解,根据注释,该注解用于标记RestTemplate使用LoadBalancerClient来配置,即客户端负载均衡器。

  // Annotation to mark a RestTemplate bean to be configured to use a LoadBalancerClient.

  public @interface LoadBalanced {}

  LoadBalancerClient

  客户端负载均衡器,具有如下能力:

  // Represents a client-side load balancer. 即客户端负载均衡器

  interface LoadBalancerClient extends ServiceInstanceChooser {

  // 使用serviceId服务执行request请求

  T execute(String serviceId, LoadBalancerRequest request) throws IOException;

  T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest request) throws IOException;

  // 将逻辑服务名http://myservice/path/to/service 替换为host:port的形式

  URI reconstructURI(ServiceInstance instance, URI original);

  }

  // 选择一个server用来发送请求的实现接口

  interface ServiceInstanceChooser {

  // 根据serviceId,从负载均衡器选择一个服务实例ServiceInstance

  ServiceInstance choose(String serviceId);

  }

  LoadBalancerClient有一个实现类RibbonLoadBalancerClient。

  在RestTemplate的doExecute()方法中,调用request.execute()之前,会被LoadBalancerInterceptor拦截。该拦截器中有一个LoadBalancerClient实例,此外该拦截器在LoadBalancerAutoConfiguration中被创建。而LoadBalancerAutoConfiguration有两个特殊的注解@ConditionalOnClass(RestTemplate.class)和@ConditionalOnBean(LoadBalancerClient.class),且注释明确说明LoadBalancerAutoConfiguration为Ribbon的自动化配置类。

  spring-cloud-commons的loadbalancer包中的配置类,以2.1.2为例

  LoadBalancerAutoConfiguration:创建LoadBalancerInterceptor、创建RestTemplateCustomizer(匿名内部类)、创建LoadBalancerRequestFactory、创建SmartInitializingSingleton(匿名内部类)

  AsyncLoadBalancerAutoConfiguration:针对AsyncRestTemplate做的类似配置

  spring-cloud-netflix-ribbon中的几个配置类,以2.1.2为例

  RibbonClientConfiguration:创建IClientConfig、创建IRule,创建IPing,创建ServerList,创建ServerListUpdater,创建ILoadBalancer(使用ZoneAwareLoadBalancer实现),创建ServerListFilter、创建RibbonLoadBalancerContext,创建RetryHandler,创建ServerIntrospector

  RibbonAutoConfiguration:创建HasFeatures,创建SpringClientFactory,创建LoadBalancerClient(使用RibbonLoadBalancerClient实现),创建LoadBalancedRetryFactory(使用RibbonLoadBalancedRetryFactory实现),创建PropertiesFactory,创建RibbonApplicationContextInitializer,创建RestTemplateCustomizer(使用匿名内部类),创建RibbonClientHttpRequestFactory

  RestCilentRibbonConfiguration:创建RestClient

  LoadBalancerAutoConfiguration

  Ribbon的自动化配置类:

  @Configuration

  @ConditionalOnClass(RestTemplate.class) // 需要RestTemplate类在classpath中

  @ConditionalOnBean(LoadBalancerClient.class) // 需要LoadBalancerClient的实现Bean在BeanFactory中

  @EnableConfigurationProperties(LoadBalancerRetryProperties.class)

  class LoadBalancerAutoConfiguration {

  @LoadBalanced

  @Autowired(required = false)

  // 工程中注册的RestTemplate的Bean会在此被加载

  private List restTemplates = Collections.emptyList();

  @Autowired(required = false)

  private List transformers = Collections.emptyList();

  // 创建SmartInitializingSingleton的Bean,负责用每个Customizer去修饰每个RestTemplate

  @Bean

  public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(

  ObjectProvider> restTemplateCustomizers) {

  return () -> restTemplateCustomizers.ifAvailable(customizers -> {

  for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {

  for (RestTemplateCustomizer customizer : customizers) {

  customizer.customize(restTemplate);

  }

  }

  });

  }

  @Configuration

  @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")

  static class LoadBalancerInterceptorConfig {

  // 创建拦截器Bean,入参为客户端负载均衡器

  @Bean

  public LoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) {

  return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);

  }

  // 创建一个RestTemplateCustomizer的Bean,负责将负载均衡拦截器加到入参RestTemplate的拦截器列表中,添加方式为get、add、set

  @Bean

  @ConditionalOnMissingBean

  public RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {

  return restTemplate -> {

  List list = new ArrayList<>(restTemplate.getInterceptors());

  list.add(loadBalancerInterceptor);

  restTemplate.setInterceptors(list);

  }

  }

  }

  }

  LoadBalancerInterceptor

  负载均衡拦截器,用于在请求最终执行前进行拦截,在拦截器的intercept()方法中,首先从request中获取服务名称serviceName,然后调用request工厂的createRequest()方法,创建一个负载均衡的request——LoadBalancerRequest实例,最后将其连同serviceName一起作为LoadBalancerClient的execute()方法的入参。

  class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {

  LoadBalancerClient loadBalancer;

  LoadBalancerRequestFactory requestFactory;

  public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) {

  // 此处调用request的getURI方法,

  final URI originalUri = request.getURI();

  String serviceName = originalUri.getHost();

  return this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));

  }

  }

  LoadBalancerRequestFactory——创建LoadBalancerRequest的工厂

  public LoadBalancerRequest createRequest(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) {

  // 返回LoadBalancerRequest的匿名内部类

  return instance -> {

  // 该LoadBalancerRequest的匿名内部类实现,先创建一个ServiceRequestWrapper的request,然后调用execution的execute方法执行请求

  HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance, this.loadBalancer);

  return execution.execute(serviceRequest, body);

  };

  }

  LoadBalancerRequest

  LoadBalancerRequest使用该接口的apply()的方法,为request添加处理动作

  interface LoadBalancerRequest {

  T apply(ServiceInstance instance);

  }

  ServiceRequestWrapper

  ServiceRequestWrapper继承自HttpRequestWrapper,HttpRequestWrapper对外提供了获取一个request的method、URI、headers、methodValue等信息的方法。

  ServiceRequestWrapper改写了默认的getURI()方法,使用客户端负载均衡器LoadBalancerClient的重构URI的方法,将入参request的URI进行重构,其具体实现在LoadBalancerClient的实现类RibbonLoadBalancerClient中。

  class ServiceRequestWrapper extends HttpRequestWrapper {

  private final ServiceInstance instance;

  private final LoadBalancerClient loadBalancer;

  public ServiceRequestWrapper(HttpRequest request, ServiceInstance instance, LoadBalancerClient loadBalancer) {}

  @Override

  public URI getURI() {

  // 调用loadBalancer的reconstructURI方法,进行URI重构,改写成host:port的形式,具体实现在RibbonLoadBalancerClient中

  return this.loadBalancer.reconstructURI(this.instance, getRequest().getURI());

  }

  }

  RibbonLoadBalancerClient

  RibbonLoadBalancerClient实现了LoadBalancerClient和ServiceInstanceChooser中的execute、reconstructURI、choose方法,完成了请求执行、URI重构和选择服务实例的任务,execute

  class RibbonLoadBalancerClient implements LoadBalancerClient {

  SpringClientFactory clientFactory;

  @Override

  public URI reconstructURI(ServiceInstance instance, URI original) {

  String serviceId = instance.getServiceId();

  RibbonLoadBalancerContext context = this.clientFactory.getLoadBalancerContext(serviceId);

  URI uri; Server server;

  if (instance instanceof RibbonServer) {

  server = ((RibbonServer) instance).getServer();

  uri = updateToSecureConnectionIfNeeded(original, ribbonServer);

  } else {

  server = new Server(instance.getScheme(), instance.getHost(), instance.getPort());

  IClientConfig clientConfig = clientFactory.getClientConfig(serviceId);

  ServerIntrospector serverIntrospector = serverIntrospector(serviceId);

  uri = updateToSecureConnectionIfNeeded(original, clientConfig, serverIntrospector, server);

  }

  // 用server中的host、port等替换原始uri

  return context.reconstructURIWithServer(server, uri);

  }

  public ServiceInstance choose(String serviceId, Object hint) {

  // 先调用getLoadBalancer方法,根据serviceId,获取一个ILoadBalancer

  // 然后调用getServer(ILoadBalancer loadBalancer, Object hint),使用loadBalancer选择一个Server,hint默认为"default"

  Server server = getServer(getLoadBalancer(serviceId), hint);

  if (server == null) { return null; }

  // 用入参serviceId、选择的Server,构造一个RibbonServer

  return new RibbonServer(serviceId, server, isSecure(server, serviceId), serverIntrospector(serviceId).getMetadata(server));

  }

  public T execute(String serviceId, LoadBalancerRequest request, Object hint) {

  // 首先选择一个RibbonServer,该部分流程与choose相同

  Server server = getServer(getLoadBalancer(serviceId), hint);

  RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server, serviceId), serverIntrospector(serviceId).getMetadata(server));

  // 调用execute的重载实现,执行请求

  return execute(serviceId, ribbonServer, request);

  }

  public T execute(String serviceId, ServiceInstance serviceIntance, LoadBalancerRequest request) {

  Server server = null;

  if (serviceInstance instanceof RibbonServer) {

  server = ((RibbonServer) serviceInstance).getServer();

  }

  RibbonLoadBalancerContext context = this.clientFactory.getLoadBalancerContext(serviceId);

  RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);

  try { 无锡做人流手术多少钱 http://www.ytsg029.com/

  // 调用apply,向服务实例发起请求

  T returnVal = request.apply(serviceInstance);

  statsRecorder.recordStats(returnVal);

  return returnVal;

  } catch (IOException ex) {

  statsRecorder.recordStats(ex); throw ex;

  } catch (Exception ex) {

  statsRecorder.recordStats(ex); throw ex;

  }

  return null;

  }

  // Ribbon 实现了ServiceInstance接口,即服务实例接口

  public static class RibbonServer implements ServiceInstance {

  private final String serviceId;

  private final Server server;

  private final boolean secure;

  private Map metadata;

  // @Override方法

  }

  }

  ILoadBalancer

  在RibbonLoadBalancerClient的choose()和execute()方法中,都是通过调用ILoadBalancer的chooseServer()方法,来选择一个服务实例Server的,该ILoadBalancer接口是由Ribbon定义的。

  在ILoadBalancer接口中,定义了软件负载均衡器的操作:一个服务实例的集合、标记一个服务停止、选择服务

  package com.netflix.loadbalancer;

  interface ILoadBalancer {

  void addServers(List newServers); // 初始化、后续添加服务列表

  Server chooseServer(Object key); // 从负载均衡器选择一个服务实例

  void markServerDown(Server server); // 标记并通知某个服务实例已经停止

  List getReachableServers(); // up/reachable状态的服务实例,可以提供正常服务

  List getAllServers(); // 所有已知的服务实例,reachable/unreachable都包括

  }

  其中的Server,代表一个服务端节点,包含了一个服务的基本信息:host、port、scheme、id、zone、元数据等等。

  在RibbonLoadBalancerClient的choose()和execute()方法中,通过getLoadBalancer()方法,来根据serviceId获取ILoadBalancer的实例,然后将其包装成RibbonServer。

  配置类RibbonClientConfiguration创建ILoadBalancer时如果配置文件里有配置,则使用配置的实现,否则默认使用ZoneAwareLoadBalancer实现。

  ClientHttpRequestExecution

  RibbonLoadBalancerClient的execute()方法中,调用了入参LoadBalancerRequest的apply方法,execute()方法在LoadBlancerInterceptor的intercept方法中调用,并传入LoadBalancerRequestFactory.createRequest创建的LoadBalancerRequest实现,其实现中最终使用ClientHttpRequestExecution的execute方法执行请求。

  总结

  使用时,注册一个使用@LoadBalanced注解修饰的RestTemplate,在需要发起请求的地方调用RestTemplate的相应的请求方法,最终调用到其doExecute方法。

  @LoadBalanced注解关联了LoadBalancerClient。

  配置类LoadBalancerAutoConfiguration注册了如下Bean:

  SmartInitializingSingleton:遍历restTemplates、遍历customizers,并customizer.customize(restTemplate)

  LoadBalancerRequestFactory:使用LoadBalancerClient构造

  LoadBalancerInterceptor:使用LoadBalancerClient和LoadBalancerRequestFactory创建

  RestTemplateCustomizer:使用LoadBalancerInterceptor构造一个匿名类,将注册的LoadBalancerInterceptor添加进restTemplate的interceptors列表中

  拦截器LoadBalancerInterceptor的intercept方法从原始请求中获取URI,然后使用LoadBalancerClient的execute方法执行请求,接收两个参数:serviceName即host和请求工厂创建的request

  请求工厂LoadBalancerRequestFactory的createRequest方法,由原始请求创建一个LoadBalancerRequest的匿名实现

  负载均衡请求LoadBalancerRequest接口只有apply方法,其匿名实现创建HttpRequest的实现类ServcieRequestWrapper的实例,然后由ClientHttpRequestExecution的execute方法执行请求,返回响应ClientHttpResponse

  ServiceRequestWrapper重写了父类HttpRequestWrapper的getURI方法,返回LoadBalancerClient的reconstructURI方法重构的URI

  ClientHttpRequestExecution的实现类是InterceptingClientHttpRequest的内部类IntercpetingRequestExecution,其execute方法遍历interceptors,如果有拦截器,就执行拦截方法,如果没有了,就执行请求。

  在4中,LoadBalancerClient的execute方法执行请求,其实现类是RibbonLoadBalancerClient。execute 先通过serviceId获取ILoadBalancer,然后调用ILoadBalancer的chooseServer方法,选择一个Server,并将之转换成RibbonServer,RibbonServer是ServiceInstance的子类,最后调用LoadBalancerRequest的apply方法,执行请求,返回响应。

推荐阅读:
  1. 如何实现Springcloud ribbon负载均衡算法
  2. SpringCloud 中怎么利用Ribbon实现负载均衡

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

ribbon springcloud 均衡

上一篇:mysql主从复制原理

下一篇:从HTTP向HTTPS迁移,Chrome浏览器的UI变化

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》