龙卷风 [Errno 24] 打开的文件太多

6
我们正在RedHat操作系统上运行Tornado 3.0服务,遇到以下错误:
[E 140102 17:07:37 ioloop:660] Exception in I/O handler for fd 11
    Traceback (most recent call last):
      File "/usr/local/lib/python2.7/dist-packages/tornado/ioloop.py", line 653, in start
        self._handlers[fd](fd, events)
      File "/usr/local/lib/python2.7/dist-packages/tornado/stack_context.py", line 241, in wrapped
        callback(*args, **kwargs)
      File "/usr/local/lib/python2.7/dist-packages/tornado/netutil.py", line 136, in accept_handler
        connection, address = sock.accept()
      File "/usr/lib/python2.7/socket.py", line 202, in accept
    error: [Errno 24] Too many open files

但我们无法弄清楚它的含义。
我们的Tornado代码如下:
import sys
from tornado.ioloop import IOLoop
from tornado.options import parse_command_line, define, options
from tornado.httpserver import HTTPServer
from tornado.netutil import bind_sockets
import tornado
sys.path.append("..")

from tornado.web import  RequestHandler, Application
from shared.bootstrap import *
from time import time
from clients import ClientFactory

from shared.configuration   import Config
from shared.logger          import Logger

from algorithms.neighborhood.application import NeighborhoodApplication
import traceback

define('port', default=8000, help="Run on the given port", type=int)
define('debug', default=True, help="Run application in debug mode", type=bool)

class WService(RequestHandler):

    _clients = {}

    def prepare(self):
        self._start_time = time()
        RequestHandler.prepare(self)

    def get(self, algorithm = None):

        self.add_header('Content-type', 'application/json')

        response = {'skus' : []}

        algorithm = 'neighborhood' if not algorithm else algorithm

        try:
            if not algorithm in self._clients:
                self._clients[algorithm] = ClientFactory.get_instance(algorithm)

            arguments = self.get_arguments_by_client(self._clients[algorithm].get_expected_arguments())

            response['skus'] = app.get_manager().make_recommendations(arguments)
            self.write(response)

        except Exception as err:
            self.write(response)
            error("Erro: " + str(err))

    def get_arguments_by_client(self, expected_arguments):
        arguments = {}
        for key in expected_arguments:
            arguments[key] = self.get_argument(key, expected_arguments[key])

        return arguments

    def on_connection_close(self):
        self.finish({'skus':[]})
        RequestHandler.on_connection_close(self)

    def on_finish(self):
        response_time = 1000.0 *(time() - self._start_time)
        log("%d %s %.2fms" % (self.get_status(), self._request_summary(), response_time))
        RequestHandler.on_finish(self)

def handling_exception(signal, frame):
    error('IOLoop blocked for %s seconds in\n%s\n\n' % ( io_loop._blocking_signal_threshold, ''.join(traceback.format_stack(frame)[-3:])))

if __name__ == "__main__":

    configuration = Config()
    Logger.configure(configuration.get_configs('logger'))

    app = NeighborhoodApplication({
                                   'application': configuration.get_configs('neighborhood'),
                                   'couchbase':   configuration.get_configs('couchbase'),
                                   'stock':       configuration.get_configs('stock')
                                   })
    app.run()
    log("Neighborhood Matrices successfully created...")
    log("Initiating Tornado Service...")

    parse_command_line()

    application = Application([
                               (r'/(favicon.ico)', tornado.web.StaticFileHandler, {"path": "./images/"}),
                               (r"/(.*)", WService)
                               ], **{'debug':options.debug, 'x-headers' : True})

    sockets = bind_sockets(options.port, backlog=1024)
    server = HTTPServer(application)
    server.add_sockets(sockets)

    io_loop = IOLoop.instance()
    io_loop.set_blocking_signal_threshold(.05, handling_exception)
    io_loop.start()

这是一个非常基础的脚本,它获取URL并在make_recommendation函数中处理后发送响应。我们尝试通过io_loop.set_blocking_signal_threshold函数将龙卷风超时设置为50毫秒,因为有时处理URL可能需要这么长时间。
该系统每分钟接收大约8000个请求,并且在大约30分钟内运行良好,但之后开始抛出“文件过多错误”并崩溃。一般情况下,请求需要大约20毫秒才能被处理,但当错误开始发生时,所需时间突然增加到几秒钟。
我们试图查看端口8000有多少连接,发现有几个打开的连接都处于“已建立”状态。
我们的Tornado脚本是否存在问题?我们认为我们的超时函数没有正常工作,但就目前我们所研究的来看,一切似乎都很正常。
如果您需要更多信息,请告诉我。
提前致谢,

@CDspace 我们已经阅读了,但无法将答案与我们的情况融合。看起来我们的实现中还缺少其他东西。 - Willian Fuks
1
只是想指出这个问题不是重复的。Ben的答案解决了这个问题。 - Willian Fuks
1个回答

14

许多Linux发行版每个进程的打开文件数限制非常低(例如250)。 您可以使用“ulimit -n”查看系统上的当前值(请确保在与您的Tornado服务器运行的环境中发出此命令)。 要提高限制,可以使用ulimit命令或修改/etc/security/limits.conf(尝试将其设置为50000)。

Tornado的HTTP服务器不会关闭Web浏览器留下的连接(截至版本3.2),因此空闲连接可能会随着时间的推移而积累。 这是建议在Tornado服务器前使用像nginx或haproxy这样的代理的原因之一; 这些服务器更加针对此类和其他潜在的DoS问题。


嗨Ben,感谢你的回答,我们将尝试设置一个nginx来看看效果如何。至于空闲连接问题,我们在实现中设置的50毫秒超时时间不应该已经处理了这些吗? - Willian Fuks
不,空闲连接会保持活动状态,而不会阻塞IOLoop(毕竟这是其目的),因此阻塞阈值在此处不适用。 - Ben Darnell
1
我明白了。有没有办法仅使用Tornado本身关闭那些空闲连接呢?使用nginx作为解决方案也可以,我们只是想知道是否可以通过Tornado解决这个问题。 - Willian Fuks
你可以通过将 no_keep_alive=True 传递给 HTTPServer 构造函数(或 Application.listen)来完全关闭连接重用。 - Ben Darnell
1
回复这个问题花了我相当长的时间,但感谢Ben的答案。我们在Tornado前面设置了一个Nginx,问题得到了解决。 - Willian Fuks
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接