Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding gunicorn to server_adapters #821

Closed
wants to merge 8 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 102 additions & 7 deletions py4web/server_adapters.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
wsservers_list = []

__all__ = [
"gunicorn",
"gunicornGevent",
"gevent",
"geventWebSocketServer",
"geventWs", # short_name
Expand All @@ -22,12 +24,14 @@
# ---------------------- utils -----------------------------------------------

# export PY4WEB_LOGS=/tmp # export PY4WEB_LOGS=
def get_log_file():
def get_log_file(out_banner = True):
log_dir = os.environ.get("PY4WEB_LOGS", None)
log_file = os.path.join(log_dir, "server-py4web.log") if log_dir else None
if log_file:
print(f"log_file: {log_file}")
return log_file
if log_dir and os.path.isdir(log_dir):
log_file = os.path.join (log_dir, 'server-py4web.log')
if out_banner:
print(f"log_file: {log_file}")
return log_file
return None


def check_level(level):
Expand Down Expand Up @@ -119,6 +123,97 @@ def get_workers(opts, default=10):


# ---------------------- servers -----------------------------------------------
def gunicorn():

from gevent import local # pip install gevent gunicorn
import threading

# To use gevent monkey.patch_all()
# run ./py4web.py run apps -s gunicornGevent ......
if isinstance(threading.local(), local.local):
print( 'gunicorn: monkey.patch_all() applied' )

class GunicornServer(ServerAdapter):
""" https://docs.gunicorn.org/en/stable/settings.html """
# https://pawamoy.github.io/posts/unify-logging-for-a-gunicorn-uvicorn-app/
# ./py4web.py run apps -s gunicorn --watch=off --port=8000 --ssl_cert=cert.pem --ssl_key=key.pem -w 6 -L 20
#
# ./py4web.py run apps -s gunicornGevent --watch=off --port=8000 --ssl_cert=cert.pem --ssl_key=key.pem -w 6 -L 20

def run(self, app_handler):
from gunicorn.app.base import BaseApplication

config = {
"bind": f"{self.host}:{self.port}",
"workers": get_workers(self.options),
"certfile": self.options.get("certfile", None),
"keyfile": self.options.get("keyfile", None),
}

if not self.quiet:

level = check_level (self.options["logging_level"])
log_file = get_log_file ( out_banner= False)

logger = logging_conf(level )
log_to = "-" if log_file is None else log_file

config.update({
"loglevel": logging.getLevelName( level ),
"access_log_format" : '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"' ,
"accesslog": log_to,
"errorlog": log_to,
})

class GunicornApplication(BaseApplication):
def load_config(self):

# export GUNICORN_WORKERS=2
# export GUNICORN_BACKLOG=4096
# export GUNICORN_worker_connections=100
#
# tested with ssep4w https://github.com/ali96343/lvsio
# export GUNICORN_worker_class=sync
# export GUNICORN_worker_class=gthread
#
# To use gevent monkey.patch_all()
# run ./py4web.py run apps -s gunicornGevent ......
# export GUNICORN_worker_class=gevent
# export GUNICORN_worker_class=gunicorn.workers.ggevent.GeventWorker
#
# pip install gunicorn[eventlet]
# export GUNICORN_worker_class=eventlet
#
# pip install tornado # for special app
# export GUNICORN_worker_class=tornado

# bad: $pip install "uvicorn[standard]" gunicorn
# $export GUNICORN_worker_class=uvicorn.workers.UvicornWorker
# $export GUNICORN_worker_class=aiohttp.worker.GunicornWebWorker
#
# time for i in {1..5000}; do curl -k http://localhost:8000/todo &>/dev/null ; done

gunicorn_vars = dict()

for k,v in os.environ.items():
if k.startswith("GUNICORN_") and v:
key = k.split('_', 1)[1].lower()
gunicorn_vars[key] = v

if gunicorn_vars:
config.update( gunicorn_vars )
print ('gunicorn config:',config)

for key, value in config.items():
self.cfg.set(key, value)

def load(self):
return app_handler

GunicornApplication().run()
return GunicornServer
gunicornGevent = gunicorn



def gevent():
Expand All @@ -139,7 +234,7 @@ def gevent():

class GeventServer(ServerAdapter):
def run(self, app_handler):
logger = "default"
logger = None #"default"

if not self.quiet:
logger = logging_conf(
Expand Down Expand Up @@ -193,7 +288,7 @@ def geventWebSocketServer():

class GeventWebSocketServer(ServerAdapter):
def run(self, app_handler):
logger = "default"
logger = None #"default"

if not self.quiet:
logger = logging_conf(
Expand Down
Loading