Netflix Ribbon - 发送真正的网络请求

128 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。


LoadBalancerInterceptor

@LoadBalanced

spring.factories

LoadBalancerAutoConfiguration

LoadBalancerInterceptor & ClientHttpRequestInterceptor

LoadBalancerClient & RibbonLoadBalancedClient

LoadBalancerRequest & LoadBalancerRequestFactory

LocalBalancerClient

spring.factories

RibbonAutoConfiguration

SpringClientFactory

RibbonLoadBalancerClient & LoadBalancerClient & LoadBalancerChooser

LocalBalancer

SpringClientFactory & NamedContextFactory

RibbonClientConfiguration

IConfig & IRule & IPing & ServerList & ServerListUpdater & ServerListFilter

ZoneAwareLoadBalancer & DynamicServerListLoadBalancer & BaseLoadBalancer & ILoadBalancer

ServerList

EurekaRibbonConfiguration

DomainExtractingServerList(DiscoveryEnableNIWSServerList) & ServerList

ServerListUpdater

PollingServerListUpdater

IRule

ZoneAvoidanceRule & PredicateBasedRule & ClientConfigEnableRoundRibbonRule & AbstractLoadBalancerRule & IRule

Netflix Ribbon - 发送真正的网络请求

  1. Interceptor

LoadBalancerInterceptor拦截器拦截RestTemplate请求,LoadBalancerInterceptor.intercept()方法调用RibbonLoadBalancerClient.execute()方法将RestTemplate请求交由LoadBalancerClient处理。

  1. LoadBalancerClient execute & choose

RibbonLoadBalancerClient.execute()方法根据serviceId获取List,RibbonLoadBalancerClient.choose()方法选择一个Server,封装成RibbonServer。

  1. LoadBalancerRequest

RibbonLoadBalancerRequest通过装饰器将HttpRequest封装成ServiceRequestWrapper。

ServiceRequestWrapper通过RibbonLoadBalancerClient.reconstructURI()获取真正请求的HTTP地址。

  1. ClientHttpRequestFactory

获取到真正的HTTP地址之后通过ClientHttpRequestFactory.createRequest(uri, method)构造出ClientHttpRequest发起真正的网络请求。

LoadBalancerRequest

在LoadBalancerInterceptor中,调用RibbonLoadBalancerClient.execute()方法需要两个参数:

serviceId:用来获取List并选择一个Server。

ClientHttpRequest:Http请求报文客户端(发起Http请求的客户端,并不是RestTemplate、LoadBalancerClient之类的模版方法客户端。RestTemplate自身不实现http申请,http申请的底层实现是ClientHttpRequest框架)

// LoadBalancerRequestFactory.createRequest


public LoadBalancerRequest<ClientHttpResponse> createRequest(
      final HttpRequest request, final byte[] body,
      final ClientHttpRequestExecution execution) {
   return instance -> {
      // 装饰器模式。getURI()方法被重载,由LoadBalancerClient.reconstructURI处理。
      HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance,
            this.loadBalancer);
      if (this.transformers != null) {
         for (LoadBalancerRequestTransformer transformer : this.transformers) {
            serviceRequest = transformer.transformRequest(serviceRequest,
                  instance);
         }
      }
      return execution.execute(serviceRequest, body);
   };
}

LoadBalancerRequest,是一个匿名内部类的实例。

// RibbonLoadBalancerClient.execute


@Override
public <T> T execute(String serviceId, LoadBalancerRequest<T> request)
      throws IOException {
   return execute(serviceId, request, null);
}

public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
      throws IOException {
   // 根据serviceId获取ILoadBalancer
   ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
   // 根据ILoadBalancer选择Server
   Server server = getServer(loadBalancer, hint);
   if (server == null) {
      throw new IllegalStateException("No instances available for " + serviceId);
   }
   // 封装Server为RibbonServer
   RibbonServer ribbonServer = new RibbonServer(serviceId, server,
         isSecure(server, serviceId),
         serverIntrospector(serviceId).getMetadata(server));

   // 
   return execute(serviceId, ribbonServer, request);
}

@Override
public <T> T execute(String serviceId, ServiceInstance serviceInstance,
      LoadBalancerRequest<T> request) throws IOException {
   Server server = null;
   if (serviceInstance instanceof RibbonServer) {
      server = ((RibbonServer) serviceInstance).getServer();
   }
   if (server == null) {
      throw new IllegalStateException("No instances available for " + serviceId);
   }

   RibbonLoadBalancerContext context = this.clientFactory
         .getLoadBalancerContext(serviceId);
   RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);

   try {
      // LoadBalancerRequest发送请求到ServiceInstance
      T returnVal = request.apply(serviceInstance);
      statsRecorder.recordStats(returnVal);
      return returnVal;
   }
   // catch IOException and rethrow so RestTemplate behaves correctly
   catch (IOException ex) {
      statsRecorder.recordStats(ex);
      throw ex;
   }
   catch (Exception ex) {
      statsRecorder.recordStats(ex);
      ReflectionUtils.rethrowRuntimeException(ex);
   }
   return null;
}

RibbonLoadBalancerClient.execute()方法调用LoadBalancerRequest.apply()方法,传入选择出来的server,对server发起指定的一个请求(LoadBalancerRequest)

http://ServiceA/product ,这个URL地址封装在LoadBalancerRequest中。

通过装饰器模式将HttpRequest和ServiceInstance(RibbonServer)封装进ServerRequestWrapper中。

将ServiceRequestWrappter交由ClientHttpRequestExecution生成ClientHttpRequest,并发起HTTP请求。

从ServiceRequestWrapper中获取出来对应的真正的请求URL地址,然后发起了一次请求

Spring-WebClientHttpRequestExecution是从ServiceRequestWrapper.getURI()中,获取对应重构之后的真正的请求URL地址。

// ServiceRequestWrapper.getURI

@Override
public URI getURI() {
   URI uri = this.loadBalancer.reconstructURI(this.instance, getRequest().getURI());
   return uri;
}

ServiceRequestWrapper.getURI()方法基于自己的逻辑重写,调用了RibbonLoadBalancerClient.reconstructURI()方法,基于选择出来的server的地址,重构请求URI。

传进去负载均衡算法选择出来的server,getRequest().getURI() 根据LoadBalancerClient.reconstructURI()重构URI返回新的RUI(http://ServiceA/product => http://192.168.1.2:7071/product) ,将ServiceA给替换成了实际选择出来的server对应的hostname:port

http://192.168.1.2:7071/product,基于底层的ClientHttpRequestExecution(有拦截器走拦截器,没有拦截器就根据request.getURI和request.getMethod调用ClientHttpRequestFactory.createRequest构造ClientHttpRequest)发起一次http请求。