Commits

Gustavo Picon committed 9d7b313

Added support for the new dynamic thread pool in cheroot.

  • Participants
  • Parent commits 9175a86
  • Branches cp4

Comments (0)

Files changed (3)

cherrypy/__init__.py

         """Check timeout on all responses. (Internal)"""
         for req, resp in self.servings:
             resp.check_timeout()
+
+
+class _ThreadPoolMonitor(Monitor):
+
+    def __init__(self, bus):
+        self._run = lambda: None
+        super(_ThreadPoolMonitor, self).__init__(bus, self.run, frequency=1)
+
+    def run(self):
+        self._run()
+
+    def configure(self, thread_pool, server_adapter, logger):
+        resizer = thread_pool.get_pool_resizer(
+            minspare=server_adapter.thread_pool_minspare,
+            maxspare=server_adapter.thread_pool_maxspare,
+            shrinkfreq=server_adapter.thread_pool_shrink_frequency,
+            logger=logger or (lambda: None)
+        )
+        self._run = resizer.run
+
+    def stop(self):
+        self._run = lambda: None
+        super(_ThreadPoolMonitor, self).stop()
+    stop.priority = 10
+
+
 engine.timeout_monitor = _TimeoutMonitor(engine)
 engine.timeout_monitor.subscribe()
 
+engine.threadpool_monitor = _ThreadPoolMonitor(engine)
+engine.threadpool_monitor.subscribe()
+
 engine.autoreload = Autoreloader(engine)
 engine.autoreload.subscribe()
 

cherrypy/lib/server.py

     for example, "HTTP/1.1" (the default). Depending on the HTTP server used,
     this should also limit the supported features used in the response."""
 
-    thread_pool = 10
+    thread_pool = 5
     """The number of worker threads to start up in the pool."""
 
-    thread_pool_max = -1
+    thread_pool_max = 30
     """The maximum size of the worker-thread pool. Use -1 to indicate no limit.
     """
 
+    thread_pool_minspare = 5
+    """The minimum number of idle workers threads that should always be ready
+    to accept new request (unless the total exceeds `thread_pool_max`).
+    """
+
+    thread_pool_maxspare = 15
+    """The number of maximum idle worker threads that will be available to
+    serve connections. Basically:
+
+    - If there is a sudden spike of traffic (or a lot of slow requests
+      being served), there are no idle workers available, and there are
+      requests waiting for a worker, ``maxspare`` threads will quickly be
+      spawned to serve the requests as soon as possible.
+    - After a sudden spike of traffic (or after a backend shortage that
+      got a lot of threads working for a long time), the thread pool will be
+      shrinked leaving at most ``maxspare`` idle threads, and then it will
+      slowly shrink the pool until it reaches the `thread_pool_minspare`
+      value if there isn't much traffic to avoid wasting resources.
+    """
+
+    thread_pool_shrink_frequency = 5
+    """The frequency in seconds in which the thread pool monitor will
+    perform shrink operations (default 5).
+    """
+
     max_request_header_size = 500 * 1024
     """The maximum number of bytes allowable in the request headers.
     If exceeded, the HTTP server should return "413 Request Entity Too Large".

cherrypy/lib/wsgi_server.py

     def __init__(self, server_adapter=cherrypy.server):
         self.server_adapter = server_adapter
         self.max_request_header_size = (
-            self.server_adapter.max_request_header_size or 0
+            server_adapter.max_request_header_size or 0
         )
         self.max_request_body_size = (
-            self.server_adapter.max_request_body_size or 0
+            server_adapter.max_request_body_size or 0
         )
 
-        server_name = (self.server_adapter.socket_host or
-                       self.server_adapter.socket_file or
-                       None)
+        server_name = (
+            server_adapter.socket_host or server_adapter.socket_file or None)
 
-        self.wsgi_version = self.server_adapter.wsgi_version
+        self.wsgi_version = server_adapter.wsgi_version
         super(CPWSGIServer, self).__init__(
             server_adapter.bind_addr,
-            minthreads=self.server_adapter.thread_pool,
-            maxthreads=self.server_adapter.thread_pool_max,
+            minthreads=server_adapter.thread_pool,
+            maxthreads=server_adapter.thread_pool_max,
             server_name=server_name,
-            protocol=self.server_adapter.protocol_version
+            protocol=server_adapter.protocol_version
         )
+        cherrypy.engine.threadpool_monitor.configure(self.requests,
+                                                     server_adapter,
+                                                     self.error_log)
+
         self.wsgi_app = cherrypy.tree
-        self.request_queue_size = self.server_adapter.socket_queue_size
-        self.timeout = self.server_adapter.socket_timeout
-        self.shutdown_timeout = self.server_adapter.shutdown_timeout
-        self.nodelay = self.server_adapter.nodelay
+        self.request_queue_size = server_adapter.socket_queue_size
+        self.timeout = server_adapter.socket_timeout
+        self.shutdown_timeout = server_adapter.shutdown_timeout
+        self.nodelay = server_adapter.nodelay
 
         if sys.version_info >= (3, 0):
-            ssl_module = self.server_adapter.ssl_module or 'builtin'
+            ssl_module = server_adapter.ssl_module or 'builtin'
         else:
-            ssl_module = self.server_adapter.ssl_module or 'pyopenssl'
-        if (
-                self.server_adapter.ssl_context or
-                self.server_adapter.ssl_certificate
-        ):
+            ssl_module = server_adapter.ssl_module or 'pyopenssl'
+        if server_adapter.ssl_context or server_adapter.ssl_certificate:
             adapter_class = ssllib.get_ssl_adapter_class(ssl_module)
             self.ssl_adapter = adapter_class(
-                self.server_adapter.ssl_certificate,
-                self.server_adapter.ssl_private_key,
-                self.server_adapter.ssl_certificate_chain)
-            if self.server_adapter.ssl_context:
-                self.ssl_adapter.context = self.server_adapter.ssl_context
+                server_adapter.ssl_certificate,
+                server_adapter.ssl_private_key,
+                server_adapter.ssl_certificate_chain)
+            if server_adapter.ssl_context:
+                self.ssl_adapter.context = server_adapter.ssl_context
 
-        self.stats['Enabled'] = getattr(
-            self.server_adapter, 'statistics', False)
+        self.stats['Enabled'] = getattr(server_adapter, 'statistics', False)
 
     def error_log(self, msg="", level=20, traceback=False):
         cherrypy.engine.log(msg, level, traceback)