六:Ribbon負載均衡
1. 概述
1.1 是什麼
Spring Cloud Ribbon是基于Netflix Ribbon實作的一套==用戶端 負載均衡==的工具。
簡單的說,Ribbon是Netflix釋出的開源項目,主要功能是==提供用戶端的軟體負載均衡算法==,将Netflix的中間層服務連接配接在一起。Ribbon用戶端元件提供一系列完善的配置項如連接配接逾時,重試等。簡單的說,就是在配置檔案中列出==Load Balancer==(簡稱LB)後面所有的機器,Ribbon會自動的幫助你基于某種規則(如簡單輪詢,随機連接配接等)去連接配接這些機器。我們也很容易使用Ribbon實作自定義的負載均衡算法。
1.2 能幹嘛
LB(負載均衡):LB,即負載均衡(Load Balance),在微服務或分布式叢集中經常用的一種應用。
負載均衡簡單的說就是==将使用者的請求平攤的配置設定到多個服務上==,進而達到系統的HA。
常見的負載均衡有軟體Nginx,LVS,硬體 F5等。
相應的在中間件,例如:dubbo和SpringCloud中均給我們提供了負載均衡,SpringCloud的負載均衡算法可以自定義。
集中式LB:
即在==服務的消費方和提供方之間使用獨立的LB設施==(可以是硬體,如F5, 也可以是軟體,如nginx), 由該設施負責把通路請求通過某種政策轉發至服務的提供方;
程序内LB:
将LB邏輯內建到消費方,消費方從服務注冊中心獲知有哪些位址可用,然後自己再從這些位址中選擇出一個合适的伺服器。
Ribbon就屬于程序内LB,它隻是一個類庫,內建于消費方程序,消費方通過它來==擷取到服務提供方的位址==。
1.3 官網資料
https://github.com/Netflix/ribbon/wiki/Getting-Started
2. Ribbon配置初步
修改microservicecloud-consumer-dept-80工程
1.修改pom.xml檔案
2.修改application.yml(追加eureka的服務注冊位址)
3.對ConfigBean進行新注解@LoadBalanced(獲得Rest時加入Ribbon的配置)
4.主啟動類添加@EnableEurekaClient
5.修改DeptController_Consumer用戶端通路類
6.啟動3個Eureka叢集,再啟動部門微服務提供者,再啟動部門微服務消費者
7.測試 http://localhost/consumer/dept/get/1 http://localhost/consumer/dept/list
http://localhost/consumer/dept/add?dname=jack
==Ribbon和Eureka整合後Consumer可以直接調用服務而不用再關心位址和端口号==
2.1 修改pom.xml檔案
<!-- 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>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
2.2 修改application.yml
eureka:
client:
register-with-eureka: false
service-url:
# defaultZone: http://localhost:7001/eureka/
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
2.3 ConfigBean進行新注解@LoadBalanced
@Bean
@LoadBalanced //Spring Cloud Ribbon是基于Netflix Ribbon實作的一套用戶端 負載均衡的工具。
public RestTemplate getRestTemplate()
{
return new RestTemplate();
}
2.4 主啟動類添加@EnableEurekaClient 2.5 修改DeptController_Consumer
3. Ribbon負載均衡
3.1 架構說明
Ribbon在工作時分成兩步:
第一步==先選擇 EurekaServer== ,它優先選擇在同一個區域内負載較少的server.
第二步再根據==使用者指定的政策==,在從server取到的服務注冊清單中選擇一個位址。其中Ribbon提供了多種政策:比如輪詢、随機和根據響應時間權重。
3.2 步驟
1.參考microservicecloud-provider-dept-8001,建立兩份,分别命名為8002,8003
2.建立8002/8003資料庫,各自微服務分别連各自的資料庫
3.修改8002/8003各自YML(==端口/資料庫連接配接/對外暴露的統一的服務執行個體名==)
4.啟動3個eureka叢集配置區,啟動3個微服務提供者(http://localhost:8001/dept/list),啟動微服務消費者
5.測試:用戶端通過Ribbo完成負載均衡并通路上一步的Dept微服務
http://localhost/consumer/dept/list 注意觀察看到傳回的資料庫名字,各不相同,負載均衡實作
==總結:Ribbon其實就是一個軟負載均衡的用戶端元件,他可以和其他所需請求的用戶端結合使用,和eureka結合隻是其中的一個執行個體。==
3.2.2 建立8002/8003資料庫
8002:
DROP DATABASE IF EXISTS cloudDB02;
CREATE DATABASE cloudDB02 CHARACTER SET UTF8;
USE cloudDB02;
CREATE TABLE dept
(
deptno BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
dname VARCHAR(60),
db_source VARCHAR(60)
);
INSERT INTO dept(dname,db_source) VALUES('開發部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('人事部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('财務部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('市場部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('運維部',DATABASE());
SELECT * FROM dept;
8003:
DROP DATABASE IF EXISTS cloudDB03;
CREATE DATABASE cloudDB03 CHARACTER SET UTF8;
USE cloudDB03;
CREATE TABLE dept
(
deptno BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
dname VARCHAR(60),
db_source VARCHAR(60)
);
INSERT INTO dept(dname,db_source) VALUES('開發部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('人事部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('财務部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('市場部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('運維部',DATABASE());
SELECT * FROM dept;
3.2.3 修改8002/8003各自YML
8002:
server:
port: 8002
spring:
application:
name: microservicecloud-dept
datasource:
url: jdbc:mysql://localhost:3306/cloudDB02
8003:
server:
port: 8003
spring:
application:
name: microservicecloud-dept
datasource:
url: jdbc:mysql://localhost:3306/cloudDB03
4. Ribbon核心元件IRule
IRule: 根據特定算法中從服務清單中選取一個要通路的服務
==RoundRobinRule== 輪詢
==RandomRule== 随機
==AvailabilityFilteringRule== 會先過濾掉由于多次通路故障而處于斷路器跳閘狀态的服務,還有并發的連接配接數量超過門檻值的服務,然後對剩餘的服務清單按照輪詢政策進行通路
==WeightedResponseTimeRule== 根據平均響應時間計算所有服務的權重,響應時間越快服務權重越大被選中的機率越高。剛啟動時如果統計資訊不足,則使用RoundRobinRule政策,等統計資訊足夠,會切換到WeightedResponseTimeRule
==RetryRule== 先按照RoundRobinRule的政策擷取服務,如果擷取服務失敗則在指定時間内會進行重試,擷取可用的服務
==BestAvailableRule== 會先過濾掉由于多次通路故障而處于斷路器跳閘狀态的服務,然後選擇一個并發量最小的服務
==ZoneAvoidanceRule== 預設規則,複合判斷server所在區域的性能和server的可用性選擇伺服器
官方源碼: https://github.com/Netflix/ribbon/tree/master/ribbon-loadbalancer/src/main/java/com/netflix/loadbalancer
//示例
@Configuration
public class ConfigBean{
//指定ribbon給出的通路政策
//RandomRule extends AbstractLoadBalancerRule; AbstractLoadBalancerRule implements IRule
@Bean
public IRule myRule()
{
//return new RoundRobinRule();
return new RandomRule();//達到的目的,用我們重新選擇的随機算法替代預設的輪詢。
//return new RetryRule();
}
}
5. Ribbon自定義
5.1 修改microservicecloud-consumer-dept-80
5.2 主啟動類添加@RibbonClient
5.3 注意配置細節
5.4 步驟
5.2 主啟動類添加@RibbonClient
//在啟動該微服務的時候就能去加載我們的自定義Ribbon配置類,進而使配置生效
@RibbonClient
public class DeptConsumer80_App
5.3 注意配置細節
官方文檔明确給出了警告:這個==自定義配置類不能放在@ComponentScan所描的目前包下以及子包下==,否則我們自定義的這個配置類就會被所有的Ribbon用戶端所共享,也就是說我們達不到特殊化定制的目的了。
5.4 步驟
5.4.1 建立Package com.atguigu.myrule(建立自定義Ribbon規則類)
5.4.2 修改主啟動類
5.4.3 測試 http://localhost:81/consumer/dept/list
5.4.1 建立自定義Ribbon規則類
@Configuration
public class MySelfRule
{
@Bean
public IRule myRule()
{
return new RandomRule();// Ribbon預設是輪詢,我自定義為随機
//return new RoundRobinRule();// Ribbon預設是輪詢,我自定義為随機
// return new RandomRule_ZY();// 我自定義為每台機器5次
}
}
5.4.2 修改主啟動類
//在啟動該微服務的時候就能去加載我們的自定義Ribbon配置類,進而使配置生效
@RibbonClient(name="MICROSERVICECLOUD-DEPT",configuration=MySelfRule.class)
public class DeptConsumer80_App
6. 自定義規則深度解析
問題:依舊輪詢政策,但是加上新需求,每個伺服器要求被調用5次。也即以前是每台機器一次,現在是每台機器5次
解析源碼: https://github.com/Netflix/ribbon/blob/master/ribbon-loadbalancer/src/main/java/com/netflix/loadbalancer/RandomRule.java
6.1 參考源碼修改為我們需求要求的RandomRule_ZY.java
public class RandomRule_ZY extends AbstractLoadBalancerRule
{
// total = 0 // 當total==5以後,我們指針才能往下走,
// index = 0 // 目前對外提供服務的伺服器位址,
// total需要重新置為零,但是已經達到過一個5次,我們的index = 1
// 分析:我們5次,但是微服務隻有8001 8002 8003 三台,OK?
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
}
}
6.2 調用
MySelfRule.java
@Configuration
public class MySelfRule
{
@Bean
public IRule myRule()
{
// return new RandomRule();// Ribbon預設是輪詢,我自定義為随機
// return new RoundRobinRule();// Ribbon預設是輪詢,我自定義為随機
return new RandomRule_ZY();// 我自定義為每台機器5次
}
}
主啟動類:
//在啟動該微服務的時候就能去加載我們的自定義Ribbon配置類,進而使配置生效
@RibbonClient(name="MICROSERVICECLOUD-DEPT",configuration=MySelfRule.class)
public class DeptConsumer80_App
6.3 測試
http://localhost:81/consumer/dept/list
七:Feign負載均衡
1. 概述
1.1 官網解釋:http://projects.spring.io/spring-cloud/spring-cloud.html#spring-cloud-feign
Feign是一個聲明式WebService用戶端。使用Feign能讓編寫Web Service用戶端更加簡單, 它的使用方法是定義一個接口,然後在上面添加注解,同時也支援JAX-RS标準的注解。Feign也支援可拔插式的編碼器和解碼器。Spring Cloud對Feign進行了封裝,使其支援了Spring MVC标準注解和HttpMessageConverters。Feign可以與Eureka和Ribbon組合使用以支援負載均衡。
Feign是一個聲明式的Web服務用戶端,使得編寫Web服務用戶端變得非常容易,
==隻需要建立一個接口,然後在上面添加注解即可。==
1.2 參考官網:https://github.com/OpenFeign/feign
Feign能幹什麼?Feign旨在使編寫Java Http用戶端變得更容易。
前面在使用Ribbon+RestTemplate時,利用RestTemplate對http請求的封裝處理,形成了一套模版化的調用方法。但是在實際開發中,由于對服務依賴的調用可能不止一處,==往往一個接口會被多處調用,是以通常都會針對每個微服務自行封裝一些用戶端類來包裝這些依賴服務的調用==。是以,Feign在此基礎上做了進一步封裝,由他來幫助我們定義和實作依賴服務接口的定義。在Feign的實作下,==我們隻需建立一個接口并使用注解的方式來配置它(以前是Dao接口上面标注Mapper注解,現在是一個微服務接口上面标注一個Feign注解即可)==,即可完成對服務提供方的接口綁定,簡化了使用Spring cloud Ribbon時,自動封裝服務調用用戶端的開發量。
Feign內建了Ribbon
利用Ribbon維護了MicroServiceCloud-Dept的服務清單資訊,并且通過輪詢實作了用戶端的負載均衡。而與Ribbon不同的是,==通過feign隻需要定義服務綁定接口且以聲明式的方法==,優雅而簡單的實作了服務調用
2. Feign使用步驟
2.1 參考microservicecloud-consumer-dept-80
2.2 建立microservicecloud-consumer-dept-feign
2.3 microservicecloud-consumer-dept-feign工程pom.xml修改,主要添加對feign的支援
2.4 修改microservicecloud-api工程
2.5 microservicecloud-consumer-dept-feign工程修改Controller,添加上一步建立的DeptClientService接口
2.6 microservicecloud-consumer-dept-feign工程修改主啟動類
2.7 測試
==Feign通過接口的方法調用Rest服務(之前是Ribbon+RestTemplate),該請求發送給Eureka伺服器==(http://MICROSERVICECLOUD-DEPT/dept/list),==通過Feign直接找到服務接口,由于在進行服務調用的時候融合了Ribbon技術,是以也支援負載均衡作用。==
2.2 建立microservicecloud-consumer-dept-feign 2.3 pom.xml修改,添加對feign的支援
<dependency><!-- 自己定義的api -->
<groupId>com.atguigu.springcloud</groupId>
<artifactId>microservicecloud-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<!-- 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>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 修改後立即生效,熱部署 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
2.4 application.yml修改
server:
port: 81 #端口改為81,80端口被系統占用
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7004/eureka/,http://eureka7003.com:7003/eureka/
2.5 修改microservicecloud-api工程
2.5.1 添加feign依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
2.5.2 建立DeptClientService接口并新增注解@FeignClient,與主啟動類的@EnableFeignClients對應
//建立service包
@FeignClient(value = "MICROSERVICECLOUD-DEPT")
public interface DeptClientService
{
@RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET)
public Dept get(@PathVariable("id") long id);
@RequestMapping(value = "/dept/list", method = RequestMethod.GET)
public List<Dept> list();
@RequestMapping(value = "/dept/add", method = RequestMethod.POST)
public boolean add(Dept dept);
}
2.5.3 mvn clean / mvn install
2.6 修改Controller
@RestController
public class DeptController_Consumer
{
@Autowired
private DeptClientService service;
@RequestMapping(value = "/consumer/dept/get/{id}")
public Dept get(@PathVariable("id") Long id)
{
return this.service.get(id);
}
@RequestMapping(value = "/consumer/dept/list")
public List<Dept> list()
{
return this.service.list();
}
@RequestMapping(value = "/consumer/dept/add")
public Object add(Dept dept)
{
return this.service.add(dept);
}
}
2.7 修改主啟動類
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages= {"com.atguigu.springcloud"})
@ComponentScan("com.atguigu.springcloud")
public class DeptConsumer80_Feign_App
{
public static void main(String[] args)
{
SpringApplication.run(DeptConsumer80_Feign_App.class, args);
}
}
2.8 測試
啟動3個eureka叢集
啟動3個部門微服務8001/8002/8003
啟動Feign
http://localhost:81/consumer/dept/list
==Feign自帶負載均衡配置項== (采用ribbon的預設輪詢政策)
轉載于:https://www.cnblogs.com/itzlg/p/10699569.html