亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

spring cloud Ribbon用法及原理解析

 更新時(shí)間:2019年10月29日 10:12:47   作者:天宇軒-王  
這篇文章主要介紹了spring cloud Ribbon用法及原理解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

這篇文章主要介紹了spring cloud Ribbon用法及原理解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

簡(jiǎn)介

這篇文章主要介紹一下ribbon在程序中的基本使用,在這里是單獨(dú)拿出來(lái)寫(xiě)用例測(cè)試的,實(shí)際生產(chǎn)一般是配置feign一起使用,更加方便開(kāi)發(fā)。同時(shí)這里也通過(guò)源碼來(lái)簡(jiǎn)單分析一下ribbon的基本實(shí)現(xiàn)原理。

基本使用

這里使用基于zookeeper注冊(cè)中心+ribbon的方式實(shí)現(xiàn)一個(gè)簡(jiǎn)單的客戶(hù)端負(fù)載均衡案例。

服務(wù)提供方

首先是一個(gè)服務(wù)提供方。代碼如下。

application.properties配置文件

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

spring.cloud.zookeeper.connect-string=192.168.0.15:2181
引導(dǎo)程序,提供了一個(gè)ribbonService的rest接口服務(wù),注冊(cè)程序到zookeeper中。

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

服務(wù)調(diào)用方

服務(wù)調(diào)用方就是進(jìn)行負(fù)載均衡的一方,利用ribbo的RestTemplate進(jìn)行負(fù)載調(diào)用服務(wù)。

RibbonConfig,配置ribbon的RestTemplate,通過(guò)@LoadBalanced注解實(shí)現(xiàn),具體原理稍后分析。

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

引導(dǎo)程序

@SpringBootApplication(scanBasePackages = "garine.learn.ribbon.loadblance")
@EnableDiscoveryClient
@RestController
public 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);
  }
}

配置文件同上,服務(wù)名稱(chēng)修改即可。

測(cè)試

啟動(dòng)兩個(gè)discovery-service,由于端口設(shè)置為0,所以是隨機(jī)端口。

啟動(dòng)服務(wù)調(diào)用方

瀏覽器訪問(wèn)服務(wù)調(diào)用方的提供的接口,路徑參數(shù)需要加上調(diào)用的服務(wù)名稱(chēng),例如http://localhost:8080/discovery-service/ribbonService,然后服務(wù)調(diào)用方使用ribbon的RestTemplate調(diào)用服務(wù)提供方的接口。

結(jié)果返回:hello too ribbon ,同時(shí)服務(wù)提供方啟動(dòng)的兩個(gè)服務(wù)都可能被調(diào)用,取決于怎么配置負(fù)載策略。

上面就是一個(gè)簡(jiǎn)單使用ribbon的例子,結(jié)合feign使用基本上是做類(lèi)似上面所寫(xiě)的工作,那么ribbon到底是怎么實(shí)現(xiàn)的呢?

原理與源碼分析

ribbon實(shí)現(xiàn)的關(guān)鍵點(diǎn)是為ribbon定制的RestTemplate,ribbon利用了RestTemplate的攔截器機(jī)制,在攔截器中實(shí)現(xiàn)ribbon的負(fù)載均衡。負(fù)載均衡的基本實(shí)現(xiàn)就是利用applicationName從服務(wù)注冊(cè)中心獲取可用的服務(wù)地址列表,然后通過(guò)一定算法負(fù)載,決定使用哪一個(gè)服務(wù)地址來(lái)進(jìn)行http調(diào)用。

Ribbon的RestTemplate

RestTemplate中有一個(gè)屬性是List<ClientHttpRequestInterceptor> interceptors,如果interceptors里面的攔截器數(shù)據(jù)不為空,在RestTemplate進(jìn)行http請(qǐng)求時(shí),這個(gè)請(qǐng)求就會(huì)被攔截器攔截進(jìn)行,攔截器實(shí)現(xiàn)接口ClientHttpRequestInterceptor,需要實(shí)現(xiàn)方法是

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

也就是說(shuō)攔截器需要完成http請(qǐng)求,并封裝一個(gè)標(biāo)準(zhǔn)的response返回。

ribbon中的攔截器

在Ribbon 中也定義了這樣的一個(gè)攔截器,并且注入到RestTemplate中,是怎么實(shí)現(xiàn)的呢?

在Ribbon實(shí)現(xiàn)中,定義了一個(gè)LoadBalancerInterceptor,具體的邏輯先不說(shuō),ribbon就是通過(guò)這個(gè)攔截器進(jìn)行攔截請(qǐng)求,然后實(shí)現(xiàn)負(fù)載均衡調(diào)用。

攔截器定義在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
  //定義注入器,用來(lái)將攔截器注入到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中是如何實(shí)現(xiàn)的?上面說(shuō)了攔截器的定義與攔截器注入器的定義,那么肯定會(huì)有個(gè)地方使用注入器來(lái)注入攔截器的。

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

@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中的注入器,調(diào)用注入方法。
   return () -> restTemplateCustomizers.ifAvailable(customizers -> {
      for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
        for (RestTemplateCustomizer customizer : customizers) {
          customizer.customize(restTemplate);
        }
      }
    });
  }
  //......
  }

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

還有關(guān)鍵的一點(diǎn)是:需要注入攔截器的目標(biāo)restTemplates到底是哪一些?因?yàn)镽estTemplate實(shí)例在context中可能存在多個(gè),不可能所有的都注入攔截器,這里就是@LoadBalanced注解發(fā)揮作用的時(shí)候了。

LoadBalanced注解

嚴(yán)格上來(lái)說(shuō),這個(gè)注解是spring cloud實(shí)現(xiàn)的,不是ribbon中的,它的作用是在依賴(lài)注入時(shí),只注入實(shí)例化時(shí)被@LoadBalanced修飾的實(shí)例。

例如我們定義Ribbon的RestTemplate的時(shí)候是這樣的

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

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

那么@LoadBalanced是如何實(shí)現(xiàn)這個(gè)功能的呢?其實(shí)都是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
@Qualifier
public @interface LoadBalanced {
}

很明顯,‘繼承'了注解@Qualifier,我們都知道以前在xml定義bean的時(shí)候,就是用Qualifier來(lái)指定想要依賴(lài)某些特征的實(shí)例,這里的注解就是類(lèi)似的實(shí)現(xiàn),restTemplates通過(guò)@Autowired注入,同時(shí)被@LoadBalanced修飾,所以只會(huì)注入@LoadBalanced修飾的RestTemplate,也就是我們的目標(biāo)RestTemplate。

攔截器邏輯實(shí)現(xiàn)

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

攔截請(qǐng)求執(zhí)行

@Override
public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
  ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
  //在這里負(fù)載均衡選擇服務(wù)
  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));
//執(zhí)行請(qǐng)求邏輯
  return execute(serviceId, ribbonServer, request);
}

我們重點(diǎn)看getServer方法,看看是如何選擇服務(wù)的

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

代碼配置隨機(jī)loadBlancer,進(jìn)入下面代碼

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

這里配置的是RandomRule,所以進(jìn)入RandomRule代碼

public Server choose(ILoadBalancer lb, Object key) {
  if (lb == null) {
    return null;
  }
  Server server = null;
​
  while (server == null) {
    if (Thread.interrupted()) {
      return null;
    }
    //獲取可用服務(wù)列表
    List<Server> upList = lb.getReachableServers();
    List<Server> allList = lb.getAllServers();
​
    //隨機(jī)一個(gè)數(shù)
    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;
}

隨機(jī)負(fù)載規(guī)則很簡(jiǎn)單,隨機(jī)整數(shù)選擇服務(wù),最終達(dá)到隨機(jī)負(fù)載均衡。我們可以配置不同的Rule來(lái)實(shí)現(xiàn)不同的負(fù)載方式。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java排序算法中的快速排序算法實(shí)現(xiàn)

    Java排序算法中的快速排序算法實(shí)現(xiàn)

    這篇文章主要介紹了Java排序算法中的快速排序算法實(shí)現(xiàn),通過(guò)一趟排序?qū)⒁判虻臄?shù)據(jù)分割成獨(dú)立的兩部分,其中一部分的所有數(shù)據(jù)都比另外一部分的所有數(shù)據(jù)都要小,然后再按此方法對(duì)這兩部分?jǐn)?shù)據(jù)分別進(jìn)行快速排序,需要的朋友可以參考下
    2023-12-12
  • springBoot 過(guò)濾器去除請(qǐng)求參數(shù)前后空格實(shí)例詳解

    springBoot 過(guò)濾器去除請(qǐng)求參數(shù)前后空格實(shí)例詳解

    這篇文章主要為大家介紹了springBoot 過(guò)濾器去除請(qǐng)求參數(shù)前后空格實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • Springboot如何同時(shí)裝配兩個(gè)相同類(lèi)型數(shù)據(jù)庫(kù)

    Springboot如何同時(shí)裝配兩個(gè)相同類(lèi)型數(shù)據(jù)庫(kù)

    這篇文章主要介紹了Springboot如何同時(shí)裝配兩個(gè)相同類(lèi)型數(shù)據(jù)庫(kù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Java壓縮集合的三種方法

    Java壓縮集合的三種方法

    這篇文章主要介紹了Java壓縮集合的三種方法,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下
    2021-01-01
  • Spring AOP 對(duì)象內(nèi)部方法間的嵌套調(diào)用方式

    Spring AOP 對(duì)象內(nèi)部方法間的嵌套調(diào)用方式

    這篇文章主要介紹了Spring AOP 對(duì)象內(nèi)部方法間的嵌套調(diào)用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • JVM常見(jiàn)垃圾收集器學(xué)習(xí)指南

    JVM常見(jiàn)垃圾收集器學(xué)習(xí)指南

    這篇文章主要為大家介紹了JVM常見(jiàn)垃圾收集器學(xué)習(xí)指南,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • 精通Java接口的使用與原理

    精通Java接口的使用與原理

    接口,在JAVA編程語(yǔ)言中是一個(gè)抽象類(lèi)型,是抽象方法的集合,接口通常以interface來(lái)聲明。一個(gè)類(lèi)通過(guò)繼承接口的方式,從而來(lái)繼承接口的抽象方法
    2022-03-03
  • Java實(shí)現(xiàn)二叉樹(shù)的建立、計(jì)算高度與遞歸輸出操作示例

    Java實(shí)現(xiàn)二叉樹(shù)的建立、計(jì)算高度與遞歸輸出操作示例

    這篇文章主要介紹了Java實(shí)現(xiàn)二叉樹(shù)的建立、計(jì)算高度與遞歸輸出操作,結(jié)合實(shí)例形式分析了Java二叉樹(shù)的創(chuàng)建、遍歷、計(jì)算等相關(guān)算法實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2019-03-03
  • java web激活郵箱并找回密碼

    java web激活郵箱并找回密碼

    這篇文章主要介紹了java web激活郵箱并找回密碼,在項(xiàng)目中要實(shí)現(xiàn)用戶(hù)注冊(cè)的郵箱激活以及忘記密碼重置密碼功能,感興趣的小伙伴們
    2015-11-11
  • 解決2022.3.1版本中?IDEA中?XML文件提示屎黃色背景的方法

    解決2022.3.1版本中?IDEA中?XML文件提示屎黃色背景的方法

    這篇文章主要介紹了解決2022.3.1版本中?IDEA中?XML文件屎黃色背景?的方法,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-01-01

最新評(píng)論