【Java进阶】Ribbon讲解实现案例
Ribbon
Ribbon [?r?b?n]
Ribbon是什么
-
Spring Cloud Ribbon是一個基于HTTP和TCP的 客戶端 負載均衡 工具
簡單的說,Ribbon是Netflix發布的開源項目,主要功能是提供客戶端的軟件負載均衡算法,將Netflix的中間層服務連接在一起。Ribbon客戶端組件提供一系列完善的配置項如連接超時,重試等。簡單的說,就是在配置文件中列出Load Balancer(簡稱LB)后面所有的機器,Ribbon會自動的幫助你基于某種規則(如簡單輪詢,隨機連接等)去連接這些機器。我們也很容易使用Ribbon實現自定義的負載均衡算法。 -
它基于Netflix Ribbon實現。通過Spring Cloud的封裝,可以讓我們輕松地將面向服務的REST模版請求自動轉換成客戶端負載均衡的服務調用。Spring Cloud Ribbon雖然只是一個工具類框架,它不像服務注冊中心、配置中心、API網關那樣需要獨立部署,但是它幾乎存在于每一個Spring Cloud構建的微服務和基礎設施中。因為微服務間的調用,API網關的請求轉發等內容,實際上都是通過Ribbon來實現的,包括后續我們將要介紹的Feign,它也是基于Ribbon實現的工具。所以,對Spring Cloud Ribbon的理解和使用,對于我們使用Spring Cloud來構建微服務非常重要。
-
面試造飛機, 工作擰螺絲
Ribbon能干嘛
- LB(負載均衡 LB,即負載均衡(Load Balance),在微服務或分布式集群中經常用的一種應用。
- 負載均衡簡單的說就是將用戶的請求平攤的分配到多個服務上,從而達到系統的HA。
- 常見的負載均衡有軟件Nginx,LVS,硬件 F5等。
- 相應的在中間件,例如:dubbo和SpringCloud中均給我們提供了負載均衡,SpringCloud的負載均衡算法可以自定義。
- 負載均衡的簡單分類
- 集中式LB 即在服務的消費方和提供方之間使用獨立的LB設施 (可以是硬件,如F5, 也可以是軟件,如nginx), 由該設施負責把訪問請求通過某種策略轉發至服務的提供方;
- 將LB邏輯集成到消費方,消費方從服務注冊中心獲知有哪些地址可用,然后自己再從這些地址中選擇出一個合適的服務器。Ribbon就屬于進程內LB,它只是一個類庫,集成于消費方進程,消費方通過它來獲取到服務提供方的地址。
注意: Ribbon就屬于進程內LB ,它只是一個類庫,集成于消費方進程,消費方通過它來 獲取到服務提供方的地址 。
具體操作
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><!--我們需要拿到實體類, 所以要配置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//配置負載均衡實現RestTemplate @LoadBalanced//IRule//AvailabilityFilteringRule: 先會過濾掉, 跳閘, 訪問故障服務器//RoundRobinRule 輪詢//RandomRule 隨機//RetryRule: 會按照輪詢獲取服務~ 如果服務獲取失敗, 則會在指定的時間內進行, 重試@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 {// 理解消費者, 不應該有service層//RestFul風格//(url, 實體: Map classs<T> responseType)@Autowiredprivate RestTemplate restTemplate; //提供多種便捷訪問遠程http服務的方法//原 private static final String REST_URL_PREFIX = "http://localhost:8001";// Ribbon 我們這里是地址 因該是一個變量 通過服務來訪問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 );}}自定義負載均衡
注意一點: 自定義類 單獨拿出來 該類不能被@ComponentScan掃描到
項目截圖
自定義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;/*** 該類不能被@ComponentScan掃描到* @author xzMa*/ public class CodingCeRandomRule extends AbstractLoadBalancerRule {//自定義 每個服務, 訪問5次, 換下一個服務(3個)//total = 0 默認=0 如果=5 我們指向下一個服務點private int total = 0; //被調用的次數private int currentIndex = 0; //當前是誰在提供服務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(); //獲得活著的服務List<Server> allList = lb.getAllServers(); //獲得全部服務int serverCount = allList.size();if (serverCount == 0) {return null;}//int index = this.chooseRandomInt(serverCount); //生成區間隨機數//server = (Server)upList.get(index); //從活著的服務, 隨機獲取一個//===================================================================if (total < 5) {server = upList.get(currentIndex);total++;} else {total = 0;currentIndex++;//判斷當前數量是否大于活著的數量if(currentIndex > upList.size()) {currentIndex = 0;}server = upList.get(currentIndex); //從活著的服務中, 獲取指定指定的服務進行操作}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();//默認是輪詢 現在我們定義為 CodingCeRandomRule// 本次自定義 頻繁操作 會出現 500 錯誤 繼續自定義寫RetryRule}}項目地址: https://github.com/xzMhehe/codingce-java
總結
以上是生活随笔為你收集整理的【Java进阶】Ribbon讲解实现案例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Java进阶】Eureka讲解与应用
- 下一篇: 【Java进阶】初识SpringClou