Ribbon
Spring Cloud Ribbon 是基于Netflix Ribbon 实现的一套客户端负载均衡的工具,主要功能是提供客户端的软件负载均衡算法,将 Netflix 的中间层服务连接在一起。Ribbon 的客户端组件提供一系列完整的配置项,如:连接超时、重试等。
在配置文件中列出 LoadBalancer (简称LB:负载均衡) 后面所有的机器,Ribbon 会自动的帮助你基于某种规则 (如简单轮询,随机连接等等) 去连接这些机器,也可以使用 Ribbon 实现自定义的负载均衡算法。
负载均衡:简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA (高可用)。
负载均衡简单分类:
- 集中式LB:即在服务的提供方和消费方之间使用独立的LB设施,如Nginx,由该设施负责把访问请求通过某种策略转发至服务的提供方。
- 进程式LB:将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选出一个合适的服务器。
Ribbon 就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。
Ribbon的角色
要实现Ribbon负载均衡至少需要一个服务注册中心、至少两个服务提供者、一个服务消费者。
负载均衡流程:
-
服务提供者在服务注册中心注册
-
服务消费者向服务注册中心发出请求
-
服务注册中心将服务提供者地址提供给消费者
-
消费者根据负载均衡策略挑选服务
实现
前置条件:学习Eureka搭建服务,学习restful发送请求
服务注册中心搭建
使用Eureka章节搭建的Eureka服务端
服务提供者搭建
负载均衡至少需要两个服务提供者
步骤:
- 新建两个maven子模块:springcloud-eureka-client-8001、springcloud-eureka-client-8002
- 添加依赖
- 编写application.yaml
- 编写controller
- 编写启动类
实现
-
新建maven子模块:springcloud-eureka-client-8001、springcloud-eureka-client-8002
-
添加依赖:两个服务提供者依赖相同
-
编写application.yaml
- springcloud-eureka-client-8001
spring: application: name: springcloud-eureka-client #服务注册中心的服务名,提供相同服务的提供者需要同名 server: port: 8001 eureka: instance: instance-id: client8001 prefer-ip-address: true #访问路径可以显示ip client: service-url: defaultZone: http://127.0.0.1:2001/eureka #表示eureka服务器的部署位置 register-with-eureka: true fetch-registry: true
- springcloud-eureka-client-8002
spring: application: name: springcloud-eureka-client #服务注册中心的服务名,提供相同服务的提供者需要同名 server: port: 8002 eureka: instance: instance-id: client8002 prefer-ip-address: true #访问路径可以显示ip client: service-url: defaultZone: http://127.0.0.1:2001/eureka #表示eureka服务器的部署位置 register-with-eureka: true fetch-registry: true
-
编写controller
-
springcloud-eureka-client-8001
@ResponseBody @Controller public class TestController { @GetMapping(value = "/payment/get") public String getPaymentById() { return "get8001"; } }
-
springcloud-eureka-client-8002
@ResponseBody @Controller public class TestController { @GetMapping(value = "/payment/get") public String getPaymentById() { return "get8002"; } }
-
-
编写启动类:两个服务提供者启动类相同
@EnableDiscoveryClient @EnableEurekaClient @SpringBootApplication public class SpringCloudEurekaClientApplication { public static void main(String[] args) { SpringApplication.run(SpringCloudEurekaClientApplication.class,args); } }
服务消费者搭建
步骤:
- 新建maven子模块:springcloud-eureka-consumer
- 添加依赖
- 编写application.yaml
- 编写配置类
- 编写自定义配置类
- 编写controller
- 编写启动类
- 测试
实现
-
新建maven子模块:springcloud-eureka-consumer
-
添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
- 编写application.yaml
spring:
application:
name: springcloud-eureka-consumer
server:
port: 80
eureka:
instance:
instance-id: consumer80
prefer-ip-address: true #访问路径可以显示ip
client:
service-url:
defaultZone: http://127.0.0.1:2001/eureka #表示eureka服务器的部署位置
register-with-eureka: true
fetch-registry: true
- 编写配置类。负载均衡在restTemplate中配置,此方法可以放在自定义配置类里,此处为区分业务放在不同类。
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced//使用@LoadBalanced注解赋予RestTemplate负载均衡的能力
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
- 编写自定义配置类,配置负载均衡策略。此类不可放在@componentScan扫描的包及其子包下,即与springboot启动类不同包
@Configuration
public class MySelfRule {
@Bean //lRule:根据特定算法中从服务列表中选取一个要访问的服务
public IRule myRule(){
return new RandomRule();//随机访问
}
}
自定义配置类放置如图
- 编写controller
@ResponseBody
@Controller
public class TestController {
//载均衡服务提供者的服务名
public static final String PAYMENT_URL="http://SPRINGCLOUD-EUREKA-CLIENT";
@Autowired
private RestTemplate restTemplate;
@GetMapping("/consumer/payment/get")
public String getPayment(){
//RestTemplate根据负载均衡策略向服务提供者发送请求
return restTemplate.getForObject(PAYMENT_URL+"/payment/get/",String.class);
}
}
- 编写启动类
@EnableEurekaClient
@SpringBootApplication
/**
name:负载均衡服务提供者的服务名
configuration:负载均衡策略类的class
*/
@RibbonClient(name = "SPRINGCLOUD-EUREKA-CLIENT", configuration = MySelfRule.class)
public class SpringCloudEurekaClientApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudEurekaClientApplication.class,args);
}
}
- 测试
- 启动服务:springcloud-eureka-server、springcloud-eureka-client-8001、springcloud-eureka-client-8002、springcloud-eureka-consumer
- 访问:http://localhost/consumer/payment/get
- 返回结果在get8001与get8002之内随机算选择
自带负载均衡策略
- RoundRobinRule:轮询。在存活服务中随机选择,默认策略。
- RandomRule:随机访问。在存活服务中随机选择。
- RetryRule:轮询重试。先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重试,默认重试间隔500毫秒。
- WeightedResponseTimeRule:响应速度决定权重。是对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,权重越大越容易被选择。
- BestAvailableRule:最优可用。优先选择并发连接数最少的服务,有固定的默认最小连接数,当所有服务的连接数都大于默认最小连接数,使用RoundRobinRule策略。
- AvailabilityFilteringRule:可用性过滤规则。先过滤掉故障实例,再选择并发连接数较小的实例。
- ZoneAvoidanceRule:区域内可用性能最优。首先判断一个zone的运行性能是否可用,剔除不可用的区域zone的所有server,然后再利用AvailabilityPredicate过滤并发连接过多的服务。
自定义负载均衡策略
自定义算法类必须继承 AbstractLoadBalancerRule类
- 创建自定义配置类
- 修改启动类
- 测试
实现
- 新建自定义配置类CustomerRule,并继承AbstractLoadBalancerRule类
@Configuration
public class CustomerRule extends AbstractLoadBalancerRule{
//自定义逻辑:调用同一个服务5次后,再轮询调用下一个服务
/*
total = 0 // 当total==5,调用下一个服务
currentIndex = 0 // 当前对外提供服务的服务器下标
*/
private int total = 0; // 总共被调用的次数,目前要求每台被调用5次
private int currentIndex = 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(); //获取全部的服务
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;
}
}
if (server == null) {
Thread.yield();
continue;
}
if (server.isAlive()) {
return (server);
}
// Shouldn't actually happen.. but must be transient or a bug.
server = null;
Thread.yield();
}
return server;
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
}
- 修改启动类
@EnableEurekaClient
@SpringBootApplication
//修改configuration,指定为自定义配置类
@RibbonClient(name = "SPRINGCLOUD-EUREKA-CLIENT", configuration = CustomerRule.class)
public class SpringCloudEurekaClientApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudEurekaClientApplication.class,args);
}
}
- 测试
- 启动服务:springcloud-eureka-server、springcloud-eureka-client-8001、springcloud-eureka-client-8002、springcloud-eureka-consumer
- 访问:http://localhost/consumer/payment/get
- 返回结果在get8001与get8002之间切换,一个结果加载5次后切换到另一个