Ribbon與Spring cloud整合源碼分析

簡(jiǎn)價(jià)

Ribbon是一種客戶端的負(fù)載均衡器。提供了多種負(fù)載均衡的算法,支持多種協(xié)議(HTTP,TCP,UDP),并提供了故障容錯(cuò)的能力。官方網(wǎng)址為:https://github.com/Netflix/ribbon

Ribbon版Hello World

我們?cè)偈褂玫臅r(shí)候需要指定Server對(duì)象也就是可以做為負(fù)載的服務(wù)器,以及負(fù)載的Rule規(guī)則,默認(rèn)采用的是輪詢的規(guī)則。
代碼如下,里面有詳細(xì)的注釋:

package com.ivan.ribbon;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;

import com.google.common.collect.Lists;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.LoadBalancerBuilder;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.reactive.LoadBalancerCommand;
import com.netflix.loadbalancer.reactive.ServerOperation;

import rx.Observable;

public class RibbonClient {
   public static void main(String[] args) throws Exception {
       // 提供服務(wù)的服務(wù)器列表, 這里可以根據(jù)具體的測(cè)試url提供多個(gè)url。
       List<Server> servers = Lists.newArrayList(new Server("localhost", 8000), new Server("localhost", 8001));
       // 負(fù)載均衡器, 這里可以設(shè)置rule
       BaseLoadBalancer loadBalancer = LoadBalancerBuilder.newBuilder().buildFixedServerListLoadBalancer(servers);
       //這個(gè)可以提交具體的執(zhí)行命令邏輯。需要傳入具體的負(fù)載均衡器
       LoadBalancerCommand<String> command = LoadBalancerCommand.<String> builder().withLoadBalancer(loadBalancer).build();
       //連續(xù)執(zhí)行10次,這樣使可以看到具體的效果了。
       for (int i = 0; i < 10; i++) {
           command.submit(new ServerOperation<String>() {
               public Observable<String> call(Server server) {
                   URL url;
                   //這里的path是能夠訪問(wèn)的url
                   String path = "/provider";
                   InputStream inputStream = null;
                   InputStreamReader inputStreamReader = null;
                   BufferedReader reader = null;
                   try {
                       url = new URL("http://" + server.getHost() + ":" + server.getPort() + path);
                       HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                       inputStream = conn.getInputStream();
                       inputStreamReader = new InputStreamReader(inputStream);
                       reader = new BufferedReader(inputStreamReader);
                       String tempLine = null;
                       StringBuffer resultBuffer = new StringBuffer();
                       while ((tempLine = reader.readLine()) != null) {
                           resultBuffer.append(tempLine);
                       }
                       String data = resultBuffer.toString();
                       System.out.println("data : " + data);
                       return Observable.just(data);
                   } catch (Exception e) {
                       return Observable.error(e);
                   } finally {
                       if (reader != null) {
                           try {
                               reader.close();
                           } catch (IOException e) {
                           }
                       }

                       if (inputStreamReader != null) {
                           try {
                               inputStreamReader.close();
                           } catch (IOException e) {
                           }
                       }

                       if (inputStream != null) {
                           try {
                               inputStream.close();
                           } catch (IOException e) {
                           }
                       }

                   }
               }
           }).toBlocking().first();

       }

   }
}

負(fù)載均衡規(guī)則

Ribbon 提供了若干個(gè)內(nèi)置的負(fù)載規(guī)則如下圖所示:


image.png
  • RoundRobinRule: 系統(tǒng)默認(rèn)的規(guī)則, 通過(guò)簡(jiǎn)單的輪詢服務(wù)列表來(lái)選擇服務(wù)器
  • AvailabilityFilteringRule: 該規(guī)則會(huì)忽略以下服務(wù)器:
    1)無(wú)法連接的服務(wù)器: 在默認(rèn)情況下, 如果 3 次連接失敗, 該服務(wù)器將會(huì)被置為
    “ 短路” 的狀態(tài), 該狀態(tài)將持續(xù) 30 秒, 如果再次連接失敗, “ 短路” 狀態(tài)的持
    續(xù) 時(shí) 間 將 會(huì) 以 幾 何 級(jí) 增 加 。 可 以 通 過(guò) 修 改
    niws.loadbalancer.<clientName>.connectionFailureCountThreshold 屬性, 來(lái)
    配置連接失敗的次數(shù)。
    2) 并發(fā)數(shù)過(guò)高的服務(wù)器: 如果連接到該服務(wù)器的并發(fā)數(shù)過(guò)高, 也會(huì)被這個(gè)規(guī)則忽
    略, 可以通過(guò)修改<clientName>.ribbon.ActiveConnectionsLimit 屬性來(lái)設(shè)定最
    高并發(fā)數(shù)。
  • WeightedResponseTimeRule: 為每個(gè)服務(wù)器賦予一個(gè)權(quán)重值, 服務(wù)器的響應(yīng)時(shí)間
    越長(zhǎng), 該權(quán)重值就是越少, 這個(gè)規(guī)則會(huì)隨機(jī)選擇服務(wù)器, 這個(gè)權(quán)重值有可能會(huì)決定
    服務(wù)器的選擇。
  • ZoneAvoidanceRule: 該規(guī)則以區(qū)域、 可用服務(wù)器為基礎(chǔ), 進(jìn)行服務(wù)器選擇。 使用
    Zone 對(duì)服務(wù)器進(jìn)行分類, 可以理解為機(jī)架或者機(jī)房。
  • BestAvailableRule: 忽略“ 短路” 的服務(wù)器, 并選擇并發(fā)數(shù)較低的服務(wù)器。
  • RandomRule: 顧名思義, 隨機(jī)選擇可用的服務(wù)器。
  • RetryRule: 含有重試的選擇邏輯, 如果使用 RoundRobinRule 選擇服務(wù)器無(wú)法連
    接, 那么將會(huì)重新選擇服務(wù)器。

與Spring Cloud整合

1:先在pom.xml文件里加入需要的依賴, 關(guān)鍵信息如下:

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Edgware.SR4</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
        </dependency>
    </dependencies>

2:寫(xiě)啟動(dòng)類,需要加入EnableDiscoveryClient人注解。代碼如下:

package com.ivan.consumer;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * 
 * 功能描述: 
 * 
 * @version 2.0.0
 * @author zhiminchen
 */

@SpringBootApplication
@EnableDiscoveryClient
public class App 
{
    public static void main( String[] args )
    {
        SpringApplication.run(App.class, args);
    }
}

3:寫(xiě)個(gè)自定義負(fù)載的規(guī)則,也可以沒(méi)有這個(gè)類,這樣默認(rèn)就是輪詢的規(guī)則。代碼如下:

package com.ivan.consumer.rule;

import java.util.List;

import org.apache.commons.lang.math.RandomUtils;
import org.springframework.stereotype.Component;

import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.Server;

/**
 * 
 * 功能描述: 自定義Rule,80%的概率選第一臺(tái)服務(wù)器,20%的概率選第二臺(tái)服務(wù)器
 * 
 * @version 2.0.0
 * @author zhiminchen
 */
@Component
public class MyRule implements IRule {

    private ILoadBalancer lb;

    @Override
    public Server choose(Object key) {
        List<Server> allServer = lb.getAllServers();
        int value = RandomUtils.nextInt(10);
        Server server = null;
        if (value > 8) {
            server = allServer.get(0);
        } else {
            server = allServer.get(1);
        }
        System.out.println("port is : " + server.getPort());
        return server;
    }

    @Override
    public void setLoadBalancer(ILoadBalancer lb) {
        this.lb = lb;
    }

    @Override
    public ILoadBalancer getLoadBalancer() {
        return this.lb;
    }

}

4:編寫(xiě)服務(wù)調(diào)用者,代碼如下:

package com.ivan.consumer.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
@Configuration
public class ConsumerController {
    
    //這個(gè)值會(huì)自動(dòng)注入的噢
    @Autowired
    private LoadBalancerClient client;
    
    //這個(gè)值也會(huì)自動(dòng)注入的噢
    @Autowired
    private SpringClientFactory factory;

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

    @RequestMapping(value = "/consumer", method = RequestMethod.GET)
    public String consumer() {
        RestTemplate template = getRestTemplate();
        // 根據(jù)應(yīng)用名稱調(diào)用服務(wù)
        String json = template.getForObject("http://provider/provider", String.class);
        return json;
    }
}

代碼效果應(yīng)該是大部分請(qǐng)求會(huì)調(diào)用到Server為零的服務(wù)器上,也就是說(shuō)我們自定義的Rule起作用了,同時(shí)可以看到控制臺(tái)有相應(yīng)的輸出記錄。

源碼分析

上面的代碼我們會(huì)有兩個(gè)疑問(wèn):

  • 為什么我們自定義的Rule,只加上了@Component注解,這個(gè)規(guī)則便能起作用。
  • SpringClientFactory 與 LoadBalancerClient 這兩個(gè)類是如何注入到我們自定義的Controller里的。
    因?yàn)镾pring Cloud是基與Spring Boot進(jìn)行構(gòu)建的, 之所以上面的類能夠起作用,核心還是因?yàn)镾pring Boot 的SpringFactoriesLoader 機(jī)制在起作用。我們可以找到spring-cloud-netflix-cord的jar包,里面有個(gè)spring.factories文件,在這個(gè)文件里,我們可以看到會(huì)自動(dòng)加載RibbonAutoConfiguration類。代碼如下圖所示:


    image.png

    在RibbonAutoConfiguration類里,我們可以看到定義了SpringClientFactory 與 LoadBalancerClient 這兩個(gè)Bean, 這就解釋了為什么我們的應(yīng)用代碼可以注入這兩個(gè)類了。代碼截圖如下圖所示:


    image.png

    至于我們自定義的Rule能夠起作用,是因?yàn)槲覀兊腟pring容器會(huì)掃描當(dāng)前錄與子錄的代碼,將Component注釋的類自動(dòng)注入到Spring IOC容器類, 如果我們沒(méi)有配置相應(yīng)的Rule, Spring Cloud會(huì)為我們默認(rèn)加載一個(gè)Rule類,這個(gè)類在RibbonClientConfiguration里定義,代碼如下圖:
    image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,715評(píng)論 19 139
  • 斷斷續(xù)續(xù)看Ribbon的源碼差不多也有7-8天了,總算告一段落。本文記錄了這些天對(duì)源碼的閱讀過(guò)程與一些分析理解,如...
    程序猿DD閱讀 6,691評(píng)論 6 11
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,290評(píng)論 6 342
  • 簡(jiǎn)介 Spring Cloud Ribbon是一個(gè)基于HTTP和TCP的客戶端負(fù)載均衡工具,它基于Netflix ...
    Chandler_玨瑜閱讀 252,284評(píng)論 22 183
  • 軟件是有生命的,你做出來(lái)的架構(gòu)決定了這個(gè)軟件它這一生是坎坷還是幸福。 本文不是講解如何使用Spring Cloud...
    Bobby0322閱讀 23,012評(píng)論 3 166

友情鏈接更多精彩內(nèi)容