Ribbon单独使用,配置自动重试,实现负载均衡和高可用方式
作者:DayDayUp丶 发布时间:2023-05-12 00:49:15
一、前言
1.1 实现目标
服务A调用服务B1和B2(B1和B2提供同种服务),当服务B1/B2在停止和重新发布阶段,或B1/B2有一个服务故障时,
需保证服务A正常调用B服务,达到无感知发布的效果(服务B高可用)
需保证服务A的请求负载均衡,避免某个B服务节点压力过大(服务B负载均衡)
说明:这里是独立使用Ribbon,不依赖于Eureka、Zookeeper等任何服务注册发现组件。
1.2 环境
JDK 1.8,SpringCloud Greenwich.SR2,SpringBoot 2.1.3.RELEASE
二、实现
本文示例,CONSUMER-SERVICE服务调用PRODUCER-SERVICE服务。在进行以下步骤前,请先启动两个普通的SpringBoot服务PRODUCER-SERVICE。
2.1 pom依赖
因为这里独立使用Ribbon,所以CONSUMER-SERVICE只需要spring-cloud-starter-netflix-ribbon,启动主类也无需更多的注解,如
@EnableEurekaClient、@EnableDiscoveryClient、@EnableCircuitBreaker等,只需保留@SpringBootApplication即可。
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2.2 RestTemplate配置
Ribbon是对RestTemplate的加强,需要为RestTemplate添加注解@LoadBalanced,使之具有负载均衡能力。如下:
@Bean
@LoadBalanced
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
RestTemplate restTemplate = new RestTemplate(factory);
restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
return restTemplate;
}
2.3 Ribbon配置
注意:网上和书上很多教程都没有提到ribbon.restclient.enabled这一配置,导致再怎么尝试都无法成功自动重试。
spring.application.name=CONSUMER-SERVICE
server.port=8801
ribbon.restclient.enabled=true
#开启重试机制
spring.cloud.loadbalancer.retry.enabled=true
#请求连接的超时时间
PRODUCER-SERVICE.ribbon.ConnectTimeout=250
#请求处理的超时时间
PRODUCER-SERVICE.ribbon.ReadTimeout=1000
#对所有操作请求都进行重试,默认false,只有GET请求会重试;这是防止POST等对数据有影响的请求在重试后因为接口未做幂等性导致数据异常,影响较大
PRODUCER-SERVICE.ribbon.OkToRetryOnAllOperations=true
#指定请求重试开关,经调试源码该属性未被使用,疑似bug,导致不论怎么设置,都是只有服务提供者的Get请求可以被自动重试
#PRODUCER-SERVICE.ribbon.RequestSpecificRetryOn=true
#切换实例的重试次数
PRODUCER-SERVICE.ribbon.MaxAutoRetriesNextServer=2
#对当前实例的重试次数
PRODUCER-SERVICE.ribbon.MaxAutoRetries=1
#服务PRODUCER-SERVICE的地址
PRODUCER-SERVICE.ribbon.listOfServers=localhost:8080,localhost:8083
2.4 Ribbon调用服务
在Controller中,注入RestTemplate,使用服务名(即spring.application.name)的方式,调用PRODUCER-SERVICE服务的GET接口,如下:
@Controller
@RequestMapping("consumer/api")
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/test")
@ResponseBody
public String test() {
ResponseEntity<String> entity = restTemplate
.getForEntity("http://PRODUCER-SERVICE/outer/data?res=3&msgKey=token123", String.class);
return entity.getBody();
}
}
三、测试运行
3.1 负载均衡测试
启动一个消费者服务CONSUMER-SERVICE,多次访问/consumer/api/test,可以通过给PRODECER-SERVICE服务的/outer/data接口添加调试日志的打印,来确认默认使用了轮询的负载均衡策略。
3.2 高可用测试
停止其中一个PRODECER-SERVICE服务实例,确认轮询到已停止的服务时,可以成功地在未停止的服务上自动重试请求。
四、无法成功自动重试的几种情况
本人在单独使用Ribbon的过程中,碰到以下几种无法自动重试其他服务节点的情况:
4.1 ribbon.restclient.enabled
遇到Ribbon的问题,网上一搜,千篇一律,也不知道作者们是否亲自实践证明可用,就随意发篇文章。言归正传,若不设置ribbon.restclient.enabled=true,在本人的实验环境中是无法自动重试的。
4.2 Maven打包有警告
在SpringCloud生态的开发中,各组件往往自动依赖了很多其他的jar包,如果向Maven本地仓库下载的过程中,网络不好,就会下载到一份不完整的jar包或pom文件,最终可能会导致打包出错。下面是在使用Maven打包项目时,比较常见下面的警告:
[WARNING] The POM for com.sun.jersey:jersey-core:jar:1.19.1 is invalid, transitive dependencies (if any) will not be available
[WARNING] The POM for com.sun.jersey:jersey-client:jar:1.19.1 is invalid, transitive dependencies (if any) will not be available
...
可以看出来,提示我们传递依赖将失效,所以有可能整个打包过程是SUCCESS的,但是最后启动jar包时,却可能报NoClassDef等缺少jar包的错误。因此,我们需要在打包时加上参数 -X 以查看具体原因:
[WARNING] The POM for com.sun.jersey:jersey-core:jar:1.19.1 is invalid, transitive dependencies (if any) will not be available: 1 problem was encountered while building the effective model for com.sun.jersey:jersey-core:[unknown-version]
[FATAL] Non-parseable POM E:\maven\repository\com\sun\jersey\jersey-project\1.19.1\jersey-project-1.19.1.pom: processing instruction can not have PITarget with reserved xml name (position: END_TAG seen ...</properties>\n\n</project>\n<?xml ... @627:7) @ E:\maven\repository\com\sun\jersey\jersey-project\1.19.1\jersey-project-1.19.1.pom, line 627, column 7
[WARNING] The POM for com.sun.jersey:jersey-client:jar:1.19.1 is invalid, transitive dependencies (if any) will not be available: 1 problem was encountered while building the effective model for com.sun.jersey:jersey-client:[unknown-version]
[FATAL] Non-parseable POM E:\maven\repository\com\sun\jersey\jersey-project\1.19.1\jersey-project-1.19.1.pom: processing instruction can not have PITarget with reserved xml name (position: END_TAG seen ...</properties>\n\n</project>\n<?xml ... @627:7) @ E:\maven\repository\com\sun\jersey\jersey-project\1.19.1\jersey-project-1.19.1.pom, line 627, column 7
[WARNING] The POM for com.sun.jersey.contribs:jersey-apache-client4:jar:1.19.1 is invalid, transitive dependencies (if any) will not be available: 1 problem was encountered while building the effective model for com.sun.jersey.contribs:jersey-apache-client4:[unknown-version]
[FATAL] Non-parseable POM E:\maven\repository\com\sun\jersey\jersey-project\1.19.1\jersey-project-1.19.1.pom: processing instruction can not have PITarget with reserved xml name (position: END_TAG seen ...</properties>\n\n</project>\n<?xml ... @627:7) @ E:\maven\repository\com\sun\jersey\jersey-project\1.19.1\jersey-project-1.19.1.pom, line 627, column 7
特别注意FATAL这个最严重的日志级别的信息, 日志提醒我们本地仓库的pom文件E:\maven\repository\com\sun\jersey\jersey-project\1.19.1\jersey-project-1.19.1.pom解析出错,然后我们去此目录下,可以发现这个文件未被下载完整。
最后的解决方式是删除此文件的父目录,重新打包,则会自动下载一份完整的pom文件,警告消失,打包成功。
4.3 OkToRetryOnAllOperations和RequestSpecificRetryOn失效
在上文的例子中,消费者服务调用的生产者服务接口是GET类型,自动重试没有任何问题。
接着,本人尝试让消费者调用生产者服务的POST接口,同时仍然设置了ribbon.OkToRetryOnAllOperations=true,结果无法成功重试,然后去调试ribbon源码,查看自动重试机制,经查,OkToRetryOnAllOperations和RequestSpecificRetryOn属性可以成功获取到,但RequestSpecificRetryOn并未被获取出来使用,疑似Ribbon的bug。
这里记录一下调试的重要部分,
(1)Ribbon配置属性类com.netflix.client.config.CommonClientConfigKey;
(2)Ribbon判断是否重试:
所以,单独使用Ribbon需谨慎!
来源:https://blog.csdn.net/songzehao/article/details/101315206


猜你喜欢
- 1.创建简单的XML文件为了便于测试,我们首先创建控制台应用程序,项目命名为CreateXml,Program.cs代码如下:using S
- FileStream,顾名思义,文件流。流,是字节流。我的理解是,硬盘上存在一个字节流,内存里也有一个字节流,它们是对应的。程序运行时,我们
- 1,USB存储设备(如:U盘,移动硬盘): //USB存储设备 插拔监听与 SD卡插拔监听一致。 private USB
- 实现“摇一摇”功能,其实很简单,就是检测手机的重力感应,具体实现代码如下:1、在 AndroidManifest.xml 中添加操作权限2、
- 一、项目简述( +IW文档)功能:本系统分用户前台和管理员后台。 本系统用例模型有三种,分别是游客、注册用户和系统管 理员。下面分别对这三个
- 前言最近在学习使用 React Native开发,iOS搞完,开始适配安卓,由于木有接触过安卓,所以碰到了很多问题,第一个问题,安卓的返回键
- 前言项目使用了SpringBoot构建项目。下面对动态调整日志的级别进行记录。从版本 1.5.1 之后就提供了基于 spring-boot-
- 本文实例讲述了C#获取指定PDF文件页数的方法。分享给大家供大家参考。具体如下:using System;using System.IO;u
- JSON.toJSONString()空字段不忽略修改使用JSON.toJSONString(object)方法,返回的json中,默认会将
- 之前看到过一个数字进度条,一直想写,今天就把这个实现下,想起来也是很简单的,先看下实现的效果:思路:绘制2根线 绘制进度条的文字,不断的改变
- Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程)用户线程即运行在前台的线程,而守护线程是运行
- 本文演示以Spark作为分析引擎,Cassandra作为数据存储,而使用Spring Boot来开发驱动程序的示例。1.前置条件安装Spar
- 这篇文章主要介绍了MyBatis Mapper接受参数的四种方式代码解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考
- 水波纹效果已经不是什么稀罕的东西了,用过5.0新控件的小伙伴都知道这个效果,可是如果使用一个TextView或者Button或者其它普通控件
- 1.方法重写子类写和父类一样的方法定义public void call(){System.out.println(“输出文字”); //父类
- git忽略的原理:git设置本地忽略必须保证git的远程仓库分支上没有这个要忽略的文件,如果远程分支上存在这个文件,本地在设置ignore
- java synthetic关键字。有synthetic标记的field和method是class内部使用的,正常的源代码里不会出现synt
- Maven 的生命周期maven 将项目的生命周期(Lifecycle)抽象为了三种,每种生命周期中又包含了多个阶段(Phase)。也就是说
- 题目描述:在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将
- 1、 在Java1.7之前,我们需要通过下面这种方法, 在finally中释放资源,这种方法有点繁琐。 BufferedReader br