2026/3/5 8:51:46
网站建设
项目流程
重庆网站制作机构,中国中国建设银行网站,维护官网,高新西区网站建设互联网红包大战元年#xff0c;笔者加入艺龙旅行网#xff0c;负责的第一个重要系统就是#xff1a;红包系统。这篇文章#xff0c;笔者分享艺龙红包领取接口频繁超时#xff0c;如何巧用线程池异步解决超时问题 。1 系统架构 接口事故如图#xff0c;用户登录艺龙…互联网红包大战元年笔者加入艺龙旅行网负责的第一个重要系统就是红包系统。这篇文章笔者分享艺龙红包领取接口频繁超时如何巧用线程池异步解决超时问题 。1 系统架构 接口事故如图用户登录艺龙 APP 后艺龙 APP 会自动调用领取红包的接口 。红包服务会查询该用户是否满足领取红包的条件若满足条件则发送消息到 RabbitMQ , 发送成功后领取接口返回响应值给前端。用户服务作为消费者异步消费 MQ 的消息将红包虚拟金额保存到用户余额体系里。从整体流程来看还是非常简单的伪代码如下但笔者刚加入团队时APP 研发团队的总监经常投诉我们因为我们的领取接口每隔一段时间后就会超时重启红包活动后接口才恢复正常。首先查看服务器日志发现日志异常集中于第三步即发送消息到 RabbitMQ 。为什么发送消息到 RabbitMQ 会失败呢 笔者首先想到的是Linux netstat 命令查看 RabbitMQ 连接状况。2 Linux netstat 命令查看 RabbitMQ 连接netstat 可以查看服务器当前端口列表及指定端口的连接状态等参数如下常用命令1 、查看当前所有 tcp 端口2 、查看当前所有 udp 端口3 、显示系统所有端口假如想查看某个应用对应的连接可以通过 grep pid 来实现如下图当笔者在生产环境使用 netstat 命令查看红包服务的连接发现 RabbitMQ 连接的特别多 基本达到了本地的最大句柄数。于是笔者开始怀疑 RabbitMQ SDK 封装有问题我们来一探究竟。3 RabbitMQ SDK 封装问题如上图RabbitMQ 工具类发送消息时首先看本地缓存 longConnection 是否有效若有效则直接复用该连接若连接失效则重新创建连接然后通过该连接发送消息。看到这里似乎也没有问题呀 但高并发场景下请求量非常高确有隐藏的风险表现在如下两点1、获取连接时通过对象的属性 longConnection 来判断是否过期 , 但我们知道对于多线程来讲在没有加锁的情况下并不靠谱。2、创建连接时极大可能存在并发问题导致会重复创建多个连接而且重复的连接并没有引用。综上使用架构封装的 RabbitMQ. SDK 在发送消息时容易产生连接泄露的问题。当连接数占满之后再次发送消息时由于长时间获取不到连接抛出异常。那么如何快速解决呢 首先我们想到的是让架构团队快速修复 SDK 的 BUG 但是这个需求时间研发和测试于是笔者想到的是线程池异步技巧来解决这个问题。4 线程池异步技巧当 APP 发起领取红包接口后红包服务开启单独的线程处理该请求然后直接将响应结果直接返回给前端。伪代码如下首先我们定义了一个单线程领取接口接收到请求后调用线程池的 execute 方法直接将响应结果返回给前端线程池会异步的处理领取红包流程 。这么做有两点好处1、将领取的流程异步化可以减少领取接口的阻塞让 Tomcat 线程可以非常顺畅的运行 2、因为是单线程处理领取流程可以规避 RabbitMQ SDK 连接泄露的 BUG 同时也可以满足业务需求。5 总结艺龙红包 RabbitMQ SDK 的 连接泄漏 BUG 非常隐蔽笔者通过 netstat 命令定位到红包服务的 RabbitMQ 连接发生了泄露 。架构团队封装的 RabbitMQ SDK 有两种方案来解决1、创建连接时完善加锁的操作 2、使用 commons-pool 这样的框架来创建连接池提高可维护性。最后因为时间的关系笔者要快速解决问题采用了异步线程池的模式 单线程处理领取流程可以规避 RabbitMQ SDK 连接泄露的 BUG 。