Spring Cloud与Docker微服务架构实战(四)—— Ribbon客户端侧负载均衡
五、使用Ribbon实现客户端侧负载均衡
简介
为Ribbon配置服务提供者地址列表后Ribbon可以基于某种负载均衡算法,自动的帮服务消费者去请求,如轮询、随机等,也可以自定义负载均衡算法。
下图是Ribbon与Eureka联用的框架图
为服务消费者整合Ribbon
为前文编写的电影微服务整合Ribbon
1.复制项目microservice-simple-consumer-movie,将ArtifactId修改为microservice-simple-consumer-movie-ribbon
2.引入Ribbon依赖,依赖如下
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-ribbon -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
3.为RestTemplate添加@LoadBalanced 注解
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
4.将Controller代码修改如下
package com.lzy.cloud.microservicesimpleconsumermovie.controller;
import com.lzy.cloud.microservicesimpleconsumermovie.pojo.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
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 java.util.List;
@RestController
public class MovieController {
private static final Logger LOGGER = LoggerFactory.getLogger(MovieController.class);
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
private LoadBalancerClient loadBalancerClient;
@GetMapping("/user/{id}")
public User findById(@PathVariable Long id){
return this.restTemplate.getForObject("http://microservice-simple-provider-user/" +id, User.class);
}
/*
* 查询 microservice-simple-provider-user的实例信息
* @return
*/
@GetMapping("/user-instance")
public List<ServiceInstance> showInfo(){
return this.discoveryClient.getInstances("microservice-simple-provider-user");
}
/*
* 打印选择了哪个节点
*/
@GetMapping("/log-instance")
public void logUserInstance(){
ServiceInstance serviceInstance = loadBalancerClient.choose("microservice-simple-provider-user");
MovieController.LOGGER.info("{}:{}:{}",serviceInstance.getServiceId(),serviceInstance.getHost(),
serviceInstance.getPort());
}
}
此时请求地址改成了"http://microservice-simple-provider-user/","microservice-simple-provider-user"是用户微服务的虚拟主机名,Ribbon和Eureka配合使用时,自动将虚拟主机名映射成微服务网络地址。
测试
启动eureka注册中心,并启动2个或多个microservice-simple-provider-user实例和Ribbon负载均衡功能的电影微服务。访问localhost:8761,结果如下
多次访问localhost:8010/user/1,返回结果如下
{
"id": 1,
"username": "account1",
"name": "张三",
"age": 20,
"balance": 100
}
同时,两个微服务实例都会打印查询日志,但是是交替打印的,多次访问localhost:8010/log-instance,控制台打印如下日志,看到每次访问不同微服务,效果更加明显
2020-09-23 15:28:08.752 INFO 2464 --- [nio-8010-exec-1] c.l.c.m.controller.MovieController : microservice-simple-provider-user:172.19.173.181:8082
2020-09-23 15:28:09.727 INFO 2464 --- [erListUpdater-0] c.netflix.config.ChainedDynamicProperty : Flipping property: microservice-simple-provider-user.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2020-09-23 15:30:09.118 INFO 2464 --- [nio-8010-exec-6] c.l.c.m.controller.MovieController : microservice-simple-provider-user:172.19.173.181:8082
2020-09-23 15:30:46.337 INFO 2464 --- [nio-8010-exec-7] c.l.c.m.controller.MovieController : microservice-simple-provider-user:172.19.173.181:8081
2020-09-23 15:30:46.700 INFO 2464 --- [nio-8010-exec-8] c.l.c.m.controller.MovieController : microservice-simple-provider-user:172.19.173.181:8082
2020-09-23 15:30:47.112 INFO 2464 --- [nio-8010-exec-9] c.l.c.m.controller.MovieController : microservice-simple-provider-user:172.19.173.181:8081
2020-09-23 15:30:47.418 INFO 2464 --- [io-8010-exec-10] c.l.c.m.controller.MovieController : microservice-simple-provider-user:172.19.173.181:8082
2020-09-23 15:30:47.641 INFO 2464 --- [nio-8010-exec-1] c.l.c.m.controller.MovieController : microservice-simple-provider-user:172.19.173.181:8081
RestTemplate其实也是一个Ribbon客户端,包含和LoadBalancerClient一样的choose行为
- 注意:虚拟主机名不能包含“_”之类的字符,否则Ribbon调用会出现异常
使用Java自定义Ribbon配置
Spring Cloud 中,Ribbon默认配置如下(格式是 BeanType beanName: ClassName):
- IClientConfig ribbonClientConfig: DefaultClientConfigImpl
- IRule ribbonRule: ZoneAvoidanceRule
- IPing ribbonPing: NoOpPing
- ServerList ribbonServerList: ConfigurationBasedServerList
- ServerFilter ribbonServerListFilter: ZonePreferenceServerListFilter
- IloadBalancer ribbonLoadBalancer: ZoneAwareLoadBalancer
简单说明:
@Bean
@ConditionalOnMissingBean
public IRule ribbonRule(IClientConfig config) {
ZoneAvoidanceRule rule = new ZoneAvoidanceRule();
rule.initWithNiwsConfig(config);
return rule;
}
BeanType是IRule,beanName是ribbonRule,ClassName是ZoneAvoidanceRule,这是一种根据服务提供者所在Zone的性能以及服务提供者可用性综合计算,选择提供者节点的负载均衡规则。
Spring Cloud 中,Ribbon默认的配置类是RibbonClientConfiguration。也可以使用一个POJO自定义Ribbon配置(自定义配置覆盖默认配置)。这种配置是细粒度的,不同Ribbon客户端可以使用不同的配置。
下面来为microservice-provider-user的Ribbon客户端自定义配置
1.复制项目microservice-consumer-movie-ribbon,将ArtifactId修改为microservice-consumer-movie-ribbon-customizing
2.创建Ribbon配置类
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/*
* 该类不应该在主应用程序上下文的 @ComponentScan 中,应该手动排除不被扫描
*/
@Configuration
public class RibbonConfiguration {
@Bean
public IRule ribbonRule() {
//修改负载均衡规则为随机
return new RandomRule();
}
}
3.创建空类,添加相应注解
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.context.annotation.Configuration;
@Configuration
@RibbonClient(name = "microservice-provider-user", configuration = RibbonConfiguration.class)
public class TestConfiguration {
}
使用@RibbonClient 注解的configuration属性,可以指定名称Ribbon客户端的配置
测试
先启动eureka注册中心,再启动2个或更多microservice-provider-user实例
再启动microservice-consumer-movie-ribbon-customizing
多次访问http://localhost:8010/log-instance,可以得到如下日志
1635 --- [nio-8010-exec-1] c.l.c.m.controller.MovieController : microservice-simple-provider-user:172.19.173.181:8082
1635 --- [nio-8010-exec-3] c.l.c.m.controller.MovieController : microservice-simple-provider-user:172.19.173.181:8082
1635 --- [nio-8010-exec-4] c.l.c.m.controller.MovieController : microservice-simple-provider-user:172.19.173.181:8081
1635 --- [nio-8010-exec-5] c.l.c.m.controller.MovieController : microservice-simple-provider-user:172.19.173.181:8081
1635 --- [nio-8010-exec-6] c.l.c.m.controller.MovieController : microservice-simple-provider-user:172.19.173.181:8081
1635 --- [nio-8010-exec-7] c.l.c.m.controller.MovieController : microservice-simple-provider-user:172.19.173.181:8081
1635 --- [nio-8010-exec-8] c.l.c.m.controller.MovieController : microservice-simple-provider-user:172.19.173.181:8082
请求会随机分布到两个节点上,说明实现了Ribbon的自定义配置
使用属性自定义Ribbon配置
使用属性自定义Ribbon客户端,比Java代码配置更加方便
- NFLoadBalancerClassName:配置ILoadBalancer的实现类
- NFLoadBalancerRuleClassName:配置IRule的实现类
- NFLoadBalancerPingClassName:配置Ping的实现类
- NIWSServerListClassName:配置ServerList的实现类
- NIWSServerListFilterClassName:配置ServerListFilter的实现类
采用属性来修改microservice-consumer-movie-ribbon-customizing,在项目的application.yml中添加以下内容即可:
microserivce-provider-user:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
脱离Eureka使用Ribbon
Ribbon支持脱离Eureka使用,此时架构图如下:
1.复制项目microservice-simple-consumer-movie-ribbon,将artifactId修改为microservice-simple-consumer-movie-ribbon-without-eureka
2.去掉Eureka的依赖,只使用Ribbon依赖,在项目pom文件中将eureka依赖注释掉
3.去掉启动类上的@EnableDiscoveryClient注解
4.修改application.yml如下:
server:
port: 8010
spring:
# 注册到服务发现中心的名称
application:
name: microservice-simple-consumer-movie
microservice-simple-provider-user:
ribbon:
listOfServers: localhost:8081, localhost:8082
listOfServers用于名为 microservice-simple-provider-user 的Ribbon客户端设置请求的地址列表
测试
启动两个user实例,启动microservice-simple-consumer-movie-ribbon-without-eureka
多次访问 http://localhost:8010/log-instance,控制台打印如下
[nio-8010-exec-5] c.l.c.m.controller.MovieController : microservice-simple-provider-user:localhost:8081
[nio-8010-exec-6] c.l.c.m.controller.MovieController : microservice-simple-provider-user:localhost:8082
[nio-8010-exec-7] c.l.c.m.controller.MovieController : microservice-simple-provider-user:localhost:8081
[nio-8010-exec-8] c.l.c.m.controller.MovieController : microservice-simple-provider-user:localhost:8082
[nio-8010-exec-9] c.l.c.m.controller.MovieController : microservice-simple-provider-user:localhost:8081
[io-8010-exec-10] c.l.c.m.controller.MovieController : microservice-simple-provider-user:localhost:8082
虽然电影微服务和用户微服务都没有注册到Eureka上,Ribbon仍然可以正常工作,请求依然会按照负载均衡策略分摊到两个用户微服务节点上