生活随笔
收集整理的這篇文章主要介紹了
监视Rails进程内存泄漏的技巧
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
Rails應(yīng)用比較容易遇到的兩類性能問(wèn)題:一類是Rails執(zhí)行很慢,CPU消耗過(guò)高;另一類是Rails進(jìn)程內(nèi)存泄漏。解決這兩類問(wèn)題都需要你首先能夠精確定位出現(xiàn)問(wèn)題的代碼,然后才知道如何對(duì)癥下藥。?
一、如何監(jiān)控Rails進(jìn)程的執(zhí)行性能?
定位消耗CPU高,執(zhí)行速度緩慢的Rails代碼,是相當(dāng)容易的事情,僅僅需要你對(duì)production.log做一點(diǎn)統(tǒng)計(jì)分析,抽取出來(lái)執(zhí)行時(shí)間最長(zhǎng)的請(qǐng)求,問(wèn)題就昭然若揭了。由于production.log對(duì)Rails請(qǐng)求的執(zhí)行時(shí)間做了詳細(xì)的統(tǒng)計(jì),例如:?
Ruby代碼
??
Completed?in?0.00693?(144?reqs/sec)?|?Rendering:?0.00489?(70%)?|?DB:?0.00000?(0%)?|?200?OK?[http://www.iteye.com/]??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]??Completed?in?0.20508?(4?reqs/sec)?|?Rendering:?0.19373?(94%)?|?DB:?0.00645?(3%)?|?200?OK?[http://www.iteye.com/news/1586]??
所以我們只需要寫一行shell命令,就搞定了!他把最耗時(shí)的前500個(gè)請(qǐng)求篩選出來(lái),保存到timing.log里面。?
Ruby代碼
??
grep?"200?OK"?production.log?|?awk?'{print?"ALL:?"?$3?"??View:?"?$8?"?DB:?"?$12?"??URL:?"?$17?}'?\??|?sort?-r?|?head?-n?500?>?timing.log??
排序好的結(jié)果例如:?
Ruby代碼
??
ALL:?5.51774??View:?5.38277?DB:?0.13338??URL:?[http://www.iteye.com/wiki/topic/131966]??ALL:?5.51316??View:?5.31300?DB:?0.19400??URL:?[http://www.iteye.com/wiki/topic/145383]??ALL:?5.51311??View:?5.39321?DB:?0.11234??URL:?[http://www.iteye.com/wiki/topic/160370]??ALL:?5.51135??View:?5.37604?DB:?0.12652??URL:?[http://www.iteye.com/wiki/topic/233365]??ALL:?5.49881??View:?5.35998?DB:?0.10637??URL:?[http://www.iteye.com/wiki/topic/265217]??
哪些請(qǐng)求執(zhí)行的慢,一目了然。 當(dāng)然除此之外,我們還可以實(shí)時(shí)監(jiān)控,在top監(jiān)視窗口顯示Rails當(dāng)前正在執(zhí)行的請(qǐng)求URL。?
二、如何監(jiān)控Rails進(jìn)程的內(nèi)存泄漏?
監(jiān)控CPU是很容易的事情,但要監(jiān)控Rails進(jìn)程的內(nèi)存泄漏,卻非常困難,原因在于production.log里面并沒(méi)有記錄進(jìn)程的內(nèi)存變化狀況,甚至你找不到任何ruby API可以用來(lái)直接查詢到進(jìn)程使用的物理內(nèi)存。實(shí)際上,要獲取一個(gè)進(jìn)程的物理內(nèi)存是一個(gè)平臺(tái)相關(guān)的操作,每個(gè)操作系統(tǒng)都會(huì)自己特定的API,并不通用,即使用C語(yǔ)言來(lái)編碼,也不是一件容易的事情。?
不過(guò)對(duì)于Linux操作系統(tǒng)來(lái)說(shuō),我們有一個(gè)捷徑可以獲取進(jìn)程的內(nèi)存狀況。Linux的/proc文件系統(tǒng)是內(nèi)核的映象,/proc/進(jìn)程pid/status 文件記錄了這個(gè)進(jìn)程的狀態(tài)信息,例如:?
Ruby代碼
??
Name:???dispatch.fcgi??State:??S?(sleeping)??SleepAVG:???????135%??Tgid:???26645??Pid:????26645??PPid:???1??TracerPid:??????0??Uid:????1002????1002????1002????1002??Gid:????100?????100?????100?????100??FDSize:?64??Groups:?14?16?17?33?100???VmSize:???245680?kB??VmLck:?????????0?kB??VmRSS:????209104?kB??VmData:???205116?kB??VmStk:???????824?kB??VmExe:???????764?kB??VmLib:??????4220?kB??Threads:????????1??SigPnd:?0000000000000000??ShdPnd:?0000000000000000??SigBlk:?0000000000000000??SigIgn:?0000000000001000??SigCgt:?0000000002006e47??CapInh:?0000000000000000??CapPrm:?0000000000000000??CapEff:?0000000000000000??
注意第14行VmRSS,記錄了該進(jìn)程使用的常駐物理內(nèi)存(Residence),這個(gè)就是該進(jìn)程實(shí)際占用的物理內(nèi)存了。因此只要我們讀取該文件第14行,就可以得到內(nèi)存信息。?
所以我們的任務(wù)變成了:在Rails處理請(qǐng)求之前記錄內(nèi)存,等Rails處理完請(qǐng)求之后,再記錄內(nèi)存,計(jì)算內(nèi)存的變化狀況,寫入到production.log里面去。完成這個(gè)工作,只需要我們?cè)赗ails應(yīng)用的app/controllers/application.rb里面添加幾行代碼:?
Ruby代碼
??
??around_filter?:record_memory????def?record_memory??????process_status?=?File.open("/proc/#{Process.pid}/status")??????13.times?{?process_status.gets?}??????rss_before_action?=?process_status.gets.split[1].to_i??????process_status.close??????yield??????process_status?=?File.open("/proc/#{Process.pid}/status")??????13.times?{?process_status.gets?}??????rss_after_action?=?process_status.gets.split[1].to_i??????process_status.close??????logger.info("CONSUME?MEMORY:???KB\tNow:?????end??
我們定義了一個(gè)AroundFilter,記錄一下處理請(qǐng)求前后的內(nèi)存變化。有了這個(gè)信息,我們接下來(lái)的事情就簡(jiǎn)單了,只需要從production.log里面抽取出來(lái)這行l(wèi)og,進(jìn)行統(tǒng)計(jì)分析就可以了,這也僅僅只需要一行shell就搞定了:?
Ruby代碼
??
grep?"CONSUME?MEMORY"?production.log?|?grep?-v?"CONSUME?MEMORY:?0"?|??\???grep?-v?"CONSUME?MEMORY:?-"?|??awk?'{print?$3?"\t"?$6?"\t"?$8?}'?|?sort?-r?-n?|?\???head?-n?500?>?memory.log???
抽取內(nèi)存記錄,去掉內(nèi)存沒(méi)有增加,去掉內(nèi)存減少(發(fā)生了GC)的請(qǐng)求,然后對(duì)那些處理請(qǐng)求之后內(nèi)存上升的記錄進(jìn)行排序,取出來(lái)前500條記錄保存到memory.log里面,結(jié)果如下所示:?
Ruby代碼
??
增加數(shù)?內(nèi)存占用????請(qǐng)求URL??-----------------------------------------------??9528??175264??http://www.iteye.com/topic/304594??9524??129512??http://knityster.iteye.com/blog/172990??9496??147544??http://www.iteye.com/forums/??9492??197800??http://duyiwuer.iteye.com/rss??9452??146668??http://www.iteye.com/forums??9452??133844??http://wildlife.iteye.com/blog/47693??9440??157824??http://www.iteye.com/rss/blogs??9424??204664??http://www.iteye.com/wiki/topic/251964??9384??142200??http://towerhe.iteye.com/blog/93704??9380??165372??http://www.iteye.com/wiki/topic/77434??9368??207460??http://superleo.iteye.com/rss??
第一列是訪問(wèn)了一個(gè)請(qǐng)求以后,Rails進(jìn)程的內(nèi)存上升了9MB多,第二列是處理完請(qǐng)求,Rails進(jìn)程當(dāng)前實(shí)際占了170多MB內(nèi)存,第三列是處理了什么請(qǐng)求。?
根據(jù)這個(gè)統(tǒng)計(jì)結(jié)果,你可以很容易找出那些造成你Rails進(jìn)程內(nèi)存泄漏的罪魁禍?zhǔn)?#xff0c;哪些請(qǐng)求一訪問(wèn)你的Rails進(jìn)程內(nèi)存就飚升已經(jīng)是一目了然的事情了,這是不是很簡(jiǎn)單?事實(shí)上通過(guò)這個(gè)辦法,JavaEye僅用了半個(gè)多小時(shí),就解決了曾經(jīng)困擾了半年多的內(nèi)存泄漏問(wèn)題,辦法雖土,卻很有效!
總結(jié)
以上是生活随笔為你收集整理的监视Rails进程内存泄漏的技巧的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。