Spring Cloud Ribbon(一)

1、RestTemplate

    1.1簡介

spring框架提供的RestTemplate類可用於在應用中調用rest服務,它簡化了與http服務的通訊方式,統一了RESTful的標準,封裝了http連接, 咱們只須要傳入url及返回值類型便可。相較於以前經常使用的HttpClient,RestTemplate是一種更優雅的調用RESTful服務的方式。html

  在Spring應用程序中訪問第三方REST服務與使用Spring RestTemplate類有關。RestTemplate類的設計原則與許多其餘Spring *模板類(例如JdbcTemplate、JmsTemplate)相同,爲執行復雜任務提供了一種具備默認行爲的簡化方法。java

  RestTemplate默認依賴JDK提供http鏈接的能力(HttpURLConnection),若是有須要的話也能夠經過setRequestFactory方法替換爲例如 Apache HttpComponents、Netty或OkHttp等其它HTTP library。nginx

  考慮到RestTemplate類是爲調用REST服務而設計的,所以它的主要方法與REST的基礎緊密相連就不足爲奇了,後者是HTTP協議的方法:HEAD、GET、POST、PUT、DELETE和OPTIONS。例如,RestTemplate類具備headForHeaders()、getForObject()、postForObject()、put()和delete()等方法。git

1.二、實現

 最新api地址:https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.htmlgithub

首先建兩個項目web

 

 

RestTemplate包含如下幾個部分:算法

    • HttpMessageConverter 對象轉換器
    • ClientHttpRequestFactory 默認是JDK的HttpURLConnection
    • ResponseErrorHandler 異常處理
    • ClientHttpRequestInterceptor 請求攔截器

 spring-cloud-server的配置spring

 

 <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

application.propertiesapi

spring.application.name=spring-cloud-server
server.port=8080

RestTemplateServer.class緩存

@RestController
public class RestTemplateServer {
    @Value("${server.port}")
    private int port;

    @GetMapping("/orders")
    public String getAllOrder(){
        System.out.println("port:"+port);
        return "測試成功";
    }
}

啓動項目訪問結果以下

 

 

 spring-cloud-user的配置文件

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
 
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
server.port=8088

業務代碼RestTemplateUser.class

@RestController
public class RestTemplateUser {

    @Autowired
    RestTemplate restTemplate;

    //由於RestTemplate不存在因此要注入
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

    @GetMapping("/user")
    public String findById(){
        return restTemplate.getForObject("http://localhost:8080/orders",String.class);
    }
}

啓動項目訪問可獲得8080服務的結果

 

 

 這樣咱們初步完成了兩個獨立項目的通訊,若是不想在經過new的方式建立RestTemplate那也能夠經過build()方法建立,修改後以下

@RestController
public class RestTemplateUser {

    @Autowired
    RestTemplate restTemplate;

    //由於RestTemplate不存在因此要注入
//    @Bean
//    public RestTemplate restTemplate(){
//        return new RestTemplate();
//    }
    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder){
        return restTemplateBuilder.build();
    }

    @GetMapping("/user")
    public String findById(){

        return restTemplate.getForObject("http://localhost:8080/orders",String.class);
    }
}

可是如今不少服務架構都是多節點的,那麼咱們就要考慮多節點負載均衡的問題,這時最早想到的是Ribbon,修改代碼

修改cloud-cloud-user的pom.xml文件,增長

 <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
            <version>2.2.3.RELEASE</version>
        </dependency>

爲演示負載均衡,啓動兩個spring-cloud-server節點,再配置一個節點並啓動

 

 

 修改完後,再修改spring-cloud-user配置文件

server.port=8088

spring-cloud-server.ribbon.listOfServers=\
  localhost:8080,localhost:8081

這樣玩後有心的人就發現了,業務再用return restTemplate.getForObject("http://localhost:8080/orders",String.class);訪問另外一個項目就不合適了,更改RestTemplateUser.class類

@RestController
public class RestTemplateUser {

    @Autowired
    RestTemplate restTemplate;

    //由於RestTemplate不存在因此要注入
//    @Bean
//    public RestTemplate restTemplate(){
//        return new RestTemplate();
//    }
    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder){
        return restTemplateBuilder.build();
    }
    @Autowired
    LoadBalancerClient loadBalancerClient;


    @GetMapping("/user")
    public String findById(){

        ServiceInstance serviceInstance=loadBalancerClient.choose("spring-cloud-server");
        String url=String.format("http://%s:%s",serviceInstance.getHost(),serviceInstance.getPort()+"/orders");
        return restTemplate.getForObject(url,String.class);
        //經過服務名稱在配置文件中選擇端口調用
       // return restTemplate.getForObject("http://localhost:8080/orders",String.class);
    }
}

訪問下面地址,多點幾回

 

 

 

 

 

 說到 了這裏那咱們如今就要來看下Ribbon了

2、Ribbon簡介

須要解決的問題:
① 如何在配置Eureka Client註冊中心時不去硬編碼Eureka Server的地址?
② 在微服務不一樣模塊間進行通訊時,如何不去硬編碼服務提供者的地址?
③ 當部署多個相同微服務時,如何實現請求時的負載均衡? 
實現負載均衡方式1:經過服務器端實現負載均衡(nginx)

 

 實現負載均衡方式2:經過客戶端實現負載均衡

 

 

Ribbon是什麼?
Ribbon是Netflix發佈的雲中間層服務開源項目,其主要功能是提供客戶端實現負載均衡算法。Ribbon客戶端組件提供一系列完善的配置項如鏈接超時,重試等。簡單的說,Ribbon是一個客戶端負載均衡器,咱們能夠在配置文件中Load Balancer後面的全部機器,Ribbon會自動的幫助你基於某種規則(如簡單輪詢,隨機鏈接等)去鏈接這些機器,咱們也很容易使用Ribbon實現自定義的負載均衡算法。
下圖展現了Eureka使用Ribbon時的大體架構: 

 

 Ribbon工做時分爲兩步:第一步選擇Eureka Server,它優先選擇在同一個Zone且負載較少的Server;第二步再根據用戶指定的策略,再從Server取到的服務註冊列表中選擇一個地址。其中Ribbon提供了不少策略,例如輪詢round robin、隨機Random、根據響應時間加權等。

爲了更好的瞭解Ribbon後面確定是要進入源碼,在進入源碼以前作個鋪墊,我再來改造上面的代碼,引入@LoadBalanced註解,修改下

@RestController
public class RestTemplateUser {

    @Autowired
    RestTemplate restTemplate;

    //由於RestTemplate不存在因此要注入
//    @Bean
//    public RestTemplate restTemplate(){
//        return new RestTemplate();
//    }
//    @Bean
//    public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder){
//        return restTemplateBuilder.build();
//    }
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder){
        return restTemplateBuilder.build();
    }

//    @Autowired
//    LoadBalancerClient loadBalancerClient;


    @GetMapping("/user")
    public String findById(){

//        ServiceInstance serviceInstance=loadBalancerClient.choose("spring-cloud-server");
//        String url=String.format("http://%s:%s",serviceInstance.getHost(),serviceInstance.getPort()+"/orders");
//        return restTemplate.getForObject(url,String.class);
        //經過服務名稱在配置文件中選擇端口調用
       return restTemplate.getForObject("http://spring-cloud-server/orders",String.class);
    }
}

啓動項目後會發現@LoadBalanced也能實現負載均衡,這裏面咱們就應該進入看下@LoadBalanced到底作了啥,在沒用@LoadBalanced以前getForObject只能識別ip的路徑,並不能識別服務名進行負載均衡,因此咱們要看下@LoadBalanced是怎麼實現的負載均衡

 

 在看碼源前先劇透下,以前某人說我寫的東西很差看懂,那我此次多花點時間畫圖,restTemplate.getForObject("http://spring-cloud-server/orders",String.class);這個方法他調用的是一個服務器名稱,咱們知道,若是要訪問一個服務器咱們一個具體的路徑才能訪問,那麼@LoadBalanced是怎麼作到的由一個服務名獲得一個具體的路徑呢,這就要說到攔截器,他在調用真實路徑前會有攔截器攔截服務器名,而後拿到服務器去解析而後拼接獲得一個真實的路徑名稱,而後拿真實路徑去訪問服務,詳細的步驟在源碼講解中具體分析。

咱們點擊@LoadBalanced進入以下圖

@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {

}

咱們會發現有一個叫@Qualifier的東西,其實這玩意就是一個標記的做用,但爲了後面的源碼分析,這裏仍是說明下@Qualifiler的用法

咱們在spring-cloud-user項目中新建一個Qualifier包,在包中建三個類

public class QualifierTest {
    private String name;

    public QualifierTest(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
//@Configuration用於定義配置類,可替換xml配置文件,
// 被註解的類內部包含有一個或多個被@Bean註解的方法,
// 這些方法將會被AnnotationConfigApplicationContext或
// AnnotationConfigWebApplicationContext類進行掃描,
// 並用於構建bean定義,初始化Spring容器。
@Configuration
public class QualifierConfiguration {
    @Qualifier
    @Bean("QualifierTest1")
    QualifierTest QualifierTest1(){
        return new QualifierTest("QualifierTest1");
    }

    @Qualifier
    @Bean("QualifierTest2")
    QualifierTest QualifierTest2(){
        return new QualifierTest("QualifierTest2");
    }
}
@RestController
public class QualifierController {
    //@Qualifier做用是找到全部申明@Qualifier標記的實例
    @Qualifier
    @Autowired
    List<QualifierTest> testClassList= Collections.emptyList();

    @GetMapping("/qualifier")
    public Object test(){
        return testClassList;
    }
}

啓動項目訪問接口結果以下

 

 

除掉QualifierConfiguration.class中其中一個@Qualifier後刷新接口,會發現結果以下,這兩個結果對比能夠證實@Qualifier其實就是一個標記的做用

 

 有了這個概念後咱們進入LoadBalancerAutoConfiguration.class這個自動裝配類中會發現有和我剛剛演示同樣的代碼,其實我就是從這個裝配類中抄的,哈哈;

 

 看到這裏相信你們就明白了,由於紅框的內容加了@LoadBalanced註解就能使RestTemplate生效是由於@Qualifier註解,有了這個概念接着往下走,在上圖這個自動裝配類中會加載注入全部加了@LoadBalanced註解的RestTemplate,這一步很關鍵,由於後面的攔截器加載跟這一步有關聯;居然咱們來到了LoadBalancerAutoConfiguration,這個自動裝配類來了,那就聊聊這裏面的Bean裝配,下面這個圖是Bean的自動裝配過程

 

 

首先看自動裝配類攔截器LoadBalancerInterceptor

@Configuration(proxyBeanMethods = false)
    @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
    static class LoadBalancerInterceptorConfig {
        //定義一個Bean
        @Bean
        public LoadBalancerInterceptor ribbonInterceptor(
                LoadBalancerClient loadBalancerClient,
                LoadBalancerRequestFactory requestFactory) {
            return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
        }
        //將定義的Bean做爲參數傳入
        @Bean
        @ConditionalOnMissingBean
        public RestTemplateCustomizer restTemplateCustomizer(
                final LoadBalancerInterceptor loadBalancerInterceptor) {
            return restTemplate -> {
                List<ClientHttpRequestInterceptor> list = new ArrayList<>(
                        restTemplate.getInterceptors());
//設置攔截器 list.add(loadBalancerInterceptor);
//設置到restTemplate中去 restTemplate.setInterceptors(list); }; } }

 

@Bean
    public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
            final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
        return () -> restTemplateCustomizers.ifAvailable(customizers -> {
//對restTemplates進行for循環,對每個restTemplate加一個包裝叫RestTemplateCustomizer
//這個包裝的意義是能夠對restTemplate再加一個自定義的攔截
for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) { for (RestTemplateCustomizer customizer : customizers) { customizer.customize(restTemplate); } } }); }

有了上面的包裝,纔有下面的攔截的增強

 

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

說到這裏再將時序圖畫一下,我最初是經過@LoadBalanced註解進入到他的裝配類LoadBalancerAutoConfiguration,而後在LoadBalancerAutoConfiguration裝配類中找到攔截器的加載和加強的,根據這個邏輯畫出的時序圖以下

 

 以前在開篇中還講到過用下面這種方式進行負載均衡訪問,其實針對LoadBalancerClient是同樣的,他裏面有一個RibbonAutoConfiguration

   @Autowired
    LoadBalancerClient loadBalancerClient;

在RibbonAutoConfiguration裝配類中會找到一個代碼若是下,他在裝配類中對LoadBalancerClient進行初始化

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

咱們看頭文件,會發現加載了LoadBalancerAutoConfiguration

 

 這時補充下時序圖以下,這就是Bean的加載過程,通過這一過程攔截器就算是加載進去了

 

 

 有了攔截器後,下一步要看的話確定就是來看下攔截器到底作了啥,進入LoadBalancerInterceptor攔截器,會發現他會最終進入以下方法

@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);
//將攔截委託給loadBalancer進行實現
return this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution)); }

跟進loadBalancer看下作了啥(LoadBalancerClient注入是在RibbonAutoConfiguration配置類中完成的),跟蹤進去發現最終仍是調用了RibbonLoadBalancerClient

 

進入execute方法,會發現裏面只作了兩件事

public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
            throws IOException {
//得到負載均衡器 ILoadBalancer loadBalancer
= getLoadBalancer(serviceId);
//根據負載均衡器返回Server,這個Server返回是指定的某一個地址,其實負載的解析在這裏就完成了 Server server
= getServer(loadBalancer, hint); 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); }

 

進入getLoadBalancer看看他作了啥,在看以前先看下他的類關係圖

 ILoadBalancer接口:定義添加服務,選擇服務,獲取可用服務,獲取全部服務方法

AbstractLoadBalancer抽像類:定義了一個關於服務實例的分組枚舉,包含了三種類型的服務:ALL表示全部服務,STATUS_UP表示正常運行的服務,STATUS_NOT_UP表示下線的服務。

 BaseLoadBalancer:

1):類中有兩個List集合,一個List集合用來保存全部的服務實例,還有一個List集合用來保存當前有效的服務實例

2):定義了一個IPingStrategy,用來描述服務檢查策略,IPingStrategy默認實現採用了SerialPingStrategy實現

3):chooseServer方法中(負載均衡的核心方法),調用IRule中的choose方法來找到一個具體的服務實例,默認實現是RoundRobinRule

4):PingTask用來檢查Server是否有效,默認執行時間間隔爲10秒

5):markServerDown方法用來標記一個服務是否有效,標記方式爲調用Server對象的setAlive方法設置isAliveFlag屬性爲false

6):getReachableServers方法用來獲取全部有效的服務實例列表

7):getAllServers方法用來獲取全部服務的實例列表

8):addServers方法表示向負載均衡器中添加一個新的服務實例列表

 DynamicServerListLoadBalancer:主要是實現了服務實例清單在運行期間的動態更新能力,同時提供了對服務實例清單的過濾功能。

 ZoneAwareLoadBalancer:主要是重寫DynamicServerListLoadBalancer中的chooseServer方法,因爲DynamicServerListLoadBalancer中負責均衡的策略依然是BaseLoadBalancer中的線性輪詢策略,這種策略不具有區域感知功能

 NoOpLoadBalancer:不作任何事的負載均衡實現,通常用於佔位(然而貌似從沒被用到過)。

有了這個概念後咱們下面就來重點看BaseLoadBalancer,在嘮嘮以前先補充下時序圖

 

 點擊getLoadBalancer進入以下代碼

 

 在向下寫前,先提早說下ILoadBalancer這個類裏面會幫咱們作一件事,他會根據負載均衡的一個算法進行一個負載的選擇,可是在負載以前他會有一個類的初始化過程,在選擇完成後ILoadBalancer實現返回,而後將ILoadBalancer作爲參數傳給Server server = getServer(loadBalancer, hint);在ILoadBalancer中他有一個實現會去調用BaseLoadBalancer.chooseServer,它會調用rule.choose(),rule的初始化是在ZoneAvoidanceRule中完成的,因此接下來看要分兩部分,ILoadBalancer作爲一個負載均衡器,而後getServer會把這個負載均衡器會傳過去後進行一個負載的計算,這個流程說完後可能不少人還在懵逼狀態,那接下來咱們就經過代碼來看他的實現,首先看ILoadBalancer的實現是誰

接着上圖來,點擊getLoadBalancer

 

 而後點擊getInstance

 

 

    @Override
    public <C> C getInstance(String name, Class<C> type) {
//這裏面經過傳送一個name和一個type獲得一個實例,這裏面是一個工廠模式,咱們點擊getInstance選擇它的NamedContextFactory實現進去 C instance
= super.getInstance(name, type); if (instance != null) { return instance; } IClientConfig config = getInstance(name, IClientConfig.class); return instantiateWithConfig(getContext(name), type, config); }

    public <T> T getInstance(String name, Class<T> type) {
//工廠模式會加載一個context AnnotationConfigApplicationContext context
= getContext(name); if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, type).length > 0) { return context.getBean(type); } return null; }

 

 

getContext方法裏面是用spring寫的,比較複雜,點擊getContext後以下圖,這裏面是有個默認緩存的,若是沒有會用createContext(name)根據名稱建立一個緩存

 

回退到AnnotationConfigApplicationContext context = getContext(name);

 

public <T> T getInstance(String name, Class<T> type) {
        AnnotationConfigApplicationContext context = getContext(name);
        if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,
                type).length > 0) {
經過type獲得一個Bean
return context.getBean(type); } return null; }

再回退到C instance = super.getInstance(name, type);進行打debug看下他返回的是什麼類型的ILoadBalancer

 

 

 從上圖能夠看到返回的是一個ZoneAwareLoadBalancer的ILoadBalancer,而後就拿着ILoadBalancer傳入getServer(loadBalancer, hint);中,這時的時序圖就以下了

 

 

 

到了這一步獲取負載均衡器這一過程就完成了,下面就是來完成過程2.經過負載均衡器中配置的默認負載均衡算法選一個合適的Server,咱們進入

Server server = getServer(loadBalancer, hint);的getServer方法,點擊進去以下,這裏面其實進行的就是針對一個服務節點的選擇,其中loadBalancer.chooseServer(hint != null ? hint : "default");就是一種算法的選擇,咱們這裏面沒有選擇算法,因此採用默認算法BaseLoadBalancer

進入默認算法截圖以下

 

 而後他會調用rule.choose(key);方法,咱們能夠在進入方法前先看下IRule是啥,經過下圖咱們能夠很清楚的看到IRule裏面全部的實現,之因此在這裏提到IRule是由於IRule是Ribbon中實現負載均衡的一個很重要的規則,他實現了重置規則、輪詢規則、隨機規則及客戶端是否啓動輪詢的規則;在後面我看機會說其中一到兩種比較經常使用的算法說明下

 

 

 咱們這裏rule.choose(key);採用的是輪詢算法,選擇PredicateBasedRule,進去後截圖以下

 

 

    @Override
    public Server choose(Object key) {
        ILoadBalancer lb = getLoadBalancer();
//根據咱們的過濾規則過濾以後會根據輪詢去進行篩選,其中lb.getAllServers是獲取一個靜態的服務列表 Optional
<Server> server = getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key); if (server.isPresent()) { return server.get(); } else { return null; } } }

咱們進入chooseRoundRobinAfterFiltering,下面的輪詢比較簡單,他先把節點數量eligible.size()傳進去,而後經過incrementAndGetModulo方法獲取一個下標

public Optional<Server> chooseRoundRobinAfterFiltering(List<Server> servers, Object loadBalancerKey) {
//獲得咱們全部的配置信息 List
<Server> eligible = getEligibleServers(servers, loadBalancerKey);
//配置數量
if (eligible.size() == 0) { return Optional.absent(); }
//進行輪詢計算
return Optional.of(eligible.get(incrementAndGetModulo(eligible.size()))); }

能夠進入incrementAndGetModulo方法看下

private int incrementAndGetModulo(int modulo) {
        for (;;) {
//獲取下一個節點的當前值
int current = nextIndex.get();
//根據這個值進行取模運算
int next = (current + 1) % modulo;
//設置下一個值
if (nextIndex.compareAndSet(current, next) && current < modulo) return current; } }

上面就是輪詢算法的實現,這個算法的實現比較簡單,下面再來看一個隨機算法的實現

 

 

 

  

    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; } //傳入節點數量,而後隨機取值,若是有人想看怎麼取的點擊這個chooseRandomInt就能夠看到,它實現就一句話,就是把數量傳進去獲得一個隨機值 int index = chooseRandomInt(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; }

隨機實現聊完後,再回到咱們跟蹤的代碼 return Optional.of(eligible.get(incrementAndGetModulo(eligible.size())));經過算法獲得具體的節點後eligible.get就能夠獲得 對應下標的服務列表,這時就獲得了什麼localhost:8082的具體端口號了,這一步完成後其實Server server = getServer(loadBalancer, hint);的活就作完了,下面的活就是拿着具體端口去重構了,更新下時序圖

 

 

 

 

 項目中全部例子源碼:https://github.com/ljx958720/spring-cloud-Ribbon-1-.git

相關文章
相關標籤/搜索