2013-12-12 20 views
1

我有一個簡單的python web服務器,它在2天/ 3天后保持失敗。經過調查,這是因爲它達到了它打開文件數量的限制。打開的文件描述符是套接字。 (ls -l /proc/pid/fd/xxx/proc/pid/fd/xxx -> socket:[yyyyy]不斷增加的數量的python web服務器上打開的套接字

我可以增加ulimit,但我寧願弄清楚發生了什麼事情。

一些背景

  • 我有50臺機器可報告每隔一小時,他們是正常運行的服務器上,通過一個簡單的帖子ID = machine_id,cpu_usage = XXX
  • 服務器只存儲這在數據庫(MongoDB的)
  • 有一個HTML頁面來監測東西,用一些jQuery的/獲得JSON在
  • ,使CPU使用率的圖表給定的機器也給了[(日處理程序,cpu_usage)]在GET? DATE_START,DATE_END,machine_id

我用這個頁面只有一個,和我說,有僅50請求一個小時隨機分配到服務器

問題可能源於:

  • jquery的getjson打開一個套接字,並從不關閉它(可能是,但我不這樣認爲,因爲我重新啓動服務器,並沒有去監控頁面)
  • python代碼和我定義的處理程序的方式'main '
  • 的mongodb
  • 別的地方我想不出

碼主:

from flask import request 

class ListenerHandler: 
    def Post(self): 
    Save(request.form.get('machine_id'), request.form.get('cpu_usage')) 
    return 'ok' 

爲蒙戈DB代碼:爲處理程序

import listener_handler 
from flask import Flask 

if __name__ == '__main__': 
    app = Flask(__name__) 

    listener_handl = None 
    @app.route('/listener', methods=['POST']) 
    def listener(): 
    global listener_handl 
    if listener_handl is None: 
     listener_handl = listener_handler.ListenerHandler() 
    return listener_handl.Post() 

    ... (other handlers for the getjson and the static monitoring page) 

    app.run() 

代碼

import pymongo 

mongo_client = pymongo.MongoClient() 
mongo_db = mongo_client.stations_monitoring 

def Save(machine_id, cpu_usage): 
    mongo_db.db['monitoring'].save({'machine': machine_id, 'cpu': cpu_usage}) 

我試圖保持代碼輕量級,我有很好的python經驗,但幾乎沒有與python web服務器,所以我真的不知道什麼是在我定義處理程序時,如果每次創建一個新的套接字,如果在年底關閉,...

我第一次有一個燒瓶服務器(此處代碼)然後轉移到龍捲風(由少數龍捲風進口和一些IOLoop.instance().start()更換app.run),但是這導致了同樣的問題

+0

可能是愚蠢的問題,但這些pymongo連接的mongod被掛的過程?不是你的過程的另一部分? –

+0

你說得對,我前段時間解決了部分問題。套接字與mongodb無關。它位於遠程計算機(客戶端)和服務器之間。客戶建立連接,但其互聯網連接蹩腳,所以它永遠掛起。在curl上添加超時幫助。但我不知道爲什麼Flask或龍捲風不處理這個問題? – Thomas

回答

0

我在燒瓶和pymongo之間的問題完全一樣;我通過清理每個請求來解決它。如果您沒有性能方面的原因讓MongoClient句柄處於打開狀態,那麼最好關閉它。

http://api.mongodb.org/python/current/api/pymongo/mongo_client.html#pymongo.mongo_client.MongoClient.disconnect

import pymongo 
class MongoConnector: 
    def __init__(self): 
     client = pymongo.MongoClient() 
     self.db = client.stations_monitoring 
    def close(self): 
     self.db.disconnect() 

def Save(machine_id, cpu_usage): 
    mongoConnector = MongoConnector() 
    mongoConnector.db['monitoring'].save({'machine': machine_id, 'cpu': cpu_usage}) 
    mongoConnector.close() 

瓶是單線程的,你的WSGI處理程序將產生您所需的各個應用程序的數量,所以你不必擔心在燒瓶級線程支持。

如果你真的想保持mongo連接並有性能的原因這樣做,MongoClient支持AutoReconnect異常與重新連接,所以你不應該自己處理它。

import pymongo 
from pymongo.errors import AutoReconnect 

class MongoConnector: 
    def __init__(self): 
     client = pymongo.MongoClient() 
     self.db = client.stations_monitoring 
    def close(self): 
     self.db.disconnect() 

mongoConnector = MongoConnector() 
def Save(machine_id, cpu_usage): 
    try: 
     mongoConnector.db['monitoring'].save({'machine': machine_id, 'cpu': cpu_usage}) 
    except AutoReconnect: 
     #should be reconnected now 
     mongoConnector.db['monitoring'].save({'machine': machine_id, 'cpu': cpu_usage}) 

[編輯]不知道爲什麼你的工作沒有。嘗試簡化你正在做的事情。如果你沒有理由讓你的獲得者,只是簡單。

testflask.py

from flask import Flask, request 
import pymongo 

app = Flask(__name__) 

def SaveLog(machine_id, cpu_usage): 
    mc = pymongo.MongoClient() 
    db = mc.stations_monitoring 
    db['monitoring'].save({'machine': machine_id, 'cpu': cpu_usage}) 
    mc.disconnect() 

@app.route('/listener', methods=['POST', 'GET']) 
def listener(): 
    SaveLog(request.form.get('machine_id'), request.form.get('cpu_usage')) 
    return 'ok' 

if __name__ == '__main__': 
    app.run() 

test_get.py錘請求的服務器。礦能做到〜50/s的

import requests 
from random import randint 

while True: 
    r = requests.get('http://localhost:5000/listener?machine_id=%s&cpu_usage=%s' %(randint(1,10000), randint(1,100))) 
    print r.text 

驗證FDS(我徘徊5-10打開的文件句柄)

ps aux | grep testflask.py | grep -v grep | awk '{print $2}' | xargs -I @ bash -c 'ls -l /proc/@/fd/ | wc -l' 
+0

非常感謝。可以肯定的是,你想在'try/except AutoReconnect'後面調用close(),對吧?但是如果你這樣做,它是如何堅持性能連接的?如果你不這樣做,它如何解決關閉插座的問題?我不知道文檔的內容 – Thomas

+0

不,第二個例子顯示了我將如何保存一個MongoClient對象,mongoConnector需要在燒瓶過程中永久打開(永遠),並且讓MongoClient類爲連接重新連接您。在我的情況中,我使用第一個簡單地打開和關閉MongoClient句柄的例子,因爲我沒有性能問題,需要我堅持連接。 –

+0

我看到你在說什麼,你只能看到打印新的連接在啓動時發生一次? –

0

我們使用pymongo +瓶+ gunicorn,並且一切正常。 pymongo將維護一個連接池,每個MongoClient實例都有一個內置的連接池。所以,如果你有太多的運行mongoclient實例,它可能會抱怨打開的文件太多。

how-does-connection-pooling-work-in-pymongo

+0

感謝您的鏈接。他們建議只打開一個客戶端,但這是我做的第一件事(檢查mongo_client是否爲無),並且我有錯誤。我再次開始使用文檔中給出的max_pool_size等參數,並會看到... – Thomas

+1

您可以分享如何使用pymongo的代碼,打開客戶端,執行查找或保存......?我一直堅持這個爲期2周,但仍然無法弄清楚,謝謝 – Thomas

相關問題