首页 > 编程知识 正文

高并发数据库如何建立索引,如何模拟高并发场景

时间:2023-05-04 20:45:09 阅读:34201 作者:2009

一、安静下午报警,Moji群里陆续传来并行MOA默认集群 /service/parallel 发生异常的提示信息线程满后,新的请求被直接拒绝,大量的业务请求被错误报告,速度版附近的人、基因、聊天室等多个业务进入降级状态,达到3358www.Sina.com/,这一系列严重影响

3358www.Sina.com/是访问http://www.Sina.com/http://www.Sina.com /的最常见客户端组件

二、问题分析

从慢请求日志中可以看到,单个请求阻止线程的时间最多为... 分钟或更长。 简单的 new JedisPool() 为什么要长时间阻止线程? 构建测试服务,再现问题,获取Jedis进行分析,根据Java中向Redis注册对象逻辑,确定特定场景

软件包相关性说明

并行10工程new JedisPool()

3358 www.Sina.com/65http://www.Sina.com/65http://www.Sina.com /

3358 www.Sina.com/65http://www.Sina.com/65http://www.Sina.com /

3358 www.Sina.com/65http://www.Sina.com/65http://www.Sina.com /

3358 www.Sina.com/65http://www.Sina.com/65http://www.Sina.com /

http://www.Sina.com/(http://www.Sina.com/)。

问题发生在通过http://www.Sina.com/http://www.Sina.com/访问下游服务的新启动实例的过程中,并通过http://www.Sina.com /访问下游实例

jstack中与JedisPool的相互作用

3358www.Sina.com/使用JMX管理连接对象,MOA在创建对象池时使用3358 www.Sina.com

公共池在JMX中注册BaseGenericObjectPool对象。 JMX要求为每个对象指定不同的名称。 commons-pool会根据默认的相同名称,在末尾添加自增加ID (new Jedi spool () ),每次都会发现ID从1开始重复名称。直到找到未使用的id,重试成功为止同时,对于一个名称ID,只有一个JedisPool对象尝试在 -上重复MOA并在3358www.Sina.com/上注册

MOARedisClient上注册的代码获取全局锁定

出现问题的条件当前进程已经创建了大量的JedisPool,并且已经使用了许多自增长ID,例如1到1w已经被使用。 此时,要创建以下JedisPool,必须遍历现有id1w次才能使用id 1注册对象: 在这1w次尝试中,如果每次有多个线程同时创建jedispoool,则必须为每个线程遍历所有ID,并且在遍历过程中每次锁定后,其他线程将无法重试,一个线程遍历1w次100个线程每次遍历1w次,总共100w次尝试需要串行执行,100个线程交替获取锁定,交替重试,最终100个线程需要3秒钟,重试问题解决过程需要100秒钟

发生问题

-执行常规发布操作

g>16:16 /service/parallel 并行任务线程池被打满、开始通过扩容和隔离实例解决

16:26 服务逐步恢复

并行MOA使用了MSC线程池组件,从活跃线程数监控可以看到每个并行MOA实例线程池被打满到恢复的时间

被阻塞的线程是能够自动恢复的,并且恢复的时间并不统一。从日志中我们首先找到了阻塞线程的慢请求

execution finished after parallel timeout: /service/phpmoa/v1_microvideo_index,isRiskFeeds, startTime = 2020-11-30 16:26:02,428, server = 10.116.88.15:20000, routeTime = 2020-11-30 16:26:02,428, blacklistTime = 2020-11-30 16:26:02,428, executeTime = 2020-11-30 16:37:21,657, timeCost = 679229

刚好是调用 /service/phpmoa/v1_microvideo_index 服务,但记录的执行时间最长可达到10分钟以上。慢日志中包含各个阶段的耗时,因此耗时的逻辑可以锁定在 blacklistTime executeTime 之间,这里只对应一行通过MOA框架MOARedisClient调用下游服务的代码

初步分析

MOARedisClient.exeuteByServer()内部,仅有2个逻辑可能出现较长的耗时,一个是RedisFactory.getRedisDao(),这里会与下游实例创建连接。另一个是doInvoke()真正发起请求,由于后者的耗时会提交到Hubble,并且未发现达到分钟级的耗时,因此问题的原因更可能出现在创建RedisDao的逻辑中。

排查瓶颈

由于RedisFactory.getRedisDao()各个阶段的耗时缺少监控,并且服务出现异常期间没有及时通过jstack打印堆栈信息,问题排查到这一步仅靠分析很难继续进行。

问题复现

我们查找了 /service/phpmoa/v1_microvideo_index 的发布记录,发现这个服务每次发布的时候,/service/parallel 都会有短暂的errorCount波动,因此推断该问题是能够通过重启 /service/phpmoa/v1_microvideo_index 来复现的。

搭建测试服务

重启线上服务有可能再次导致服务异常、影响线上业务,所以我们先尝试在线上环境复制上下游项目、发布成不同的ServiceUri,并增加一个测试接口,通过压测平台制造流量,搭建起和线上调用链路基本一致的测试环境。

增加监控

除了在MOAMCF的代码中增加各阶段耗时的日志外,对于并行MOA出现线程池满拒绝请求、以及出现10秒以上慢请求的场景,均增加了自动打印jstack的机制。

获得排查依据

在适当调整模拟流量的压力后,重启测试的 /service/phpmoa/v1_microvideo_index 服务后,问题提复现了。这一次我们拿到了详细的耗时信息,以及线程池满后的jstack堆栈信息,才进一步分析到问题的根本原因。

四、问题验证

测试服务验证 问题复现后的jstack堆栈,611个线程停留在等待锁的步骤将JMX关闭后,对比其他未关闭的实例没有再复现该问题

与问题现象匹配 并行MOA的特征 调用的下游服务极多、下游实例数极多,需要创建大量的JedisPool下游重启过程中并行MOA需要创建大量新的JedisPool,并且并行创建的线程数很多(最多800个)问题发生过程 下游服务发布后出问题(microvideo_index)、下游实例数多的服务发布问题严重(230个)、发布速度快的服务问题严重(2分钟)、多个服务同时发布的时候问题严重(microvideo_index和user_location在同一时间段做发布)各个并行MOA实例能够自动恢复,但恢复的时间点差异较大(具体耗时取决于已有ID数量、并行创建JedisPool的线程数据量,各实例的情况可能不一致)异常期间并行MOA服务的CPU使用率大幅升高(在频繁获取锁)相同时刻其他并行MOA的集群未出问题(因为请求量低、并行创建JedisPool的线程少)

五、解决方案

问题影响范围

业务上使用JedisPool的场景,多通过MCFRedisDao封装。RedisDao主要用于两个场景

MomoStore

通过MomoStore访问Redis数据源、访问OneStore底层使用RedisDao。由于MomoStore对于新实例的连接建立是在接收事件通知后单线程执行的,受并发创建JedisPool的影响较少。

MOARedisClient

由于与下游新实例创建连接的动作是在业务请求中完成的,所以使用MOARedisClient的场景受并发创建JedisPool影响的可能性较大。当服务与并行MOA具备类似的特征:下游服务多、实例多,并行执行的请求多,在下游服务发布时也容易出现相同的问题。使用MOARedisClient在某些场景下的执行时间超出设定的timeout时间,也与该问题有关。

修复方案

最简单有效的解决方案是关闭JedisPoolJMX配置,可以在MCF的代码中统一修改、通过升级MCF版本修复。对于已接入Mesh的服务,由于MOARedisClient实际与下游通信的地址是127.0.0.1,所需建立的连接池很少,所以不会受该问题影响。后续我们会扫描所有使用MOARedisClient、但尚未接入Mesh的服务,推动升级MCF版本消除这一隐患。

其他改进项

MSC线程池中加入线程池满自动打印jstack的机制。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。