我叫你不要重试,你非得重试。这下玩坏了吧? (中)

简介: 我叫你不要重试,你非得重试。这下玩坏了吧? (中)

FailoverCluster源码


源码位于org.apache.dubbo.rpc.cluster.support.FailoverClusterInvoker中:

image.png

等等,不对啊,前面刚刚说的是 3 次,怎么一转眼就是 2 次了呢?

你别急啊

image.png

你看第 61 行的最后还有一个 "+1" 呢?

你想一想。我们想要在接口调用失败后,重试 n 次,这个 n 就是 DEFAULT_RETRIES ,默认为 2 。那么我们总的调用次数就是 n+1 次了。

所以这个 "+1" 是这样来的,很小的一个知识点,送给大家。

另外图中标记了红色五角星★的地方,第62到64行。也是很关键的地方。对于 retries 参数,在官网上的描述是这样的:

image.png

不需要重试,请设为 0 。我们前面分析了,当设置为 0 的时候,只会调用一次。

但是我也看见过retries配置为 -1 的。-1+1=0。调用0次明显是一个错误的含义。但是程序也正常运行,且只调用一次。

这就是标记了红色五角星的地方的功劳了。

防御性编程。哪怕你设置为 -10000 也只会调用一次。

下面这个图片是我对 doInvoke 方法进行一个全面的解读,基本上每一行主要的代码都加了注释,可以点开大图查看:

微信图片_20220427210659.png


如上所示,FailoverClusterInvoker 的 doInvoke 方法主要的工作流程是:

  • 首先是获取重试次数,然后根据重试次数进行循环调用,在循环体内,如果失败,则进行重试。
  • 在循环体内,首先是调用父类 AbstractClusterInvoker 的 select 方法,通过负载均衡组件选择一个 Invoker,然后再通过这个 Invoker 的 invoke 方法进行远程调用。
  • 如果失败了,记录下异常,并进行重试。

注意一个细节:在进行重试前,重新获取最新的 invoker 集合,这样做的好处是,如果在重试的过程中某个服务挂了,可以通过调用 list 方法保证 copyInvokers 是最新的可用的 invoker 列表。

整个流程大致如此,不是很难理解。


HttpClient 使用样例


接下来,我们看看 apache 的 HttpClients 中的重试是怎么回事。

也就是这个类:org.apache.http.impl.client.HttpClients

首先,废话少说,弄个 Demo 跑一下。

先看 Controller 的逻辑:

@RestController
public class TestController {
    @PostMapping(value = "/testRetry")
    public void testRetry() {
        try {
            System.out.println("时间:" + new Date() + ",数据库插入成功");
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

同样是睡眠 5s,模拟超时的情况。

HttpUtils 封装如下:

public class HttpPostUtils {
    public static String retryPostJson(String uri) throws Exception {
        HttpPost post = new HttpPost(uri);
        RequestConfig config = RequestConfig.custom()
                .setConnectTimeout(1000)
                .setConnectionRequestTimeout(1000)
                .setSocketTimeout(1000).build();
        post.setConfig(config);
        String responseContent = null;
        CloseableHttpResponse response = null;
        CloseableHttpClient client = null;
        try {
            client = HttpClients.custom().build();
            response = client.execute(post, HttpClientContext.create());
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                responseContent = EntityUtils.toString(response.getEntity(), Consts.UTF_8.name());
            }
        } finally {
            if (response != null) {
                response.close();
            }
            if (client != null){
                client.close();
            }
        }
        return responseContent;
    }
}

先解释一下其中的三个设置为 1000ms 的参数:

connectTimeout:客户端和服务器建立连接的timeout

connectionRequestTimeout:从连接池获取连接的timeout

socketTimeout:客户端从服务器读取数据的timeout

大家都知道一次http请求,抽象来看,必定会有三个阶段

  • 一:建立连接
  • 二:数据传送
  • 三:断开连接

当建立连接的操作,在规定的时间内(ConnectionTimeOut )没有完成,那么此次连接就宣告失败,抛出 ConnectTimeoutException。

后续的 SocketTimeOutException 就一定不会发生。

当连接建立起来后,才会开始进行数据传输,如果数据在规定的时间内(SocketTimeOut)沒有传输完成,则抛出 SocketTimeOutException。如果传输完成,则断开连接。

测试 Main 方法代码如下:


public class MainTest {
    public static void main(String[] args) {
        try {
            String returnStr = HttpPostUtils.retryPostJson("http://127.0.0.1:8080/testRetry/");
            System.out.println("returnStr = " + returnStr);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


首先我们不启动服务,那么根据刚刚的分析,客户端和服务器建立连接会超时,则抛出 ConnectTimeoutException 异常。

直接执行 main 方法,结果如下:


image.png

符合我们的预期。

现在我们把 Controller 接口启动起来。

由于我们的 socketTimeout 设置的时间是 1000ms,而接口里面进行了 5s 的睡眠。

根据刚刚的分析,客户端从服务器读取数据肯定会超时,则抛出 SocketTimeOutException 异常。

Controller 接口启动起来后,我们运行 main 方法输出如下:

image.png

这个时候,其实接口是调用成功了,只是客户端没有拿到返回。

image.png

这个情况和我们前面说的 Dubbo 的情况一样,超时是针对客户端的。

即使客户端超时了,服务端的逻辑还是会继续执行,把此次请求处理完成。

执行结果确实抛出了 SocketTimeOutException 异常,符合预期。

但是,说好的重试呢?

目录
相关文章
|
机器学习/深度学习 存储 缓存
mPaaS扫码速度过快导致出错的问题
mPaaS扫码速度过快导致出错的问题
140 1
|
6月前
|
分布式计算 UED 流计算
Java编程问题之重试机制问题之在使用重试机制时的问题如何解决
Java编程问题之重试机制问题之在使用重试机制时的问题如何解决
|
Cloud Native Go
如何处理面试拒绝:失败并不是终点
如何处理面试拒绝:失败并不是终点
94 0
|
Java 数据库连接 数据库
这些让Spring事务失效的操作千万别犯!
在Spring框架中,事务是一种关键机制,用于确保数据库操作的一致性和完整性。然而,有时候事务可能会失效,导致意外的结果或数据不一致。下面是一些可能导致Spring事务失效的常见场景,以及相应的代码示例和解释。
这些让Spring事务失效的操作千万别犯!
|
数据采集 Python
python爬虫怎么解决超时timeou错误
python爬虫怎么解决超时timeou错误
|
存储 测试技术
kindle 应用程序出错,无法启动选定的应用程序,请重试。问题排查过程及处理方案。...
kindle 应用程序出错,无法启动选定的应用程序,请重试。问题排查过程及处理方案。...
567 0
|
设计模式 Kubernetes 算法
leader说用下httpclient的重试,但我没用,因为我有更好的方案。
leader说用下httpclient的重试,但我没用,因为我有更好的方案。
202 0
|
JSON Java 程序员
写了这么久的业务连异常都不知道怎么处理吗
前言 文本已收录至我的GitHub仓库,欢迎Star:github.com/bin39232820… 种一棵树最好的时间是十年前,其次是现在
228 0
|
XML Dubbo 应用服务中间件
我叫你不要重试,你非得重试。这下玩坏了吧? (上)
我叫你不要重试,你非得重试。这下玩坏了吧? (上)
131 0
我叫你不要重试,你非得重试。这下玩坏了吧? (上)
|
负载均衡 Dubbo 应用服务中间件
我叫你不要重试,你非得重试。这下玩坏了吧? (下)
我叫你不要重试,你非得重试。这下玩坏了吧? (下)
388 0
我叫你不要重试,你非得重试。这下玩坏了吧? (下)