Python OSError:[Errno 24]在Twisted中使用reactor.run()时打开的文件过多

Python OSError:[Errno 24]在Twisted中使用reactor.run()时打开的文件过多,python,python-2.7,twisted,centos7,Python,Python 2.7,Twisted,Centos7,我遇到了一个奇怪的问题:我运行了大量的utils.getProcessOutputAdvalue('cmd',[args])命令,结果取决于我是使用task.react()还是reactor.run() 当使用高于400的limit时(在我的情况下),我得到以下错误: Upon execvpe echo ['echo', 'Testing 0...'] in environment id 42131264 :Traceback (most recent call last): File "/

我遇到了一个奇怪的问题:我运行了大量的
utils.getProcessOutputAdvalue('cmd',[args])
命令,结果取决于我是使用
task.react()
还是
reactor.run()

当使用高于400的
limit
时(在我的情况下),我得到以下错误:

Upon execvpe echo ['echo', 'Testing 0...'] in environment id 42131264
:Traceback (most recent call last):
  File "/home/vagrant/.env/sm/lib/python2.7/site-packages/Twisted-15.5.0-py2.7-linux-x86_64.egg/twisted/internet/process.py", line 428, in _fork
    self._setupChild(**kwargs)
  File "/home/vagrant/.env/sm/lib/python2.7/site-packages/Twisted-15.5.0-py2.7-linux-x86_64.egg/twisted/internet/process.py", line 803, in _setupChild
    for fd in _listOpenFDs():
  File "/home/vagrant/.env/sm/lib/python2.7/site-packages/Twisted-15.5.0-py2.7-linux-x86_64.egg/twisted/internet/process.py", line 638, in _listOpenFDs
    return detector._listOpenFDs()
  File "/home/vagrant/.env/sm/lib/python2.7/site-packages/Twisted-15.5.0-py2.7-linux-x86_64.egg/twisted/internet/process.py", line 553, in _listOpenFDs
    self._listOpenFDs = self._getImplementation()
  File "/home/vagrant/.env/sm/lib/python2.7/site-packages/Twisted-15.5.0-py2.7-linux-x86_64.egg/twisted/internet/process.py", line 576, in _getImplementation
    after = impl()
  File "/home/vagrant/.env/sm/lib/python2.7/site-packages/Twisted-15.5.0-py2.7-linux-x86_64.egg/twisted/internet/process.py", line 606, in _procFDImplementation
    return [int(fd) for fd in self.listdir(dname)]
OSError: [Errno 24] Too many open files: '/proc/23421/fd'
Unhandled error in Deferred:
如果我使用的是
task.react()

在简历中:

  • python pyerr.py-l100-r
    OK
  • python pyerr.py-l100-t
    OK
  • python pyerr.py-l100-w
    OK
  • python pyerr.py-l400-r
    OSERR
  • python pyerr.py-l400-t
    OK
  • python pyerr.py-l400-w
    OSERR
问题是,我有一个使用reactor的大型应用程序,因为它是一个响应SMTP连接的应用程序(因此无法使用
task.react
,我不想停止reactor)

我一直认为,
task.react
只是在延迟完成后停止反应堆,但我想它做的不止这些


编辑:这里有一个
pstree
比较
任务。反应
vs
反应器。运行

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from progress.bar import IncrementalBar
from twisted.internet import defer
from twisted.internet import task
from twisted.internet import utils
from twisted.python import usage


class Options(usage.Options):
    optFlags = [['reactor', 'r', 'Use reactor.run().'],
                ['task', 't', 'Use task.react().'],
                ['cwr', 'w', 'Use callWhenRunning().']]
    optParameters = [['limit', 'l', 255, 'Number of file descriptors to open.'],
                     ['cmd', 'c', 'echo Testing {i}...', 'Command to run.']]


def run(opt):
    limit = int(opt['limit'])
    cmd, args = opt['cmd'].split(' ', 1)
    bar = IncrementalBar('Running {cmd}'.format(cmd=opt['cmd']), max=limit)
    requests = []
    for i in range(0, limit):
        try:
            _args = args.format(i=i)
            args = _args
        except KeyError:
            pass
        requests.append(utils.getProcessOutputAndValue('echo', [args]))
        bar.next()
    bar.finish()
    return defer.gatherResults(requests)


@defer.inlineCallbacks
def main(reactor, opt):
    d = defer.Deferred()
    limit = int(opt['limit'])
    cmd, args = opt['cmd'].split(' ', 1)
    bar = IncrementalBar('Running {cmd}'.format(cmd=opt['cmd']), max=limit)
    for i in range(0, limit):
        try:
            _args = args.format(i=i)
            args = _args
        except KeyError:
            pass
        yield utils.getProcessOutputAndValue('echo', [args])
        bar.next()
    bar.finish()
    defer.returnValue(d.callback(True))


if __name__ == '__main__':
    opt = Options()
    opt.parseOptions()

    if opt['reactor']:
        from twisted.internet import reactor
        task.deferLater(reactor, 0, run, opt)
        reactor.run()

    elif opt['task']:
        from twisted.internet.task import react
        react(main, [opt])

    elif opt['cwr']:
        from twisted.internet import reactor
        reactor.callWhenRunning(run, opt)
        reactor.run()
reactor.run(python pyerr.py-l400-r)

init-+-VBoxService---7*[{VBoxService}]
     |-acpid
     |-atd
     |-cron
     |-dbus-daemon
     |-dhclient
     |-6*[getty]
     |-master-+-pickup
     |        `-qmgr
     |-mysqld---18*[{mysqld}]
     |-nginx---4*[nginx]
     |-php5-fpm---2*[php5-fpm]
     |-puppet---{puppet}
     |-rpc.idmapd
     |-rpc.statd
     |-rpcbind
     |-rsyslogd---3*[{rsyslogd}]
     |-ruby---{ruby}
     |-sshd-+-3*[sshd---sshd---sftp-server]
     |      |-sshd---sshd---2*[sftp-server]
     |      |-sshd---sshd---bash---pstree
     |      `-sshd---sshd---bash---python-+-323*[echo]
     |                                    `-5*[python]
     |-systemd-logind
     |-systemd-udevd
     |-upstart-file-br
     |-upstart-socket-
     `-upstart-udev-br
init-+-VBoxService---7*[{VBoxService}]
     |-acpid
     |-atd
     |-cron
     |-dbus-daemon
     |-dhclient
     |-6*[getty]
     |-master-+-pickup
     |        `-qmgr
     |-mysqld---18*[{mysqld}]
     |-nginx---4*[nginx]
     |-php5-fpm---2*[php5-fpm]
     |-puppet---{puppet}
     |-rpc.idmapd
     |-rpc.statd
     |-rpcbind
     |-rsyslogd---3*[{rsyslogd}]
     |-ruby---{ruby}
     |-sshd-+-3*[sshd---sshd---sftp-server]
     |      |-sshd---sshd---2*[sftp-server]
     |      |-sshd---sshd---bash---pstree
     |      `-sshd---sshd---bash---python---echo
     |-systemd-logind
     |-systemd-udevd
     |-upstart-file-br
     |-upstart-socket-
     `-upstart-udev-br
task.react(python-pyerr.py-l400-t)

init-+-VBoxService---7*[{VBoxService}]
     |-acpid
     |-atd
     |-cron
     |-dbus-daemon
     |-dhclient
     |-6*[getty]
     |-master-+-pickup
     |        `-qmgr
     |-mysqld---18*[{mysqld}]
     |-nginx---4*[nginx]
     |-php5-fpm---2*[php5-fpm]
     |-puppet---{puppet}
     |-rpc.idmapd
     |-rpc.statd
     |-rpcbind
     |-rsyslogd---3*[{rsyslogd}]
     |-ruby---{ruby}
     |-sshd-+-3*[sshd---sshd---sftp-server]
     |      |-sshd---sshd---2*[sftp-server]
     |      |-sshd---sshd---bash---pstree
     |      `-sshd---sshd---bash---python-+-323*[echo]
     |                                    `-5*[python]
     |-systemd-logind
     |-systemd-udevd
     |-upstart-file-br
     |-upstart-socket-
     `-upstart-udev-br
init-+-VBoxService---7*[{VBoxService}]
     |-acpid
     |-atd
     |-cron
     |-dbus-daemon
     |-dhclient
     |-6*[getty]
     |-master-+-pickup
     |        `-qmgr
     |-mysqld---18*[{mysqld}]
     |-nginx---4*[nginx]
     |-php5-fpm---2*[php5-fpm]
     |-puppet---{puppet}
     |-rpc.idmapd
     |-rpc.statd
     |-rpcbind
     |-rsyslogd---3*[{rsyslogd}]
     |-ruby---{ruby}
     |-sshd-+-3*[sshd---sshd---sftp-server]
     |      |-sshd---sshd---2*[sftp-server]
     |      |-sshd---sshd---bash---pstree
     |      `-sshd---sshd---bash---python---echo
     |-systemd-logind
     |-systemd-udevd
     |-upstart-file-br
     |-upstart-socket-
     `-upstart-udev-br
注意这两者之间的区别

 |      `-sshd---sshd---bash---python-+-323*[echo]
 |                                    `-5*[python]
还有这个

 |      `-sshd---sshd---bash---python---echo
在一个cas中,流程似乎没有在完成后立即关闭

我已在4台不同的机器上测试了此问题:

  • Ubuntu 14.04
  • Centos 6
  • Centos 7
问题完全相同

要想试一试,请尝试运行
watch-n0.1“pstree”
,查看流程是如何演变的


编辑:多亏了Glyph-answer,我明白了为什么会发生这种情况,但如何使其适应我的实际情况?

我正在使用Twisted开发的应用程序是一个基于Milter的SMTP过滤器,下面是它的工作原理(假设我们要检查电子邮件签名):

  • 连接在端口25上打开
  • milter协议获取所有电子邮件详细信息
  • milter调用一个远程“模块”服务器,该服务器将通过
    /usr/bin/openssl mime
    调用处理签名检查
  • 模块将返回一个答案,指示签名是否有效
在本例中,我的问题是,我同时获得150个连接,将有150个对模块(TCP协议)的调用,并且该模块将在每个连接中调用openssl命令一次

模块是完全不可知的,因此不知道是否有其他调用正在运行。在您看来,我应该把
延迟信号量
放在哪里

我这里的问题是smtp连接也是不可知的,不知道其他可能的连接


在您看来,处理这种并行性的正确方法是什么?

这里的问题与
任务.react
reactor.run
之间的区别无关,而是与
运行
函数的实现之间细微但显著的区别有关

区别在于
run
并行生成
limit
进程,同时生成数千个打开的文件描述符,很容易突破系统的限制。但是,
main
正在等待每个进程完全完成执行,甚至在启动下一个进程之前,这意味着它一次使用的进程不会超过4个或5个

原因是
main
inlineCallbacks
修饰,并生成每个
getProcessOutputValue
Deferred
,这将暂停
main
的执行,直到
Deferred
完成

在实际应用中,这两种方法都不理想。您需要一些并行性,但不是无限的。Twisted附带了一些实用程序,例如,以促进有限的并行性,而不将所有内容限制为一次只运行一个任务。Jean-Paul Calderone在10年前写了一篇文章这就解释了如何使用这个

但是,为了证明该问题与
任务无关。react
,下面是您示例的一个修改版本,它消除了
运行
函数,并使用
main
进行苹果对苹果的比较:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from progress.bar import IncrementalBar
from twisted.internet import defer
from twisted.internet import task
from twisted.internet import utils
from twisted.python import usage


class Options(usage.Options):
    optFlags = [['reactor', 'r', 'Use reactor.run().'],
                ['task', 't', 'Use task.react().'],
                ['cwr', 'w', 'Use callWhenRunning().']]
    optParameters = [['limit', 'l', 255, 'Number of file descriptors to open.'],
                     ['cmd', 'c', 'echo Testing {i}...', 'Command to run.']]


@defer.inlineCallbacks
def main(reactor, opt):
    d = defer.Deferred()
    limit = int(opt['limit'])
    cmd, args = opt['cmd'].split(' ', 1)
    bar = IncrementalBar('Running {cmd}'.format(cmd=opt['cmd']), max=limit)
    for i in range(0, limit):
        try:
            _args = args.format(i=i)
            args = _args
        except KeyError:
            pass
        yield utils.getProcessOutputAndValue('echo', [args])
        bar.next()
    bar.finish()
    defer.returnValue(d.callback(True))


if __name__ == '__main__':
    opt = Options()
    opt.parseOptions()

    if opt['reactor']:
        from twisted.internet import reactor
        task.deferLater(reactor, 0, main, reactor, opt)
        reactor.run()

    elif opt['task']:
        from twisted.internet.task import react
        react(main, [opt])

    elif opt['cwr']:
        from twisted.internet import reactor
        reactor.callWhenRunning(main, reactor, opt)
        reactor.run()
编辑,回答问题中的编辑:


由于您真正的问题在于传入连接,而不仅仅是
for
循环,而不是使用
DeferredSemaphore
,因此您可能需要维护一个计数器,并利用从返回的对象或从返回的
Deferred
的结果实现,当有太多的并发连接正在工作时,对其调用
pauseProducing()
,当该工作完成时,对其调用
resumeProducing()

这就是
任务的全部内容。react
执行的操作。将您的示例粘贴到Linux服务器上,我能够针对您提供的所有值成功运行您的示例,因此我不确定出了什么问题。可能是一些本地配置问题?我已经用
pstree
运行了两个示例,当使用
-r
开关时,我看到了300个python子流程,我将在上午向您粘贴一个详细的示例。我的印象是,进程一旦终止就会挂起,并导致/proc limit错误。我添加了
pstree
图,并详细介绍了一些我的测试,这个问题已经在4台不同的机器和3个不同的操作系统上进行了测试。请注意,我更正了结果:失败的是-r开关
python pyerr.py-l400-r
,而不是-tI开关,我无法重现您的fa