2011-11-20 122 views
4

我曾与Tornado相当多的工作,但这是我第一次遇到这种错误。我一直在研究一个非常基本的URL缩写。 URL由不同的应用程序放入数据库中,这只是从MongoDB存储中读取URL并重定向客户端。在我编写基本代码之后,我对它进行了一次简单的Siege测试,在大约运行围攻30秒后(针对4个应用程序线程运行siege -c 64 -t 5m -r 1 http://example.com/MKy),我开始获得500个响应。看着错误日志,我看到了这个;龙卷风“错误:[Errno 24]太多打开的文件”错误

ERROR:root:500 GET /MKy (127.0.0.1) 2.05ms 
ERROR:root:Exception in I/O handler for fd 4 
Traceback (most recent call last): 
    File "/opt/python2.7/lib/python2.7/site-packages/tornado-2.1-py2.7.egg/tornado/ioloop.py", line 309, in start 
    File "/opt/python2.7/lib/python2.7/site-packages/tornado-2.1-py2.7.egg/tornado/netutil.py", line 314, in accept_handler 
    File "/opt/python2.7/lib/python2.7/socket.py", line 200, in accept 
error: [Errno 24] Too many open files 
ERROR:root:Uncaught exception GET /MKy (127.0.0.1) 
HTTPRequest(protocol='http', host='shortener', method='GET', uri='/MKy', version='HTTP/1.0', remote_ip='127.0.0.1', body='', headers={'Host': 'shortener', 'Accept-Encoding': 'gzip', 'X-Real-Ip': '94.23.155.32', 'X-Forwarded-For': '94.23.155.32', 'Connection': 'close', 'Accept': '*/*', 'User-Agent': 'JoeDog/1.00 [en] (X11; I; Siege 2.66)'}) 
Traceback (most recent call last): 
    File "/opt/python2.7/lib/python2.7/site-packages/tornado-2.1-py2.7.egg/tornado/web.py", line 1040, in wrapper 
    File "main.py", line 58, in get 
    File "main.py", line 21, in dbmongo 
    File "/opt/python2.7/lib/python2.7/site-packages/apymongo-0.0.1-py2.7-linux-x86_64.egg/apymongo/connection.py", line 349, in __init__ 
    File "/opt/python2.7/lib/python2.7/site-packages/apymongo-0.0.1-py2.7-linux-x86_64.egg/apymongo/connection.py", line 510, in __find_master 
    File "/opt/python2.7/lib/python2.7/site-packages/apymongo-0.0.1-py2.7-linux-x86_64.egg/apymongo/connection.py", line 516, in __try_node 
    File "/opt/python2.7/lib/python2.7/site-packages/apymongo-0.0.1-py2.7-linux-x86_64.egg/apymongo/database.py", line 301, in command 
    File "/opt/python2.7/lib/python2.7/site-packages/apymongo-0.0.1-py2.7-linux-x86_64.egg/apymongo/collection.py", line 441, in find_one 
    File "/opt/python2.7/lib/python2.7/site-packages/apymongo-0.0.1-py2.7-linux-x86_64.egg/apymongo/cursor.py", line 539, in loop 
    File "/opt/python2.7/lib/python2.7/site-packages/apymongo-0.0.1-py2.7-linux-x86_64.egg/apymongo/cursor.py", line 560, in _refresh 
    File "/opt/python2.7/lib/python2.7/site-packages/apymongo-0.0.1-py2.7-linux-x86_64.egg/apymongo/cursor.py", line 620, in __send_message 
    File "/opt/python2.7/lib/python2.7/site-packages/apymongo-0.0.1-py2.7-linux-x86_64.egg/apymongo/connection.py", line 735, in _send_message_with_response 
    File "/opt/python2.7/lib/python2.7/site-packages/apymongo-0.0.1-py2.7-linux-x86_64.egg/apymongo/connection.py", line 591, in __stream 
    File "/opt/python2.7/lib/python2.7/site-packages/apymongo-0.0.1-py2.7-linux-x86_64.egg/apymongo/connection.py", line 200, in get_stream 
    File "/opt/python2.7/lib/python2.7/site-packages/apymongo-0.0.1-py2.7-linux-x86_64.egg/apymongo/connection.py", line 559, in __connect 
AutoReconnect: could not connect to [('127.0.0.1', 27017)] 

重要的(我猜);

error: [Errno 24] Too many open files

该代码; (这很简单)

import tornado.ioloop 
import tornado.web 
import tornado.escape 
import apymongo 
import time 
import sys 

#Useful stuff (Connect to Mongo) 
class setup(tornado.web.RequestHandler): 
    def dbmongo(self): 
     if not hasattr(self, '_dbmongo'): 
      self._dbmongo = apymongo.Connection("127.0.0.1", 27017) 
     return self._dbmongo 


#Basic method to lookup URLs from Mongo and redirect accordingly 
class expand(setup): 
    @tornado.web.asynchronous 
    def get(self, url): 
     self.mongo = self.dbmongo() 

     #Lookup the URL 
     cursor = self.mongo.rmgshortlinks.links.find_one({'short':url}, self.direct) 

    def direct(self, response): 
     if response == None: 
      self.send_error(404) 
      self.finish() 
      return 

     link = tornado.escape.url_unescape(response['long']) 

     #Bounce the client 
     self.write("<!DOCTYPE html><html><head><meta charset=\"UTF-8\" /><meta http-equiv=\"refresh\" content=\"0;URL="+link+"\"</head><body><a href=\""+link+"\">Click Here</a></body></html>") 
     self.finish(); 


#Define the URL routes 
application = tornado.web.Application([ 
    (r"/([a-zA-Z0-9]+)", expand) 
]) 

#Start the server 
if __name__ == "__main__": 
    listening_port = int(sys.argv[1]) 

    if listening_port > 0: 
     application.listen(listening_port) 
     tornado.ioloop.IOLoop.instance().start() 
    else: 
     sys.stderr.write("No port specified!") 

我使用的dev服务器有8个内核和64GB内存,运行RedHat Enterprise Linux 5和Python 2.6。我以前从未遇到过与Tornado/Async Mongo应用程序有关的这些问题。

可能有用的信息;

[[email protected] ~]# ulimit -a 
core file size   (blocks, -c) 0 
data seg size   (kbytes, -d) unlimited 
scheduling priority    (-e) 0 
file size    (blocks, -f) unlimited 
pending signals     (-i) 31374 
max locked memory  (kbytes, -l) 64 
max memory size   (kbytes, -m) unlimited 
open files      (-n) 1024 
pipe size   (512 bytes, -p) 8 
POSIX message queues  (bytes, -q) 819200 
real-time priority    (-r) 0 
stack size    (kbytes, -s) 8192 
cpu time    (seconds, -t) unlimited 
max user processes    (-u) 31374 
virtual memory   (kbytes, -v) unlimited 
file locks      (-x) unlimited 

(打开的文件设置为仅1024,但我会认为那是绰绰有余)

是龙卷风/ Apymongo没有正确关闭连接?应用程序位于NGINX之后,但使用HTTP连接,Apymongo应通过TCP连接,但可能使用套接字。即使如此,它应该共享/池连接不应该吗?

编辑

至于建议,移动应用到我们的测试服务器中的一个具有61440一最大打开文件限制,同样的错误约30秒攻城的磨合。

+0

另一方面:如果可能,请不要使用元刷新标记发送HTML。重定向请求的正确方法是使用HTTP 301或302响应。 –

+0

@ThomasK我们这样做的原因很多,主要的原因是它保留/覆盖了引用标题。 Twitter的t.co根据客户端做了类似的事情(尝试卷曲t。与真实的浏览器标头的URL) – Smudge

+0

您是否尝试过在此处找到的解决方案:http://stackoverflow.com/questions/2569620/socket-accept-error-24-to-many-open-files –

回答

4

非常简单,为每个请求实例化RequestHandler对象。这意味着您保存的缓存对象位于RequestHandler(例如,展开)对象上。

如果您要在dbmongo(...)函数中添加一个简单的“打印'CREATED!'”,您会发现它是在每个GET请求中创建的。

您需要做的是将处理程序附加到类对象,或者根据需要添加“全局”,但最好的情况是将其放在Tornado应用程序对象上。

简单:

class setup(tornado.web.RequestHandler): 
    @classmethod 
    def dbmongo(cls): 
     if not hasattr(cls, '_dbmongo'): 
      cls._dbmongo = apymongo.Connection("127.0.0.1", 27017) 
     return cls._dbmongo 

第二种方法就是使之成为全球在你的文件:

dbmongo_connection = None 
def dbmongo(): 
    if not dbmongo_connection: 
     dbmongo_connection = apymongo.Connection("127.0.0.1", 27017) 
    return dbmongo_connection 

这些都具有这是,如果你有很多的类同样的问题想要使用数据库连接,很难分享它。由于数据库是共享实体,因此您可能需要为整个应用程序提供一个共享实体

class MongoMixin(object): 
    def mongodb(self): 
     if not hasattr(self.application, 'mongodb'): 
      self.application.mongodb = apymongo.Connection(self.application.settings.get("mongohost", "127.0.0.1"), 27017) 
     return self.application.mongodb 

class expand(tornado.web.RequestHandler, MongoMixin): 
    def get(self): 
     db = self.mongodb() 
+0

修复它,我去了第一个例子,因为在我的情况下,每一个(好的,一个)方法(')内置'设置'班,但我可以看到其他方法如何产生类似的效果 – Smudge

相关问题