已用上小米 MIX 4,雷軍回顧 MIX 系列過去 5 款機型:最喜歡 MIX 2 全陶瓷尊享版
Ribbon:負載均衡(基於客戶端)
Ribbon是什麼?
- Spring Cloud Ribbon 是基於Netflix Ribbon 實現的一套客戶端負載均衡的工具。
- 簡單的說,Ribbon 是 Netflix 釋出的開源專案,主要功能是提供客戶端的軟體負載均衡演算法,將 Netflix 的中間層服務連線在一起。Ribbon 的客戶端元件提供一系列完整的配置項,如:連線超時、重試等。簡單的說,就是在配置檔案中列出 LoadBalancer (簡稱LB:負載均衡) 後面所有的及其,Ribbon 會自動的幫助你基於某種規則 (如簡單輪詢,隨機連線等等) 去連線這些機器。我們也容易使用 Ribbon 實現自定義的負載均衡演算法!
Ribbon能幹嘛?
- LB,即負載均衡 (LoadBalancer) ,在微服務或分散式叢集中經常用的一種應用。
- 負載均衡簡單的說就是將使用者的請求平攤的分配到多個服務上,從而達到系統的HA (高用)。
- 常見的負載均衡軟體有 Nginx、Lvs 等等。
- Dubbo、SpringCloud 中均給我們提供了負載均衡,SpringCloud 的負載均衡演算法可以自定義。
- 負載均衡簡單分類:
- 集中式LB
- 即在服務的提供方和消費方之間使用獨立的LB設施,如Nginx(反向代理伺服器),由該設施負責把訪問請求通過某種策略轉發至服務的提供方!
- 程序式 LB
- 將LB邏輯整合到消費方,消費方從服務註冊中心獲知有哪些地址可用,然後自己再從這些地址中選出一個合適的伺服器。
- Ribbon 就屬於程序內LB,它只是一個類庫,集成於消費方程序,消費方通過它來獲取到服務提供方的地址!
- 集中式LB
整合Ribbon
springcloud-consumer-dept-80向pom.xml中新增Ribbon和Eureka依賴
<!--Ribbon--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> <version>1.4.6.RELEASE</version> </dependency> <!--Eureka: Ribbon需要從Eureka服務中心獲取要拿什麼--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> <version>1.4.6.RELEASE</version> </dependency>
在application.yml檔案中配置Eureka
# Eureka配置
eureka:
client:
register-with-eureka: false # 不向 Eureka註冊自己
service-url: # 從三個註冊中心中隨機取一個去訪問
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
主啟動類加上@EnableEurekaClient註解,開啟Eureka
//Ribbon 和 Eureka 整合以後,客戶端可以直接呼叫,不用關心IP地址和埠號
@SpringBootApplication
@EnableEurekaClient //開啟Eureka 客戶端
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class, args);
}
}
自定義Spring配置類:ConfigBean.java 配置負載均衡實現RestTemplate
@Configuration
public class ConfigBean {
//@Configuration -- spring applicationContext.xml
@LoadBalanced //配置負載均衡實現RestTemplate
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
修改conroller:DeptConsumerController.java
//Ribbon:我們這裡的地址,應該是一個變數,通過服務名來訪問
//private static final String REST_URL_PREFIX = "http://localhost:8001";
private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-PROVIDER-DEPT";
使用Ribbon實現負載均衡
流程圖:
1.新建兩個服務提供者Moudle:springcloud-provider-dept-8003、springcloud-provider-dept-8002
2.參照springcloud-provider-dept-8001 依次為另外兩個Moudle新增pom.xml依賴 、resourece下的mybatis和application.yml配置,Java程式碼
3.啟動所有服務測試(根據自身電腦配置決定啟動服務的個數),訪問http://eureka7001.com:7002/檢視結果
測試訪問http://localhost/consumer/dept/list 這時候隨機訪問的是服務提供者8003
再次訪問http://localhost/consumer/dept/list這時候隨機的是服務提供者8001
以上這種每次訪問http://localhost/consumer/dept/list隨機訪問叢集中某個服務提供者,這種情況叫做輪詢,輪詢演算法在SpringCloud中可以自定義。
如何切換或者自定義規則呢?
在springcloud-provider-dept-80模組下的ConfigBean中進行配置,切換使用不同的規則
@Configuration
public class ConfigBean {
//@Configuration -- spring applicationContext.xml
/**
* IRule:
* RoundRobinRule 輪詢策略
* RandomRule 隨機策略
* AvailabilityFilteringRule : 會先過濾掉,跳閘,訪問故障的服務~,對剩下的進行輪詢~
* RetryRule : 會先按照輪詢獲取服務~,如果服務獲取失敗,則會在指定的時間內進行,重試
*/
@Bean
public IRule myRule() {
return new RandomRule();//使用隨機策略
//return new RoundRobinRule();//使用輪詢策略
//return new AvailabilityFilteringRule();//使用輪詢策略
//return new RetryRule();//使用輪詢策略
}
}
也可以自定義規則,在myRule包下自定義一個配置類MyRule.java,注意:該包不要和主啟動類所在的包同級,要跟啟動類所在包同級:
MyRule.java
/**
* @Description: 自定義規則
*/
@Configuration
public class MyRule {
@Bean
public IRule myRule(){
return new MyRandomRule();//預設是輪詢RandomRule,現在自定義為自己的
}
}
主啟動類開啟負載均衡並指定自定義的MyRule配置類
//Ribbon 和 Eureka 整合以後,客戶端可以直接呼叫,不用關心IP地址和埠號
@SpringBootApplication
@EnableEurekaClient
//在微服務啟動的時候就能載入自定義的Ribbon類(自定義的規則會覆蓋原有預設的規則)
@RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT",configuration = MyRule.class)//開啟負載均衡,並指定自定義的規則
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class, args);
}
}
自定義的規則(這裡我們參考Ribbon中預設的規則程式碼自己稍微改動):MyRandomRule.java
public class MyRandomRule extends AbstractLoadBalancerRule {
/**
* 每個服務訪問5次則換下一個服務(總共3個服務)
* <p>
* total=0,預設=0,如果=5,指向下一個服務節點
* index=0,預設=0,如果total=5,index+1
*/
private int total = 0;//被呼叫的次數
private int currentIndex = 0;//當前是誰在提供服務
//@edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE")
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 < 5) {
server = upList.get(currentIndex);
total++;
} else {
total = 0;
currentIndex++;
if (currentIndex > upList.size()) {
currentIndex = 0;
}
server = upList.get(currentIndex);//從活著的服務中,獲取指定的服務來進行操作
}
//======================================================
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
}
}