Ribbon實現負載均衡
Spring Cloud Ribbon是基於Netflix Ribbon實現的一套【客戶端】負載均衡工具。
Ribbon會自動幫助你基於某種規則(簡單輪詢、隨機連線等),也可以實現自定義的負載均衡演算法。
負載均衡介紹:
簡單來說負載均衡就是將使用者的請求ping平攤的分配到多個任務上,從而是系統達到HA(高可用)。
目前主要分兩種負載均衡:
1. 集中式LB:偏硬體,服務的消費方和提供方之間使用獨立的LB設施(可以是硬體,如F5。也可以是軟體,如Nginx),由該設施負責把訪問請求以某種策略轉發至服務的提供方。
2. 程序內LB:偏軟體, 將LB邏輯整合到消費方,消費方從服務註冊中心指導哪些地址可用,再自己選擇一個合適的伺服器(如Ribbon)。
總結:Ribbon其實就是一個軟負載均衡的客戶端元件,可以和其他需要請求的客戶端結合使用。
Ribbon初步配置:
分析圖如下:
實現前提:構建服務提供者叢集,新開兩個provider模組,註冊入Eureka,提供服務,叢集中服務名稱必須一致。
Ribbon是客戶端負載均衡工具!所以應該配置在客戶端。此處使用consume工程為例,配置僅需要在客戶端配置即可。配置步驟如下:
1.加入依賴,因為Riboon需要依賴Eureka執行,所以要同時加入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>
2.對實現類加入@LoadBalanced註解,即以之前實現的RestTemplate為例。
在consume工程中的SpringBoot配置類ConfigBean中增加如下註解:
3. 在application.yml檔案中配置向註冊中心註冊,如果是作為消費者模組不提供服務,不應該註冊自己;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 註解相當於代表該類是 applicationContext.xml */ @Configuration public class ConfigBean { @Bean @LoadBalanced public RestTemplate getRestTemplate(){ return new RestTemplate(); } }
server:
port: 9001
eureka:
client:
register-with-eureka: false #作為消費者不提供服務,不應該註冊自己
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
4. 主啟動類中加入@EnableEurekaClient註解;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
@EnableEurekaClient
public class DeptConsumer_9001_App {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_9001_App.class, args);
}
}
5. 以上步驟1~4完成後即可在controller中直接通過服務名訪問系統中的微服務,服務名作為URI
consumer應用中controller實現如下:
/* 原服務url */
//private static final String REST_URL_PREFIX = "http://localhost:8001";
/* 以服務名作為URI */
private static final String REST_URL_PREFIX = "http://SPRINGCLOUDDEMO-PROVIDER-DEPT";
@Autowired
private RestTemplate restTemplate;
@RequestMapping(value = "/dept/list")
public List<Dept> list() {
return restTemplate.getForObject(REST_URL_PREFIX + "/provider/dept/list", List.class);
}
完成以上5個步驟就已經實現客戶端負載均衡功能(此處使用的是預設輪詢的規則進行負載均衡),可以訪問客戶端進行測試(略過)。
Ribbon核心元件IRule:根據特定演算法從服務列表中選取一個需要訪問的服務;
其中IRule是一個介面,有七個自帶的落地實現類,可以實現不同的負載均衡演算法規則:
RoundRobinRule | 輪詢規則(預設方法) |
RandomRule | 隨機 |
AvailabilityFilteringRule | 先過濾掉由於多次訪問故障而處於斷路器跳閘狀態的服務,還有併發的連線數量超過閾值的服務,然後對剩餘的服務進行輪詢 |
WeightedResponseTimeRule | 根據平均響應時間計算服務的權重。統計資訊不足時會按照輪詢,統計資訊足夠會按照響應的時間選擇服務 |
RetryRule | 正常時按照輪詢選擇服務,若過程中有服務出現故障,在輪詢一定次數後依然故障,則會跳過故障的服務繼續輪詢。 |
BestAvailableRule | 先過濾掉由於多次訪問故障而處於斷路器跳閘狀態的服務,然後選擇一個併發量最小的服務 |
ZoneAvoidanceRule | 預設規則,符合判斷server所在的區域的效能和server的可用性選擇服務 |
如何切換Ribbon中賦值均衡的規則,而不是使用預設的輪詢方式?
只需在客戶端(consume)工程中的SpringBoot配置類ConfigBean配置一個返回具體方法的bean即可,如下:
@Bean
public IRule MyRule(){
/* RandomRule為Ribbon中自帶規則實現之一,隨機規則 */
return new RandomRule();
}
自定義Ribbon負載均衡演算法
如果覺得Ribbon中自帶的負載均衡規則無法滿足你的需求,那也可以自定義Ribbon負載均衡演算法。
需要注意的地方;
自定義的Ribbon演算法類不能放在主啟動類所在的包及子包下(確切來說是不能放在@ComponentScan註解的包及子包下),否則會被全域性應用到Ribbon服務中。 應該把自定義演算法類放在另外新建的包下,且這個類應該是為【配置類】即加上註解@Configuration。
實現自定義Ribbon負載均衡演算法的步驟:
1。將原來consumer工程中的配置類ConfigBean中返回 IRule 的Bean刪掉,然後新建一個包,定義配置類,返回自定義的負載均衡規則的實現類:
package com.wangcw.ribbon;
import com.netflix.loadbalancer.IRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MySelfRule
{
@Bean
public IRule myRule() {
/* 此處返回自定義的負載演算法規則 */
return new RandomRule_Myself();
}
}
2.實現自定義規則實現類RandomRule_Myself(此處實現每臺伺服器呼叫5次後,再繼續輪詢下一臺伺服器)
package com.wangcw.ribbon;
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;
public class RandomRule_Myself extends AbstractLoadBalancerRule {
/*
total = 0 // 當total==5以後,我們指標才能往下走,
index = 0 // 當前對外提供服務的伺服器地址,
total需要重新置為零,但是已經達到過一個5次,我們的index = 1 */
/* 總共被呼叫的次數,目前要求每臺被呼叫5次 */
private int total = 0;
/* 當前提供服務的機器號 */
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) {
return null;
}
if (total < 5) {
server = upList.get(currentIndex);
total++;
} else {
total = 0;
currentIndex++;
if (currentIndex >= upList.size()) {
currentIndex = 0;
}
}
if (server == null) {
Thread.yield();
continue;
}
if (server.isAlive()) {
return (server);
}
server = null;
Thread.yield();
}
return server;
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {}
}
3.在啟動類上加上註解 @RibbonClient(name = "微服務名",configuration = XXX.class) 註解指定需要用到負載均衡的微服務名及自定義演算法的class物件。
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
@EnableEurekaClient
@RibbonClient(name="SPRINGCLOUDDEMO-PROVIDER-DEPT",configuration=MySelfRule.class)
public class DeptConsumer_9001_App {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_9001_App.class, args);
}
}
4.啟動測試就可以發現已經生效。