【问题复盘】第三方接口变慢导致服务崩溃

一、事件经过

-1、一个不在公司的下午,接到客户投诉,说平台不能访问了。

0、介入调查,发现服务器http请求无法访问,https请求却可以正常访问,一时有些无法理解;(后来发现,http和https协议是两个不同的线程池。)

1、排查发现Tomcat的线程数达到maxThreads设定的值,于是选择调大maxThreads,原以为问题会这样就被解决了,但在重启服务后,线程数飙升,不一会儿线程数又达到最大值;

Linux查看Tomcat线程命令 (可用top命令查看进程ID)

ps -T -p <Tomcat进程ID> | wc -l

详解tomcat的连接数与线程池 - 编程迷思 - 博客园 (cnblogs.com)

2、开始陷入迷惘,因为最近的代码只是简单修复了一些bug,不应该会造成线程数剧增。 为了进一步确认是否是代码造成的问题,将代码回滚到之前正常的版本,结果线程数同样剧增,直至设定的最大值。

3、困惑加深,难道不是代码的问题? 陷入毫无头绪之中,于是选择以日志作为突破口,有一行WARN日志引起了注意。 这行WARN日志会反复出现,而且出现的同时伴随着不断增加的线程数,由此断定,这行日志就是问题的关键。

4、柳暗花明。 但这行日志看不懂,于是开始了面向百度解决问题。去网上找各种关于这个日志的博客,尝试了博客里的多种方法,也试过了GPT提供的方法,但始终无法确定日志产生的原因,这行WARN日志依旧一直存在。

5、或许,一开始方向就错了。 解决警告日志的问题,就应该先定位到,具体是哪一行代码产生的警告日志。或许是夜太深了,连排查问题的基本思路都迷糊了。

6、突然,在网上看到一篇说明这个报警日志的博客,里面提到了一句,产生这个报警日志的原因在于调用了第三方接口,问题是出现在第三方平台。

关键文章

7、起初,这篇博客没有引起我的注意,因为印象中好像平台基本没有调用第三方接口。但当试了各种方法都没有用以后,想起了这篇博客说的,再试试或许能行呢? 刚好也想到最近确实有调用一个上传记录的第三方接口,于是选择将那部分代码注释了,然后进行测试。

8、果然,一注释掉那行代码,线程数就立刻不增加了。再测试一下那个三方接口,发现请求一次居然要花费5秒钟,之前那个接口调用只需要1-2秒,某些神秘原因导致接口变慢。而设备访问自己平台的频次是2秒一次,2秒没有结果后,就会重新再次发起请求。相当于因为请求超时,然后设备一直不停的访问。(在排查过程中,有那么几次怀疑服务器是被人攻击了,因为在设备配置的是ip+端口号,有心人想要攻击实在太容易了)

9、注释三方接口代码重新部署后,服务又恢复了正常。

10、悬着的心终于放下了,看看外面,天空已经露出了一丝丝鱼肚白。。。

二、问题代码优化

  • 代码业务逻辑

设备上传数据到平台,平台再把数据上传到第三方平台。

  • 初始代码

初始逻辑:在controller层,拿到数据后处理后,调用postDataToAPI方法上传数据。

@Autowired
private RestTemplate restTemplate;
/**
 * 发送POST请求
 * @param url
 * @param requestBody
 * @return
 */
public  boolean postDataToAPI(String url, String requestBody) {
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    HttpEntity<String> entity = new HttpEntity<>(requestBody, headers);
    ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
    String bodyStr = response.getBody().toString();
    JSONObject responseBodyObject = JSONObject.parseObject(bodyStr);
    String code = responseBodyObject.getString("ResultCode");
    if (!StringUtils.isEmpty(code) && "0".equals(code)) {
        return true;
    }
    return false;
}
  • 改进后的代码

改进逻辑:

controller层拿到数据后,不调用postDataToAPI方法,而是将数据保存到数据库,然后将成功结果返回。

调用三方接口上传数据的过程,单独启用一个定时任务执行。在执行的过程中,使用FixedThreadPool线程池来多线程执行,增加上传数据的效率。 如果数据上传成功,则删除数据库数据,失败则保留至下一轮尝试再次上传。

// 固定线程数的线程池
private final ExecutorService executorService = Executors.newFixedThreadPool(5);
@Scheduled(fixedRate = 60000)
public void timedUpload(){
    // 获取第一页数据
    List<TemptData> list1 = getDataByPage(0, 8);
    // 获取第二页数据
    List<TemptData> list2 = getDataByPage(8, 8);
    // 获取第三页数据
    List<TemptData> list3 = getDataByPage(16, 8);
    // 获取第四页数据
    List<TemptData> list4 = getDataByPage(24, 8);
    // 获取第五页数据
    List<TemptData> list5 = getDataByPage(32, 8);
    // 提交任务给线程池执行
    executorService.submit(() -> executeUpload(list1));
    executorService.submit(() -> executeUpload(list2));
    executorService.submit(() -> executeUpload(list3));
    executorService.submit(() -> executeUpload(list4));
    executorService.submit(() -> executeUpload(list5));
}

// 查询数据
private List<TemptData> getDataByPage(int start, int pageSize) {
    return temptDataMapper.getDataList(start, pageSize);
}

// 上传数据
public void executeUpload(List<TemptData> list) {
    if (!list.isEmpty()){
        for (TemptData temptData : list) {
            sendToDongshun(temptData);
        }
    }
}

注意:

  • getDataByPage获取数据时,需要考虑重复消费的问题。因为可能在60秒内,线程还没有执行完,然后下一轮又开始拿到相同的数据执行了。
  • 需要考虑到异常导致数据上传失败的问题,可以采用try catch finally的方式,将上传失败的数据保留和标记。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/685817.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CMMI软件能力成熟度评估标准

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl CMMI概述 CMMI&#xff0c;全称为Capability Maturity Model Integration&#xff0c;即能力成熟度模型集成&#xff0c;是在原有的CMM&#xff08;Capability Maturity Mo…

C语言小例程8/100

题目&#xff1a;输出特殊图案&#xff0c;请在c环境中运行&#xff0c;看一看 程序分析&#xff1a;字符共有256个。不同字符&#xff0c;图形不一样。 #include<stdio.h> int main() {char a176,b219;printf("%c%c%c%c%c\n",b,a,a,a,b);printf("%c%c%c…

【Python报错】已解决ModuleNotFoundError: No module named ‘packaging’

成功解决“ModuleNotFoundError: No module named ‘packaging’”错误的全面指南 在Python编程中&#xff0c;遇到ModuleNotFoundError: No module named packaging这样的错误&#xff0c;通常意味着你的Python环境中缺少名为packaging的模块&#xff0c;或者该模块没有被正确…

达梦 执行查询语句时报[-544]:Out of sort buf space

达梦数据库有时执行SQL中有时报[-544]:Out of sort buf space, try to adjust SORT_BUF_GLOBAL_SIZE, SORT_BUF_SIZE, SORT_BLK_SIZE. 第一反应是这条语句占用排序区太大。但真实原因是前面执行的语句耗光了全局排序区&#xff0c;后面SQL任何小的排序操作都会报这个错误从而执…

『哈哥赠书 - 54期』-『架构思维:从程序员到CTO』

文章目录 ⭐️ 架构思维&#xff1a;从程序员到CTO⭐️ 本书简介⭐️ 作者简介⭐️ 编辑推荐⭐️ 不想成为架构师的程序员不是好CTO 在程序员的职业规划中&#xff0c;成为软件架构师是一个非常有吸引力的选择。但是对于如何才能成为一名架构师&#xff0c;不少同学认为只要代码…

用langchain搭配最新模型ollama打造属于自己的gpt

langchain 前段时间去玩了一下langchain,熟悉了一下大模型的基本概念&#xff0c;使用等。前段时间meta的ollama模型发布了3.0,感觉还是比较强大的&#xff0c;在了解过后&#xff0c;自己去用前后端代码&#xff0c;调用ollama模型搭建了一个本地的gpt应用。 核心逻辑 开始搭…

智谱 GLM4 模型开源,意料之中的尺寸,意料之外的效果

最近智谱开了GLM-4-9B的模型&#xff0c;不是6B&#xff0c;是9B。 一共开源了四个模型&#xff0c;Base版本模型&#xff08;GLM-4-9B&#xff09;、Chat版本模型&#xff08;GLM-4-9B-Chat和GLM-4-9B-Chat-1M&#xff09;和多模态模型&#xff08;GLM-4V-9B-Chat&#xff09…

Vue3的ref创建一个全局变量,非常好用!

1. 前言 Vue3的ref对象我们都知道其用法,通过ref可以创建一个响应式对象使用,同时可以用compute,watch等Vue3的API对其进行操作 不同于Vue2的是,Vue3使用的是组合式API,这也就意味着,我可以在外部单独创建一个ref对象,然后保存,通过导出的方式,给其他的页面使用 理论存在,开始…

SOA的发展历史

1.SOA的发展历程 回顾SOA发展历程&#xff0c;我们把其大致分为了三个阶段&#xff0c;下面将分别介绍每个阶段的重要标准和规范。 1.1.萌芽阶段 这一阶段以XML技术为标志&#xff0c;时间大致从20世纪90年代末到21世纪初。XML系W3C所建&#xff0c;源自流行的标准通用标记语…

course-nlp——8-translation-transformer

本文参考自https://github.com/fastai/course-nlp。 注意力机制和 Transformer Nvidia AI 研究员 Chip Huyen 写了一篇很棒的文章《Top 8 trends from ICLR 2019》&#xff0c;其中的趋势之一是 RNN 正在失去研究人员的青睐。 这是有原因的&#xff0c;RNN 可能很麻烦&#…

网络编程(UPD和TCP)

//发送数据 //UDP协议发送数据 package com.example.mysocketnet.a02UDPdemo;import java.io.IOException; import java.net.*;public class SendMessageDemo {public static void main(String[] args) throws IOException {//发送数据//1.创建DatagramSocket对象(快递公司)//…

MySQL--MHA高可用及读写分离

一、什么是高可用 1.企业级高可用标准&#xff1a;全年无故障时间 全年无故障时间全年故障时间具体时间99.9%0.1%525.6 minkeeplive双主 &#xff08;切换需要人为干预&#xff09;99.99%0.01%52.56 minMHA &#xff08;半自动化&#xff09;99.999%0.001%5.256 minPXC、MGR、…

超详细!新手入门PMP®考试指南,收藏起来备考更高效​!

回复数字“6”&#xff0c;查看PMP考试过关口诀 无论你是刚刚踏入项目管理领域的新手&#xff0c;对于PMP考试充满好奇与期待&#xff1b; 还是已经在职场中摸爬滚打多年&#xff0c;希望通过PMP认证来进一步提升自己的项目管理能力和职业竞争力。 相信这份指南都会为你提供…

超速解读多模态InternVL-Chat1.5 ,如何做到开源SOTA——非官方首发核心技巧版(待修订)

解读InternVL-chat1.5系列 最近并行是事情太杂乱了&#xff0c;静下心来看一看优秀的开源项目,但是AI技术迭代这么快&#xff0c;现在基本是同时看五、六个方向的技术架构和代码&#xff0c;哪个我都不想放&#xff0c;都想知道原理和代码细节&#xff0c;还要自己训练起来&am…

企业自建邮件系统的优势,安全性更高,功能更灵活,维护更便捷

在当今企业信息管理的浪潮中&#xff0c;企业邮件系统显得尤为关键&#xff0c;它不仅加强了内部的沟通效率&#xff0c;还对外展示了企业的专业形象。然而&#xff0c;传统租用企业邮箱服务存在一些不足&#xff0c;如缺乏灵活性、数据管理混乱和难以实现个性化需求&#xff0…

自定义Springboot Starter

创建一个Springboot Starter&#xff0c;借助该Starter我们可以自定义欢迎消息。 本Starter的内容不是重点&#xff0c;重点是创建Starter的流程。 1. 创建Starter工程 1.1 创建Springboot项目 1.2 导入相关依赖&#xff0c;删除spring-boot-maven-plugin <?xml version&…

【Python机器学习】预处理对监督学习的作用

还是用cancer数据集&#xff0c;观察使用MinMaxScaler对学习SVC的作用。 首先&#xff0c;在原始数据上拟合SVC&#xff1a; cancerload_breast_cancer() X_train,X_test,y_train,y_testtrain_test_split(cancer.data,cancer.target,random_state0 ) svmSVC(C100) svm.fit(X_t…

OpenCV的小部件最基本范例

OpenCV也有与PYQT类似的小部件&#xff0c;例如滑块slider。OpenCV可以用与PYQT类似的“信号与槽”方法&#xff0c;也可以在函数中直接查询小部件的值。 import cv2 import numpy as npcv2.namedWindow(Show1) image np.zeros((100, 400, 3), np.uint8) # 创建一个空白内容…

【WP】猿人学_19_乌拉乌拉乌拉

https://match.yuanrenxue.cn/match/19 发包测试 经过发包测试&#xff0c;并没有携带加密参数&#xff0c;但是使用python无法复现&#xff0c;requests&#xff0c;httpx以及异步都不行&#xff0c;网上搜索了一下&#xff0c;这是使用了JA3指纹。可能是我做的时间比较晚&…

O2OA(翱途)开发应用平台(v9)开发实战(3)-如何做信息发布

内容管理就是用来发布信息的&#xff0c;比如说发布单位的内部信息&#xff1a;像公司新闻、通知公告、规章制度等等。 接下来我们来介绍一下如何创建&#xff0c;比如我要创建一个栏目&#xff0c;专门用来发布公司的规章制度 需求 规章制度 首先从菜单打开“内容管理设置…