【异常】原来提示SocketTimeoutException:connect timed out还可能是外部因素导致
创始人
2024-05-10 22:09:38
0

一、现象截图

一大早收到ELK的邮件提醒,让我来看看,又是哪个妖怪在作孽?
在这里插入图片描述

二、问题定位

2.1 SocketTimeoutException:connect timed out

经验告诉我,这个问题一般是第三方平台的问题,大部分原因是发起Http请求,但是请求超时导致,很多HTTP framework(如本文中涉及的Hutool工具中的HttpUtil,底层是HttpURLConnection)本身有超时机制的,实现超时,就是在应用层代码里启动一个Timer,如果Timer超时,则手动取消请求。比如用户现在网络环境较差,当客户端发起一个请求时,通信层开始请求与服务器建立连接(包括在重试),如果在5S之内还没有连接到服务器,那么就会判定为超时。Http请求超时一般与我们的代码无关。

2.2 ICardServiceImpl的代码第122行出错了

啊?ICardServiceImpl是对接第三方平台,用于积分充值的功能,是组内大神写的代码,按理是不会出问题,而且已经上线了快1年了都,最近又没有变更,所以,第一直觉,故障的原因是因为外部变更导致的。

122行,那不就是这段代码吗?也就是下面这段方法中第三行中的代码逻辑
在这里插入图片描述

private void recharge2ICard(String workNo, Integer amount) {String icardPoint = String.format("%d.%02d", amount / 100, amount % 100);ICardRechargeReq req = new ICardRechargeReq(null, workNo, null, iCardRechargeKey, icardPoint);String respStr = HttpUtil.post(iCardRechargeUrl, JSONUtil.toJsonStr(req), 30 * 1000);ICardRechargeResp resp = JSONUtil.toBean(respStr, ICardRechargeResp.class);Assert.isTrue(resp.getHead().get(0).checkMac(iCardRechargeKey), "充值失败:一卡通返回信息校验失败!");String retCode = resp.getHead().get(0).getRcode();Assert.isTrue(Objects.equals(ICardReturnCode.OK.getCode(), retCode), "充值失败:" + ICardReturnCode.getMsg(retCode));}

这段逻辑中会超时,而且超时时间设置的是,底层逻辑如下,即在30000毫秒(30 秒)无响应,将提示超时,上面的报错提示,就是这一段它在超时后提示的异常。
在这里插入图片描述
这段代码中存在以下问题
(1)错误提示不友好,超时了没有给反馈结果给前端。用户体验不好
(2)错误日志Assert.isTrue的方式可读性不高,而且容易逻辑判断出错
(3)没有考虑到返回不是json的情况,下面这个错误也是频发的。在这里插入图片描述

三、问题深入分析

经过咨询运维同事还有网络同事之后,他们用了一堆很专业的网络命令,比如ping、tracepath命令在我们的服务器层面去排查,发现确实有问题,最后发现了是线路欠费断了导致???什么?没有监控的吗?这种欠费的低级错误也会犯??算了,外部原因不深究了,那就来一波网络排查指令语法知识干货科普吧~

3.1 tracepath指令

指令作用:

追踪数据到达目标主机的路由信息,同时还能够发现MTU值。
它跟踪路径到目的地,沿着这条路径发现MTU。它使用UDP端口或一些随机端口。
它类似于Traceroute命令,只是不需要超级用户特权,并且没有花哨的选项。
它用于检测网络延迟,但是,它不需要 root 权限,并且默认安装在 Ubuntu 中。
它跟踪到指定目的地的路由并识别其中的每一跳。
如果您的网络较弱,它会识别出网络较弱的点。

指令适用范围:

RedHat、RHEL、Ubuntu、CentOS、SUSE、openSUSE、Fedora。

指令语法:

tracepath 
tracepath [ -n] [ -l pktlen] destination [ port]

指令选项列表:

选项说明
-p后面跟设置要使用的初始目标端口
-n不查看主机名字,以数字形式只显示 IP 地址。
-4仅使用IPv4
-6仅使用IPv6
-b同时显示 IP 地址和主机名
-l设置初始化的数据包长度,IPv4 默认为 65535,IPv6 默认为 128000
-m后面跟 设置最大 TTL 值,默认为 30
-V打印版本并退出

输出解答

核心:后面看不到地址,是超时,就代表断了

在这里插入图片描述

1?[LOCALHOST]pmtu 1500
第一列第二列行的其余部分
显示探针的TTL,后面是冒号。通常TTL的值是从网络中得到的,但有时回复并不包含必要的信息,我们不得不猜测它。在这种情况下,数字后面跟着?。显示网络跳,对探测作出答复。如果探测未发送到网络,则为路由器地址或者[localhost]地址。显示了有关到达相关工作跳的路径的各种信息。作为规则,它包含RTT的值。此外,它可以显示路径MTU,当它改变。如果路径是不对称的,或者探测在到达指定跳之前完成,则显示前向和后向跳数之间的差异。这一信息不可靠。F.E.第三行显示1的不对称性,这是因为第一次TTL为2的探针在第一跳时由于路径MTU发现而被拒绝。

最后一行总结了到达目的地的所有路径的信息,显示了检测到的路径MTU、到达目的地的跳数以及我们对从目的地到我们的跳数的猜测,这在路径不对称时可能有所不同。

四、优化与重构现有代码逻辑

既然发现了问题,就应该着手修改问题了,刚好趁着这个契机,重构这一段代码,然后让测试同学重新测试下,嘿嘿。告别不好的用户体验,以及再见了无用的告警!

4.1 核心逻辑1:重构充值到一卡通的逻辑

    private Pair recharge2ICard(String workNo, Integer amount) {Pair handleResult;String iCardPoint = String.format("%d.%02d", amount / 100, amount % 100);ICardRechargeReq req = new ICardRechargeReq(null, workNo, null, iCardRechargeKey, iCardPoint);String respStr;try {respStr = HttpUtil.post(iCardRechargeUrl, JSONUtil.toJsonStr(req), 30 * 1000);} catch (Exception e) {log.error("充值失败! 无法链接一卡通提供的URL = 【{}】,请检查网络配置!", iCardRechargeUrl);handleResult = new Pair<>(Boolean.FALSE, "充值失败! 无法链接一卡通提供的URL!请检查网络配置!");return handleResult;}log.info("充值信息:user={},amount={},respStr={}", workNo, amount, respStr);if (StrUtil.isBlank(respStr)) {log.error("充值失败! respStr是空的 respStr= 【{}】,可能原因是网络阻塞导致!", respStr);handleResult = new Pair<>(Boolean.FALSE, respStr);return handleResult;}ICardRechargeResp resp = JSONUtil.toBean(respStr, ICardRechargeResp.class);if (!resp.getHead().get(0).checkMac(iCardRechargeKey)) {String err = "充值失败:一卡通返回信息校验失败!";handleResult = new Pair<>(Boolean.FALSE, err);return handleResult;}String retCode = resp.getHead().get(0).getRcode();if (!ObjectUtil.equals(ICardReturnCode.OK.getCode(), retCode)) {String err = "充值失败:" + ICardReturnCode.getMsg(retCode);handleResult = new Pair<>(Boolean.FALSE, err);return handleResult;}return new Pair<>(Boolean.TRUE, null);}

4.2 核心逻辑2:服务调用逻辑

        Pair handleResult = recharge2ICard(userInfo.getWorkNo(), body.getAmount());if (Boolean.FALSE.equals(handleResult.getKey())) {return R.failed("充值到一卡通失败,请联系客服处理!");}

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
客厅放八骏马摆件可以吗(家里摆... 今天给各位分享客厅放八骏马摆件可以吗的知识,其中也会对家里摆八骏马摆件好吗进行解释,如果能碰巧解决你...
苏州离哪个飞机场近(苏州离哪个... 本篇文章极速百科小编给大家谈谈苏州离哪个飞机场近,以及苏州离哪个飞机场近点对应的知识点,希望对各位有...