【SpringCloud】SpringCloud基础教程(4)
前言:
本文内容:Ribbon:负载均衡及Ribbon、使用Ribbon实现负载均衡、自定义负载均衡策略
推荐免费SpringCloud基础教程视频:【狂神说Java】SpringCloud最新教程IDEA版_哔哩哔哩_bilibili
Ribbon:负载均衡及Ribbon
Ribbon是什么
- Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡工具
- Ribbon主要提供客户端软件负载均衡算法,将Netflix的中间层服务连接在一起。Ribbon的客户端组件提供一系列完整的配置
Ribbon能干什么
- LB,即负载均衡(Load Balance),在微服务或分布式集群中经常使用
- 负载均衡简单说就是将用户的请求平均分摊到多个服务上,从而达到系统的HA(高可用)
- 常见的负载均衡有Nginx、Lvs等
- Dubbo和SpringCloud均为我们提供了负载均衡,SpringCloud的负载均衡算法可以自定义
- 负载均衡简单分类:
- 集中式LB
- 即在服务的消费方和提供方之间使用独立的LB设施,如Nginx,由该设施负责把访问请求通过某种策略转发至服务提供方
- 进程式LB
- 将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后再从这些地址中选出一个合适的服务器
- Ribbon属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取服务提供方地址
- 集中式LB
环境搭建
-
打开
springcloud-consumer-dept-80
的pom文件,引入依赖1
2
3
4
5
6
7
8<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> -
打开
application
,添加配置1
2
3
4
5
6# Eureka
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002,http://eureka7003.com:7003 -
打开主启动类,添加注解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17package com.jokerdig.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* @author Joker大雄
* @data 2022/8/3 - 15:28
**/
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class,args);
}
} -
在
config
包下的ConfigBean
中配置负载均衡1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20package com.jokerdig.springcloud.config;
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;
/**
* @author Joker大雄
* @data 2022/8/3 - 15:14
**/
public class ConfigBean {
// Ribbon负载均衡
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
} -
修改
controller
包下的DeptConsumerController
类中的内容1
2
3// private static final String REST_URL_PREFIX="http://localhost:8001";
// Ribbon实现
private static final String REST_URL_PREFIX="http://SPRINGCLOUD-PROVIDER-DEPT"; -
启动端口为7001,7002,7003,8001,80的模块,运行测试
http://localhost/consumer/dept/list
1
[{"deptno":1,"dname":"开发部","db_source":"db01"},{"deptno":2,"dname":"生产部","db_source":"db01"},{"deptno":3,"dname":"研发部","db_source":"db01"},{"deptno":4,"dname":"人事部","db_source":"db01"},{"deptno":5,"dname":"运维部","db_source":"db01"}]
-
小结
Ribbon
和Eureka
整合后,客户端可以直接调用,不用关心IP地址和端口号
Ribbon:使用Ribbon实现负载均衡
创建多个服务提供者进行负载均衡
-
新建
db02
和db03
数据库db02
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28create database db02;
use db02;
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for dept
-- ----------------------------
DROP TABLE IF EXISTS `dept`;
CREATE TABLE `dept` (
`deptno` bigint(0) NOT NULL AUTO_INCREMENT,
`dname` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`db_source` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
PRIMARY KEY (`deptno`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '部门表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of dept
-- ----------------------------
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());
SET FOREIGN_KEY_CHECKS = 1;db03
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28create database db03;
use db03;
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for dept
-- ----------------------------
DROP TABLE IF EXISTS `dept`;
CREATE TABLE `dept` (
`deptno` bigint(0) NOT NULL AUTO_INCREMENT,
`dname` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`db_source` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
PRIMARY KEY (`deptno`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '部门表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of dept
-- ----------------------------
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());
SET FOREIGN_KEY_CHECKS = 1; -
创建
springcloud-provider-dept-8002
(数据库db02)和springcloud-provider-dept-8003
数据库db03)的Module
,内容与springcloud-provider-dept-8002
基本一致,除了数据库的变化博主8G内存同时运行4个项目已经占用已经满了,这部分推荐16G以上内存的电脑运行
orz
-
按顺序启动7001,7002,7003(可选),8001,8002,8003(可选),80等端口的项目
/(ㄒoㄒ)/~~
-
运行测试
http://localhost/consumer/dept/list
每一次刷新访问,都会按照负载均衡的算法去访问最优的服务提供者
1
2
3
4
5
6
7
8// 每一次刷新访问
[{"deptno":1,"dname":"开发部","db_source":"db01"},{"deptno":2,"dname":"生产部","db_source":"db01"},{"deptno":3,"dname":"研发部","db_source":"db01"},{"deptno":4,"dname":"人事部","db_source":"db01"},{"deptno":5,"dname":"运维部","db_source":"db01"}]
// 每一次刷新访问
[{"deptno":1,"dname":"开发部","db_source":"db03"},{"deptno":2,"dname":"生产部","db_source":"db03"},{"deptno":3,"dname":"研发部","db_source":"db03"},{"deptno":4,"dname":"人事部","db_source":"db03"},{"deptno":5,"dname":"运维部","db_source":"db03"}]
// 每一次刷新访问
[{"deptno":1,"dname":"开发部","db_source":"db02"},{"deptno":2,"dname":"生产部","db_source":"db02"},{"deptno":3,"dname":"研发部","db_source":"db02"},{"deptno":4,"dname":"人事部","db_source":"db02"},{"deptno":5,"dname":"运维部","db_source":"db02"}]
Ribbon:自定义负载均衡策略
-
打开
springcloud-consumer-dept-80
项目config
包下的ConfigBean
,配置官方的IRule
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35package com.jokerdig.springcloud.config;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
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;
/**
* @author Joker大雄
* @data 2022/8/3 - 15:14
**/
public class ConfigBean {
// 配置负载均衡实现RestTemplate
// IRule
// RoundRobinRule 轮询
// RandomRule 随机
// AvailabilityFilterIngRule:过滤掉故障服务
// RetryRule 先按照轮询获取服务,如果获取失败,在指定时间内重试
// 负载均衡
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
// 随机 这是官方写好的
// 自定义Rule的时候这个方法要注释掉
public IRule myIRule(){
return new RandomRule();
}
} -
启动所有的Module进行测试
http://localhost/consumer/dept/list
每一次刷新,都会随机访问服务提供者
1
2
3// 每一次刷新访问
[{"deptno":1,"dname":"开发部","db_source":"db01"},{"deptno":2,"dname":"生产部","db_source":"db01"},{"deptno":3,"dname":"研发部","db_source":"db01"},{"deptno":4,"dname":"人事部","db_source":"db01"},{"deptno":5,"dname":"运维部","db_source":"db01"}]
// .......省略 -
自定义
IRule
,新建包com.jokerdig.myrule
,在包下新建JokerdigRule
和JokerdigRandomRule
这里包没有建在
springcloud
下而是它上一级的jokerdig
下是为了防止被主启动类扫描到,导致自定义与原生的发生冲突JokerdigRule
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20package com.jokerdig.myrule;
import com.netflix.loadbalancer.IRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Joker大雄
* @data 2022/8/5 - 13:03
**/
public class JokerdigRule {
// 自定义
public IRule myIRule(){
return new JokerdigRandomRule();
}
}JokerdigRandomRule
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83package com.jokerdig.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;
import java.util.concurrent.ThreadLocalRandom;
public class JokerdigRandomRule extends AbstractLoadBalancerRule {
// 自定义 这里所写内容仅供测试和了解自定义 并非实际使用
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;
}
// nt index = chooseRandomInt(serverCount); // 生成区间随机数
// server = upList.get(index); // 从存活的服务中随机获取一个
// 自定义
if(total<5){
server = upList.get(currentIndex);
total++;
}else{
total = 0;
currentIndex++;
if(currentIndex>upList.size()-1){
currentIndex = 0;
}
server = upList.get(currentIndex);
}
if (server == null) {
Thread.yield();
continue;
}
if (server.isAlive()) {
return (server);
}
server = null;
Thread.yield();
}
return server;
}
protected int chooseRandomInt(int serverCount) {
return ThreadLocalRandom.current().nextInt(serverCount);
}
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
public void initWithNiwsConfig(IClientConfig clientConfig) {
// TODO Auto-generated method stub
}
} -
修改主启动类,添加
@RibbonClient
的注解1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21package com.jokerdig.springcloud;
import com.jokerdig.myrule.JokerdigRule;
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;
/**
* @author Joker大雄
* @data 2022/8/3 - 15:28
**/
// 在微服务启动就能加载自定义的Ribbon类
public class DeptConsumer_80 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_80.class,args);
}
} -
启动所有模块,进行测试
http://localhost/consumer/dept/list
每刷新到第5次,会更换所访问服务提供者