文章目录
写在前面
- 上一篇文章 spring cloud 项目搭建 :1、eureka 服务治理:
https://blog.csdn.net/a__int__/article/details/116791386 - 下一篇文章 spring cloud 项目搭建 :3、Feign接口负载均衡:
https://blog.csdn.net/a__int__/article/details/117438047
1、ribbon介绍
1、轮询:用户获取资源时,从几个服务中轮询获取
2、随机:随机获取
3、加强轮询:在轮询的基础上设置一个权重值,根据权重轮询
4、hash算法:根据hash值选择,该算法无法解决热点请求,会把某个时间段的请求路由到某个单机上,造成雪崩
5、最小连接数:把请求分配给活动连接数最小的后端服务器
6、最短响应时间:通过ping或者正常的响应时间来分配
7、加权最小连接数:对性能较好的后端服务器设置较高的权重,承担更多的链接负载。
1.1、在消费者模块添加ribbon
添加依赖
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-ribbon -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!--erueka的依赖也需要-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
yml里面添加配置(配置eureka的注册中心地址)
server.port: 80
# eureka配置
eureka:
client:
register-with-eureka: false # 服务消费者,不需要往注册中心注册
service-url:
defaultZone: http://127.0.0.1:7001/eureka/,http://127.0.0.2:7002/eureka/,http://127.0.0.3:7003/eureka/
向启动类添加注解@EnableEurekaClient
向RestTemplate上添加注解@LoadBalanced
修改访问链接(修改成通过服务名访问)
private static final String URL_PREFIX = “http://SPRINGCLOUD-PROVIDER-DEPT”;
1.2、测试
启动注册中心、服务提供者、服务消费者
访问:http://127.0.0.1/consumer/dept/get
2、模拟集群环境
建三个服务提供者
2.1、建库建表
让三个服务提供者分别使用不同的数据
新建库test2、test3,并插入数据
create database test2;
use test2;
CREATE TABLE `dept` (
`deptno` bigint(20) NOT NULL AUTO_INCREMENT,
`dname` varchar(60) CHARACTER SET utf8mb4 DEFAULT NULL,
`db_source` varchar(60) CHARACTER SET utf8mb4 DEFAULT NULL,
PRIMARY KEY (`deptno`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COMMENT='部门表';
INSERT INTO `dept` VALUES ('1', '开发部', database());
INSERT INTO `dept` VALUES ('2', '财务部', database());
INSERT INTO `dept` VALUES ('3', '市场部', database());
INSERT INTO `dept` VALUES ('4', '人事部', database());
INSERT INTO `dept` VALUES ('5', '运维部', database());
INSERT INTO `dept` VALUES ('6', '销售部', database());
create database test3;
use test3;
CREATE TABLE `dept` (
`deptno` bigint(20) NOT NULL AUTO_INCREMENT,
`dname` varchar(60) CHARACTER SET utf8mb4 DEFAULT NULL,
`db_source` varchar(60) CHARACTER SET utf8mb4 DEFAULT NULL,
PRIMARY KEY (`deptno`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COMMENT='部门表';
INSERT INTO `dept` VALUES ('1', '开发部', database());
INSERT INTO `dept` VALUES ('2', '财务部', database());
INSERT INTO `dept` VALUES ('3', '市场部', database());
INSERT INTO `dept` VALUES ('4', '人事部', database());
INSERT INTO `dept` VALUES ('5', '运维部', database());
INSERT INTO `dept` VALUES ('6', '销售部', database());
2.2、新建模块
新建如下两个模块(maven)
往这两个模块的pom.xml加入依赖
<dependencies>
<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>
<!-- 你们会发现springboot的依赖都没引入,直接引入mybatis的stater,springboot的依赖就自动引进来了-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- jetty,和tomcat一样是一个web容器,Jetty相比与Tomcat是轻量级的-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<!-- 热部署工具:写完代码等待一会就部署上去了,不用手动重启 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<!-- 导入我们自己的模块 -->
<dependency>
<groupId>org.example</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<!-- eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- actuator完善监控信息 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
把springcloud-provider-dept-8001模块的application.yml复制进这两个模块,只需要修改下面几个个地方
接下来将mapper.xml、java文件复制进去,为了方便区分,我们在启动类上加上端口号
2.3、启动
启动三个eureka注册中心、三个服务提供者、一个服务消费者
访问:http://127.0.0.1:7001/
访问:http://127.0.0.1/consumer/dept/get
通过多次测试发现,ribbon默认的负载均衡应该是轮询
3、自定义负载均衡
3.1、使用已经定义好的一些负载均衡
使用系统里预置的一些负载均衡策略
// RoundRobinRule 默认,轮询
// RandomRule 随机
// AvailabilityFilteringRule 会过滤掉,跳闸,访问故障的服务,对剩下的进行轮询
// RetryRule 会先按照轮询获取服务,如果服务获取失败,则会在指定的时间内进行、重试
随机策略: 使用某个服务的时候,ribbon会随机选择一个
3.2、自己写一个负载均衡策略
注意:自己写的负载均衡策略必须@Configuration,但注意它不在主应用程序上下文的@ComponentScan中,否则将由所有@RibbonClients共享。解决方式:将它放在 springboot主启动的上级目录中,如下图
!
加载自定义的ribbon
@RibbonClient(name = “SPRINGCLOUD-PROVIDER-DEPT”,configuration = CustomRule.class)
上面写的那个MyRule返回的只是系统的随机负载均衡策略,
接下来我们根据随机负载均衡策略里面的代码自己写一个CustomRule
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.*;
import org.springframework.context.annotation.Configuration;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
public class CustomRule extends AbstractLoadBalancerRule {
// 每个服务访问5个,完了之后再访问下一个
private int total = 0; // 被调用的字数
private int currentIndex = 0; // 当前是谁在提供服务
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
} else {
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;
server = upList.get(currentIndex);
}
if (server == null) {
Thread.yield();
} else {
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 this.choose(this.getLoadBalancer(), key);
}
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
}
使用自定义策略