Bläddra i källkod

HUE-7860 [core] Integrate non IO blocking Python Webserver
Add runserver command

Ying Chen 7 år sedan
förälder
incheckning
62cf649

+ 11 - 0
desktop/conf.dist/hue.ini

@@ -71,6 +71,17 @@
   # Email backend
   ## django_email_backend=django.core.mail.backends.smtp.EmailBackend
 
+  # Set to true to use CherryPy as the webserver, set to false
+  # to use Gunicorn as the webserver. Defaults to CherryPy if
+  # key is not specified.
+  ## use_cherrypy_server=true
+
+  # Gunicorn work class: gevent or evenlet, gthread or sync.
+  ## gunicorn_work_class=eventlet
+
+  # The number of Gunicorn worker processes. If not specified, it uses: (number of CPU * 2) + 1.
+  ## gunicorn_number_of_workers=None
+
   # Webserver runs as this user
   ## server_user=hue
   ## server_group=hue

+ 11 - 0
desktop/conf/pseudo-distributed.ini.tmpl

@@ -75,6 +75,17 @@
   # Email backend
   ## django_email_backend=django.core.mail.backends.smtp.EmailBackend
 
+  # Set to true to use CherryPy as the webserver, set to false
+  # to use Gunicorn as the webserver. Defaults to CherryPy if
+  # key is not specified.
+  ## use_cherrypy_server=true
+
+  # Gunicorn work class: gevent or evenlet, gthread or sync.
+  ## gunicorn_work_class=eventlet
+
+  # The number of Gunicorn worker processes. If not specified, it uses: (number of CPU * 2) + 1.
+  ## gunicorn_number_of_workers=None
+
   # Webserver runs as this user
   ## server_user=hue
   ## server_group=hue

+ 18 - 0
desktop/core/src/desktop/conf.py

@@ -128,6 +128,24 @@ def is_https_enabled():
   """Hue is configured for HTTPS."""
   return bool(SSL_CERTIFICATE.get() and SSL_PRIVATE_KEY.get())
 
+USE_CHERRYPY_SERVER = Config(
+  key="use_cherrypy_server",
+  help=_("If set to true, CherryPy will be used. Otherwise, Gunicorn will be used as the webserver."),
+  type=coerce_bool,
+  default=True)
+
+GUNICORN_WORKER_CLASS = Config(
+  key="gunicorn_work_class",
+  help=_("Gunicorn work class: gevent or evenlet, gthread or sync."),
+  type=str,
+  default="eventlet")
+
+GUNICORN_NUMBER_OF_WORKERS = Config(
+  key="gunicorn_number_of_workers",
+  help=_("The number of Gunicorn worker processes. If not specified, it uses: (number of CPU * 2) + 1."),
+  type=int,
+  default=None)
+
 HTTP_HOST = Config(
   key="http_host",
   help=_("HTTP host to bind to."),

+ 6 - 2
desktop/core/src/desktop/management/commands/runcpserver.py

@@ -24,7 +24,8 @@ import sys
 from django.utils.translation import ugettext as _
 
 SERVER_HELP = r"""
-  Run Hue using CherryPy server.
+  Run Hue using either the CherryPy server or the Spawning server, based on
+  the current configuration.
 """
 
 LOG = logging.getLogger(__name__)
@@ -39,7 +40,10 @@ class Command(BaseCommand):
     return SERVER_HELP
 
 def runserver():
-  cmdv = supervisor.DjangoCommandSupervisee("runcherrypyserver").cmdv
+  script_name = "rungunicornserver"
+  if conf.USE_CHERRYPY_SERVER.get():
+    script_name = "runcherrypyserver"
+  cmdv = supervisor.DjangoCommandSupervisee(script_name).cmdv
   os.execv(cmdv[0], cmdv)
   LOG.error("Failed to exec '%s' with argument '%s'" % (cmdv[0], cmdv[1],))
   sys.exit(-1)

+ 156 - 0
desktop/core/src/desktop/management/commands/rungunicornserver.py

@@ -0,0 +1,156 @@
+#!/usr/bin/env python
+# Licensed to Cloudera, Inc. under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  Cloudera, Inc. licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import unicode_literals
+
+import os
+import sys
+
+import multiprocessing
+
+import gunicorn.app.base
+
+from desktop import conf
+from django.core.management.base import BaseCommand
+from django.core.wsgi import get_wsgi_application
+from django.utils.translation import ugettext as _
+
+from gunicorn import util
+from gunicorn.six import iteritems
+
+GUNICORN_SERVER_HELP = r"""
+  Run Hue using the Gunicorn WSGI server in asynchronous mode.
+"""
+
+
+class Command(BaseCommand):
+  help = _("Gunicorn Web server for Hue.")
+
+  def handle(self, *args, **options):
+    rungunicornserver()
+
+  def usage(self, subcommand):
+    return GUNICORN_SERVER_HELP
+
+def number_of_workers():
+  return (multiprocessing.cpu_count() * 2) + 1
+
+
+def handler_app(environ, start_response):
+  os.environ.setdefault("DJANGO_SETTINGS_MODULE", "desktop.settings")
+  return get_wsgi_application()
+
+
+class StandaloneApplication(gunicorn.app.base.BaseApplication):
+
+  def __init__(self, app, options=None):
+    self.options = options or {}
+    self.app_uri = 'desktop.wsgi:application'
+    super(StandaloneApplication, self).__init__()
+
+  def load_config(self):
+    config = dict([(key, value) for key, value in iteritems(self.options)
+                    if key in self.cfg.settings and value is not None])
+    for key, value in iteritems(config):
+      self.cfg.set(key.lower(), value)
+
+  def chdir(self):
+    # chdir to the configured path before loading,
+    # default is the current dir
+    os.chdir(self.cfg.chdir)
+
+    # add the path to sys.path
+    sys.path.insert(0, self.cfg.chdir)
+
+  def load_wsgiapp(self):
+    self.chdir()
+
+    # load the app
+    return util.import_app(self.app_uri)
+
+  def load(self):
+    return self.load_wsgiapp()
+
+
+def rungunicornserver():
+  bind_addr = conf.HTTP_HOST.get() + ":" + str(conf.HTTP_PORT.get())
+
+  options = {
+      'access_log_format': None,
+      'accesslog': '-',
+      'backlog': None,
+      'bind': [bind_addr],
+      'ca_certs': None,
+      'capture_output': None,
+      'cert_reqs': None,
+      'certfile': None,
+      'chdir': None,
+      'check_config': None,
+      'ciphers': None,
+      'config': None,
+      'daemon': None,
+      'do_handshake_on_connect': None,
+      'enable_stdio_inheritance': None,
+      'errorlog': '-',
+      'forwarded_allow_ips': None,
+      'graceful_timeout': None,
+      'group': None,
+      'initgroups': None,
+      'keepalive': None,
+      'keyfile': None,
+      'limit_request_field_size': None,
+      'limit_request_fields': None,
+      'limit_request_line': None,
+      'logconfig': None,
+      'logger_class': None,
+      'loglevel': 'debug',
+      'max_requests': None,
+      'max_requests_jitter': None,
+      'paste': None,
+      'pidfile': None,
+      'preload_app': None,
+      'proc_name': None,
+      'proxy_allow_ips': None,
+      'proxy_protocol': None,
+      'pythonpath': None,
+      'raw_env': None,
+      'raw_paste_global_conf': None,
+      'reload': None,
+      'reload_engine': None,
+      'sendfile': None,
+      'spew': None,
+      'ssl_version': None,
+      'statsd_host': None,
+      'statsd_prefix': None,
+      'suppress_ragged_eofs': None,
+      'syslog': None,
+      'syslog_addr': None,
+      'syslog_facility': None,
+      'syslog_prefix': None,
+      'threads': conf.CHERRYPY_SERVER_THREADS.get(),
+      'timeout': None,
+      'umask': None,
+      'user': None,
+      'worker_class': conf.GUNICORN_WORKER_CLASS.get(),
+      'worker_connections': None,
+      'worker_tmp_dir': None,
+      'workers': conf.GUNICORN_NUMBER_OF_WORKERS.get() if conf.GUNICORN_NUMBER_OF_WORKERS.get() is not None else number_of_workers()
+  }
+  StandaloneApplication(handler_app, options).run()
+
+if __name__ == '__main__':
+    rungunicornserver()