PageRenderTime 95ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/venv/Lib/site-packages/gunicorn/workers/gthread.py

https://gitlab.com/chaifegn/myblog
Python | 363 lines | 348 code | 5 blank | 10 comment | 3 complexity | 5641d6acd6154425d581351ab6b0e92b MD5 | raw file
  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. # design:
  6. # a threaded worker accepts connections in the main loop, accepted
  7. # connections are are added to the thread pool as a connection job. On
  8. # keepalive connections are put back in the loop waiting for an event.
  9. # If no event happen after the keep alive timeout, the connectoin is
  10. # closed.
  11. from collections import deque
  12. from datetime import datetime
  13. import errno
  14. from functools import partial
  15. import os
  16. import socket
  17. import ssl
  18. import sys
  19. from threading import RLock
  20. import time
  21. from .. import http
  22. from ..http import wsgi
  23. from .. import util
  24. from . import base
  25. from .. import six
  26. try:
  27. import concurrent.futures as futures
  28. except ImportError:
  29. raise RuntimeError("""
  30. You need to install the 'futures' package to use this worker with this
  31. Python version.
  32. """)
  33. try:
  34. from asyncio import selectors
  35. except ImportError:
  36. from gunicorn import selectors
  37. class TConn(object):
  38. def __init__(self, cfg, listener, sock, addr):
  39. self.cfg = cfg
  40. self.listener = listener
  41. self.sock = sock
  42. self.addr = addr
  43. self.timeout = None
  44. self.parser = None
  45. # set the socket to non blocking
  46. self.sock.setblocking(False)
  47. def init(self):
  48. self.sock.setblocking(True)
  49. if self.parser is None:
  50. # wrap the socket if needed
  51. if self.cfg.is_ssl:
  52. self.sock = ssl.wrap_socket(self.sock, server_side=True,
  53. **self.cfg.ssl_options)
  54. # initialize the parser
  55. self.parser = http.RequestParser(self.cfg, self.sock)
  56. def set_timeout(self):
  57. # set the timeout
  58. self.timeout = time.time() + self.cfg.keepalive
  59. def close(self):
  60. util.close(self.sock)
  61. def __lt__(self, other):
  62. return self.timeout < other.timeout
  63. __cmp__ = __lt__
  64. class ThreadWorker(base.Worker):
  65. def __init__(self, *args, **kwargs):
  66. super(ThreadWorker, self).__init__(*args, **kwargs)
  67. self.worker_connections = self.cfg.worker_connections
  68. self.max_keepalived = self.cfg.worker_connections - self.cfg.threads
  69. # initialise the pool
  70. self.tpool = None
  71. self.poller = None
  72. self._lock = None
  73. self.futures = deque()
  74. self._keep = deque()
  75. self.nr_conns = 0
  76. @classmethod
  77. def check_config(cls, cfg, log):
  78. max_keepalived = cfg.worker_connections - cfg.threads
  79. if max_keepalived <= 0 and cfg.keepalive:
  80. log.warning("No keepalived connections can be handled. " +
  81. "Check the number of worker connections and threads.")
  82. def init_process(self):
  83. self.tpool = futures.ThreadPoolExecutor(max_workers=self.cfg.threads)
  84. self.poller = selectors.DefaultSelector()
  85. self._lock = RLock()
  86. super(ThreadWorker, self).init_process()
  87. def handle_quit(self, sig, frame):
  88. self.alive = False
  89. # worker_int callback
  90. self.cfg.worker_int(self)
  91. self.tpool.shutdown(False)
  92. time.sleep(0.1)
  93. sys.exit(0)
  94. def _wrap_future(self, fs, conn):
  95. fs.conn = conn
  96. self.futures.append(fs)
  97. fs.add_done_callback(self.finish_request)
  98. def enqueue_req(self, conn):
  99. conn.init()
  100. # submit the connection to a worker
  101. fs = self.tpool.submit(self.handle, conn)
  102. self._wrap_future(fs, conn)
  103. def accept(self, listener):
  104. try:
  105. client, addr = listener.accept()
  106. # initialize the connection object
  107. conn = TConn(self.cfg, listener, client, addr)
  108. self.nr_conns += 1
  109. # enqueue the job
  110. self.enqueue_req(conn)
  111. except socket.error as e:
  112. if e.args[0] not in (errno.EAGAIN,
  113. errno.ECONNABORTED, errno.EWOULDBLOCK):
  114. raise
  115. def reuse_connection(self, conn, client):
  116. with self._lock:
  117. # unregister the client from the poller
  118. self.poller.unregister(client)
  119. # remove the connection from keepalive
  120. try:
  121. self._keep.remove(conn)
  122. except ValueError:
  123. # race condition
  124. return
  125. # submit the connection to a worker
  126. self.enqueue_req(conn)
  127. def murder_keepalived(self):
  128. now = time.time()
  129. while True:
  130. with self._lock:
  131. try:
  132. # remove the connection from the queue
  133. conn = self._keep.popleft()
  134. except IndexError:
  135. break
  136. delta = conn.timeout - now
  137. if delta > 0:
  138. # add the connection back to the queue
  139. with self._lock:
  140. self._keep.appendleft(conn)
  141. break
  142. else:
  143. self.nr_conns -= 1
  144. # remove the socket from the poller
  145. with self._lock:
  146. try:
  147. self.poller.unregister(conn.sock)
  148. except socket.error as e:
  149. if e.args[0] != errno.EBADF:
  150. raise
  151. # close the socket
  152. conn.close()
  153. def is_parent_alive(self):
  154. # If our parent changed then we shut down.
  155. if self.ppid != os.getppid():
  156. self.log.info("Parent changed, shutting down: %s", self)
  157. return False
  158. return True
  159. def run(self):
  160. # init listeners, add them to the event loop
  161. for s in self.sockets:
  162. s.setblocking(False)
  163. self.poller.register(s, selectors.EVENT_READ, self.accept)
  164. timeout = self.cfg.timeout or 0.5
  165. while self.alive:
  166. # notify the arbiter we are alive
  167. self.notify()
  168. # can we accept more connections?
  169. if self.nr_conns < self.worker_connections:
  170. # wait for an event
  171. events = self.poller.select(0.02)
  172. for key, mask in events:
  173. callback = key.data
  174. callback(key.fileobj)
  175. if not self.is_parent_alive():
  176. break
  177. # hanle keepalive timeouts
  178. self.murder_keepalived()
  179. # if the number of connections is < to the max we can handle at
  180. # the same time there is no need to wait for one
  181. if len(self.futures) < self.cfg.threads:
  182. continue
  183. result = futures.wait(self.futures, timeout=timeout,
  184. return_when=futures.FIRST_COMPLETED)
  185. if not result.done:
  186. break
  187. else:
  188. [self.futures.remove(f) for f in result.done]
  189. self.tpool.shutdown(False)
  190. self.poller.close()
  191. def finish_request(self, fs):
  192. if fs.cancelled():
  193. fs.conn.close()
  194. return
  195. try:
  196. (keepalive, conn) = fs.result()
  197. # if the connection should be kept alived add it
  198. # to the eventloop and record it
  199. if keepalive:
  200. # flag the socket as non blocked
  201. conn.sock.setblocking(False)
  202. # register the connection
  203. conn.set_timeout()
  204. with self._lock:
  205. self._keep.append(conn)
  206. # add the socket to the event loop
  207. self.poller.register(conn.sock, selectors.EVENT_READ,
  208. partial(self.reuse_connection, conn))
  209. else:
  210. self.nr_conns -= 1
  211. conn.close()
  212. except:
  213. # an exception happened, make sure to close the
  214. # socket.
  215. self.nr_conns -= 1
  216. fs.conn.close()
  217. def handle(self, conn):
  218. keepalive = False
  219. req = None
  220. try:
  221. req = six.next(conn.parser)
  222. if not req:
  223. return (False, conn)
  224. # handle the request
  225. keepalive = self.handle_request(req, conn)
  226. if keepalive:
  227. return (keepalive, conn)
  228. except http.errors.NoMoreData as e:
  229. self.log.debug("Ignored premature client disconnection. %s", e)
  230. except StopIteration as e:
  231. self.log.debug("Closing connection. %s", e)
  232. except ssl.SSLError as e:
  233. if e.args[0] == ssl.SSL_ERROR_EOF:
  234. self.log.debug("ssl connection closed")
  235. conn.sock.close()
  236. else:
  237. self.log.debug("Error processing SSL request.")
  238. self.handle_error(req, conn.sock, conn.addr, e)
  239. except socket.error as e:
  240. if e.args[0] not in (errno.EPIPE, errno.ECONNRESET):
  241. self.log.exception("Socket error processing request.")
  242. else:
  243. if e.args[0] == errno.ECONNRESET:
  244. self.log.debug("Ignoring connection reset")
  245. else:
  246. self.log.debug("Ignoring connection epipe")
  247. except Exception as e:
  248. self.handle_error(req, conn.sock, conn.addr, e)
  249. return (False, conn)
  250. def handle_request(self, req, conn):
  251. environ = {}
  252. resp = None
  253. try:
  254. self.cfg.pre_request(self, req)
  255. request_start = datetime.now()
  256. resp, environ = wsgi.create(req, conn.sock, conn.addr,
  257. conn.listener.getsockname(), self.cfg)
  258. environ["wsgi.multithread"] = True
  259. self.nr += 1
  260. if self.alive and self.nr >= self.max_requests:
  261. self.log.info("Autorestarting worker after current request.")
  262. resp.force_close()
  263. self.alive = False
  264. if not self.cfg.keepalive:
  265. resp.force_close()
  266. elif len(self._keep) >= self.max_keepalived:
  267. resp.force_close()
  268. respiter = self.wsgi(environ, resp.start_response)
  269. try:
  270. if isinstance(respiter, environ['wsgi.file_wrapper']):
  271. resp.write_file(respiter)
  272. else:
  273. for item in respiter:
  274. resp.write(item)
  275. resp.close()
  276. request_time = datetime.now() - request_start
  277. self.log.access(resp, req, environ, request_time)
  278. finally:
  279. if hasattr(respiter, "close"):
  280. respiter.close()
  281. if resp.should_close():
  282. self.log.debug("Closing connection.")
  283. return False
  284. except socket.error:
  285. exc_info = sys.exc_info()
  286. # pass to next try-except level
  287. six.reraise(exc_info[0], exc_info[1], exc_info[2])
  288. except Exception:
  289. if resp and resp.headers_sent:
  290. # If the requests have already been sent, we should close the
  291. # connection to indicate the error.
  292. self.log.exception("Error handling request")
  293. try:
  294. conn.sock.shutdown(socket.SHUT_RDWR)
  295. conn.sock.close()
  296. except socket.error:
  297. pass
  298. raise StopIteration()
  299. raise
  300. finally:
  301. try:
  302. self.cfg.post_request(self, req, environ, resp)
  303. except Exception:
  304. self.log.exception("Exception in post_request hook")
  305. return True