源码地址:https://gitee.com/peachtec/springcloud
什么是Ribbon
Spring Cloud Ribbon 是基于NetFlix Ribbon 实现的一套客户端负载均衡的工具
简单的说,Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将Netflix的中间层服务连接在一起;Ribbon的客户端组件提供一系列完整的配置,例如:连接超时、重试等等;简单地说,就是在配置文件中列出LoadBalance(简称LB:负载均衡)后面的所有的机器,Ribbon会自动帮助你基于某种规则,例如简单轮训、随机等去连接这些机器,我们也很容易使用Ribbon实现自定义的负载均衡算法。
Ribbon能干啥
LB,即负载均衡(LoadBalancer),在微服务或分布式集群中经常用的一种应用
负载均衡,简单的说就是将用户的请求平摊到多个服务上,从而达到高可用
常见的负载均衡软件有:Nginx,Lvs等
dubbo,SpringCloud中均提供了负载均衡,但SpringCloud的负载均衡算法可以自定义
负载均衡的分类
- 集中式LB
即在服务的消费方和提供方之间使用独立的LB设施,如nginx反向代理服务器,由设施负责吧访问通过某种策略转发到服务的提供方 - 进程式LB
将LB逻辑集成到消费方,消费方从服务注册中心获知哪些地址可用,然后自己再从这些地址中选出一个合适的服务器
Ribbon属于进程内LB,它只是一个类库,集成在消费方进程,消费者通过它获取到服务提供的地址
怎么使用Ribbon
Ribbon是一个类库,集成在消费方进程,所以我们只需要在消费者项目中去配置Ribbon,代码依旧使用之前的项目,端口为80的模块为我的消费者模块
- 导入jar包依赖
<!-- Ribbon -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<!-- Eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
- 编写配置
#Eureka 客户端配置
eureka:
client:
register-with-eureka: false # 不向Eureka注册自己
service-url: # 注册中心地址
defaultZone: http://eureka7002:7002/eureka/,http://eureka7003:7003/eureka/,http://eureka7004:7004/eureka/
- 配置负载均衡,通过一个注解进行配置
@Configuration
public class RestTemplateConfig {
//配置负载均衡实现RestTemplate
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
- 在主启动类中开启Eureka
@SpringBootApplication
@EnableEurekaClient
public class Resumer_80 {
public static void main(String[] args) {
SpringApplication.run(Resumer_80.class, args);
}
}
- 编写控制层接口,通过负载均衡访问的地址应该是一个变量,通过服务名访问
/**
* 通过负载均衡访问的地址应该是一个变量,通过服务名访问
*/
private static final String URL_PREFIX = "http://SPRINGCLOUD-SERVER/dept";
@GetMapping("/get/{id}")
public Dept getDeptById(@PathVariable Integer id) {
return restTemplate.getForObject(URL_PREFIX + "/get/" + id, Dept.class);
}
上面把Eureka和Ribbon整合在消费者模块了,但是实际上还没有真正的使用负载均衡,接下就实现负载均衡,将服务提供模块、数据库进行复制,基本上和之前的8001的服务提供模块一样。
- 创建服务需要的数据库,我现在的电脑同时跑三个服务提供者服务应该能抗住
drop database if exists db01;
create database if not exists db01 default character set utf8mb4 collate utf8mb4_general_ci;
use db01;
drop table if exists `dept`;
create table if not exists `dept`
(
`id` int primary key auto_increment comment '编号',
`name` varchar(60) not null comment '部门',
`db_source` varchar(20) not null comment '数据库名'
) comment '部门表';
insert into dept (name, db_source)
values ('开发部', database()),('人事部', database()),('市场部', database()),('销售部', database()),('运维部', database()),('行政部', database());
drop database if exists db02;
create database if not exists db02 default character set utf8mb4 collate utf8mb4_general_ci;
use db02;
drop table if exists `dept`;
create table if not exists `dept`
(
`id` int primary key auto_increment comment '编号',
`name` varchar(60) not null comment '部门',
`db_source` varchar(20) not null comment '数据库名'
) comment '部门表';
insert into dept (name, db_source)
values ('开发部', database()),('人事部', database()),('市场部', database()),('销售部', database()),('运维部', database()),('行政部', database());
drop database if exists db03;
create database if not exists db03 default character set utf8mb4 collate utf8mb4_general_ci;
use db03;
drop table if exists `dept`;
create table if not exists `dept`
(
`id` int primary key auto_increment comment '编号',
`name` varchar(60) not null comment '部门',
`db_source` varchar(20) not null comment '数据库名'
) comment '部门表';
insert into dept (name, db_source)
values ('开发部', database()),('人事部', database()),('市场部', database()),('销售部', database()),('运维部', database()),('行政部', database());
- 创建服务提供者模块8002和8003端口,其代码参考8001模块,需要注意项目名需要保持一致,否则就是其他的服务了
spring:
application:
name: springcloud-server
- 将所有的服务提供者启动,可以再Eureka的后台查看是否被注册进去了
- 测试结果,可以看到,每次去访问,都是从不同的数据库获取到的数据,并且默认的算法为轮训
除了默认的负载均衡算法,我们还可以自定义实现负载均衡算法,去实现IRule接口类,官方给了我们以下的实现 - AvailabilityFilteringRule:先过滤跳闸的服务,对剩下的进行轮训
- RoundRobinRule:轮训,默认算法
- RandomRule:随机
- WeightedResponseTimeRule:权重
- RetryRule:会先按照轮训获取服务,如果服务获取失败,会在执行的时间内进行重试
示例:每个服务访问3次,然后换下一个服务
public class MyRule extends AbstractLoadBalancerRule {
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
@Override
public Server choose(Object key) {
return this.choose(this.getLoadBalancer(), key);
}
/**
* 需求
* 每个服务访问3次,然后换下一个服务
*/
private int total = 0;//执行的次数
private int index = 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();
if (allList.size() == 0) {
return null;
}
if (total < 3) {
total++;
} else {
total = 0;
index++;
if (index >= upList.size()) {
index = 0;
}
}
server = upList.get(index);
if (server == null) {
Thread.yield();
} else {
if (server.isAlive()) {
return server;
}
server = null;
Thread.yield();
}
}
return server;
}
}
然后,将我们自定义的算法注册到spring中,并在启动类中进行配置
//注册
@Configuration
public class RuleConfig {
@Bean
public IRule myrule(){
return new MyRule();
}
}
//启动类中配置
@SpringBootApplication
@EnableEurekaClient
//在微服务启动时加载自定义的Ribbon类configuration指定配置类
@RibbonClient(name = "SPRINGCLOUD-SERVER", configuration = MyRule.class)
public class Resumer_80 {
public static void main(String[] args) {
SpringApplication.run(Resumer_80.class, args);
}
}
注意,Ribbon的自定义配置不能放在和启动类同级的包中