Spring Cloud Ribbon是基于Netflix Ribbon實現(xiàn)的一套客戶端的 負載均衡的工具瀑志。
Ribbon源碼:https://github.com/Netflix/ribbon
一、Ribbon的配置初步
1污秆、修改 microservicecloud-consumer-dept-80 工程
(1)劈猪、修改pom.xml文件
?? 添加Ribbon 相關依賴
<!-- Ribbon相關 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
(2)、修改application.yml文件 追加eureka的服務注冊地址
server:
port: 80
eureka:
client:
register-with-eureka: false #自己不能注冊自己
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
(3)良拼、對ConfigBean進行添加新注解@Load Balanced 獲得Rest時加入Ribbon的配置
@Configuration //配置類战得,類似spring里面的applicationContext.xml
public class ConfigBean {
/**
* RestTemplate提供了多種便捷訪問遠程Http服務的方法
* 是一種簡單便捷的訪問restful服務模板類,是Spring提供的用于訪問Rest服務的客戶端模板工具集
* 使用restTemplate訪問restful接口非常的簡單粗暴無腦庸推。
* (url, requestMap, ResponseBean.class)這三個參數分別代表
* REST請求地址常侦、請求參數、HTTP響應轉換被轉換成的對象類型予弧。
*/
@Bean //注入bean
@LoadBalanced //Spring Cloud Ribbon是基于Netflix Ribbon實現(xiàn)的一套客戶端,負載均衡的工具刮吧。 開啟負載均衡
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
(4)、主啟動類DeptConsumer80_App.java添加注解@EnableEurekaClient
@SpringBootApplication
@EnableEurekaClient
public class DeptConsumer80_App {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer80_App.class, args);
}
}
(5)掖蛤、修改DeptController_Consumer.java客戶端訪問類
package com.smilexl.springcloud.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import com.smilexl.springcloud.entities.Dept;
@RestController
public class DeptController_Consumer {
/**
* RestTemplate提供了多種便捷訪問遠程Http服務的方法
* 是一種簡單便捷的訪問restful服務模板類杀捻,是Spring提供的用于訪問Rest服務的客戶端模板工具集
* 使用restTemplate訪問restful接口非常的簡單粗暴無腦。
* (url, requestMap, ResponseBean.class)這三個參數分別代表
* REST請求地址蚓庭、請求參數致讥、HTTP響應轉換被轉換成的對象類型。
*/
// private static final String REST_URL_PREFIX = "http://localhost:8001";
private static final String REST_URL_PREFIX = "http://SERVICESPRINGCLOUD-DEPT"; //改為微服務名字
@Autowired
private RestTemplate restTemplate;
// 添加
@GetMapping("/consumer/dept/add")
public Boolean add(Dept dept) {
return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", dept, Boolean.class);
}
// 根據id查詢
@GetMapping("/consumer/dept/get/{id}")
public Dept get(@PathVariable("id") Long id) {
return restTemplate.getForObject(REST_URL_PREFIX + "/dept/get/" + id, Dept.class);
}
// 根據id查詢
@SuppressWarnings("unchecked")
@GetMapping("/consumer/dept/list")
public List<Dept> list() {
return restTemplate.getForObject(REST_URL_PREFIX + "/dept/list", List.class);
}
}
(6)器赞、先啟動3個eureka集群垢袱,再啟動microservicecloud-provider-dept-8001并注冊進eureka
(7)、啟動microservicecloud-consumer-dept-80港柜,測試
(8)请契、小總結
Ribbon和Rureka整合后Cousumer可以直接調用服務而不用再關心地址和端口號
二咳榜、Ribbon負載均衡
1、架構說明
Ribbon在工作時分成兩步
? 第一步先選擇Eureka Server爽锥,它優(yōu)先選擇在同一個區(qū)域內負載較少的server涌韩,
? 第二步再根據用戶指定的策略,在從server取到的服務注冊列表中選擇一個地址氯夷,
? 其中Ribbon提供了多種策略:比如輪詢臣樱、隨機和根據響應時間加權。
2腮考、參考microservicecloud-provider-dept-8001雇毫,新建8002、8003
- pom.xml文件中添加如下依賴
<dependencies>
<dependency><!-- 引入自己定義的api通用包踩蔚,可以使用Dept部門Entity -->
<groupId>cn.smilexl.springcloud</groupId>
<artifactId>microservicecloud-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!-- 將微服務provider注冊進eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- actuator監(jiān)控信息完善 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
3棚放、新建8002、8003數據庫寂纪,各微服務分別連自己的數據庫
- 8002 SQL腳本
DROP DATABASE IF EXISTS cloudDB02;
CREATE DATABASE cloudDB02 CHARACTER SET UTF8;
USE cloudDB02;
CREATE TABLE `dept` (
`deptno` bigint(20) NOT NULL AUTO_INCREMENT,
`dname` varchar(60) DEFAULT NULL,
`db_source` varchar(60) DEFAULT NULL,
PRIMARY KEY (`deptno`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
INSERT INTO `dept` VALUES ('1', '開發(fā)部', 'clouddb01');
INSERT INTO `dept` VALUES ('2', '人事部', 'clouddb01');
INSERT INTO `dept` VALUES ('3', '財務部', 'clouddb01');
INSERT INTO `dept` VALUES ('4', '市場部', 'clouddb01');
INSERT INTO `dept` VALUES ('5', '運維部', 'clouddb01');
INSERT INTO `dept` VALUES ('6', '資訊部', 'clouddb01');
- 8003 SQL腳本
DROP DATABASE IF EXISTS cloudDB03;
CREATE DATABASE cloudDB03 CHARACTER SET UTF8;
USE cloudDB03;
CREATE TABLE `dept` (
`deptno` bigint(20) NOT NULL AUTO_INCREMENT,
`dname` varchar(60) DEFAULT NULL,
`db_source` varchar(60) DEFAULT NULL,
PRIMARY KEY (`deptno`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
INSERT INTO `dept` VALUES ('1', '開發(fā)部', 'clouddb01');
INSERT INTO `dept` VALUES ('2', '人事部', 'clouddb01');
INSERT INTO `dept` VALUES ('3', '財務部', 'clouddb01');
INSERT INTO `dept` VALUES ('4', '市場部', 'clouddb01');
INSERT INTO `dept` VALUES ('5', '運維部', 'clouddb01');
INSERT INTO `dept` VALUES ('6', '資訊部', 'clouddb01');
4席吴、修改8002、8003各子的yml
- 8002 的yml
server:
port: 8002
mybatis:
# config-location: classpath:mybatis/mybatis.cfg.xml # mybatis配置文件所在路徑
type-aliases-package: cn.smilexl.springcloud.entities # 所有Entity別名類所在包
mapper-locations: classpath:mapper/**/*.xml # mapper映射文件
spring:
application:
name: microservicecloud-dept # 微服務名
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 當前數據源操作類型
driver-class-name: org.gjt.mm.mysql.Driver # mysql驅動包
url: jdbc:mysql://47.99.218.123:3306/clouddb02 # 數據庫名稱
username: root
password: 123456
dbcp2:
min-idle: 5 # 數據庫連接池的最小維持連接數
initial-size: 5 # 初始化連接數
max-total: 5 # 最大連接數
max-wait-millis: 200 # 等待連接獲取的最大超時時間
eureka:
client: #客戶端注冊進eureka服務列表內
service-url:
# defaultZone: http://localhost:7001/eureka
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka
instance:
instance-id: microservicecloud-dept-8002 #自定義服務名稱信息
prefer-ip-address: true #訪問路徑可以顯示IP地址
#點擊超鏈接后顯示信息
info:
app.name: microservicecloud
company.name: www.smilexl.cn
build.artifactId: $project.artifactId$
build.version: $project.version$
- 8003 的yml
server:
port: 8003
mybatis:
# config-location: classpath:mybatis/mybatis.cfg.xml # mybatis配置文件所在路徑
type-aliases-package: cn.smilexl.springcloud.entities # 所有Entity別名類所在包
mapper-locations: classpath:mapper/**/*.xml # mapper映射文件
spring:
application:
name: microservicecloud-dept # 微服務名
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 當前數據源操作類型
driver-class-name: org.gjt.mm.mysql.Driver # mysql驅動包
url: jdbc:mysql://47.99.218.123:3306/clouddb03 # 數據庫名稱
username: root
password: 123456
dbcp2:
min-idle: 5 # 數據庫連接池的最小維持連接數
initial-size: 5 # 初始化連接數
max-total: 5 # 最大連接數
max-wait-millis: 200 # 等待連接獲取的最大超時時間
eureka:
client: #客戶端注冊進eureka服務列表內
service-url:
# defaultZone: http://localhost:7001/eureka
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka
instance:
instance-id: microservicecloud-dept-8003 #自定義服務名稱信息
prefer-ip-address: true #訪問路徑可以顯示IP地址
#點擊超鏈接后顯示信息
info:
app.name: microservicecloud
company.name: www.smilexl.cn
build.artifactId: $project.artifactId$
build.version: $project.version$
注意:
5捞蛋、啟動3個Dept微服務并且各自測試通過
http://localhost:8001/dept/list
http://localhost:8002/dept/list
http://localhost:8003/dept/list
6、啟動 microservicecloud-consumer-dept-80
http://localhost/consumer/dept/list
客戶端通過Ribbon完成負載均衡柬姚。注意觀看返回的數據庫名字拟杉,各不相同,負載均衡實現(xiàn)
7量承、總結
?? Ribbon其實就是一個負載均衡的客戶端組件搬设,他可以和其他所需要請求的客戶端結合使用,和eureka結合只是其中的一個實例撕捍。
三拿穴、Ribbon核心組件IRule
IRule:根據特定算法中從服務列表中選取一個要訪問的服務
- RoundRobinRule:輪詢;
- RandomRule:隨機忧风;
- AvailabilityFilteringRule:會過濾掉由于多次訪問故障而處于斷路器跳閘狀態(tài)的服務默色,還有并發(fā)的連接數量超過閾值的服務,然后對剩余的服務列表按照輪詢策略進行訪問狮腿;
- WeightedResponseTimeRule: 根據平均響應時間計算所有服務的權重腿宰,響應時間越快服務權重越大被選中的概率越高。剛啟動時如果統(tǒng)計信息不足缘厢,則使用RoundTobinRule策略吃度,等統(tǒng)計信息足夠會切換到WeightedResponseTimeRule;
- RetryRule: 先按照RoudRobinRule的策略獲取服務贴硫,如果獲取服務失敗則在指定時間內進行重試椿每,獲取可用服務,如果還是失敗的就會自動把這個失敗的服務隔離掉;
- BestAvailableRule: 會先過濾掉由于多次訪問故障而處于斷路器跳閘狀態(tài)的服務,然后選擇一個并發(fā)量小的服務间护;
- ZoneAvoidanceRule: 默認規(guī)則亦渗,復合判斷server所在區(qū)域的性能和server的可用性選擇服務器;
修改Ribbon的算法:修改 microservicecloud-consumer-dept-80 中 /cfgbeans/ConfigBean.java文件
@Configuration //配置類兑牡,類似spring里面的applicationContext.xml
public class ConfigBean {
/**
* RestTemplate提供了多種便捷訪問遠程Http服務的方法
* 是一種簡單便捷的訪問restful服務模板類央碟,是Spring提供的用于訪問Rest服務的客戶端模板工具集
* 使用restTemplate訪問restful接口非常的簡單粗暴無腦。
* (url, requestMap, ResponseBean.class)這三個參數分別代表
* REST請求地址均函、請求參數亿虽、HTTP響應轉換被轉換成的對象類型。
*/
@Bean //注入bean
@LoadBalanced //Spring Cloud Ribbon是基于Netflix Ribbon實現(xiàn)的一套客戶端,負載均衡的工具苞也。 開啟負載均衡
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
@Bean
public IRule myRule() {
return new RandomRule();//達到的目的:用我們從新選擇的隨機算法代替默認的輪詢算法
}
}
//相對于洛勉,<bean id="restTemplate" class="com.smilexl.springcloud.cfgbean.RestTemplate">
四、Ribbon 自定義算法
1如迟、在 microservicecloud-consumer-dept-80 主啟動類上添加 @RibbonClient
package cn.smilexl.springcloud;
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;
import cn.smilexl.myrule.MyselfRule;
@SpringBootApplication
@EnableEurekaClient
//在啟動該微服務的時候就能去加載我們的自定義Ribbon配置類收毫,從而使配置生效。
@RibbonClient(name="MICROSERVICECLOUD-DEPT",configuration=MyselfRule.class)
public class DeptConsumer80_App {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer80_App.class, args);
}
}
@RibbonClient(name="MICROSERVICECLOUD-DEPT",configuration=MyselfRule.class)
- name:微服務的名字
- configuration:自定義的算法類
意思:對微服務 MICROSERVICECLOUD-DEPT殷勘,使用 MyselfRule.java類中的規(guī)則
注意配置事項: 這個自定義類(MyselfRule.java)不能放在@ComponentScan(即:主啟動類)所掃描的當前包下以及子包下此再,否則我們自定義的這個配置類就會被所有的Ribbon客戶端所共享,達不到特殊化定制的目的了玲销。
2输拇、步驟
(1)、新建package cn.smilexl.myrule 包
(2)贤斜、在此包下新建自定義Ribbon規(guī)則類 MyselfRule.java
package cn.smilexl.myrule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
@Configuration
public class MyselfRule {
@Bean
public IRule myRule() {
return new RandomRule();//Ribbon默認為輪詢策吠,自定義為隨機
}
}
3、Ribbon自定義規(guī)則深度解析
需求:依舊輪詢策略瘩绒,但是加上新需求猴抹,每個服務器要求被調用5次。也即以前是每臺機器一次锁荔,現(xiàn)在是每臺機器5次蟀给。
參考源碼修改為需求要求的 RoundRobinRule_LL
(1)、參考源碼修改為需要的算法策略(RoundRobinRule_LL.java)
package cn.smilexl.myrule;
import java.util.List;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
/**
* 自定義需求規(guī)則類
* --依舊輪詢策略堕战,但是加上新需求坤溃,每個服務器要求被調用5次。也即以前是每臺機器一次嘱丢,現(xiàn)在是每臺機器5次
*/
public class RoundRobinRule_LL extends AbstractLoadBalancerRule {
/**
* Randomly choose from all living servers
*/
private int total = 0; //總共被調用的次數薪介,目前要求每臺被調用5次
private int currentIndex = 0; //當前提供服務的機器號
@SuppressWarnings("unused")
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);
//定義判斷規(guī)則
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 arg0) {
// TODO Auto-generated method stub
}
}
(2)、在配置類中注入自定義的算法策略(MyselfRule.java)
package cn.smilexl.myrule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.netflix.loadbalancer.IRule;
@Configuration
public class MyselfRule {
@Bean
public IRule myRule() {
// return new RandomRule();//Ribbon默認為輪詢越驻,自定義為隨機
// return new RoundRobinRule();//輪詢
return new RoundRobinRule_LL(); //調用自定義的算法規(guī)則汁政,每臺輪詢調用5次
}
}
(3)道偷、最終工程目錄結構如下:
(4)、依次啟動工程并測試結果
依次啟動工程:
microservicecloud-eureka-7001记劈;
microservicecloud-eureka-7002勺鸦;
microservicecloud-eureka-7003;
microservicecloud-provider-dept-8001目木;
microservicecloud-provider-dept-8002换途;
microservicecloud-provider-dept-8003;
microservicecloud-consumer-dept-80刽射;
測試:localhost/consumer/dept/get/1