亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

淺談Spring Cloud Ribbon的原理

發布時間:2020-09-03 19:29:34 來源:腳本之家 閱讀:158 作者:白色的海 欄目:編程語言

Ribbon是Netflix發布的開源項目,主要功能是提供客戶端的軟件負載均衡算法,將Netflix的中間層服務連接在一起。Ribbon客戶端組件提供一系列完善的配置項如連接超時,重試等。簡單的說,就是在配置文件中列出Load Balancer(簡稱LB)后面所有的機器,Ribbon會自動的幫助你基于某種規則(如簡單輪詢,隨即連接等)去連接這些機器。我們也很容易使用Ribbon實現自定義的負載均衡算法。

說起負載均衡一般都會想到服務端的負載均衡,常用產品包括LBS硬件或云服務、Nginx等,都是耳熟能詳的產品。

而Spring Cloud提供了讓服務調用端具備負載均衡能力的Ribbon,通過和Eureka的緊密結合,不用在服務集群內再架設負載均衡服務,很大程度簡化了服務集群內的架構。

具體也不想多寫虛的介紹,反正哪里都能看得到相關的介紹。

直接開擼代碼,通過代碼來看Ribbon是如何實現的。

配置

淺談Spring Cloud Ribbon的原理

詳解:

1.RibbonAutoConfiguration配置生成RibbonLoadBalancerClient實例。

代碼位置:

spring-cloud-netflix-core-1.3.5.RELEASE.jar

org.springframework.cloud.netflix.ribbon

RibbonAutoConfiguration.class

@Configuration
@ConditionalOnClass({ IClient.class, RestTemplate.class, AsyncRestTemplate.class, Ribbon.class})
@RibbonClients
@AutoConfigureAfter(name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
@AutoConfigureBefore({LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class})
@EnableConfigurationProperties(RibbonEagerLoadProperties.class)
public class RibbonAutoConfiguration {

 // 略

 @Bean
 @ConditionalOnMissingBean(LoadBalancerClient.class)
 public LoadBalancerClient loadBalancerClient() {
  return new RibbonLoadBalancerClient(springClientFactory());
 }
  // 略
}

先看配置條件項,RibbonAutoConfiguration配置必須在LoadBalancerAutoConfiguration配置前執行,因為在LoadBalancerAutoConfiguration配置中會使用RibbonLoadBalancerClient實例。

RibbonLoadBalancerClient繼承自LoadBalancerClient接口,是負載均衡客戶端,也是負載均衡策略的調用方。

2.LoadBalancerInterceptorConfig配置生成:

1).負載均衡攔截器LoadBalancerInterceptor實例

包含:

LoadBalancerClient實現類的RibbonLoadBalancerClient實例

負載均衡的請求創建工廠LoadBalancerRequestFactory:實例

2).RestTemplate自定義的RestTemplateCustomizer實例

代碼位置:

spring-cloud-commons-1.2.4.RELEASE.jar

org.springframework.cloud.client.loadbalancer

LoadBalancerAutoConfiguration.class

@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {
 // 略
 @Bean
 @ConditionalOnMissingBean
 public LoadBalancerRequestFactory loadBalancerRequestFactory(
   LoadBalancerClient loadBalancerClient) {
  return new LoadBalancerRequestFactory(loadBalancerClient, transformers);
 }

 @Configuration
 @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
 static class LoadBalancerInterceptorConfig {
  @Bean
  public LoadBalancerInterceptor ribbonInterceptor(
    LoadBalancerClient loadBalancerClient,
    LoadBalancerRequestFactory requestFactory) {
   return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
  }

  @Bean
  @ConditionalOnMissingBean
  public RestTemplateCustomizer restTemplateCustomizer(
    final LoadBalancerInterceptor loadBalancerInterceptor) {
   return new RestTemplateCustomizer() {
    @Override
    public void customize(RestTemplate restTemplate) {
     List<ClientHttpRequestInterceptor> list = new ArrayList<>(
       restTemplate.getInterceptors());
     list.add(loadBalancerInterceptor);
     restTemplate.setInterceptors(list);
    }
   };
  }
 }
 // 略
}

先看配置條件項:

要求在項目環境中必須要有RestTemplate類。

要求必須要有LoadBalancerClient接口的實現類的實例,也就是上一步生成的RibbonLoadBalancerClient。

3.通過上面一步創建的RestTemplateCustomizer配置所有RestTemplate實例,就是將負載均衡攔截器設置給RestTemplate實例。

@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {
 // 略

 @Bean
 public SmartInitializingSingleton loadBalancedRestTemplateInitializer(
   final List<RestTemplateCustomizer> customizers) {
  return new SmartInitializingSingleton() {
   @Override
   public void afterSingletonsInstantiated() {
    for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
     for (RestTemplateCustomizer customizer : customizers) {
      customizer.customize(restTemplate);
     }
    }
   }
  };
 }

 // 略
 @Configuration
 @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
 static class LoadBalancerInterceptorConfig {
  @Bean
  public LoadBalancerInterceptor ribbonInterceptor(
    LoadBalancerClient loadBalancerClient,
    LoadBalancerRequestFactory requestFactory) {
   return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
  }

  @Bean
  @ConditionalOnMissingBean
  public RestTemplateCustomizer restTemplateCustomizer(
    final LoadBalancerInterceptor loadBalancerInterceptor) {
   return new RestTemplateCustomizer() {
    @Override
    public void customize(RestTemplate restTemplate) {
     List<ClientHttpRequestInterceptor> list = new ArrayList<>(
       restTemplate.getInterceptors());
     list.add(loadBalancerInterceptor);
     restTemplate.setInterceptors(list);
    }
   };
  }
 }
 // 略
}

restTemplate.setInterceptors(list)這個地方就是注入負載均衡攔截器的地方LoadBalancerInterceptor。

從這個地方實際上也可以猜出來,RestTemplate可以通過注入的攔截器來構建相應的請求實現負載均衡。

也能看出來可以自定義攔截器實現其他目的。

4.RibbonClientConfiguration配置生成ZoneAwareLoadBalancer實例

代碼位置:

spring-cloud-netflix-core-1.3.5.RELEASE.jar

org.springframework.cloud.netflix.ribbon

RibbonClientConfiguration.class

@SuppressWarnings("deprecation")
@Configuration
@EnableConfigurationProperties
//Order is important here, last should be the default, first should be optional
// see https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
@Import({OkHttpRibbonConfiguration.class, RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class})
public class RibbonClientConfiguration {
 // 略
 @Bean
 @ConditionalOnMissingBean
 public ILoadBalancer ribbonLoadBalancer(IClientConfig config,
   ServerList<Server> serverList, ServerListFilter<Server> serverListFilter,
   IRule rule, IPing ping, ServerListUpdater serverListUpdater) {
  if (this.propertiesFactory.isSet(ILoadBalancer.class, name)) {
   return this.propertiesFactory.get(ILoadBalancer.class, config, name);
  }
  return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList,
    serverListFilter, serverListUpdater);
 }

 // 略
}

ZoneAwareLoadBalancer繼承自ILoadBalancer接口,該接口有一個方法:

 /**
  * Choose a server from load balancer.
  * 
  * @param key An object that the load balancer may use to determine which server to return. null if 
  *   the load balancer does not use this parameter.
  * @return server chosen
  */
 public Server chooseServer(Object key);

ZoneAwareLoadBalancer就是一個具體的負載均衡實現類,也是默認的負載均衡類,通過對chooseServer方法的實現選取某個服務實例。

攔截&請求

淺談Spring Cloud Ribbon的原理

1.使用RestTemplate進行Get、Post等各種請求,都是通過doExecute方法實現

代碼位置:
spring-web-4.3.12.RELEASE.jar

org.springframework.web.client

RestTemplate.class

public class RestTemplate extends InterceptingHttpAccessor implements RestOperations {

 // 略

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

  Assert.notNull(url, "'url' must not be null");
  Assert.notNull(method, "'method' must not be null");
  ClientHttpResponse response = null;
  try {
   ClientHttpRequest request = createRequest(url, method);
   if (requestCallback != null) {
    requestCallback.doWithRequest(request);
   }
   response = request.execute();
   handleResponse(url, method, response);
   if (responseExtractor != null) {
    return responseExtractor.extractData(response);
   }
   else {
    return null;
   }
  }
  catch (IOException ex) {
   String resource = url.toString();
   String query = url.getRawQuery();
   resource = (query != null ? resource.substring(0, resource.indexOf('?')) : resource);
   throw new ResourceAccessException("I/O error on " + method.name() +
     " request for \"" + resource + "\": " + ex.getMessage(), ex);
  }
  finally {
   if (response != null) {
    response.close();
   }
  }
 }

 // 略

}

支持的各種http請求方法最終都是調用doExecute方法,該方法內調用創建方法創建請求實例,并執行請求得到響應對象。

2.生成請求實例創建工廠

上一步代碼中,調用createRequest方法創建請求實例,這個方法是定義在父類中。

先整理出主要的繼承關系:

淺談Spring Cloud Ribbon的原理

createRequest方法實際是定義在HttpAccessor抽象類中。

public abstract class HttpAccessor {
 private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
 public void setRequestFactory(ClientHttpRequestFactory requestFactory) {
  Assert.notNull(requestFactory, "ClientHttpRequestFactory must not be null");
  this.requestFactory = requestFactory;
 }
 public ClientHttpRequestFactory getRequestFactory() {
  return this.requestFactory;
 }
 protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
  ClientHttpRequest request = getRequestFactory().createRequest(url, method);
  if (logger.isDebugEnabled()) {
   logger.debug("Created " + method.name() + " request for \"" + url + "\"");
  }
  return request;
 }
}

在createRequest方法中調用getRequestFactory方法獲得請求實例創建工廠,實際上getRequestFactory并不是當前HttpAccessor類中定義的,而是在子類InterceptingHttpAccessor中定義的。

public abstract class InterceptingHttpAccessor extends HttpAccessor {

 private List<ClientHttpRequestInterceptor> interceptors = new ArrayList<ClientHttpRequestInterceptor>();

 public void setInterceptors(List<ClientHttpRequestInterceptor> interceptors) {
  this.interceptors = interceptors;
 }

 public List<ClientHttpRequestInterceptor> getInterceptors() {
  return interceptors;
 }

 @Override
 public ClientHttpRequestFactory getRequestFactory() {
  ClientHttpRequestFactory delegate = super.getRequestFactory();
  if (!CollectionUtils.isEmpty(getInterceptors())) {
   return new InterceptingClientHttpRequestFactory(delegate, getInterceptors());
  }
  else {
   return delegate;
  }
 }
}

在這里做了個小動作,首先還是通過HttpAccessor類創建并獲得SimpleClientHttpRequestFactory工廠,這個工廠主要就是在沒有攔截器的時候創建基本請求實例。

其次,在有攔截器注入的情況下,創建InterceptingClientHttpRequestFactory工廠,該工廠就是創建帶攔截器的請求實例,因為注入了負載均衡攔截器,所以這里就從InterceptingClientHttpRequestFactory工廠創建。

3.通過工廠創建請求實例

創建實例就看工廠的createRequest方法。

public class InterceptingClientHttpRequestFactory extends AbstractClientHttpRequestFactoryWrapper {

 private final List<ClientHttpRequestInterceptor> interceptors;

 public InterceptingClientHttpRequestFactory(ClientHttpRequestFactory requestFactory,
   List<ClientHttpRequestInterceptor> interceptors) {

  super(requestFactory);
  this.interceptors = (interceptors != null ? interceptors : Collections.<ClientHttpRequestInterceptor>emptyList());
 }


 @Override
 protected ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod, ClientHttpRequestFactory requestFactory) {
  return new InterceptingClientHttpRequest(requestFactory, this.interceptors, uri, httpMethod);
 }

}

就是new了個InterceptingClientHttpRequest實例,并且把攔截器、基本請求實例創建工廠注進去。

4.請求實例調用配置階段注入的負載均衡攔截器的攔截方法intercept

可從第1步看出,創建完請求實例后,通過執行請求實例的execute方法執行請求。

ClientHttpRequest request = createRequest(url, method);
if (requestCallback != null) {
 requestCallback.doWithRequest(request);
}
response = request.execute();

實際請求實例是InterceptingClientHttpRequest,execute實際是在它的父類中。

類定義位置:

spring-web-4.3.12.RELEASE.jar

org.springframework.http.client

InterceptingClientHttpRequest.class

看一下它們的繼承關系。

淺談Spring Cloud Ribbon的原理

在execute方法中實際調用了子類實現的executeInternal方法。

public abstract class AbstractClientHttpRequest implements ClientHttpRequest {

 private final HttpHeaders headers = new HttpHeaders();

 private boolean executed = false;

 @Override
 public final HttpHeaders getHeaders() {
  return (this.executed ? HttpHeaders.readOnlyHttpHeaders(this.headers) : this.headers);
 }

 @Override
 public final OutputStream getBody() throws IOException {
  assertNotExecuted();
  return getBodyInternal(this.headers);
 }

 @Override
 public final ClientHttpResponse execute() throws IOException {
  assertNotExecuted();
  ClientHttpResponse result = executeInternal(this.headers);
  this.executed = true;
  return result;
 }

 protected void assertNotExecuted() {
  Assert.state(!this.executed, "ClientHttpRequest already executed");
 }

 protected abstract OutputStream getBodyInternal(HttpHeaders headers) throws IOException;

 protected abstract ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException;

}

其實就是InterceptingClientHttpRequest類的executeInternal方法,其中,又調用了一個執行器InterceptingRequestExecution的execute,通關判斷如果有攔截器注入進來過,就調用攔截器的intercept方法。

這里的攔截器實際上就是在配置階段注入進RestTemplate實例的負載均衡攔截器LoadBalancerInterceptor實例,可參考上面配置階段的第2步。

class InterceptingClientHttpRequest extends AbstractBufferingClientHttpRequest {

 // 略

 @Override
 protected final ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
  InterceptingRequestExecution requestExecution = new InterceptingRequestExecution();
  return requestExecution.execute(this, bufferedOutput);
 }


 private class InterceptingRequestExecution implements ClientHttpRequestExecution {

  private final Iterator<ClientHttpRequestInterceptor> iterator;

  public InterceptingRequestExecution() {
   this.iterator = interceptors.iterator();
  }

  @Override
  public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
   if (this.iterator.hasNext()) {
    ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
    return nextInterceptor.intercept(request, body, this);
   }
   else {
    ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), request.getMethod());
    for (Map.Entry<String, List<String>> entry : request.getHeaders().entrySet()) {
     List<String> values = entry.getValue();
     for (String value : values) {
      delegate.getHeaders().add(entry.getKey(), value);
     }
    }
    if (body.length > 0) {
     StreamUtils.copy(body, delegate.getBody());
    }
    return delegate.execute();
   }
  }
 }

}

5.負載均衡攔截器調用負載均衡客戶端

在負載均衡攔截器LoadBalancerInterceptor類的intercept方法中,又調用了負載均衡客戶端LoadBalancerClient實現類的execute方法。

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, requestFactory.createRequest(request, body, execution));
 }
}

在配置階段的第1步,可以看到實現類是RibbonLoadBalancerClient。

6.負載均衡客戶端調用負載均衡策略選取目標服務實例并發起請求

在RibbonLoadBalancerClient的第一個execute方法以及getServer方法中可以看到,實際上是通過ILoadBalancer的負載均衡器實現類作的chooseServer方法選取一個服務,交給接下來的請求對象發起一個請求。

這里的負載均衡實現類默認是ZoneAwareLoadBalancer區域感知負載均衡器實例,其內部通過均衡策略選擇一個服務。

ZoneAwareLoadBalancer的創建可以參考配置階段的第4步。

public class RibbonLoadBalancerClient implements LoadBalancerClient {
 @Override
 public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
  ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
  Server server = getServer(loadBalancer);
  if (server == null) {
   throw new IllegalStateException("No instances available for " + serviceId);
  }
  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 {
   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;
 }
  
 // 略 

 protected Server getServer(ILoadBalancer loadBalancer) {
  if (loadBalancer == null) {
   return null;
  }
  return loadBalancer.chooseServer("default"); // TODO: better handling of key
 }

 protected ILoadBalancer getLoadBalancer(String serviceId) {
  return this.clientFactory.getLoadBalancer(serviceId);
 }

 public static class RibbonServer implements ServiceInstance {
  private final String serviceId;
  private final Server server;
  private final boolean secure;
  private Map<String, String> metadata;

  public RibbonServer(String serviceId, Server server) {
   this(serviceId, server, false, Collections.<String, String> emptyMap());
  }

  public RibbonServer(String serviceId, Server server, boolean secure,
    Map<String, String> metadata) {
   this.serviceId = serviceId;
   this.server = server;
   this.secure = secure;
   this.metadata = metadata;
  }

  // 略
 }

}

代碼擼完,總結下。

普通使用RestTemplate請求其他服務時,內部使用的就是常規的http請求實例發送請求。

為RestTemplate增加了@LoanBalanced 注解后,實際上通過配置,為RestTemplate注入負載均衡攔截器,讓負載均衡器選擇根據其對應的策略選擇合適的服務后,再發送請求。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

淮北市| 福贡县| 女性| 台前县| 桐柏县| 佛学| 垫江县| 芮城县| 西华县| 松原市| 武宣县| 彰化县| 通州市| 南雄市| 蓬安县| 岚皋县| 乌苏市| 抚松县| 吉林省| 盐山县| 故城县| 板桥市| 湾仔区| 普兰县| 台江县| 松滋市| 闽清县| 芮城县| 汕头市| 安吉县| 易门县| 宁乡县| 永康市| 贞丰县| 汾西县| 铜川市| 清徐县| 莲花县| 昭苏县| 甘南县| 神农架林区|