微服务之Springcloud 从零基础到入门——Ribbon篇
一. Ribbon简介
Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具。Ribbon虽然只是一个工具类框架,但它并不是需要独立部署的东西。像服务注册中心、配置中心、API网关都需要独立部署,但是Ribbon几乎存在于每一个微服务和基础设施中。因为微服务间的调用,API网关的请求转发等内容,都是离不开负载均衡的。所以,对Spring Cloud Ribbon的理解和使用,对于我们使用Spring Cloud来构建微服务非常重要。
二. 负载均衡简介
负载均衡主要包含硬负载和软负载两个方面,硬负载一般是通过硬件来实现,比较常用的比如F5;软负载则主要通过在服务器上安装一些用于负载均衡功能或模块等软件来完成请求分发工作。软负载也主要分为服务端的负载均衡器和客户端的负载均衡器。Ngnix是具有代表性的一个服务端负载均衡器,而Ribbon是一个客户端软负载均衡器。
Ribbon实现负载均衡流程主要如图:
首先服务消费者会从注册中心找到相应的服务名称,然后Ribbon会根据一定的负载策略给消费者分配一个服务
三. RestTemplate实现Ribbon
本实验已经在Eureka服务注册中心注册了两个相同的服务,其中服务器的搭建与服务提供者的创建可以参考我写的Eureka篇。
微服务之Springcloud 从零基础到入门——Eureka篇
注册的这两个服务的名称为CLOUD-PAYMENT-SERVICE
首先创建服务消费者,该服务消费者会去调用Eureka注册中心的服务。以下是创建服务消费者的流程。
- 引入依赖。因为Eureka已经默认集成了Ribbon,所以该处直接引入以下jar包。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- 编写application.yml配置文件
server:
port: 8601
spring:
application:
name: cloud-order-service #注册到服务中心的名称
eureka:
client:
# 是否入驻进服务端
register-with-eureka: true
# 是否从server端抓取已有注册信息
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
- 注入Restemplate模板到容器,在这边暂且基于RestTemplate实现服务调用,之后的笔记中会用到Openfeign。@LoadBalanced代表开启Ribbon的负载均衡策略,默认的策略为轮询算法。
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced //Ribbon的负载均衡
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
- 使用RestTemplate实现服务调用。如代码所示,相当于访问
http://localhost:8601/consumer/payment/findById/{id}就是调用了http://CLOUD-PAYMENT-SERVICE/payment/findById/id。而此时CLOUD-PAYMENT-SERVICE包含了之前注册上去的两个服务,那么Ribbon会通过默认的负载策略给服务消费者分配其中的一个服务去提供服务。
@RestController
public class OrderController {
public static final String URL="http://CLOUD-PAYMENT-SERVICE";
@Resource
private RestTemplate restTemplate;
@GetMapping("/consumer/payment/findById/{id}")
public CommonResult<Payment> findById(@PathVariable Long id){
return restTemplate.getForObject(URL+"/payment/findById/"+id,CommonResult.class);
}
}
四. Ribbon自带的负载均衡策略
首先Ribbon默认的负载策略为轮询算法,那么除了轮询算法Ribbon还包括以下几种负载策略。
策略类 | 命名 | 说明 |
---|---|---|
RandomRule | 随机策略 | 随机选择一个服务 |
RoundRobinRule | 轮询策略 | 按顺序循环选择一个服务 |
RetryRule | 重试策略 | 在一个配置时间段内选择服务不成功会一直尝试选择一个可用的服务 |
BestAvailabelRule | 最低并发策略 | 忽略熔断器打开的服务,选择并发连接最低的服务 |
AvailibilityFilterRule | 可用过滤策略 | 过滤掉一直链接失败和高并发连接的服务 |
ResponseTimeWeightedRule | 响应时间加权策略 | 服务响应时间越短,权重越大,被选中提供服务的概率越高 |
ZoneAviodanceRule | 区域权衡策略 | 综合服务所在区域性能和服务的可用性来轮询选择服务,会剔除不可用的服务 |
五. 改变Ribbon的负载均衡策略
- 通过配置类编写负载策略,在配置类中返回相应的Irule接口实现类,以下代码返回值为随机负载策略(注意:该MyRule类不能被Springboot主启动类扫描到,也就是至少在主启动类包的上一层包中)
@Configuration
public class MyRule {
@Bean
public IRule myrule(){
return new RandomRule();
}
}
- 修改主启动类,添加@RibbonClient注解,其中name是服务提供者的应用名称,configuration为MyRule配置类。可以理解为对该服务采用该负载算法
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration = MyRule.class) //指定负载算法
public class Order8601Application {
public static void main(String[] args) {
SpringApplication.run(Order8601Application.class,args);
}
}
六. 实现自己的负载均衡策略
- 编写自己的负载均衡器,该类实现了LoadBalance接口,主要代码如下,该代码用到了CAS原理,有不懂的读者请自行查找资料,该段代码的主要意思就是实现了一个轮询算法。(自己也写一个新的负载规则也没头绪,所以只能复写一个)
@Component
public class MyLoadBalance implements LoadBalance{
private AtomicInteger atomicInteger=new AtomicInteger(0);
public final int getAndIncrement(){
int current;
int next;
do{
current=atomicInteger.get();
next=current+1;
}while (!this.atomicInteger.compareAndSet(current,next));
System.out.println("第"+next+"次访问");
return next;
}
@Override
public ServiceInstance instances(List<ServiceInstance> serviceInstances) {
int index=getAndIncrement() % serviceInstances.size();
return serviceInstances.get(index);
}
}
- 在controller中实现负载均衡
@RestController
public class OrderController {
@Resource
private RestTemplate restTemplate;
@Resource //引入自己写的负载均衡器
private LoadBalance loadBalance;
@Resource //用于在注册中心找服务
DiscoveryClient discoveryClient;
@GetMapping(value = "consumer/payment/lb")
public String myBalance(){
//找到服务提供者的所有实例
List<ServiceInstance> instances=discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
if (instances ==null || instances.size()==0){
return null;
}
//根据自己的负载均衡器得到相应的服务
ServiceInstance instance=loadBalance.instances(instances);
//得到其所在服务器的地址
URI uri=instance.getUri();
return restTemplate.getForObject(uri+"/payment/lb",String.class);
}
}
七. 下一篇介绍
本文在使用服务消费者调用服务时采用的是RestTemplate,后续将对OpenFeign做介绍,来实现服务的调用。