2015-11-03 83 views
0

我想診斷爲什麼我的Python服務器應用程序泄漏內存。該應用程序接受圖片的請求url使用Vips調整其大小並返回圖片。在每次請求之後,內存使用量會隨着原始圖像的大小而大致增長。Python服務器應用程序泄漏內存

from fapws import base 
import fapws._evwsgi as evwsgi 
from gi.repository import Vips 
import urllib2 
import hmac 
import hashlib 
import base64 
import StringIO 
from boto.s3.connection import S3Connection 
from boto.s3.bucket import Bucket 

def start(): 
    evwsgi.start('0.0.0.0', '80') 
    evwsgi.set_base_module(base) 

    def lfrThumbnail(environ, start_response): 
     try: 
      parameters = environ['PATH_INFO'].split('/') 
      s3File = 'my s3 url' + parameters[0] 
      width = float(parameters[1]) 
      height = float(parameters[2]) 
      hmacSignatureUser = parameters[3] 

      hmacSignature = some hasing code... 

      if not (hmacSignatureUser == hmacSignature): 
       print hmacSignatureUser 
       print hmacSignature 
       print hmacSignatureUser == hmacSignature 
       raise Exception 

      bufferedImage = urllib2.urlopen(s3File).read() 
      image = Vips.Image.new_from_buffer(bufferedImage, '') 

      imageWidth = float(image.width) 
      imageHeight = float(image.height) 
      imageAspectRatio = imageWidth/imageHeight 
      if (width > imageWidth) or (height > imageHeight): 
       image = image 
      elif abs((imageAspectRatio/(width/height)) - 1) < 0.05: 
       image = image.resize(width/imageWidth) 
      else: 
       scaleRatioWidth = width/imageWidth 
       scaleRatioHeight = height/imageHeight 
       maxScale = max(scaleRatioWidth, scaleRatioHeight) 
       image = image.resize(maxScale) 
       cropStartX = (image.width - width)/2 
       cropStartY = (image.height - height)/2 
       image = image.crop(cropStartX, cropStartY, width, height) 

     except Exception, e: 
      start_response('500 INTERNAL SERVER ERROR', [('Content-Type','text')]) 
      return ['Error generating thumbnail'] 

     start_response('200 OK', [ 
      ('Content-Type','image/jpeg'), 
      ('Cache-Control: max-stale', '31536000') 
     ]) 
     return [image.write_to_buffer('.jpg[Q=90]')] 

    evwsgi.wsgi_cb(('/lfr/', lfrThumbnail)) 

    evwsgi.set_debug(0) 
    evwsgi.run() 

if __name__ == '__main__': 
    start() 

我使用muppy,該pympler跟蹤但是每個DIFF嘗試後的圖像打開/關閉操作顯示正在使用的只有幾個字節。

外部C庫可能導致內存泄漏嗎?如果是這樣,那麼如何調試呢。

如果它是什麼我跑碼頭工人,容器

回答

3

內的蟒蛇服務器相關的我的libvips維護者。這聽起來像是vips操作緩存:vips將最後幾項操作保留在內存中,如果可能的話,重新使用結果。在某些情況下,這可能是一個巨大的表現。

對於Web服務,您可能在其他地方緩存,因此您不會希望這樣做,或者至少不需要大型緩存。你可以用vips_cache_set_max()和朋友控制緩存大小:

http://www.vips.ecs.soton.ac.uk/supported/current/doc/html/libvips/VipsOperation.html#vips-cache-set-max

從Python的是:

Vips.cache_set_max(0) 

要完全關閉緩存。您可以將緩存設置爲通過內存使用,文件描述符使用或操作次數進行限制。

您還可以設置其他一些有用的東西來觀察資源使用情況。 Vips.leak_set(True)使vips在退出時報告泄漏對象,並報告峯值像素緩衝區內存使用情況。 Vips.cache_set_trace(True)使它跟蹤所有被調用的操作,並顯示緩存命中。

在你的代碼中,我也會啓用順序模式。將access = Vips.Access.SEQUENTIAL添加到您的new_from_buffer()

默認行爲是打開圖像進行完全隨機訪問(因爲vips不知道最終在圖像上運行的操作)。對於像JPG這樣的東西,這意味着vip會在打開時將圖像解碼爲大型未壓縮數組。如果圖像小於100MB,它會將這個數組保存在內存中。

但是,對於簡單的調整大小,您只需要從頂部到底部訪問像素,以便您可以在打開時提示順序訪問。在這種模式下,vips將只能從輸入中一次解壓縮幾條掃描線,並且永遠不會保留整個未壓縮的圖像。你應該看到內存使用和延遲的降低。

還有很多其他的東西可以處理,比如exif autorotate,色彩管理,透明度,jpeg縮小加載等等,我相信你知道。到vipsthumbnail來源可能是一個有用的參考:

https://github.com/jcupitt/libvips/blob/master/tools/vipsthumbnail.c

+0

使用Vips.leak_set(真),貴賓只報告當服務器停止峯值內存使用量,這也是Python腳本退出時。我應該在處理請求後關閉vip嗎? – Nicholas

+1

我會讓vips運行,您將節省啓動/關閉時間。峯值內存報告對於測試非常方便,在生產中不是非常有用。 C API有一個能夠獲得當前高峯內存的東西,但它不會暴露給Python,我會添加它。libvips有一個相當大的測試套件,全部用Python編寫,其中一個測試是「整個測試套件是否完整,沒有內存泄漏?」,所以希望你不應該在服務器中看到泄漏,而且更多的是再檢查一遍。 – user894763