SpringCloud笔记(Hoxton)——Netflix之Ribbon负载均衡
Ribbon使用
Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡工具
,简单的说,Ribbon是Netflix 发布的开源项目,主要功能是提供客户端软件的负载均衡算法,将Netflix的中间层服务连接在一起。Ribbon客户端组件提供了一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮你基于某种规则(如简单轮训,随机连接等)去连接这些机器。我们也很容易实现自定义的负载均衡算法。
代码示例
注册中心
这里是Eureka作为服务注册中心,为Ribbon提供服务端信息的获取,比如说服务的IP地址和端口,使用前面搭建好的项目(eureka-server)。
Provider
- 在idea中新建两个项目运行主类,选择前面创建好的项目(eureka-provider)。
- 设置不同运行主类,使用不同端口号,然后启动三个不同端口号的provider。
接口实现
调用不同的服务端,会返回对应服务端的接口。
@RestController
@RequestMapping("/api")
public class PoroviderController {
@Value("${server.port}")
private String serverPort;
@PostMapping("/sayHello")
public String sayHello(String name) {
return "我是服务端" + serverPort + ",你好" + name;
}
}
Consumer
新建一个spring-boot工程,取名为consumer-ribbon,在pom文件引入ribbon需要的依赖。
添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
配置端口、注册中心等
server:
port: 8091
servlet:
context-path: /ribbon
eureka:
client:
# false表示不向注册中心注册自己
register-with-eureka: false
service-url:
defaultZone: http://localhost:8761/eureka/
spring:
application:
name: consumer-ribbon
RestTemplate配置
- SpringCloud为客户端负载均衡创建了特定的注解@LoadBalanced,我们只需要使用该注解修饰创建RestTemplate实例的@Bean函数,SpringCloud就会让RestTemplate使用相关的负载均衡策略,默认情况下使用Ribbon。
- 除了@LoadBalanced之外,Ribbon还提供@RibbonClient注解。该注解可以为Ribbon客户端声明名称和自定义配置。 name属性可以设置客户端的名称, configuration属性则会设置Ribbon相关的自定义配 类。
@Configuration
public class RestTemplateConfig {
/**
* @return org.springframework.web.client.RestTemplate
* @description 注入一个可以进行负载均衡的RestTemple用于服务问调用
* @author fengfan
* @date 2022/5/18 14:43
*/
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
全局配置负载
@Configuration
public class RibbonConfig {
/**
* @return com.netflix.loadbalancer.IRule
* @description 配置随机负载策略
* @author fengfan
* @date 2022/5/20 16:04
*/
@Bean
public IRule iRule(){
return new RandomRule();
}
}
单独配置负载
- 配置文件方法实现
# 服务提供方的单独配置设置(服务名称大小写必须和注册的一样)
EUREKA-PROVIDER:
ribbon:
ConnectTimeout: 1000 #服务请求连接超时时间(毫秒)
ReadTimeout: 3000 #服务请求处理超时时间(毫秒)
OkToRetryOnAllOperations: true #对超时请求启用重试机制
MaxAutoRetriesNextServer: 1 #切换重试实例的最大个数
MaxAutoRetries: 1 # 切换实例后重试最大次数
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #修改负载均衡算法
- 代码配置实现
@Configuration
public class MyRuleConfig {
@Bean
public IRule iRule() {
return new RandomRule();
}
}
@SpringBootApplication
@EnableEurekaClient
/**
* 在启动改微服务的时候就能去加载我们自定义的Ribbon配置类,从而配置生效
* 注意:MyRuleConfig 必须是 @Configuration ,但请注意,它不在主应用程序上下文
* 的 @ComponentScan 中,否则将由所有 @RibbonClients 共享。
* 如果您使用 @ComponentScan (或 @SpringBootApplication ),
* 则需要采取措施避免包含(例如将其放在一个单独的,不重叠的包中,或者指定要在 @ComponentScan )
*/
@RibbonClient(name = "EUREKA-PROVIDER", configuration = MyRuleConfig.class)
public class ConsumerRibbonApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerRibbonApplication.class, args);
}
}
自定义负载策略
这里自己将RandomRule抄一份,实际情况根据需求自己实现
public class ZDYRule extends AbstractLoadBalancerRule {
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
@Override
public Server choose(Object o) {
return this.choose(this.getLoadBalancer(), o);
}
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;
}
int index = this.chooseRandomInt(serverCount);
server = (Server)upList.get(index);
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);
}
}
@Configuration
public class MyRuleConfig {
@Bean
public IRule iRule() {
return new ZDYRule();
}
}
客户端调用实现
@RestController
@RequestMapping("/api")
public class ConsumerRibbonController {
@Resource
private RestTemplate restTemplate;
@PostMapping("/getServerInfo")
public String getServerInfo(){
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("name", "consumer8081");
ResponseEntity<String> responseEntity = restTemplate.postForEntity("http://EUREKA-PROVIDER/provider/api/sayHello", body, String.class);
return responseEntity.getBody();
}
}
Ribbon的负载均衡策略
- com.netflix.loadbalancer.RandomRule:从提供服务的实例中以随机的方式;
- com.netflix.loadbalancer.RoundRobinRule:以线性轮询的方式,就是维护一个计数器,从提供服务的实例中按顺序选取,第一次选第一个,第二次选第二个,以此类推,到最后一个以后再从头来过;
- com.netflix.loadbalancer.RetryRule:在RoundRobinRule的基础上添加重试机制,即在指定的重试时间内,反复使用线性轮询策略来选择可用实例;
- com.netflix.loadbalancer.WeightedResponseTimeRule:对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,越容易被选择;
- com.netflix.loadbalancer.BestAvailableRule:选择并发较小的实例;
- com.netflix.loadbalancer.AvailabilityFilteringRule:先过滤掉故障实例,再选择并发较小的实例;
- com.netflix.loadbalancer.ZoneAwareLoadBalancer:采用双重过滤,同时过滤不是同一区域的实例和故障实例,选择并发较小的实例。
测试
多次请求的服务端,端口随机变化,证明配置成功。