python web服务器上打开的套接字数量不断增加

时间:2013-12-12 09:53:32

标签: python mongodb sockets flask tornado

我有一个简单的python网络服务器,它在2天/ 3天后仍然失败。经过调查,这是因为它达到了打开限制的文件数量。打开的文件描述符是套接字。 (ls -l /proc/pid/fd/xxx/proc/pid/fd/xxx -> socket:[yyyyy]

我可以增加ulimit,但我想知道发生了什么。

某些背景

  • 我有50台机器通过简单的POST id = machine_id,cpu_usage = xxx
  • 每小时向服务器报告它们已启动并运行
  • 服务器只是将其存储在数据库(mongodb)
  • 有一个监控内容的html页面,有一些jquery / get json可以为一个给定的机器制作cpu使用情况的图表
  • 在GET上有[(date,cpu_usage)]的处理程序?date_start,date_end,machine_id

我是唯一使用此页面的人,正如我所说,每小时只有50个请求随机分发到服务器

问题可能源于:

  • jquery的getjson打开了一个套接字并且从不关闭它(可能是但我不这么认为,因为我重新启动了服务器而没有进入监控页面)
  • python代码以及我在'main'中定义处理程序的方式
  • mongodb的
  • 我无法想到的其他地方

主要代码:

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()

处理程序的代码:

from flask import request

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

mongo db的代码:

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 webserver,所以当我定义处理程序时,如果每次都创建一个新套接字,我真的不知道引擎盖下发生了什么,如果它在结束时关闭,......

我首先有一个烧瓶服务器(这里是代码),然后转移到龙卷风(由一些龙卷风进口和一些app.run替换IOLoop.instance().start())但这导致了同样的问题

2 个答案:

答案 0 :(得分:1)

我在烧瓶和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()

Flask是单线程的,您的WSGI处理程序将生成所需数量的单个应用程序,因此您无需担心烧瓶级别的线程支持。

如果你真的想坚持mongo连接并且有性能原因,MongoClient支持重新连接的A​​utoReconnect异常,所以你不必自己处理它。

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'

答案 1 :(得分:0)

我们正在使用pymongo + flask + gunicorn,一切都很好。 pymongo将维护一个连接池,每个MongoClient实例都有一个内置的连接池。因此,如果您运行的mongoclient实例太多,可能会抱怨打开的文件太多。

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