文章目录
1、什么是 Ribbon ?
Ribbon 是 Netflixfa 发布的一个负载均衡器,主要用于控制 HTTP 和 TCP客户端行为。
Ribbon 有两个主要作用:
- 服务调用:基于 Ribbon 实现服务调用, 是通过拉取到的所有服务列表组成(服务名-请求路径的)映射关系。借助 RestTemplate 最终进行调用
- 负载均衡:当有多个服务提供者时,Ribbon可以根据负载均衡的算法自动的选择需要调用的服务地址
在服务消费方通过 RestTemplate 调用微服务的时候,会拼接服务的访问地址等信息,如下所示:
这种方式虽然可以,但是不太方便。所以可以使用 Ribbon 的服务调用来解决,在后面我们去了解具体使用方法。
Ribbon 可以单独使用,但是大多数情况下在 SpringCloud 中使用,配合注册中心进行使用。
2、什么是负载均衡 ?
负载均衡是一种基础的网络服务,按照指定的负载均衡算法,将流量分配到后端服务集群上,从而为系统提供并行扩展的能力。
- 服务端负载均衡:发送请求到服务器,然后通过负载均衡算法,在多个服务器之间选择一个进行访问;即在服务器端再进行负载均衡算法分配
- 客户端负载均衡:客户端会有一个服务器地址列表,在发送请求前通过负载均衡算法选择一个服务器,然后进行访问;即在客户端就进行负载均衡算法分配,Ribbon 就是一个客户端负载均衡器。
在微服务中要保持系统的高可用行,所以将多个相同的服务提供者注册到注册中心上,那么服务消费者需要具体的选择服务提供者,这个过程就由 Ribbon 中的负载均衡完成。
3、Ribbon 中的组件
Ribbon 的关键组件如下:
ServerList:可以响应客户端的特定服务的服务器列表。<clientName>.ribbon.NIWSServerListFilterClassName配置项可以配置获取服务器的方法,在默认情况使用的是ConfigurationBasedServerList类,这个类可以从配置文件中获取所有服务列表:#这个名称表示具体的服务 #供 RestTemplate 类去调用,如果省略服务名称则表示配置作用在所有的服务中, 服务名称: ribbon: listOfServers: www.microsoft.com:80,www.yahoo.com:80,www.google.com:80但是配合注册中心时,服务列表是从注册中心获取的,所以上述配置可以省略,这时使用的是
DiscoveryEnabledNIWSServerList类。ServerListFilter:可以动态获得的具有所需特征的候选服务器列表的过滤器。<clientName>.ribbon.NIWSServerListFilterClassName配置项可以配置过滤器使用的类,默认使用ZonePreferenceServerListFilter类ServerListUpdater:用于执行动态服务器列表更新。Rule:负载均衡策略,用于确定从服务器列表返回哪个服务器。<clientName>.ribbon.NFLoadBalancerRuleClassName配置项可以配置负载均衡策略使用的类,默认使用ZoneAvoidanceRule类Ping:客户端用于快速检查服务器当时是否处于活动状态。<clientName>.ribbon.NFLoadBalancerPingClassName配置项可以配置检查服务器使用的类,默认使用DummyPing类,当和注册中心配合使用时,使用的是NIWSDiscoveryPing类LoadBalancer:负载均衡器,负责负载均衡调度的管理。将以上组件组合到这个类中一起工作。<clientName>.ribbon.NFLoadBalancerClassName配置项可以配置负载均衡器使用的类,默认使用ZoneAwareLoadBalancer
在上述几个组件中,最需要关注的就是 Rule 组件。
4、Rule 组件
Rule 组件用于负载均衡策略,Ribbon 提供了一个 IRule 接口是 Ribbon 中负载均衡的顶级接口。IRule 接口中定义了选择负载均衡策略的基本操作,可以通过配置文件或者配置类选择具体的负载均衡策略。
| 策略 | 名称 | 说明 |
|---|---|---|
| com.netflix.loadbalancer.RoundRobinRule | 轮询策略 | 按顺序选择服务,默认就是这种服务。 |
| com.netflix.loadbalancer.RandomRule | 随机策略 | 随机选择服务 |
| com.netflix.loadbalancer.RetryRule | 重试策略 | 在一个时间段选择服务失败,则选择尝试一个可用服务 |
| com.netflix.loadbalancer.WeightedResponseTimeRule | 权重策略 | 会计算每个服务的权重,越高的被调用的可能性越大。 |
| com.netflix.loadbalancer.BestAvailableRule | 最佳策略 | 遍历所有的服务实例,过滤掉故障实例,并返回请求数最小的实例返回。 |
| com.netflix.loadbalancer.AvailabilityFilteringRule | 可用过滤策略 | 过滤掉故障和请求数超过阈值的服务实例,再从剩下的实力中轮询调用 |
| com.netflix.loadbalancer.ZoneAvoidanceRule | 复合策略 | 使用ZoneAvoidancePredicate和AvailabilityPredicate来判断是否选择某个server,前一个判断判定一个 zone的运行性能是否可用,剔除不可用的zone,后一个用于过滤掉连接数过多的 Server。 |
5、实现负载均衡
5.1、单独使用 Ribbon
- 引入依赖:如果不配和注册中心使用,那么需要单独引用 Ribbon 的依赖。
<dependencies> <!--Ribbon的依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> <version>版本号</version> </dependency> </dependencies> - 修改启动类:向 IOC 容器中注入
RestTemplate类,并添加@LoadBalanced注解@SpringBootApplication public class Application { @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(Application.class, args); } } - 修改配置文件:在配置文件中添加有关 Ribbon 的设置
#microservice-provider-user 表示需要调用的服务名 eurek-user-service: ribbon: listOfServers: localhost:9003,localhost:9004,localhost:9005 - 具体使用:在使用``调用服务的时候,IP地址是一个字符串,不同于之前的具体的地址,这就是使用 Ribbon 的好处。
@RestController @RequestMapping("/order") public class orderController { @Autowired private RestTemplate restTemplate; @RequestMapping(value = "/buy", produces = "text/plain;charset=UTF-8") @ResponseBody public String buy(int id) throws IOException { //这里的"http://microservice-provider-user/product/在没有使用 Ribbon之前,应该是 "http://localhost:9003/product/ //在使用了 Ribbon 之后就直接与配置文件中的配置对应,自动完成负载均衡 return this.restTemplate.getForObject("http://microservice-provider-user/product/"+id,String.class); }
5.2、与注册中心配合使用
- 引入依赖:当与注册中心使用时,可以省略对 Ribbon 依赖的导入,因为 Consul 和 Eureka 中已经含有 Ribbon 依赖。
- 修改启动类:向 IOC 容器中注入
RestTemplate类,并添加@LoadBalanced注解@SpringBootApplication @EnableEurekaClient public class OrderMain{ public static void main(String[] args) { SpringApplication.run(OrderMain.class, args); } @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } } - 设置赋值均衡策略:Ribbon 默认使用的是轮询策略,也可以通过如下配置替换轮询策略
eurek-user-service: ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule - 具体使用:
@RestController @RequestMapping("/order") public class orderController { //省略了获取元数据的步骤 //@Autowired //private DiscoveryClient discoveryClient; @Autowired private RestTemplate restTemplate; @RequestMapping(value = "/buy", produces = "text/plain;charset=UTF-8") @ResponseBody public String buy(int id) throws IOException { //省略了获取元数据的步骤,在 restTemplate 进行访问的时候 //去注册中心寻找 microservice-provider-user 这个服务,然后进行负载均衡,返回一个正在的 IP 地址 return this.restTemplate.getForObject("http://microservice-provider-user/product/"+id,String.class); }
5.3、重试
为了保证微服务的高可用,一般服务都是以集群的形式注册到注册中心,所以就需要 Ribbon 进行负载均衡。当消费者去调服务时, 通过 Ribbon 返回了一个具体的服务地址。当这个服务出现问题无法访问时,服务消费者会出现异常。为了解决这个问题,Ribbon 引入了重试机制,当消费者请求失败时,会根据配置进行重新发起请求。
- 引入依赖:想要实现重试必须导入如下所示依赖,当引入这个依赖之后,就能进行重试了
<dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency> - 添加重试配置:重试的配置 Ribbon 已经配置了,但是可以通过如下配置项覆盖默认的配置。
#service-product5表示服务名,根据个人服务进行配置,没有固定值 service-product5: ribbon: ConnectTimeout: 250 # Ribbon的连接超时时间 ReadTimeout: 1000 # Ribbon的数据读取超时时间 OkToRetryOnAllOperations: true # 是否对所有操作都进行重试 MaxAutoRetriesNextServer: 1 # 切换实例的重试次数 MaxAutoRetries: 1 # 对当前实例的重试次数
6、注解 @LoadBalanced
在使用负载均衡的过程中,我们就在启动类中添加了一个@LoadBalanced注解,这个注解主要用于给RestTemplate类添加一个 LoadBalancerClient接口的实现类,而这个类就是实现负载均衡的关键。
下面我们对整个流程进行解析:
在启动项目时,会引入
LoadBalancerAutoConfiguration配置类,这个类 Ribbon 中负载均衡的配置类。
我们重点关注下列这个方法:@Bean @ConditionalOnMissingBean public RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) { return (restTemplate) -> { List<ClientHttpRequestInterceptor> list = new ArrayList(restTemplate.getInterceptors()); list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); }; }上述方法中:
LoadBalancerInterceptor类,用于实现对客户端发起请求时进行拦截,以实现客户端负载均衡。RestTemplateCustomizer类,用于给 RestTemplate 增加LoadBalancerInterceptor拦截器。- 通过调用
RestTemplateCustomizer实例来给客户端负载均衡中被@LoadBalanced注解修饰的RestTemplate对象列表增LoadBalancerInterceptor拦截器。
LoadBalancerInterceptor拦截器:
在这个类中实例化了一个LoadBalancerClient接口的实现,LoadBalancerClient只是一个负载均衡器接口。当一个被
@LoadBalanced注解修饰的RestTemplate对象向外发起HTTP请求时,会被LoadBalancerInterceptor类的intercept函数所拦截。从 HttpRequest 的 URI 对象中通过 getHost() 就可以拿到服务名,然后根据服务名来选择实例并发起实际的请求。BlockingLoadBalancerClient和RibbonLoadBalancerClient都是接口的实现类
7、Ribbon懒加载
Ribbon 在进行客户端负载均衡的时候并不是在启动的时候就加载,实在实际请求的时候才加载,这回导致第一次请求会比较的缓慢,甚至可能会出现超时的情况。所以我们可以指定具体的客户端名称来开启懒加载,即在启动的时候便加载素养的配置项的应用上下文。
具体服务名:
eager-load:
enabled: true
clients: ribbon-client-a, ribbon-client-b, ribbon-client-c