分享 探讨如何统计 Ruby 应用服务器使用内存方法

rubyist518 · 2015年10月14日 · 最后由 rubyist518 回复于 2015年10月14日 · 2680 次阅读

最近在解决探针获取 Ruby 应用服务器的内存使用的情况,将解决的思路总结一下,希望对此感兴趣的伙伴一起探讨。

先对比应用服务器:PumaPassenger,下面对比这 2 个服务器内存统计,

Puma (2.13.4)

单进程模式:直接获取进程 id: Process.pid

memory = `ps -o rss= #{Process.pid}`.to_f / 1024 #单位:MB

cluster 模式:以启动 2 个 worker 进程为例: Puma cluster 从上面截图可以看到,Puma 启动后会出现 3 个进程:1 个 master 进程和 2 个 worker 进程。 内存的使用情况 (见RSS列):

(109908 + 109868 + 7256 ).to_f / 1024 = 221.7109375 #单位:MB

而对于探针来说,一个探针实例是伴随进程一起启动的,也就说一个探针只能识别自己所在的进程 id,那如何获取应用服务器使用的内存?我们用其中 1 个 woker 进程所在的进程组 [PGID] 看一下:(为啥不是父进程?, 见下文 Passenger) puma 2 这 3 个进程都在相同的进程组里,而且进程组号为 master 的进程 id,那我们就可以用这个信息获取应用服务器的所使用的内存:

  1. 得到 1 个 worker 进程 id: Process.pid
  2. 获取所在进程组:Process.getpgrp
  3. 获取到进程组内所有的进程: ruby `ps -o pid,pgid -e | grep -w "#{pgrp_id}"`.split(/\s+/).uniq 4.累加进程组内进程内存和即为应用服务器使用内存: ruby pids.inject(0.0){|m, pid| m + memory(pid)} ## Passenger (5.0.20) 启动 Passenger 后的 Process 信息: passenger 1 对 Passenger 架构感兴趣的请移步到这儿. 查看一下 worker 所在进程组和父进程: passenger 2 通过 PPID 可以看出 Passenger core —> Passenger AppPreloader —> Passenger RubyApp 三者为爷 - 父-子关系,当服务器请求量增大时AppPreloader会产生新的进程来响应请求,从而新的RubyApp进程的PPID即为AppPreloaderPID,这样看来就可以将同一个PPID的进程加起来得到应用服务器的内存?

由于 Passenger 会根据服务器的负载量动态调整进程数,当服务器请求量较小时,Passenger 会 kill 多余的进程,会出现下面的情况: pssenger 3

AppPreloader也被 Passenger 杀掉了。原RubyApp进程的PPID变成了 1。这时如果服务器的请求量增大,应用服务器进程会成为这样: passener 4

Passenger core 产生新的AppPreloader进程,并且AppPreloader产生新的RubyApp进程,这时如果只用PPID统计应用服务器内存就会不准确,所以要统计 Passenger 的使用的内存还得通过累加在同一个进程组 (PGID) 的所有进程使用的内存和得到。

由于UnicornRainbows都与 Puma 的 cluster 模式 [master+worker 模式] 类似,内存统计的方式可以参考上文的 Puma。

总结:

由于Thin启动多个 server 后没有类似的特点,上面方法不适用于 Thin,有好方法的伙伴们可以告知😄

在解决探针统计应用服务器的内存问题上,摸索出了上面的一条路子,如果小伙伴们有其他更好的方式,可以一起探讨一下。

10 月 15 日更新

对于Thin,有一种思路是使用foreman来管理应用进程,这样就可以统一计算应用服务器内存方法。 Thin 欢迎拍砖。

满满的干货啊

图看不到,上传到 Ruby China 吧

@huacnlee 谢谢,重新上传了图片。

做内存监控的话,推荐通过文件来读取: /proc/pid/status 比通过调用系统命令 ps 性能会好一些,而且还能统计更多信息,比如 VmRSS / VmHWM / VmPeak 等等

@quakewang,是的 对于 linux 系统使用查看系统进程文件的方式是更便捷的方式。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号