中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

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

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

springcloud中Ribbon怎么用

發布時間:2021-09-28 10:23:29 來源:億速云 閱讀:118 作者:小新 欄目:編程語言

小編給大家分享一下springcloud中Ribbon怎么用,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

基本使用

這里使用基于zookeeper注冊中心+ribbon的方式實現一個簡單的客戶端負載均衡案例。

服務提供方

首先是一個服務提供方。代碼如下。

application.properties配置文件

spring.application.name=discovery-serviceserver.port=0service-B.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRulebootstrap.properties配置文件

spring.cloud.zookeeper.connect-string=192.168.0.15:2181引導程序,提供了一個ribbonService的rest接口服務,注冊程序到zookeeper中。

@SpringBootApplication@EnableDiscoveryClient@RestControllerpublic class DiscoverClient {  public static void main(String[] args) {    SpringApplication.run(DiscoverClient.class, args);  }  @RequestMapping("/ribbonService")  public String ribbonService(){    return "hello too ribbon";  }}

服務調用方

服務調用方就是進行負載均衡的一方,利用ribbo的RestTemplate進行負載調用服務。

RibbonConfig,配置ribbon的RestTemplate,通過@LoadBalanced注解實現,具體原理稍后分析。

@Configurationpublic class RibbonConfig {  /**   * 實例化ribbon使用的RestTemplate   * @return   */  @Bean  @LoadBalanced  public RestTemplate rebbionRestTemplate(){    return new RestTemplate();  }    /**  * 配置隨機負載策略,需要配置屬性service-B.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule  */  @Bean  public IRule ribbonRule() {    return new RandomRule();  }}

引導程序

@SpringBootApplication(scanBasePackages = "garine.learn.ribbon.loadblance")@EnableDiscoveryClient@RestControllerpublic class TestRibbonApplocation {  public static void main(String[] args) {    SpringApplication.run(TestRibbonApplocation.class, args);  }  @Autowired  @LoadBalanced  RestTemplate restTemplate;  @GetMapping("/{applicationName}/ribbonService")  public String ribbonService(@PathVariable("applicationName") String applicationName){    return restTemplate.getForObject("http://" + applicationName+"/ribbonService", String.class);  }}

配置文件同上,服務名稱修改即可。

測試

啟動兩個discovery-service,由于端口設置為0,所以是隨機端口。

啟動服務調用方

瀏覽器訪問服務調用方的提供的接口,路徑參數需要加上調用的服務名稱,例如http://localhost:8080/discovery-service/ribbonService,然后服務調用方使用ribbon的RestTemplate調用服務提供方的接口。

結果返回:hello too ribbon ,同時服務提供方啟動的兩個服務都可能被調用,取決于怎么配置負載策略。

上面就是一個簡單使用ribbon的例子,結合feign使用基本上是做類似上面所寫的工作,那么ribbon到底是怎么實現的呢?

原理與源碼分析

ribbon實現的關鍵點是為ribbon定制的RestTemplate,ribbon利用了RestTemplate的攔截器機制,在攔截器中實現ribbon的負載均衡。負載均衡的基本實現就是利用applicationName從服務注冊中心獲取可用的服務地址列表,然后通過一定算法負載,決定使用哪一個服務地址來進行http調用。

Ribbon的RestTemplate

RestTemplate中有一個屬性是List<ClientHttpRequestInterceptor> interceptors,如果interceptors里面的攔截器數據不為空,在RestTemplate進行http請求時,這個請求就會被攔截器攔截進行,攔截器實現接口ClientHttpRequestInterceptor,需要實現方法是

ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)throws IOException;

也就是說攔截器需要完成http請求,并封裝一個標準的response返回。

ribbon中的攔截器

在Ribbon 中也定義了這樣的一個攔截器,并且注入到RestTemplate中,是怎么實現的呢?

在Ribbon實現中,定義了一個LoadBalancerInterceptor,具體的邏輯先不說,ribbon就是通過這個攔截器進行攔截請求,然后實現負載均衡調用。

攔截器定義在org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration.LoadBalancerInterceptorConfig#ribbonInterceptor

@Configuration@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")static class LoadBalancerInterceptorConfig {  @Bean  //定義ribbon的攔截器  public LoadBalancerInterceptor ribbonInterceptor(     LoadBalancerClient loadBalancerClient,     LoadBalancerRequestFactory requestFactory) {   return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);  }  @Bean  @ConditionalOnMissingBean  //定義注入器,用來將攔截器注入到RestTemplate中,跟上面配套使用  public RestTemplateCustomizer restTemplateCustomizer(     final LoadBalancerInterceptor loadBalancerInterceptor) {   return restTemplate -> {        List<ClientHttpRequestInterceptor> list = new ArrayList<>(            restTemplate.getInterceptors());        list.add(loadBalancerInterceptor);        restTemplate.setInterceptors(list);      };  }}

ribbon中的攔截器注入到RestTemplate

定義了攔截器,自然需要把攔截器注入到、RestTemplate才能生效,那么ribbon中是如何實現的?上面說了攔截器的定義與攔截器注入器的定義,那么肯定會有個地方使用注入器來注入攔截器的。

在org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration#loadBalancedRestTemplateInitializerDeprecated方法里面,進行注入,代碼如下。

@Configuration@ConditionalOnClass(RestTemplate.class)@ConditionalOnBean(LoadBalancerClient.class)@EnableConfigurationProperties(LoadBalancerRetryProperties.class)public class LoadBalancerAutoConfiguration {  @LoadBalanced  @Autowired(required = false)  private List<RestTemplate> restTemplates = Collections.emptyList();  @Bean  public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(     final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {    //遍歷context中的注入器,調用注入方法。   return () -> restTemplateCustomizers.ifAvailable(customizers -> {      for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {        for (RestTemplateCustomizer customizer : customizers) {          customizer.customize(restTemplate);        }      }    });  }  //......  }

遍歷context中的注入器,調用注入方法,為目標RestTemplate注入攔截器,注入器和攔截器都是我們定義好的。

還有關鍵的一點是:需要注入攔截器的目標restTemplates到底是哪一些?因為RestTemplate實例在context中可能存在多個,不可能所有的都注入攔截器,這里就是@LoadBalanced注解發揮作用的時候了。

LoadBalanced注解

嚴格上來說,這個注解是spring cloud實現的,不是ribbon中的,它的作用是在依賴注入時,只注入實例化時被@LoadBalanced修飾的實例。

例如我們定義Ribbon的RestTemplate的時候是這樣的

@Bean  @LoadBalanced  public RestTemplate rebbionRestTemplate(){    return new RestTemplate();  }

因此才能為我們定義的RestTemplate注入攔截器。

那么@LoadBalanced是如何實現這個功能的呢?其實都是spring的原生操作,@LoadBalance的源碼如下

/** * Annotation to mark a RestTemplate bean to be configured to use a LoadBalancerClient * @author Spencer Gibb */@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@Qualifierpublic @interface LoadBalanced {}

很明顯,‘繼承'了注解@Qualifier,我們都知道以前在xml定義bean的時候,就是用Qualifier來指定想要依賴某些特征的實例,這里的注解就是類似的實現,restTemplates通過@Autowired注入,同時被@LoadBalanced修飾,所以只會注入@LoadBalanced修飾的RestTemplate,也就是我們的目標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, requestFactory.createRequest(request, body, execution));  }}

攔截請求執行

@Overridepublic <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);}

我們重點看getServer方法,看看是如何選擇服務的

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

代碼配置隨機loadBlancer,進入下面代碼

public Server chooseServer(Object key) {  if (counter == null) {    counter = createCounter();  }  counter.increment();  if (rule == null) {    return null;  } else {    try {      //使用配置對應負載規則選擇服務      return rule.choose(key);    } catch (Exception e) {      logger.warn("LoadBalancer [{}]: Error choosing server for key {}", name, key, e);      return null;    }  }}

這里配置的是RandomRule,所以進入RandomRule代碼

public Server choose(ILoadBalancer lb, Object key) {  if (lb == null) {    return null;  }  Server server = null;  while (server == null) {    if (Thread.interrupted()) {      return null;    }    //獲取可用服務列表    List<Server> upList = lb.getReachableServers();    List<Server> allList = lb.getAllServers();    //隨機一個數    int serverCount = allList.size();    if (serverCount == 0) {      /*       * No servers. End regardless of pass, because subsequent passes       * only get more restrictive.       */      return null;    }    int index = rand.nextInt(serverCount);    server = upList.get(index);    if (server == null) {      /*       * The only time this should happen is if the server list were       * somehow trimmed. This is a transient condition. Retry after       * yielding.       */      Thread.yield();      continue;    }    if (server.isAlive()) {      return (server);    }    // Shouldn't actually happen.. but must be transient or a bug.    server = null;    Thread.yield();  }  return server;}

隨機負載規則很簡單,隨機整數選擇服務,最終達到隨機負載均衡。我們可以配置不同的Rule來實現不同的負載方式。

以上是“springcloud中Ribbon怎么用”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

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

AI

道真| 潜江市| 克山县| 略阳县| 古田县| 贡山| 和顺县| 灵石县| 全州县| 晴隆县| 阳信县| 岳西县| 乌拉特中旗| 保亭| 万荣县| 高雄县| 崇仁县| 佛教| 新田县| 新和县| 天台县| 泗水县| 澜沧| 漾濞| 南京市| 宁强县| 沧州市| 七台河市| 长宁县| 彩票| 贵阳市| 佛坪县| 大足县| 宣恩县| 寿宁县| 乌拉特前旗| 宝丰县| 正镶白旗| 平定县| 湟中县| 双流县|