Ribbon简介
Ribbon 是 Netflix 的组件之一,负责注册表的负载平衡,并帮助控制 HTTP 和 TCP 客户端行为。 Spring Cloud Netflix Ribbon一般与Ribbon结合使用,利用Eureka中读取的服务信息,在调用时合理加载服务节点。
Spring Cloud中注册中心和Ribbon可以一起使用。 Ribbon自动从注册中心获取提供商列表信息,并基于内置的负载均衡算法请求服务。
基于Spring Cloud Netflix Ribbon 实现负载均衡的结构图如图 4-1 所示,可以看到各个微服务实例会和Ribbon 服务一样注册到 Eureka 服务器 然后 Ribbon从 Eur eka 服务器中获取所有注册服务的服务列表 ,一旦获取服务列衰, Ribbon就能通过各种负载均衡策略实现服务调用。
根据服务列表的获取和存储方式,负载均衡可以分为客户端负载均衡和服务器端负载均衡两种形式 上图中展示的是一种客户端负均衡机制。 Ribbon 作为一种独立的客户端负载均衡实现工具,一方面可以通过 Eureka 完成负载均衡,另一方面它也支持直接访问服务列表。
服务器端负载均衡
服务器端负载均衡机制示意图如图4-3所示。首先,客户端向负载均衡器发送请求,然后负载均衡器负责将收到的每个请求转发到正在运行的微服务节点,最后接收到请求的微服务节点进行响应处理。基于服务器端的负载均衡机制实现起来比较简单。您只需要在客户端和每个微服务实例之间设置一个集中式负载均衡器。因为当服务请求越来越大时,负载均衡器就会成为系统的瓶颈。为了避免集中式负载均衡带来的问题,客户端负载均衡也是常用的方法。
客户端负载均衡
客户端负载均衡机制的主要优点是不存在集中式负载均衡带来的瓶颈问题,因为每个客户端都有自己的负载均衡器。客户端负载均衡就是在客户端程序内部设置一个调度算法。当向服务器发出请求时,首先执行调度算法计算出目标服务器地址,然后调用目标地址服务。
客户端负载均衡是一种常见的负载均衡实现方案。许多框架,包括Dubbo和SpringCloud,都使用客户端负载均衡机制。
Ribbon主要有以下几大子模块。
ribbon-core
:该模块为 Ribbon 项目的核心,主要包括负载均衡器接口定义、 客户端、接口定义、内置的负载均衡实现等 API。ribbon-eureka
:为 Eureka 客户端提供的负载均衡实现类。ribbon-httpclient
:对 Apache HttpClient 进行封装,该模块提供了 有负载均衡功,能的阻ST 客户。
Ribbon的主要功能
- 服务电话
基于Ribbon实现服务调用,是通过拉取到的所有服务列表组成的映射关系,借助RestTemplate
进行最终的调用。
- 负载均衡
当有多个服务提供者时,Ribbon可以根据负载均衡算法自动选择要调用的地址。
Ribbon的负载均衡器主要与集群中的各个服务器进行通信。负载均衡器必须提供以下基本功能:
- 维护服务器IP、DNS、名称等信息
- 根据特定的逻辑在服务器列表中循环。
为了实现负载均衡的基础功能, Ribbon 的负载均衡器有以下 大子模块。- 规则逻辑组件将确定从服务器列表返回哪个服务器实例
- Ping:该组件主要使用定时器来保证服务器网络能够连通。
- ServerList :服务器列表,可以通过静态的配置确定负载的服务器, 可以动态指定
服务器列表。 如果动态指定服务器列表, 会有后台的线程来刷新该列表
Ribbon 实现客户端负载均衡
Netflix Ribbon 会自动地基于某种负载均衡算法(如随机、轮询等)去连接服务实例。Spring Cloud Netflix Ribbon Netfl bbon 做了封装以便更好地使用 Spring Boot 的自动配置理念即spring cloud集成了Ribbon。通过Sping Cloud Netflix Ribbon 大大简化了在微服务架构中使用客户端负载均衡的成本,通过
注解就能简单实现在面向服务的接口调用中自动集成负载均衡功能。
接上一节,搭建好eureka多注册中心后,使用Ribbon完成负载均衡功能。由于Ribbon是基于客户端的负载均衡,所以只需要修改客户端代码和配置即可。以下是上一节中的远程调用代码:
@RestController
@RequestMapping(value = "/test")
public class TestController {
//获取服务中心实例
@Autowired
private DiscoveryClient discoveryClient;
//java代码访问api接口的实例对象
@Autowired
private RestTemplate restTemplate;
//bill_service的调用
@GetMapping(value = "/bill/{id}")
public BillMessage testMethod1(@PathVariable Integer id){
List<ServiceInstance> bill_service = discoveryClient.getInstances("bill_service");
//该服务只有一个
ServiceInstance instance = bill_service.get(0);
BillMessage billMessage = restTemplate.getForObject(instance.getUri() + "/bill/" + id, BillMessage.class);
System.out.println(instance.getUri());
return billMessage;
}
}
多个服务通过服务ID获取实例。在分布式环境中,会存在部署在不同服务器上的同名服务,即同名但不同IP地址的服务。 Ribbon的负载均衡(负载均衡算法)会决定调用哪一个。 ) 分配。
如何在客户端使用Ribbon?
在创建并注入RestTemplate
对象时加入@LoadBalanced
注解:
@SpringBootApplication
@EnableEurekaClient
public class TestModuleApplication {
//java代码访问url获取返回值的对象
@LoadBalanced //spring cloud提供连接Ribbon的注解
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate(); }
public static void main(String[] args) {
SpringApplication.run(TestModuleApplication.class, args);
}
}
使用该注解会在客户端启动Ribbon,并通过客户端Ribbon的负载均衡调度算法获取服务。
RestTemplate是spring-web项目的一个REST客户端,提供API去调用HTTP服务,其本身不具有负载均衡功能,也与Spring Cloud没有关系,为何加入了@LoadBalanced
就具有负载均衡功能呢?
在使用@LoadBalanced
注解后,spring容器在启动时会为被这些修饰的RestTemplate添加拦截器,并使用LoadBalancerClient
处理请求,该类本身具有负载均衡的客户端,通过间接处理使RestTemplate具有了负载均衡的功能。
如何引入依赖?
Spring Cloud项目集成了Ribbon,需要导入依赖:
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<version>...</version>
</dependency>
如果之前以及引入了eureka
的依赖就不需要再导入ribbon
的依赖了,因为前者已经集成了后者。
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>...</version>
</dependency>
之前API接口获取的服务实例是通过注册中心获取的,并没有使用Ribbon的负载均衡功能:
//bill_service的调用
@GetMapping(value = "/bill/{id}")
public BillMessage testMethod1(@PathVariable Integer id){
List<ServiceInstance> bill_service = discoveryClient.getInstances("bill_service");
//该服务只有一个
ServiceInstance instance = bill_service.get(0);
BillMessage billMessage = restTemplate.getForObject(instance.getUri() + "/bill/" + id, BillMessage.class);
System.out.println(instance.getUri());
return billMessage;
}
}
使用Ribbon组件后,您不再需要通过注册中心获取实例和IP地址等信息。相反,您可以直接使用 Ribbon 作为中间件并请求 Ribbon。然后它的负载均衡算法会从注册中心获取它。那么新的界面也会简单很多:
//使用Ribbon获取服务
@GetMapping(value = "/bill1/{id}")
public BillMessage testMethod2(@PathVariable Integer id){
BillMessage billMessage = restTemplate.getForObject("http://bill-service/bill/"+id,BillMessage.class);
return billMessage;
}
启动服务:
中间遇到了一个小错误。使用Ribbon实现负载均衡时,服务名不能由下划线改为下划线或其他,否则会报错:
Request URI does not contain a valid hostname: http://bill_service/1
将所有服务名称更改为下划线类型:
输入测试模块的url查询结果:
需要注意的是服务路径的拼接:
BillMessage billMessage = restTemplate.getForObject("http://bill-service/bill/"+id,BillMessage.class);
使用Ribbon插件只需通过服务名(bill-service)
就可以获取该服务的ip信息,在加上url
地址就可以调用其服务了。
负载均衡策略
负载均衡策略也可以通过配置文件修改。当然,spring还提供了@RibbonClient注解来自定义负载均衡。
SpringCloud重试机制配置
SpringCloud retry重试是一个很棒的特性,可以有效处理单点故障。主要功能是在请求服务实例时。例如,如果你的User服务启动了2,并且它们都注册在eureka中,那么正常情况下,当请求User服务时,ribbon会默认轮询这两个实例。 。此时,如果其中一个实例发生故障、宕机或超时,并且没有配置重试策略,调用者将得到错误消息或超时无响应或断路器返回的消息。我们希望的是,如果其中一个失败,会自动切换到另一个来访问。最简单的方法是重试。
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
Ribbon 中重试机制的配置
@Bean
@LoadBalanced
RestTemplate restTemplate() {
HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory();
httpRequestFactory.setReadTimeout(5000);
httpRequestFactory.setConnectTimeout(5000);
return new RestTemplate(httpRequestFactory);
}