You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							146 lines
						
					
					
						
							4.9 KiB
						
					
					
				
			
		
		
	
	
							146 lines
						
					
					
						
							4.9 KiB
						
					
					
				| # -*- coding: utf-8 -
 | |
| #
 | |
| # This file is part of gunicorn released under the MIT license.
 | |
| # See the NOTICE for more information.
 | |
| 
 | |
| import copy
 | |
| import os
 | |
| import sys
 | |
| 
 | |
| try:
 | |
|     import tornado
 | |
| except ImportError:
 | |
|     raise RuntimeError("You need tornado installed to use this worker.")
 | |
| import tornado.web
 | |
| import tornado.httpserver
 | |
| from tornado.ioloop import IOLoop, PeriodicCallback
 | |
| from tornado.wsgi import WSGIContainer
 | |
| from gunicorn.workers.base import Worker
 | |
| from gunicorn import __version__ as gversion
 | |
| 
 | |
| 
 | |
| # `io_loop` arguments to many Tornado functions have been removed in Tornado 5.0
 | |
| # <http://www.tornadoweb.org/en/stable/releases/v5.0.0.html#backwards-compatibility-notes>
 | |
| IOLOOP_PARAMETER_REMOVED = tornado.version_info >= (5, 0, 0)
 | |
| 
 | |
| 
 | |
| class TornadoWorker(Worker):
 | |
| 
 | |
|     @classmethod
 | |
|     def setup(cls):
 | |
|         web = sys.modules.pop("tornado.web")
 | |
|         old_clear = web.RequestHandler.clear
 | |
| 
 | |
|         def clear(self):
 | |
|             old_clear(self)
 | |
|             if "Gunicorn" not in self._headers["Server"]:
 | |
|                 self._headers["Server"] += " (Gunicorn/%s)" % gversion
 | |
|         web.RequestHandler.clear = clear
 | |
|         sys.modules["tornado.web"] = web
 | |
| 
 | |
|     def handle_exit(self, sig, frame):
 | |
|         if self.alive:
 | |
|             super(TornadoWorker, self).handle_exit(sig, frame)
 | |
| 
 | |
|     def handle_request(self):
 | |
|         self.nr += 1
 | |
|         if self.alive and self.nr >= self.max_requests:
 | |
|             self.log.info("Autorestarting worker after current request.")
 | |
|             self.alive = False
 | |
| 
 | |
|     def watchdog(self):
 | |
|         if self.alive:
 | |
|             self.notify()
 | |
| 
 | |
|         if self.ppid != os.getppid():
 | |
|             self.log.info("Parent changed, shutting down: %s", self)
 | |
|             self.alive = False
 | |
| 
 | |
|     def heartbeat(self):
 | |
|         if not self.alive:
 | |
|             if self.server_alive:
 | |
|                 if hasattr(self, 'server'):
 | |
|                     try:
 | |
|                         self.server.stop()
 | |
|                     except Exception:
 | |
|                         pass
 | |
|                 self.server_alive = False
 | |
|             else:
 | |
|                 if not self.ioloop._callbacks:
 | |
|                     self.ioloop.stop()
 | |
| 
 | |
|     def run(self):
 | |
|         self.ioloop = IOLoop.instance()
 | |
|         self.alive = True
 | |
|         self.server_alive = False
 | |
|         if IOLOOP_PARAMETER_REMOVED:
 | |
|             PeriodicCallback(self.watchdog, 1000).start()
 | |
|             PeriodicCallback(self.heartbeat, 1000).start()
 | |
|         else:
 | |
|             PeriodicCallback(self.watchdog, 1000, io_loop=self.ioloop).start()
 | |
|             PeriodicCallback(self.heartbeat, 1000, io_loop=self.ioloop).start()
 | |
| 
 | |
|         # Assume the app is a WSGI callable if its not an
 | |
|         # instance of tornado.web.Application or is an
 | |
|         # instance of tornado.wsgi.WSGIApplication
 | |
|         app = self.wsgi
 | |
|         if not isinstance(app, tornado.web.Application) or \
 | |
|            isinstance(app, tornado.wsgi.WSGIApplication):
 | |
|             app = WSGIContainer(app)
 | |
| 
 | |
|         # Monkey-patching HTTPConnection.finish to count the
 | |
|         # number of requests being handled by Tornado. This
 | |
|         # will help gunicorn shutdown the worker if max_requests
 | |
|         # is exceeded.
 | |
|         httpserver = sys.modules["tornado.httpserver"]
 | |
|         if hasattr(httpserver, 'HTTPConnection'):
 | |
|             old_connection_finish = httpserver.HTTPConnection.finish
 | |
| 
 | |
|             def finish(other):
 | |
|                 self.handle_request()
 | |
|                 old_connection_finish(other)
 | |
|             httpserver.HTTPConnection.finish = finish
 | |
|             sys.modules["tornado.httpserver"] = httpserver
 | |
| 
 | |
|             server_class = tornado.httpserver.HTTPServer
 | |
|         else:
 | |
| 
 | |
|             class _HTTPServer(tornado.httpserver.HTTPServer):
 | |
| 
 | |
|                 def on_close(instance, server_conn):
 | |
|                     self.handle_request()
 | |
|                     super(_HTTPServer, instance).on_close(server_conn)
 | |
| 
 | |
|             server_class = _HTTPServer
 | |
| 
 | |
|         if self.cfg.is_ssl:
 | |
|             _ssl_opt = copy.deepcopy(self.cfg.ssl_options)
 | |
|             # tornado refuses initialization if ssl_options contains following
 | |
|             # options
 | |
|             del _ssl_opt["do_handshake_on_connect"]
 | |
|             del _ssl_opt["suppress_ragged_eofs"]
 | |
|             if IOLOOP_PARAMETER_REMOVED:
 | |
|                 server = server_class(app, ssl_options=_ssl_opt)
 | |
|             else:
 | |
|                 server = server_class(app, io_loop=self.ioloop,
 | |
|                                       ssl_options=_ssl_opt)
 | |
|         else:
 | |
|             if IOLOOP_PARAMETER_REMOVED:
 | |
|                 server = server_class(app)
 | |
|             else:
 | |
|                 server = server_class(app, io_loop=self.ioloop)
 | |
| 
 | |
|         self.server = server
 | |
|         self.server_alive = True
 | |
| 
 | |
|         for s in self.sockets:
 | |
|             s.setblocking(0)
 | |
|             if hasattr(server, "add_socket"):  # tornado > 2.0
 | |
|                 server.add_socket(s)
 | |
|             elif hasattr(server, "_sockets"):  # tornado 2.0
 | |
|                 server._sockets[s.fileno()] = s
 | |
| 
 | |
|         server.no_keep_alive = self.cfg.keepalive <= 0
 | |
|         server.start(num_processes=1)
 | |
| 
 | |
|         self.ioloop.start()
 | |
| 
 |