一个读阻塞问题的思维盲区反思

问题

今天在公司内部论坛看到一个小兄弟分享个因 Redis 下线且 Jedis 配置错误导致了应用线程阻塞、进而导致 APIG 阻塞的连锁事故。这位小兄弟刨根问底的工程师精神引起了自己的思考。 他用的服务使用了常见的 redis+db 的部署方式,本预期在 redis 不可用情况下,请求能分流 db 进行读操作,但实际并没有。因为错误地配置了如下配置:

blockWhenExhausted = true (获取连接资源失败时一直阻塞等待,This controls behavior when a thread asks for a connection, but there aren’t any that are free and the pool can’t create more (due to maxTotal). If set to true, the calling thread will block for maxWaitMillis before throwing an exception. )

maxWaitMillis = -1(阻塞等待,永不超时,How long to wait in milliseconds if calling JedisPool.getResource() will block. The default is -1, which means block indefinitely. I would set this to the same as the socketTimeout configured. Related to blockWhenExhausted.)

这样就导致了 App 所有上来的 read 请求到阻塞了,示意图如下:

img_2.png

解决办法: buffer

前段时间正好和一个前阿里的同学聊到淘宝是如何解决海量买家页面读数据的问题,你会发现这些都是同一类阻塞的问题,包括为什么要做 DB 的主从分离、TCP 滑动窗口下的 Recv-Q 队列设计等。这里我们只讨论阻塞读,不讨论阻塞写。

在生活中,这种问题也普遍存在,如等红绿灯,排队收银等,举个我切身感受的例子,在我们食堂摆有抽纸台,供大家饭后使用,抽纸台旁边有个垃圾桶,中午高峰期时,大家经常积压在抽纸台附近,因为用完后要把纸巾扔进垃圾桶,可大家都挤在那里了,因为垃圾桶离抽纸台太近了,你看,这就是阻塞的问题,至今食堂还没解决,如果把垃圾桶放远一点,就会顺畅了。

img_3.png

解决这类问题,总结起来,思路就是增加 buffer 缓存,只不过缓存加到哪里,加多少,什么类型。DB 的缓存加到 redis,淘宝数据的缓存加到对应 Region 里,缓冲起了3个作用:

  1. 缓冲,让突发的流量有一定的缓冲期,高峰期过后就能立马处理,防止抖动,不会马上异常,提高了处理的平滑性(但始终没法解决无限海量的问题,不过这也是个伪命题)
  2. 批量,当高峰期处理过后,能够一次性从缓冲区处理一批请求,提高吞吐率,比如 TCP 一次性从 Recv-Q 缓冲队列中做一次性读就是这个思路
  3. 分流,多个 buffer 层为热点数据的一份拷贝,供请求者读,比如 Redis 的作用

很多设计都是使用了这种思路,这里的 buffer 也不一定指数据队列,缓冲时间、缓冲距离我认为也属于一种 buffer 设计。

回到 Jedis 问题,你以为就结束了吗?

回到这个问题,你肯定会想,把 maxWaitMillis 配置一定的缓冲时间,防止无限等待,让应用请求失败就可以了,如果这样,可能你错了,这位小兄弟考虑这样一个场景,这也是触发我们思考的地方:

img_4.png

  1. 阻塞等待时间配置了 maxWaitMillis = 10s,10s 后超时报错
  2. 当前依旧有大量请求并发过来
  3. 因当前大量请求阻塞了 10s,导致了 APIG、App 依旧阻塞,问题并没有得到解决

因 10s 的等待也暂用了 App 的线程资源导致还是有大量阻塞,只不过 10s 缓解了问题程度,但因资源占用,依旧有很大的 panic 风险,问题没有得到解决。

进一步方案

小兄弟发现了问题,给了一个方案,他使用了一个 monit 守护线程,定时去尝试连接 redis,如果监测连接异常数达到一定阈值就配置 blockWhenExhausted = false,让 jedis 立马返回报错,防止阻塞扩大,否则继续保持 blockWhenExhausted = true,保持缓冲,用于抖动的场景。这个办法不一定是最优雅的,但也是个办法,我鼓励小兄弟给社区去提个 issue,让社区用更自然的代码解决 ;)

总结反思

从一个小的技术问题,让我反思,看似简单理所当然的问题未必简单,反而这种认为”理所当然”成为了我们思维的弊端和盲区,容易忽略问题背后的问题,让我们侃侃而谈,做技术还是需要一些工程师精神,往往看似平常的东西更要多一份思考,共勉!