三、SpringCloud五大神獸之Ribbon(負載均衡)
Ribbon是Netflix釋出的開源專案,主要功能是提供客戶端的軟體負載均衡演算法,將Netflix的中間層服務連線在一起。Ribbon客戶端元件提供一系列完善的配置項如連線超時,重試等。簡單的說,就是在配置檔案中列出Load Balancer(簡稱LB)後面所有的機器,Ribbon會自動的幫助你基於某種規則(如簡單輪詢,隨即連線等)去連線這些機器。我們也很容易使用Ribbon實現自定義的負載均衡演算法。
說起負載均衡一般都會想到服務端的負載均衡,常用產品包括LBS硬體或雲服務、Nginx等,都是耳熟能詳的產品。
而Spring Cloud提供了讓服務呼叫端具備負載均衡能力的Ribbon,通過和Eureka的緊密結合,不用在服務叢集內再架設負載均衡服務,很大程度簡化了服務叢集內的架構。
一、pom引入包
<!-- Ribbon相關 Spring Cloud Ribbon是基於Netflix Ribbon實現的一套客戶端負載均衡的工具。--> <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> <!--Ribbon相關end-->
二、application.yml配置
server:
port: 8080
#從eureka中獲取微服務
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
eureka服務端為叢集,因此defalutZone配置3個url,如果為單個則配置1個即可。
三、實現負載均衡
3.1預設最簡單負載均衡方式
在配置類中,用@LoadBalanced註解,放置在獲取RestTemplate的方法上:
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
啟動類中:
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name="MICROSERVICECLOUD-DEPT")
public class DeptConsumer80_App
{
public static void main( String[] args )
{
SpringApplication.run(DeptConsumer80_App.class,args);
}
}
RibbonClient註解裡面的name為微服務提供者的名稱,本例中由8001,8002,8003組成MICROSERVICECLOUD-DEPT叢集,這樣子即可完成Ribbon的負載均衡,此預設策略為輪訓方式,除此以外還有其餘其中策略:
RoundRobinRule 輪詢 RandomRule 隨機 AvaliabilityFilteringRule 會先過濾由於多次訪問故障而處於斷路器跳閘的狀態的服務和併發的連線數量超過閾值的服務,然後對剩餘的服務列表按照輪詢策略 WeightedResponseTimeRule 根據平均響應時間計算所有服務的權重,響應時間越快服務權重越大 RetryRule 先按照RoundRobinRule策略獲取服務,如果獲取服務失敗會在指定時間內重試 BestAvailableRule 會先過濾掉由於多次訪問故障二處於斷路器跳閘狀態的服務,然後選擇一個併發量最小的服務 ZoneAvoidanceRule 預設規則,複合判斷server所在的區域的效能和server的可用性選擇伺服器
3.2只用其他策略:
要使用其他策略,只需要在配置類中執行建立個方法,返回IRule物件,方法的具體實現中返回相應策略的物件即可
@Bean
public IRule myRule(){
return new RandomRule();// 隨機
}
3.3自定義負載均衡策略:
有時候提供的幾種負載均衡策略無法滿足實際需求,需要自定義負載均衡策略
3.3.1建立個配置類,不能和啟動類在同一個包(非常重要)
package com.myrule;
import com.netflix.loadbalancer.IRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Created by YQ11053 on 2018/10/5 0005.
*/
@Configuration
public class MySelfRule {
@Bean
public IRule myRule(){
// return new RetryRule(); //定義為RetryRule
return new RoundRobinRule_ZHF();//自定義負載均衡方式
}
}
自定義負載均衡類,需要繼承AbstractLoadBalancerRule類
package com.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;
/**
* Created by YQ11053 on 2018/10/5 0005.
*/
public class RoundRobinRule_ZHF extends AbstractLoadBalancerRule{
//自定義需求:輪訓策略,每臺執行5次後進行切換
// total = 0 // 當total==5以後,我們指標才能往下走,
// index = 0 // 當前對外提供服務的伺服器地址,
// total需要重新置為零,但是已經達到過一個5次,我們的index = 1
private int total = 0; // 總共被呼叫的次數,目前要求每臺被呼叫5次
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 = rand.nextInt(serverCount);// java.util.Random().nextInt(3);
// server = upList.get(index);
// private int total = 0; // 總共被呼叫的次數,目前要求每臺被呼叫5次
// private int currentIndex = 0; // 當前提供服務的機器號
if(total < 5) {
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;
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
// TODO Auto-generated method stub
}
}
本例項實現的自定義策略為每臺執行5次後進行輪訓,如果其他自定義策略,只需要在程式碼相應地方進行演算法修改即可。
專案結構為:
3.3.2主啟動類中
package com.zhanghf;
import com.myrule.MySelfRule;
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;
/**
* Hello world!
*
*/
@SpringBootApplication
@EnableEurekaClient
//自定義Ribbon的輪訓策略:在啟動該微服務的時候就能去載入我們的自定義Ribbon配置類,從而使配置生效
//非常重要:MySelfRule這個類不能建立在和啟動類同一個包,即com.zhanghf;下
@RibbonClient(name="MICROSERVICECLOUD-DEPT",configuration=MySelfRule.class)
public class DeptConsumer80_App
{
public static void main( String[] args )
{
SpringApplication.run(DeptConsumer80_App.class,args);
}
}