【Java进阶】Ribbon讲解实现案例
Ribbon
Ribbon [?r?b?n]
Ribbon是什么
-
Spring Cloud Ribbon是一個(gè)基于HTTP和TCP的 客戶端 負(fù)載均衡 工具
簡(jiǎn)單的說(shuō),Ribbon是Netflix發(fā)布的開(kāi)源項(xiàng)目,主要功能是提供客戶端的軟件負(fù)載均衡算法,將Netflix的中間層服務(wù)連接在一起。Ribbon客戶端組件提供一系列完善的配置項(xiàng)如連接超時(shí),重試等。簡(jiǎn)單的說(shuō),就是在配置文件中列出Load Balancer(簡(jiǎn)稱(chēng)LB)后面所有的機(jī)器,Ribbon會(huì)自動(dòng)的幫助你基于某種規(guī)則(如簡(jiǎn)單輪詢(xún),隨機(jī)連接等)去連接這些機(jī)器。我們也很容易使用Ribbon實(shí)現(xiàn)自定義的負(fù)載均衡算法。 -
它基于Netflix Ribbon實(shí)現(xiàn)。通過(guò)Spring Cloud的封裝,可以讓我們輕松地將面向服務(wù)的REST模版請(qǐng)求自動(dòng)轉(zhuǎn)換成客戶端負(fù)載均衡的服務(wù)調(diào)用。Spring Cloud Ribbon雖然只是一個(gè)工具類(lèi)框架,它不像服務(wù)注冊(cè)中心、配置中心、API網(wǎng)關(guān)那樣需要獨(dú)立部署,但是它幾乎存在于每一個(gè)Spring Cloud構(gòu)建的微服務(wù)和基礎(chǔ)設(shè)施中。因?yàn)槲⒎?wù)間的調(diào)用,API網(wǎng)關(guān)的請(qǐng)求轉(zhuǎn)發(fā)等內(nèi)容,實(shí)際上都是通過(guò)Ribbon來(lái)實(shí)現(xiàn)的,包括后續(xù)我們將要介紹的Feign,它也是基于Ribbon實(shí)現(xiàn)的工具。所以,對(duì)Spring Cloud Ribbon的理解和使用,對(duì)于我們使用Spring Cloud來(lái)構(gòu)建微服務(wù)非常重要。
-
面試造飛機(jī), 工作擰螺絲
Ribbon能干嘛
- LB(負(fù)載均衡 LB,即負(fù)載均衡(Load Balance),在微服務(wù)或分布式集群中經(jīng)常用的一種應(yīng)用。
- 負(fù)載均衡簡(jiǎn)單的說(shuō)就是將用戶的請(qǐng)求平攤的分配到多個(gè)服務(wù)上,從而達(dá)到系統(tǒng)的HA。
- 常見(jiàn)的負(fù)載均衡有軟件Nginx,LVS,硬件 F5等。
- 相應(yīng)的在中間件,例如:dubbo和SpringCloud中均給我們提供了負(fù)載均衡,SpringCloud的負(fù)載均衡算法可以自定義。
- 負(fù)載均衡的簡(jiǎn)單分類(lèi)
- 集中式LB 即在服務(wù)的消費(fèi)方和提供方之間使用獨(dú)立的LB設(shè)施 (可以是硬件,如F5, 也可以是軟件,如nginx), 由該設(shè)施負(fù)責(zé)把訪問(wèn)請(qǐng)求通過(guò)某種策略轉(zhuǎn)發(fā)至服務(wù)的提供方;
- 將LB邏輯集成到消費(fèi)方,消費(fèi)方從服務(wù)注冊(cè)中心獲知有哪些地址可用,然后自己再?gòu)倪@些地址中選擇出一個(gè)合適的服務(wù)器。Ribbon就屬于進(jìn)程內(nèi)LB,它只是一個(gè)類(lèi)庫(kù),集成于消費(fèi)方進(jìn)程,消費(fèi)方通過(guò)它來(lái)獲取到服務(wù)提供方的地址。
注意: Ribbon就屬于進(jìn)程內(nèi)LB ,它只是一個(gè)類(lèi)庫(kù),集成于消費(fèi)方進(jìn)程,消費(fèi)方通過(guò)它來(lái) 獲取到服務(wù)提供方的地址 。
具體操作
pom
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>springcloud</artifactId><groupId>cn.com.codingce</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>springcloud-consumer-dept</artifactId><dependencies><!--我們需要拿到實(shí)體類(lèi), 所以要配置api -module--><dependency><groupId>cn.com.codingce</groupId><artifactId>springcloud-api</artifactId><version>1.0-SNAPSHOT</version></dependency><!--熱部署工具--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--Ribbon--><!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-ribbon --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-ribbon</artifactId><version>1.4.6.RELEASE</version></dependency><!--Eureka 客戶端--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-eureka</artifactId><version>1.4.6.RELEASE</version></dependency></dependencies> </project>ConfigBean
package cn.com.codingce.springcloud.config;import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate;@Configuration public class ConfigBean { //configuration -- spring applicationContext.xml//配置負(fù)載均衡實(shí)現(xiàn)RestTemplate @LoadBalanced//IRule//AvailabilityFilteringRule: 先會(huì)過(guò)濾掉, 跳閘, 訪問(wèn)故障服務(wù)器//RoundRobinRule 輪詢(xún)//RandomRule 隨機(jī)//RetryRule: 會(huì)按照輪詢(xún)獲取服務(wù)~ 如果服務(wù)獲取失敗, 則會(huì)在指定的時(shí)間內(nèi)進(jìn)行, 重試@Bean@LoadBalancedpublic RestTemplate getRestTemplate() {return new RestTemplate();} }DeptConsumerController
package cn.com.codingce.springcloud.controller;import cn.com.codingce.pojo.Dept; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate;import java.util.List;@RestController public class DeptConsumerController {// 理解消費(fèi)者, 不應(yīng)該有service層//RestFul風(fēng)格//(url, 實(shí)體: Map classs<T> responseType)@Autowiredprivate RestTemplate restTemplate; //提供多種便捷訪問(wèn)遠(yuǎn)程http服務(wù)的方法//原 private static final String REST_URL_PREFIX = "http://localhost:8001";// Ribbon 我們這里是地址 因該是一個(gè)變量 通過(guò)服務(wù)來(lái)訪問(wèn)private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";@RequestMapping("/consumer/dept/add")public boolean add(Dept dept) {return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", dept, Boolean.class);}//http://localhost:8001/dept/list@RequestMapping("/consumer/dept/get/{id}")public Dept get(@PathVariable("id") Long id) {return restTemplate.getForObject(REST_URL_PREFIX + "/dept/get/" + id, Dept.class );}@RequestMapping("/consumer/dept/list")public List<Dept> list() {return restTemplate.getForObject(REST_URL_PREFIX + "/dept/list" , List.class );}}自定義負(fù)載均衡
注意一點(diǎn): 自定義類(lèi) 單獨(dú)拿出來(lái) 該類(lèi)不能被@ComponentScan掃描到
項(xiàng)目截圖
自定義CodingCeRandomRule
package cn.com.codingce.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;/*** 該類(lèi)不能被@ComponentScan掃描到* @author xzMa*/ public class CodingCeRandomRule extends AbstractLoadBalancerRule {//自定義 每個(gè)服務(wù), 訪問(wèn)5次, 換下一個(gè)服務(wù)(3個(gè))//total = 0 默認(rèn)=0 如果=5 我們指向下一個(gè)服務(wù)點(diǎn)private int total = 0; //被調(diào)用的次數(shù)private int currentIndex = 0; //當(dāng)前是誰(shuí)在提供服務(wù)public CodingCeRandomRule() {}// @SuppressWarnings({"RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE"})public Server choose(ILoadBalancer lb, Object key) {if (lb == null) {return null;} else {Server server = null;while(server == null) {//線程中斷if (Thread.interrupted()) {return null;}List<Server> upList = lb.getReachableServers(); //獲得活著的服務(wù)List<Server> allList = lb.getAllServers(); //獲得全部服務(wù)int serverCount = allList.size();if (serverCount == 0) {return null;}//int index = this.chooseRandomInt(serverCount); //生成區(qū)間隨機(jī)數(shù)//server = (Server)upList.get(index); //從活著的服務(wù), 隨機(jī)獲取一個(gè)//===================================================================if (total < 5) {server = upList.get(currentIndex);total++;} else {total = 0;currentIndex++;//判斷當(dāng)前數(shù)量是否大于活著的數(shù)量if(currentIndex > upList.size()) {currentIndex = 0;}server = upList.get(currentIndex); //從活著的服務(wù)中, 獲取指定指定的服務(wù)進(jìn)行操作}if (server == null) {Thread.yield();} else {if (server.isAlive()) {return server;}server = null;Thread.yield();}}return server;}}protected int chooseRandomInt(int serverCount) {return ThreadLocalRandom.current().nextInt(serverCount);}@Overridepublic Server choose(Object key) {return this.choose(this.getLoadBalancer(), key);}@Overridepublic void initWithNiwsConfig(IClientConfig clientConfig) {} }CodingCeRule
package cn.com.codingce.myrule;import com.netflix.loadbalancer.IRule; import com.netflix.loadbalancer.RandomRule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;@Configuration public class CodingCeRule {@Beanpublic IRule myRule() {return new CodingCeRandomRule();//默認(rèn)是輪詢(xún) 現(xiàn)在我們定義為 CodingCeRandomRule// 本次自定義 頻繁操作 會(huì)出現(xiàn) 500 錯(cuò)誤 繼續(xù)自定義寫(xiě)RetryRule}}項(xiàng)目地址: https://github.com/xzMhehe/codingce-java
總結(jié)
以上是生活随笔為你收集整理的【Java进阶】Ribbon讲解实现案例的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【Java进阶】Eureka讲解与应用
- 下一篇: 【Java进阶】初识SpringClou