监视Rails进程内存泄漏的技巧

Rails应用比较容易遇到的两类性能问题:一类是Rails执行很慢,CPU消耗过高;另一类是Rails进程内存泄漏。解决这两类问题都需要你首先能够精确定位出现问题的代码,然后才知道如何对症下药。 

一、如何监控Rails进程的执行性能 

定位消耗CPU高,执行速度缓慢的Rails代码,是相当容易的事情,仅仅需要你对production.log做一点统计分析,抽取出来执行时间最长的请求,问题就昭然若揭了。由于production.log对Rails请求的执行时间做了详细的统计,例如: 

Ruby代码    收藏代码
  1. Completed in 0.00693 (144 reqs/sec) | Rendering: 0.00489 (70%) | DB: 0.00000 (0%) | 200 OK [http://www.iteye.com/]  
  2. Completed in 0.17238 (5 reqs/sec) | Rendering: 0.10011 (58%) | DB: 0.06244 (36%) | 200 OK [http://www.iteye.com/topic/49441?page=7]  
  3. Completed in 0.20508 (4 reqs/sec) | Rendering: 0.19373 (94%) | DB: 0.00645 (3%) | 200 OK [http://www.iteye.com/news/1586]  



所以我们只需要写一行shell命令,就搞定了!他把最耗时的前500个请求筛选出来,保存到timing.log里面。 

Ruby代码    收藏代码
  1. grep "200 OK" production.log | awk '{print "ALL: " $3 "  View: " $8 " DB: " $12 "  URL: " $17 }' \  
  2. | sort -r | head -n 500 > timing.log  



排序好的结果例如: 

Ruby代码    收藏代码
  1. ALL: 5.51774  View: 5.38277 DB: 0.13338  URL: [http://www.iteye.com/wiki/topic/131966]  
  2. ALL: 5.51316  View: 5.31300 DB: 0.19400  URL: [http://www.iteye.com/wiki/topic/145383]  
  3. ALL: 5.51311  View: 5.39321 DB: 0.11234  URL: [http://www.iteye.com/wiki/topic/160370]  
  4. ALL: 5.51135  View: 5.37604 DB: 0.12652  URL: [http://www.iteye.com/wiki/topic/233365]  
  5. ALL: 5.49881  View: 5.35998 DB: 0.10637  URL: [http://www.iteye.com/wiki/topic/265217]  



哪些请求执行的慢,一目了然。 当然除此之外,我们还可以实时监控,在top监视窗口显示Rails当前正在执行的请求URL 

二、如何监控Rails进程的内存泄漏 

监控CPU是很容易的事情,但要监控Rails进程的内存泄漏,却非常困难,原因在于production.log里面并没有记录进程的内存变化状况,甚至你找不到任何ruby API可以用来直接查询到进程使用的物理内存。实际上,要获取一个进程的物理内存是一个平台相关的操作,每个操作系统都会自己特定的API,并不通用,即使用C语言来编码,也不是一件容易的事情。 

不过对于Linux操作系统来说,我们有一个捷径可以获取进程的内存状况。Linux的/proc文件系统是内核的映象,/proc/进程pid/status 文件记录了这个进程的状态信息,例如: 

Ruby代码    收藏代码
  1. Name:   dispatch.fcgi  
  2. State:  S (sleeping)  
  3. SleepAVG:       135%  
  4. Tgid:   26645  
  5. Pid:    26645  
  6. PPid:   1  
  7. TracerPid:      0  
  8. Uid:    1002    1002    1002    1002  
  9. Gid:    100     100     100     100  
  10. FDSize: 64  
  11. Groups: 14 16 17 33 100   
  12. VmSize:   245680 kB  
  13. VmLck:         0 kB  
  14. VmRSS:    209104 kB  
  15. VmData:   205116 kB  
  16. VmStk:       824 kB  
  17. VmExe:       764 kB  
  18. VmLib:      4220 kB  
  19. Threads:        1  
  20. SigPnd: 0000000000000000  
  21. ShdPnd: 0000000000000000  
  22. SigBlk: 0000000000000000  
  23. SigIgn: 0000000000001000  
  24. SigCgt: 0000000002006e47  
  25. CapInh: 0000000000000000  
  26. CapPrm: 0000000000000000  
  27. CapEff: 0000000000000000  



注意第14行VmRSS,记录了该进程使用的常驻物理内存(Residence),这个就是该进程实际占用的物理内存了。因此只要我们读取该文件第14行,就可以得到内存信息。 

所以我们的任务变成了:在Rails处理请求之前记录内存,等Rails处理完请求之后,再记录内存,计算内存的变化状况,写入到production.log里面去。完成这个工作,只需要我们在Rails应用的app/controllers/application.rb里面添加几行代码: 

Ruby代码    收藏代码
  1.   around_filter :record_memory  
  2.   def record_memory  
  3.     process_status = File.open("/proc/#{Process.pid}/status")  
  4.     13.times { process_status.gets }  
  5.     rss_before_action = process_status.gets.split[1].to_i  
  6.     process_status.close  
  7.     yield  
  8.     process_status = File.open("/proc/#{Process.pid}/status")  
  9.     13.times { process_status.gets }  
  10.     rss_after_action = process_status.gets.split[1].to_i  
  11.     process_status.close  
  12.     logger.info("CONSUME MEMORY: #{rss_after_action - rss_before_action} \  
  13. KB\tNow: #{rss_after_action} KB\t#{request.url}")  
  14.   end  



我们定义了一个AroundFilter,记录一下处理请求前后的内存变化。有了这个信息,我们接下来的事情就简单了,只需要从production.log里面抽取出来这行log,进行统计分析就可以了,这也仅仅只需要一行shell就搞定了: 

Ruby代码    收藏代码
  1. grep "CONSUME MEMORY" production.log | grep -v "CONSUME MEMORY: 0" |  \  
  2.  grep -v "CONSUME MEMORY: -" |  awk '{print $3 "\t" $6 "\t" $8 }' | sort -r -n | \  
  3.  head -n 500 > memory.log   



抽取内存记录,去掉内存没有增加,去掉内存减少(发生了GC)的请求,然后对那些处理请求之后内存上升的记录进行排序,取出来前500条记录保存到memory.log里面,结果如下所示: 

Ruby代码    收藏代码
  1. 增加数 内存占用    请求URL  
  2. -----------------------------------------------  
  3. 9528  175264  http://www.iteye.com/topic/304594  
  4. 9524  129512  http://knityster.iteye.com/blog/172990  
  5. 9496  147544  http://www.iteye.com/forums/  
  6. 9492  197800  http://duyiwuer.iteye.com/rss  
  7. 9452  146668  http://www.iteye.com/forums  
  8. 9452  133844  http://wildlife.iteye.com/blog/47693  
  9. 9440  157824  http://www.iteye.com/rss/blogs  
  10. 9424  204664  http://www.iteye.com/wiki/topic/251964  
  11. 9384  142200  http://towerhe.iteye.com/blog/93704  
  12. 9380  165372  http://www.iteye.com/wiki/topic/77434  
  13. 9368  207460  http://superleo.iteye.com/rss  



第一列是访问了一个请求以后,Rails进程的内存上升了9MB多,第二列是处理完请求,Rails进程当前实际占了170多MB内存,第三列是处理了什么请求。 

根据这个统计结果,你可以很容易找出那些造成你Rails进程内存泄漏的罪魁祸首,哪些请求一访问你的Rails进程内存就飚升已经是一目了然的事情了,这是不是很简单?事实上通过这个办法,JavaEye仅用了半个多小时,就解决了曾经困扰了半年多的内存泄漏问题,办法虽土,却很有效!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值