Development of an internal social media platform with personalised dashboards for students
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.

ggevent.py 6.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. # -*- coding: utf-8 -
  2. #
  3. # This file is part of gunicorn released under the MIT license.
  4. # See the NOTICE for more information.
  5. import errno
  6. import os
  7. import sys
  8. from datetime import datetime
  9. from functools import partial
  10. import time
  11. _socket = __import__("socket")
  12. # workaround on osx, disable kqueue
  13. if sys.platform == "darwin":
  14. os.environ['EVENT_NOKQUEUE'] = "1"
  15. try:
  16. import gevent
  17. except ImportError:
  18. raise RuntimeError("You need gevent installed to use this worker.")
  19. from gevent.pool import Pool
  20. from gevent.server import StreamServer
  21. from gevent.socket import wait_write, socket
  22. from gevent import pywsgi
  23. import gunicorn
  24. from gunicorn.http.wsgi import base_environ
  25. from gunicorn.workers.async import AsyncWorker
  26. from gunicorn.http.wsgi import sendfile as o_sendfile
  27. VERSION = "gevent/%s gunicorn/%s" % (gevent.__version__, gunicorn.__version__)
  28. def _gevent_sendfile(fdout, fdin, offset, nbytes):
  29. while True:
  30. try:
  31. return o_sendfile(fdout, fdin, offset, nbytes)
  32. except OSError as e:
  33. if e.args[0] == errno.EAGAIN:
  34. wait_write(fdout)
  35. else:
  36. raise
  37. def patch_sendfile():
  38. from gunicorn.http import wsgi
  39. if o_sendfile is not None:
  40. setattr(wsgi, "sendfile", _gevent_sendfile)
  41. class GeventWorker(AsyncWorker):
  42. server_class = None
  43. wsgi_handler = None
  44. def patch(self):
  45. from gevent import monkey
  46. monkey.noisy = False
  47. # if the new version is used make sure to patch subprocess
  48. if gevent.version_info[0] == 0:
  49. monkey.patch_all()
  50. else:
  51. monkey.patch_all(subprocess=True)
  52. # monkey patch sendfile to make it none blocking
  53. patch_sendfile()
  54. # patch sockets
  55. sockets = []
  56. for s in self.sockets:
  57. if sys.version_info[0] == 3:
  58. sockets.append(socket(s.FAMILY, _socket.SOCK_STREAM,
  59. fileno=s.sock.fileno()))
  60. else:
  61. sockets.append(socket(s.FAMILY, _socket.SOCK_STREAM,
  62. _sock=s))
  63. self.sockets = sockets
  64. def notify(self):
  65. super(GeventWorker, self).notify()
  66. if self.ppid != os.getppid():
  67. self.log.info("Parent changed, shutting down: %s", self)
  68. sys.exit(0)
  69. def timeout_ctx(self):
  70. return gevent.Timeout(self.cfg.keepalive, False)
  71. def run(self):
  72. servers = []
  73. ssl_args = {}
  74. if self.cfg.is_ssl:
  75. ssl_args = dict(server_side=True, **self.cfg.ssl_options)
  76. for s in self.sockets:
  77. s.setblocking(1)
  78. pool = Pool(self.worker_connections)
  79. if self.server_class is not None:
  80. environ = base_environ(self.cfg)
  81. environ.update({
  82. "wsgi.multithread": True,
  83. "SERVER_SOFTWARE": VERSION,
  84. })
  85. server = self.server_class(
  86. s, application=self.wsgi, spawn=pool, log=self.log,
  87. handler_class=self.wsgi_handler, environ=environ,
  88. **ssl_args)
  89. else:
  90. hfun = partial(self.handle, s)
  91. server = StreamServer(s, handle=hfun, spawn=pool, **ssl_args)
  92. server.start()
  93. servers.append(server)
  94. while self.alive:
  95. self.notify()
  96. gevent.sleep(1.0)
  97. try:
  98. # Stop accepting requests
  99. for server in servers:
  100. if hasattr(server, 'close'): # gevent 1.0
  101. server.close()
  102. if hasattr(server, 'kill'): # gevent < 1.0
  103. server.kill()
  104. # Handle current requests until graceful_timeout
  105. ts = time.time()
  106. while time.time() - ts <= self.cfg.graceful_timeout:
  107. accepting = 0
  108. for server in servers:
  109. if server.pool.free_count() != server.pool.size:
  110. accepting += 1
  111. # if no server is accepting a connection, we can exit
  112. if not accepting:
  113. return
  114. self.notify()
  115. gevent.sleep(1.0)
  116. # Force kill all active the handlers
  117. self.log.warning("Worker graceful timeout (pid:%s)" % self.pid)
  118. [server.stop(timeout=1) for server in servers]
  119. except:
  120. pass
  121. def handle_request(self, *args):
  122. try:
  123. super(GeventWorker, self).handle_request(*args)
  124. except gevent.GreenletExit:
  125. pass
  126. except SystemExit:
  127. pass
  128. def handle_quit(self, sig, frame):
  129. # Move this out of the signal handler so we can use
  130. # blocking calls. See #1126
  131. gevent.spawn(super(GeventWorker, self).handle_quit, sig, frame)
  132. if gevent.version_info[0] == 0:
  133. def init_process(self):
  134. # monkey patch here
  135. self.patch()
  136. # reinit the hub
  137. import gevent.core
  138. gevent.core.reinit()
  139. #gevent 0.13 and older doesn't reinitialize dns for us after forking
  140. #here's the workaround
  141. gevent.core.dns_shutdown(fail_requests=1)
  142. gevent.core.dns_init()
  143. super(GeventWorker, self).init_process()
  144. else:
  145. def init_process(self):
  146. # monkey patch here
  147. self.patch()
  148. # reinit the hub
  149. from gevent import hub
  150. hub.reinit()
  151. # then initialize the process
  152. super(GeventWorker, self).init_process()
  153. class GeventResponse(object):
  154. status = None
  155. headers = None
  156. sent = None
  157. def __init__(self, status, headers, clength):
  158. self.status = status
  159. self.headers = headers
  160. self.sent = clength
  161. class PyWSGIHandler(pywsgi.WSGIHandler):
  162. def log_request(self):
  163. start = datetime.fromtimestamp(self.time_start)
  164. finish = datetime.fromtimestamp(self.time_finish)
  165. response_time = finish - start
  166. resp_headers = getattr(self, 'response_headers', {})
  167. resp = GeventResponse(self.status, resp_headers, self.response_length)
  168. if hasattr(self, 'headers'):
  169. req_headers = [h.split(":", 1) for h in self.headers.headers]
  170. else:
  171. req_headers = []
  172. self.server.log.access(resp, req_headers, self.environ, response_time)
  173. def get_environ(self):
  174. env = super(PyWSGIHandler, self).get_environ()
  175. env['gunicorn.sock'] = self.socket
  176. env['RAW_URI'] = self.path
  177. return env
  178. class PyWSGIServer(pywsgi.WSGIServer):
  179. pass
  180. class GeventPyWSGIWorker(GeventWorker):
  181. "The Gevent StreamServer based workers."
  182. server_class = PyWSGIServer
  183. wsgi_handler = PyWSGIHandler