目录
1. 前言
Ribbon
是 Netflix
开发的一个负载均衡组件,它在服务体系中起着重要作用,与其他 SpringCloud
组件结合可以发挥出强大作用,它的负载策略有多种,默认轮询
说到负载均衡,ribbon
与 lvs、nginx
不一样,nginx
是服务端负载均衡,ribbon
是客户端负载均衡,具体表现为客户端从注册中心拿到服务的所有实例,然后以负载均衡方式去调用服务,默认以轮询的方式去调用服务实例
ribbon
与 eureka
配合使用的大致架构如下
2. ribbon
实现负载均衡
在 上一篇 文章中,已实现 eureka
客户端的注册行为,本篇文章将实现 eureka
客户端的调用,即服务提供方提供接口供服务消费方来消费,调用,并实现 ribbon
的客户端负载均衡
依然使用上一篇文章的项目,如下
2.1. Maven
主要依赖
eureka-client-consumer
服务消费方项目添加如下依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
spring-cloud-starter-netflix-eureka-client
:如果已经引入了这个eureka
客户端的依赖,此时可以不需要引入上述spring-cloud-starter-netflix-ribbon
依赖了,因为eureka
客户端的依赖已经包含了
spring-cloud-starter-netflix-ribbon
依赖eureka-client-producer
:作为服务的提供方,需要提供接口供消费方使用。这里我们可以连接数据库,至于连接数据库,以及ORM
持久层框架的依赖,这里不再赘述,自己引入即可
2.2. application.properties
配置文件
2.2.1. eureka-client-producer
服务提供方
server.port=8080
#注册进eureka的名称
spring.application.name=eureka-client-producer
#JDBC 配置
spring.datasource.druid.url=jdbc:mysql://127.0.0.1:3306/shiro?characterEncoding=utf8&useSSL=false&autoReconnect=true&serverTimezone=UTC
spring.datasource.druid.username=root
spring.datasource.druid.password=123456
spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
#druid 连接池配置
spring.datasource.druid.initial-size=3
spring.datasource.druid.min-idle=3
spring.datasource.druid.max-active=10
spring.datasource.druid.max-wait=60000
#指定 mapper 文件路径
mybatis.mapper-locations=classpath:org/example/mapper/*.xml
mybatis.configuration.cache-enabled=true
#开启驼峰命名
mybatis.configuration.map-underscore-to-camel-case=true
#打印 SQL 语句
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
eureka.client.service-url.defaultZone=http://eureka7001:8761/eureka/
eureka.instance.prefer-ip-address=true
2.2.2. eureka-client-consumer
服务消费方
server.port=8090
#注册进eureka的名称
spring.application.name=eureka-client-consumer
eureka.client.service-url.defaultZone=http://eureka7001:8761/eureka/
eureka.instance.prefer-ip-address=true
2.3. eureka-client-producer
服务提供方的主要代码
2.3.1. service
层
查询数据库
@Slf4j
@Service
public class UserServiceImpl implements UserService {
@Value(value = "${server.port}")
private Integer port;
@Autowired
private UserMapper userMapper;
@Override
public ResultVo selectOne(Integer id) {
log.info("服务提供方的端口为:" + port);
User user = userMapper.selectByPrimaryKey(id);
ResultVo resultVo = new ResultVo();
BeanUtils.copyProperties(user, resultVo);
resultVo.setPort(port);
return resultVo;
}
}
2.3.2. controller
层
对外提供的接口
@Slf4j
@Controller
@RequestMapping(path = "/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping(path = "/selectUserById")
@ResponseBody
public ResultVo selectUserById(Integer id) {
return userService.selectOne(id);
}
}
2.3.3. 启动类
@EnableEurekaClient
@Slf4j
@SpringBootApplication
public class AppProducer {
public static void main(String[] args) {
SpringApplication.run(AppProducer.class, args);
log.info("------AppProducer Running------");
}
}
2.4. eureka-client-consumer
服务消费方的主要代码
2.4.1. config
配置类
@Configuration
public class UserConsumerConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
2.4.2. service
层
@Slf4j
@Service
public class UserConsumerServiceImpl implements UserConsumerService {
@Autowired
private RestTemplate restTemplate;
@Autowired
private LoadBalancerClient loadBalancerClient;
/**
* ribbon 实现负载均衡
*/
@Override
public ResultVo findOneById(Integer id) {
// eureka-client-producer:是生产者端服务名称
ResponseEntity<ResultVo> responseEntity = restTemplate.getForEntity("http://eureka-client-producer/user/selectUserById?id=" + id, ResultVo.class);
ResultVo resultVo = responseEntity.getBody();
assert resultVo != null;
log.info("User为:" + resultVo.toString());
log.info("调用服务提供方的端口为:" + resultVo.getPort());
return resultVo;
}
}
eureka-client-producer
:就是配置文件application.properties
配置的spring.application.name
的值
2.4.3. controller
层
@Slf4j
@Controller
@RequestMapping(path = "/UserConsumer")
public class UserConsumerController {
@Autowired
private UserConsumerService userConsumerService;
/**
* ribbon 实现负载均衡
*/
@GetMapping(path = "/findOneById")
@ResponseBody
public ResultVo findOneById(Integer id) {
return userConsumerService.findOneById(id);
}
}
2.4.4. 启动类
@EnableEurekaClient
@Slf4j
@SpringBootApplication
public class AppConsumer {
public static void main(String[] args) {
SpringApplication.run(AppConsumer.class, args);
log.info("------AppConsumer Running------");
}
}
3. 启动测试
3.1. eureka
集群改单机
由于机器资源有限,服务启动太多有点卡,所以将 eureka
集群模式改为单机模式,以节省机器资源,配置文件 application.properties
改为如下
server.port=8761
#false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
eureka.client.fetch-registry=false
#false表示不向注册中心注册自己
eureka.client.register-with-eureka=false
#集群版
#eureka.client.service-url.defaultZone=http://eureka7002:8762/eureka/,http://eureka7003:8763/eureka/
#单机版
eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
#注册进对方注册中心展示的实例名称
eureka.instance.hostname=eureka7001
3.2. 启动各个服务测试
分别启动 eureka-client-producer
,eureka-client-consumer
,eureka-server-one
,这 3
个服务项目,首先查看 eureka
注册中心的服务注册情况,浏览器输入:http://eureka7001:8761/
如下
- 为了测试负载均衡,对
eureka-client-producer
服务要启动两个实例,即启动两个端口不同的实例服务;一个端口为8080
,一个为8070
3.3. 测试
使用 postman
测试接口,也就是接口 http://127.0.0.1:8090/UserConsumer/findOneById?id=1
我们发送 5
次请求,查看控制台日志情况,如下
观察日志,可以看到服务提供方 eureka-client-producer
分别以 8080,8070
端口被调用,并且是轮询的方式,这也印证了 ribboon
是默认以轮询的方式去调用服务实例
4. Ribbon
配置负载均衡策略
4.1. ribbon
支持的策略
ribbon
支持 7
种负载均衡策略
策略类 | 命名 | 描述 |
---|---|---|
RandomRule | 随机策略 | 随机选择 server |
RoundRobinRule | 轮询策略 | 按照顺序选择 server ( 默认策略) |
RetryRule | 重试策略 | 在一个配置时间段内,当选择 server 不成功,则一直尝试选择一个可用的 server |
BestAvailableRule | 最低并发策略 | 逐个考察 server,如果 server 断路器打开,则忽略,再选择其中并发链接最低的 server |
AvailabilityFilteringRule | 可用过滤策略 | 过滤掉一直失败并被标记为 circuit tripped 的 server,过滤掉那些高并发链接的 server(active connections 超过配置的阈值) |
ResponseTimeWeightedRule | 响应时间加权重策略 | 根据 server 的响应时间分配权重,响应时间越长,权重越低,被选择到的概率也就越低。响应时间越短,权重越高,被选中的概率越高,这个策略很贴切,综合了各种因素,比如:网络,磁盘,io 等,都直接影响响应时间 |
ZoneAvoidanceRule | 区域权重策略 | 综合判断 server 所在区域的性能,和 server 的可用性,轮询选择 server 并且判断一个 AWS Zone 的运行性能是否可用,剔除不可用的 Zone 中的所有 server |
4.2. 对某个服务配置启用某种策略
在服务消费方 eureka-client-consumer
的配置类 UserConsumerConfig
中添加如下配置
/**
* 配置随机负载均衡策略
*/
@Bean
public IRule ribbonRule() {
return new RandomRule();
}
4.3. 启动服务测试
postman
测试接口如下
我们发送 9
次请求,查看控制台日志情况,如下
可以看到,我们配置的随机负载均衡策略已经生效