tornado版本: 4.4.1linux
tornado在linux下使用epoll解决并发,解决c10k问题。web
关于epoll了解能够自行搜索了解。并发
tornado的epoll实现主要在tornado.ioloop里面。app
咱们经过tornado的启动流程学习和分析。socket
tornado的例子中启动服务的流程:函数
import tornado.ioloop import tornado.web class MyHandler(tornado.web.RequestHandler): def get(self, *args, **kwargs): self.write("helloworld") def make_app(): return tornado.web.Application([ (r"/", MyHandler), ]) if __name__ == "__main__": app = make_app() app.listen(port=9999) tornado.ioloop.IOLoop.current().start()
make_app会生成一个tornado的web.Application实例,这里先不关心这个实例,接下来看app.listen方法:tornado
def listen(self, port, address="", **kwargs): from tornado.httpserver import HTTPServer server = HTTPServer(self, **kwargs) server.listen(port, address) return server
listen方法会建立一个httpserver,并调用这个server的listen:oop
def listen(self, port, address=""): sockets = bind_sockets(port, address=address) self.add_sockets(sockets) def add_sockets(self, sockets): if self.io_loop is None: self.io_loop = IOLoop.current() # 这里生成ioloop for sock in sockets: self._sockets[sock.fileno()] = sock add_accept_handler(sock, self._handle_connection, io_loop=self.io_loop)
看到bind_sockets生成监听的socket,而后调用add_sockets, 看到这里会生成self.io_loop, IOLoop.current()方法的定义:学习
@staticmethod def current(instance=True): current = getattr(IOLoop._current, "instance", None) if current is None and instance: return IOLoop.instance() return current
能够看到,这里用_current保证当前只有一个ioloop,一般,使用这个方法获取ioloop。刚启动时,这里current为None,因此会调用IOLoop.instance()code
@staticmethod def instance(): if not hasattr(IOLoop, "_instance"): with IOLoop._instance_lock: if not hasattr(IOLoop, "_instance"): # New instance after double check IOLoop._instance = IOLoop() return IOLoop._instance
仍是经过_instance获取ioloop实例,没有的话经过IOLoop()建立, 经过看IOLoop的实现,发现继承于Configurable, 而Configurable重写了__new__方法:
def __new__(cls, *args, **kwargs): base = cls.configurable_base() init_kwargs = {} if cls is base: impl = cls.configured_class() if base.__impl_kwargs: init_kwargs.update(base.__impl_kwargs) else: impl = cls init_kwargs.update(kwargs) instance = super(Configurable, cls).__new__(impl) # initialize vs __init__ chosen for compatibility with AsyncHTTPClient # singleton magic. If we get rid of that we can switch to __init__ # here too. instance.initialize(*args, **init_kwargs) return instance
能够看到IOLoop()实际是经过configured_class方法建立的实例,而经过查看initialize的实现,只是将执行了IOLoop._current.instance = self,因此重点放到的Configurable的configured_class实现:
@classmethod def configured_class(cls): # type: () -> type """Returns the currently configured class.""" base = cls.configurable_base() if cls.__impl_class is None: base.__impl_class = cls.configurable_default() return base.__impl_class
能够看到最后调用的仍是IOLoop的configurable_default,转向IOLoop的configurable_default的实现:
@classmethod def configurable_default(cls): if hasattr(select, "epoll"): from tornado.platform.epoll import EPollIOLoop return EPollIOLoop if hasattr(select, "kqueue"): # Python 2.6+ on BSD or Mac from tornado.platform.kqueue import KQueueIOLoop return KQueueIOLoop from tornado.platform.select import SelectIOLoop return SelectIOLoop
重点来了,这里能够看到,configurable_default是个工厂函数,方法中经过hasattr(select, "epoll")和hasattr(select, "kqueue")条件区分返回epoll,kqueue和select模型,即所说的tornado在linux下使用epoll模型,mac下使用kqueue,win下使用select模型。 咱们接着看EPollIOLoop的实现:
class EPollIOLoop(PollIOLoop): def initialize(self, **kwargs): super(EPollIOLoop, self).initialize(impl=select.epoll(), **kwargs)
能够看到这里初始化了epoll,而epoll的方法封装都PollIOLoop中,因此在linux下,咱们tornado.ioloop.IOLoop.current()取到的就是EPollIOLoop实例 在PollIOLoop中,实现了add_handler, update_handler, remove_handler, start, stop方法,对应的epoll的各类操做。
如今返回到开始的httpserver建立listen方法这里,在add_sockets方法中,初始化ioloop后,经过查看add_accept_handler方法:
def add_accept_handler(sock, callback, io_loop=None): if io_loop is None: io_loop = IOLoop.current() def accept_handler(fd, events): for i in xrange(_DEFAULT_BACKLOG): try: connection, address = sock.accept() except socket.error as e: # _ERRNO_WOULDBLOCK indicate we have accepted every # connection that is available. if errno_from_exception(e) in _ERRNO_WOULDBLOCK: return # ECONNABORTED indicates that there was a connection # but it was closed while still in the accept queue. # (observed on FreeBSD). if errno_from_exception(e) == errno.ECONNABORTED: continue raise callback(connection, address) io_loop.add_handler(sock, accept_handler, IOLoop.READ)
能够看到调用的刚才初始化的PollIOLoop实例的add_handler方法,将sockets放入到epoll监听的套接字列表中。
最后调用tornado.ioloop.IOLoop.current().start()即PollIOLoop实例的start方法开始 执行epoll的poll
PollIOLoop中对epoll的使用详细的阅读源码便可。