背景

本系统(支付系统)会在每个月特定时间(如账单日某个时间)接收上游系统发起的大量请求并进行处理,并在处理完成后返回结果给上游系统。而本系统接收到请求进行处理的过程是调用第三方(支付公司)进行处理并获取结果。

系统原实现方案没有采用任何控制请求并发数的措施,接收到上游系统的请求后,就发送给支付渠道进行处理。这样实际上就是来一个请求就启动一个tomcat线程进行处理。

上游系统调用本系统,本系统调用第三方公司同步接口,获得结果后本系统将结果同步返回给上游系统。
假设上游系统的并发数为n,则一开始本系统一秒钟接收n个请求,并将这些请求发往第三方进行处理。假设第三方处理请求需要的时间为t,则对于上游系统来讲,本系统的响应时间比t略大。由于整个调用链上第三方处理时间较长,最短1s,最长可达10s,所以一次完整请求的处理时间是比较长的。本系统处理上游系统请求的TPS = n/t(实际略大于t),由于并发数n不大同时响应时间t较大,所以tps不高。本系统和下游第三方基本没什么压力。

此方案存在的问题:系统接口响应时间依赖于第三方接口响应时间,最长可能长达十几秒。另外就是tps过低。实际上游系统并发量为16,响应时间平均为3s,则tps为5左右,支付系统平均每秒处理5个请求,一小时只能处理18000左右的请求数。

改进方案

对本系统进行改造,主要是把本系统的请求处理接口由同步接口改为异步接口。

即同步接口不再等第三方处理完成之后才返回,而是在本系统接受到请求并进行内部处理,在发送给第三方进行处理之前返回。然后再由异步任务将请求发送给第三方进行处理,将处理结果再通过回调方式或者提供查询接口提供给上游系统。

经此改造后,本系统对上游系统的响应时间由平均3s缩短到30ms。此种改进方案的本质是将上游系统的请求接受过来,然后根据下游的处理能力进行请求分发。那么必须要有一种机制能将请求储存起来,然后根据实际情况将请求取出来发送给下游第三方进行处理。

考虑两种方案,其中一种是消息队列,将请求放到消息队列中慢慢处理,但无法保证消息不丢失。故采用第二种方案:将请求全部入库,然后通过线程池来执行后续任务。

问题及解决办法

1.大量请求没有处理

改进后出现的第一个问题是大量请求在任务表中没有得到处理。原因是:大量请求在一定时间内进入任务表,同时通过线程池将请求取出来执行。由于接受请求的接口响应时间极短(30ms-50ms),tps约为400。线程池核心线程数为40,最大线程数为80,四个服务器实例加起来为320,而下游系统的响应时间平均为3s,tps为100左右。导致每秒约有300个请求得不到处理而进入队列等待,很快四个服务器实例的线程池队列全部放满,之后的请求就无法得到处理了。当上游系统的请求全部接受完毕,主线程停止之后,因任务队列满而得不到处理的请求就在任务表中得不到处理。

解决办法是将任务表中被拒绝的请求用一个状态字段来标记,然后通过补偿任务捞出来再次执行。但是在此过程中又出现了第二个问题。

2.并发更新数据库导致死锁

补偿任务处理逻辑是:从任务表中取出请求记录,当该记录状态为被拒绝,且更新为已执行时更新成功,则将请求发送给第三方进行处理。
这里使用数据库乐观锁来更新状态,防止并发更新和重复执行请求。但是是将状态作为乐观锁标识,更新语句以状态为条件来更新。这种做法带来了死锁问题。原因是:mysql的innoDB行锁是通过给索引项加锁实现的。而索引分为主键索引和非主键索引,如果一条sql语句操作了主键索引,MySQL就会锁定这条主键索引;如果一条语句操作了非主键索引,MySQL会先锁定该非主键索引,再锁定相关的主键索引。如果两个线程同时来更新一条记录,一个锁住了主键索引,在等待其他相关索引。另一个锁定了非主键索引,在等待主键索引。这样就会发生死锁。解决办法是加version字段,更新时以主键id和version作为条件来更新。Version上无索引,更新时只会锁定主键索引,就不会造成死锁。

其实这里也可以使用redis分布式锁,但是需要设置合适的锁过期时间。

后续思考

经过上述改进后,一台服务器的tps为100左右,四台加起来400。为了保证请求不丢失,将所有请求全部入库,此时实际上是将压力全部丢给了数据库,每秒400个并发写入操作(当然,实际上为了减少数据库压力,将上游系统的并发降低,tps也更低一点)。当然,这个并发数还不算高,数据库压力不大。但是,如果并发数进一步增大,则数据库压力将大为增加,此时就不合适采用这种方案了。可能还是需要使用rabbitmq消息队列,通过确认机制来保证消息的可靠性。

线程池处理高并发请求相关推荐

  1. springboot 压测 50并发 线程等待_线程池+CountDownLatch——高并发就是这么简单

    今天和大家分享的是:在开发服务端API时候,如何合理的运用线程池+CountDownLatch来保证API的高并发访问. 首先,作为Java开发的同学来说,java.util.concurrent并发 ...

  2. tomcat使用线程池配置高并发连接

    1:配置executor属性 打开/conf/server.xml文件,在Connector之前配置一个线程池: [html] view plain copy <Executor name=&q ...

  3. 利用libevent 和线程池实现高并发服务器的设计

    主进程添加监听套接字的事件并进行事件循环,将连接描述符放入定义的数据结构中,并在主进程中进行写管道,触发子线程的读管道事件,然后从连接结构中获取连接描述符进行和客户端进行通信.其中主进程和子线程都有不 ...

  4. 解秘 Node.js 单线程实现高并发请求原理,以及串联同步执行并发请求的方案

    最近在做一个支持多进程请求的 Node 服务,要支持多并发请求,而且请求要按先后顺序串联同步执行返回结果. 对,这需求就是这么奇琶,业务场景也是那么奇琶. 需求是完成了,为了对 Node.js 高并发 ...

  5. python线程池模块_python并发编程之进程池,线程池,协程

    需要注意一下 不能无限的开进程,不能无限的开线程 最常用的就是开进程池,开线程池.其中回调函数非常重要 回调函数其实可以作为一种编程思想,谁好了谁就去掉 只要你用并发,就会有锁的问题,但是你不能一直去 ...

  6. mysql 保证事物完整性_数据库高并发请求,如何保证数据完整性?详解MySQL/InnoDB的加锁...

    本文是对MySQL/InnoDB中,乐观锁.悲观锁.共享锁.排它锁.行锁.表锁.死锁概念的理解,这些在面试中也经常遇到,如数据库高并发请求,如何保证数据完整性?今天我查阅资料进行了MySQL/Inno ...

  7. python线程池模块_python并发编程之进程池,线程池,协程(Python标准模块--concurrent.futures(并发未来))...

    需要注意一下 不能无限的开进程,不能无限的开线程 最常用的就是开进程池,开线程池.其中回调函数非常重要 回调函数其实可以作为一种编程思想,谁好了谁就去掉 只要你用并发,就会有锁的问题,但是你不能一直去 ...

  8. PostgreSQL数据库 OLTP高并发请求性能优化

    PostgreSQL数据库 OLTP高并发请求性能优化   2015-10-14 11:00:00|  作者:德哥:分类: PgSQL PerfTuning| 2015年度PG大象会报名地址: htt ...

  9. Web大规模高并发请求和抢购的解决方案

    电商的秒杀和抢购,对我们来说,都不是一个陌生的东西.然而,从技术的角度来说,这对于Web系统是一个巨大的考验.当一个Web系统,在一秒钟内收到数以万计甚至更多请求时,系统的优化和稳定至关重要.这次我们 ...

最新文章

  1. 武汉大学计算机学院放假时间,计算机学院关于2019年学生放暑假的通知
  2. html表格的表头怎么合并单元格,使用tableGrob合并表头单元格
  3. .net core 使用RSA获取私钥证书并签名
  4. 前端学习(3064):vue+element今日头条管理-状态管理
  5. Sql Server 2005 row_number()分页性能测试
  6. 大厂面试算法系列-如何从无序链表中移除重复项(二)-递归法
  7. 判断成绩linux程序编程,程序输入输出 ,编写判断成绩的程序
  8. linux裁剪内核和移植,嵌入式Linux内核裁剪及移植的研究与实现
  9. 腾讯开工日1.5亿美元领投Reddit,美国贴吧最新估值30亿美元
  10. 给已经做好的GHO文件添加密码
  11. sudo rosdep init 出现 ERROR: cannot download default sources list from:
  12. SQL2000 挂起
  13. (附源码)springboot应用支撑平台和应用系统 毕业设计 984655
  14. 笔记本电脑中预装的office产品不小心被删除了解决方案
  15. 【文献阅读】Commission Fee is not Enough: A Hierarchical Reinforced Framework for Portfolio Management
  16. linux内核函数出错的返回值
  17. 7-14 然后是几点(C语言)
  18. NeurIPS 2022 | 最强斗地主AI!网易互娱AI Lab提出基于完美信息蒸馏的方法
  19. 推荐一个不错的学术资源论坛
  20. 普通二维码跳转到小程序

热门文章

  1. Python求文件行数
  2. java 连接 Access数据库的两种方法
  3. 两种常用的哈希算法与Hmac算法
  4. 日常刷题-反转二叉树
  5. c语言多组数据判断回文字符串,详解判断回文字符串跟回文数算法的C语言代码...
  6. 计算机网络英文介绍,《计算机网络英文版》.doc
  7. 贪吃的大嘴 多重背包 dp
  8. 千万级支付对账系统是怎么设计的?
  9. 第三方 App 分享微信小程序链接
  10. 如何查看github趋势榜