spring cloud之Ribbon(三)

目錄

Ribbon

什麼是Ribbon

Ribbon做什麼用的

Ribbon的初步配置

Ribbon負載均衡

Ribbon核心組件IRule

Ribbon自定義

 

什麼是Ribbon

Spring Cloud Ribbon是基於Netflix Ribbon實現的一套客戶端       負載均衡的工具

簡單的說,Ribbon是Netflix發佈的開源項目,主要功能是提供客戶端的軟件負載均衡算法,將Netflix的中間層服務連接在一起。

Ribbon客戶端組件提供一系列完善的配置項如連接超時,重試等。簡單的說,就是在配置文件中列出Load Balancer(簡稱LB)後面所有的機器,

Ribbon會自動的幫助你基於某種規則(如簡單輪詢,隨機連接等)去連接這些機器。我們也很容易使用Ribbon實現自定義的負載均衡算法。

常見的負載均衡軟件有:Nginx,LVS,硬件有F5(一個字貴)等

       在深圳很多肯德基/麥當勞,都有客戶端/人工點餐,在多個客戶端使用中,正常(一般)人都會去人少的那一臺機器,這樣效率更高;Ribbon就是負責這個效率的

Ribbon做什麼用的

LB負載均衡  分兩種

集中式LB(偏硬件)

即在服務的消費方和提供方之間使用獨立的LB設施(可以是硬件,如F5, 也可以是軟件,如nginx), 由該設施負責把訪問請求通過某種策略轉發至服務的提供方;

進程內LB(偏軟件)

將LB邏輯集成到消費方,消費方從服務註冊中心獲知有哪些地址可用,然後自己再從這些地址中選擇出一個合適的服務器。

Ribbon就屬於進程內LB,它只是一個類庫,集成於消費方進程,消費方通過它來獲取到服務提供方的地址。

點擊進入     Ribbon在github地址  

架構說明:

Ribbon在工作時分成兩步

第一步先選擇 EurekaServer ,它優先選擇在同一個區域內負載較少的server.

第二步再根據用戶指定的策略,在從server取到的服務註冊列表中選擇一個地址。

其中Ribbon提供了多種策略:比如輪詢、隨機和根據響應時間加權。

 

Ribbon的初步配置

修改springcloud-consumer-dept-80工程/客戶端

完成真正的通過微服務名字從eureka上找到並訪問

需要獲取新的功能/插件   (spring boot項目中)

  •   GAV-maven  pom.xml文件  jar包導入

  •     註解支持   對應的@EnableXXX

③     編碼  業務

<dependencies>
		<dependency><!-- 自己定義的api -->
			<groupId>com.jiangjy.springcloud</groupId>
			<artifactId>springcloud-model-api</artifactId>
			<version>${project.version}</version>
		</dependency>
		<!-- Ribbon相關,詳情見文檔 -->
		<!-- eureka客戶端 -->
		<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>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-config</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<!-- 修改後立即生效,熱部署 -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>springloaded</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
		</dependency>
	</dependencies>

修改application.yml   追加eureka的服務註冊地址

server:
  port: 80

eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
      

 對ConfigBean進行新註解@LoadBalanced    獲得Rest時加入Ribbon的配置

主啓動類DeptConsumer80_App添加@EnableEurekaClient

修改DeptController_Consumer客戶端訪問類

先啓動3個eureka集羣后,再啓動springcloud-provider-dept-8001並註冊進eureka

啓動springcloud-consumer-dept-80

測試

http://localhost/consumer/dept/getById/1

http://localhost/consumer/dept/getAll

小總結:   Ribbon和Eureka整合後Consumer可以直接調用服務而不用再關心地址和端口號

 

Ribbon負載均衡

  • 參考springcloud-provider-dept-8001,新建兩份,分別命名爲8002,8003
  • 新建8002/8003數據庫,各自微服務分別連各自的數據庫
  • 修改8002/8003各自YML: 端口/數據庫連接

備註:對外暴露的統一服務器實例名:一定不能改

啓動3個eureka集羣配置區

啓動3個Dept微服務啓動並各自測試通過

http://localhost:8001/dept/getAll

http://localhost:8002/dept/getAll

http://localhost:8003/dept/getAll

啓動springcloud-consumer-dept-80

客戶端通過Ribbon完成負載均衡並訪問上一步的Dept微服務

總結:Ribbon其實就是一個軟負載均衡的客戶端組件,

他可以和其他所需請求的客戶端結合使用,和eureka結合只是其中的一個實例。

Ribbon核心組件IRule

Rule:根據特定算法中從服務列表中選取一個要訪問的服務

默認自帶的7種方法:

RoundRobinRule     輪詢

RandomRule      隨機

AvailabilityFilteringRule    會先過濾掉由於多次訪問故障而處於斷路器跳閘狀態的服務,還有併發的連接數量超過閾值的服務,然後對剩餘的服務列表按照輪詢策略進行訪問

WeightedResponseTimeRule      根據平時響應時間計算所有服務的權重,響應時間越快服務權重越大被選中的概率越高。剛啓動時如果統計信息不足,則使用RoundRobinRule策略,等統計信息足夠,會切換到WeightedResponseTimeRule

RetryRule  先按照RoundRobinRule的策略獲取服務,如果獲取服務失敗則在指定時間內進行充實,獲取可用的服務

BestAvailableRule  會先過濾掉由於多次訪問故障而處於熔斷器跳閘狀態的服務,然後選擇一個併發量最小的服務

ZoneAvoidanceRule  默認規則,複合判斷server所在區域的性能和server的可用性選擇服務器

 

在ConfigBean.java中

@Bean
	public IRule myRule() {
		return new RoundRobinRule();//輪詢算法
//		return new RandomRule();//使用隨機算法替代默認的輪詢算法
//		return new RetryRule();
	}

 

 

Ribbon自定義

 

修改springcloud-model-consumer-dept-80

主啓動類加@RibbonClient

       在啓動該微服務的時候就能去加載我們的自定義Ribbon配置類,從而使配置生效,形如:@RibbonClient(name=”SPRINGCLOUD-MODEL-DEPT”,configuration=MySelfRule.class)

注意配置細節

官方文檔明確給出了警告:這個自定義配置類不能放在@ComponentScan所掃描的當前包下以及子包下,否則我們自定義的這個配置類就會被所有的Ribbon客戶端所共享,也就是說:我們達不到特殊化定製的目的;而@ComponentScan 就在主啓動類的@SpringBootApplication 裏面

 

步驟

  • 新建package  com.jiangjy.myrule    

              新建自定義Ribbon的規則類MySelfRule.java

package com.jiangjy.myrule;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;

@Configuration   //配置spring.xml形式的註解
public class MySelfRult {

	@Bean //交給容器管理
	public IRule myRule() {
		
		return new RandomRule();//Ribbon默認輪詢,自定義隨機

	}
}

修改主啓動類

package com.jiangjy.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import com.jiangjy.myrule.MySelfRult;
@SpringBootApplication
@EnableEurekaClient  //和eureka整合並使用客戶端
//在啓動微服務的時候就能去加載我們自定義的Ribbon配置類,從而使配置生效
@RibbonClient(name="SPRINGCLOUD-MODEL-DEPT",configuration=MySelfRult.class)
public class DeptConsumer80_APP {
	public static void main(String[] args) {
		SpringApplication.run(DeptConsumer80_APP.class, args);
	}
}

 

圖解

測試及結果

 

自定義規則深度解析

問題:在輪詢策略的基礎上,添加新的需求,每次服務器要求被調用6次,即以前每臺機器一次,現在是每臺機器6次。

 

解析源碼:點擊進入https://github.com/Netflix/ribbon/blob/master/ribbon-loadbalancer/src/main/java/com/netflix/loadbalancer/RandomRule.java

參考源碼修改爲我們需求需要的RandomRule_Jack.java

package com.jiangjy.myrule;

import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;

import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

public class RandomRule_Jack extends AbstractLoadBalancerRule {

	//total = 0  //當total==6以後,我們的指針才往下走
	//index = 0  //當前對外提供服務的服務器地址
	// total 需要重新置爲0,但是達到過一個6次,index = 1
	// 分析:我們需要6次,但是微服務只有8001,8002,8003 三臺機器
	//所以需要我們每讀取6次, index加1次,到達三次(三臺機器滿足條件)
	
	private int total = 0; //總共被調用的次數,目前要求每臺被調用6次
	private int currentIndex = 0; //當前提供服務的機器號碼
	
    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 = chooseRandomInt(serverCount);
            server = upList.get(index);*/
            
            //修改部分
            if (total < 6) {
				server = upList.get(currentIndex);
				total++;
			}else {
				total = 0;
				currentIndex++;
				if (currentIndex >= upList.size()) {
					currentIndex = 0;
				}
			}
            
            

            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;

    }

    protected int chooseRandomInt(int serverCount) {
        return ThreadLocalRandom.current().nextInt(serverCount);
    }

	@Override
	public Server choose(Object key) {
		return choose(getLoadBalancer(), key);
	}

	@Override
	public void initWithNiwsConfig(IClientConfig clientConfig) {
		// TODO Auto-generated method stub
		
	}
}

 

調用

package com.jiangjy.myrule;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;

@Configuration   //配置spring.xml形式的註解
public class MySelfRult {

	@Bean //交給容器管理
	public IRule myRule() {
		
//		return new RandomRule();//Ribbon默認輪詢,自定義隨機
		
		return new RandomRule_Jack();//自定義每臺機器6次
	}
}

 

測試結果:

 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章