Ribbon
- Ribbon是一款基于Http和TCP的客户端负载均衡工具,它是基于NetFlix Ribbon实现的
- 他不像springcloud服务注册中心,配置中心,API网关那样独立部署,他几乎存在于所有的springcloud微服务中,包括feign提供的生命是服务调用也是基于ribbon实现的
- ribbon使用多种负载均衡算法,例如:轮询,随机等。
- 作用:解决并提供了微服务负载均衡的问题
负载均衡的种类
第一类:集中式负载均衡,即在consumer和provider之间使用独立的负载均衡设施(可以使硬件,也可以是软件如Nginx),有该设施负责把访问请求通过某种策略转发给provider
第二类:进程内负载均衡。将负载均衡逻辑集成到consumer,consumer从服务注册中心获得有哪些微服务可用,然后自己再根据某种算法选择一个合适provider ribbon属于后者,他是一个类库,集成于consumer进程,consumer通过它来获取provider。
Ribbon原理图
- 服务方provider注册到eureka中
- 消费方consumer注册到eureka中
- Ribbon从eureka中获取注册服务列表,根据选定的策略从中选择一个服务
- consumer调用3中选出的provider进行访问调用
@Service
public class ProductService{
@Autowired
public LoadBalancerClient loadClient;
@GetMapping("/getAll")
public List<Product> getAll(){
ServiceInstance si=loadClient.choose("product-provider");
StringBuffer sb=new StringBuffer();
sb.append("http://");
sb.append(si.getHost());
sb.append(":");
sb.append(si.getPort);
sb.append("/productList");
RestTemplate rt=new RestTemplate();
ParameterTypeReference typeRef=new ParameterTypeReference<List<Product>>(){};
ResponseEntity<List<Product>> resp=rt.exchange(sb.toString(),HttpMethod.GET,null,typeRef);
List<Product> plist=resp.getBody();
return plist
}
}
Ribbon的七种负载均衡算法
- 轮询(默认使用): RoundRobinRule;
轮询策略每次都顺序取下一个;如果有3个provider,则第1次取第1个,第2次取第2个…依次类推 - 权重轮询:WeightResposneTimeRule1.
根据每个的响应事件分配一个权重,响应时间越长,权重越小,被选中的概率越低 原理:一开始为轮询策略,并开启一个定时器,每30秒收集依次每个provider的平均响应时间,给每个provider附上一个权重,并按照权重随机选择3. provider,越重越高的provider会被更大概率选中 - 随机策略RandomRule
从provider列表中随机选择一个provider - 最少并发数策略BestAvailableRule
选择正在请求中的并发数最小的,除非这个provider正在熔断汇总 - 在“选定的负载均衡策略”基础上进行重试机制RetryRule
1.“选定的负载均衡策略”这个策略是轮询策略RoundRobinRule 2.该重试策略先设定一个阈值时间段,如果在这个阈值时间段内当选择provider不成功,则一直尝试采用“选定的负载均衡策略:轮询策略”最后选择一个可用的provider - 可用性敏感策略AvailabilityFilteringRule过滤性能差的的provider
第一种:过滤掉在中处于一直连接失败的provider 第二种:过滤高并发的provider - 区域敏感性策略ZoneAvoidanceRule
1.以一个区域为单位考察可用性,对于不可用的区域整个丢弃,从剩下区域中选可用的provider 2.如果这个ip区域内有一个或多个实例不可达或响应变慢,都会降低该ip区域内其他ip被选中的权重。
修改策略
- 修改配置文件
eureka-provider.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
- 注解设置:修改启动类ConsumerApplicaiton
@EnableEurekaClient
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
点对点直连
- 方便ribbon开发调试,生产者和消费者直接连接,不经过eureka。
- 我们平时运行的微服务都是集群的,如果需要两台机器进行调试,但是我们注册在Eureka中的,而Ribbon又是根据一定策略选择服务。所有无法点对点直连。
- pom.xml
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.agan.springcloud</groupId>
<artifactId>spring-cloud-in-action</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>eureka-consumer-remove-eureka</artifactId>
<name>eureka-consumer</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- Resource:application.properties和logback.xml
spring.application.name=eureka-consumer
server.port=8082
#禁用 eureka
ribbon.eureka.enabled=false
#指定具体的服务实例清单
eureka-provider.ribbon.listOfServers=192.168.10.61:8081,192.168.10.16:8081
- 控制层
@RestController
public class ProductController {
@Autowired
private ProductService productService;
@RequestMapping(value="list",method=RequestMethod.GET)
public List<Product> listProduct(){
List<Product> list=this.productService.listProduct();
return list;
}
}
- Pojo对象
public class Product {
private int id;
private String name;
public Product() {
super();
}
public Product(int id, String name) {
super();
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
- ProductService
@Service
public class ProductService {
@Autowired
private LoadBalancerClient loadBalancerClient;
public List<Product> listProduct(){
ServiceInstance si=loadBalancerClient.choose("eureka-provider");
StringBuffer sb=new StringBuffer("");
sb.append("http://");
sb.append(si.getHost());
sb.append(":");
sb.append(si.getPort());
sb.append("/list");
System.out.println(sb.toString());
RestTemplate rt=new RestTemplate();
ParameterizedTypeReference<List<Product>> typeRef
=new ParameterizedTypeReference<List<Product>>(){};
ResponseEntity<List<Product>> resp=rt.exchange(sb.toString(), HttpMethod.GET, null, typeRef) ;
List<Product> plist=resp.getBody();
return plist;
}
}
- 启动类
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
总结
- 虽然SpringCloud生态已经停止维护了,但是组件的原理和策略确实非常值得学的。
- 像Spring Cloud Alibaba的一些组件的思想与SpringCloud类似甚至相同。
|