最近这一两周其实打击挺大的,因为这段日子笔者每天都花大量时间在优化回流的服务端系统。精力都集中在减少N+1查询上,几乎把常用接口的N+1问题都解决掉了,慢查询也优化到所剩无几。然而,系统的性能还是不稳定,每到峰值时期,响应时间犹如脱缰的野马径直往上飙。

每次优化掉一些耗时较长的请求,都会信誓旦旦地跟同事说“今天应该不卡了”,然而每次到了高峰期,总会啪啪打脸,有大量响应时间400ms以上的请求不断冒出来,按都按不住,整个系统几乎处于不可用的状态。老实说以前总觉得响应时间500-600ms没什么的,但是最近看到大量请求的响应时间超过300ms的时候,血压也会跟着升高。

Screenshot 2023-04-03 at 07.49.17.png

优化进入到了死胡同,笔者也很纳闷,数据库慢查询几乎没多少了,CPU的使用率也不是很高,内存也一直在5 ~ 8G之间徘徊,按理说我这8核16G的机器资源还有很大的盈余,升级机器也解决不了什么问题,实在是不知道性能瓶颈在哪。

一筹莫展之际,笔者突发奇想,不知道是不是Puma的Worker不够,CPU利用率不够高导致的?看了一下现在的配置是

threads 0,32
workers 4

而且就在puma.rb这个配置文件里面,笔者还发现了这样的代码片段。

before_fork do
  require 'puma_worker_killer'
  PumaWorkerKiller.config do |config|
    config.ram           = 5120 # mb
    config.frequency     = 5    # seconds
    config.percent_usage = 0.98
    config.rolling_restart_frequency = 6 * 3600 # 12 hours in seconds, or 12.hours if using Rails
    config.pre_term = -> (worker) { puts "Worker #{worker.inspect} being killed" }
    config.rolling_pre_term = -> (worker) { puts "Worker #{worker.inspect} being killed by rolling restart" }
  end
  PumaWorkerKiller.start
end

看到这段话的时候

before_fork do
  ...
  config.ram           = 5120 # mb
end

真的是茅舍顿开,并不是回流服务多省内存,有这个配置在难怪内存使用一直在5G ~ 8G这个区间徘徊。因为回流前期比较穷,笔者都在尽可能节约服务器成本,Ruby又是出了名吃内存的主,所以引入了PumaWorkerKiller来强行让puma的内存使用限制在5G左右的水平(当时机器总内存是8G),适当的时候kill掉一些线程来释放内存。

在这些配置的干扰下,客户量上来了,CPU跟内存却还一直停留在较低水平。更要命的是配置不在代码库中,往往很难察觉到它们的存在。

马上把内存限制扩大到10G

before_fork do
  ....
  config.ram           = 10240 # mb
  ....
end

Worker数量也调整大一点,跟CPU的核数对应上

threads 0,32
workers 8

经历过两周优化的挫折,笔者已经不再对自己的优化手段报有什么期望了,每次都是期望越大失望越大。然而万万没想到,这次优化效果立竿见影,原来500ms的指标一下子就跌到了正常水平了。

Screenshot 2023-04-03 at 07.51.51.png

我是想过会有改善,就是没想到改善这么明显....当初为了节省费用而编写的配置代码,如今却成了性能瓶颈。后来笔者有把Worker数量调整回4个,发现单单提升了内存的限制,系统性能也会有所提升,但不够明显,还是要上管齐下才行,看来worker数量跟CPU的核数一致会是比较合适的选择。

Puma的维护者也建议我们要自己多尝试

Feel free to experiment, but be careful not to set the number of maximum threads to a large number, as you may exhaust resources on the system (or cause contention for the Global VM Lock, when using MRI).

我喜欢这种顶着限速跑的感觉:

15491680245269_.pic.jpg

人是容易先入为主的动物,特别是在一个地方呆久了,往往会“如入鲍鱼之肆,久而不闻其臭”。以往的性能问题大多都出现在数据库查询中,笔者也下意识地花了很多精力去做数据库调优,却是万万没想到,这次的性能瓶颈出现在平时关注最少的CPU跟内存上。

毕竟从指标上看CPU跟内存有很大的盈余,却没想到这表面的“盈余”是人为限制的结果。无论是对待Bug还是系统性能瓶颈,适当跳出来以局外人的身份审视说不定会有意想不到的效果。这次误打误撞把性能问题解决掉了,也算是运气好吧。