uwsgi 和 wsgi 协议


Author: yifei / Created: June 21, 2017, 11:37 p.m. / Modified: June 22, 2017, 10:11 a.m. / Edit

uWSGI is a web server than runs python web frameworks. uwsgi(lower case) is the protocol it communicates with front end web servers(nginx)

wsgi 协议

YN:

值得注意的是, wsgi实际上定义了一个同步的模型, 也就是每一个客户请求会调用一个同步的函数, 这样也就无法发挥curio的特性.

see: https://bottlepy.org/docs/dev/async.html

http://uwsgi-docs-cn.readthedocs.io/zh_CN/latest/WSGIquickstart.html

digital ocean's article is more practical https://www.digitalocean.com/community/tutorials/how-to-deploy-python-wsgi-applications-using-uwsgi-web-server-with-nginx

两个最简单的例子

其中实现simple_app函数也就是实现了wsgi协议.需要注意的有一下三点:

1. environ字典中包含的变量
2. start_response的参数
3. simple_app的调用次序和返回值

HELLO_WORLD = b"Hello world!\n" def simple_app(environ, start_response): """Simplest possible application object""" status = '200 OK' response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers) return [HELLO_WORLD] class AppClass: """Produce the same output, but using a class (Note: 'AppClass' is the "application" here, so calling it returns an instance of 'AppClass', which is then the iterable return value of the "application callable" as required by the spec. If we wanted to use instances of 'AppClass' as application objects instead, we would have to implement a '__call__' method, which would be invoked to execute the application, and we would need to create an instance for use by the server or gateway. """ def __init__(self, environ, start_response): self.environ = environ self.start = start_response def __iter__(self): status = '200 OK' response_headers = [('Content-type', 'text/plain')] self.start(status, response_headers) yield HELLO_WORLD

而对于server/gateway来说, 每接收到一个http客户端, 都会调用一次这个application callale

import os, sys enc, esc = sys.getfilesystemencoding(), 'surrogateescape' def unicode_to_wsgi(u): # Convert an environment variable to a WSGI "bytes-as-unicode" string return u.encode(enc, esc).decode('iso-8859-1') def wsgi_to_bytes(s): return s.encode('iso-8859-1') def run_with_cgi(application): environ = {k: unicode_to_wsgi(v) for k,v in os.environ.items()} environ['wsgi.input'] = sys.stdin.buffer environ['wsgi.errors'] = sys.stderr environ['wsgi.version'] = (1, 0) environ['wsgi.multithread'] = False environ['wsgi.multiprocess'] = True environ['wsgi.run_once'] = True if environ.get('HTTPS', 'off') in ('on', '1'): environ['wsgi.url_scheme'] = 'https' else: environ['wsgi.url_scheme'] = 'http' headers_set = [] headers_sent = [] def write(data): out = sys.stdout.buffer if not headers_set: raise AssertionError("write() before start_response()") elif not headers_sent: # Before the first output, send the stored headers status, response_headers = headers_sent[:] = headers_set out.write(wsgi_to_bytes('Status: %s\r\n' % status)) for header in response_headers: out.write(wsgi_to_bytes('%s: %s\r\n' % header)) out.write(wsgi_to_bytes('\r\n')) out.write(data) out.flush() def start_response(status, response_headers, exc_info=None): if exc_info: try: if headers_sent: # Re-raise original exception if headers sent raise exc_info[1].with_traceback(exc_info[2]) finally: exc_info = None # avoid dangling circular ref elif headers_set: raise AssertionError("Headers already set!") headers_set[:] = [status, response_headers]

Note: error checking on the headers should happen here,

    # *after* the headers are set.  That way, if an error
    # occurs, start_response can only be re-called with
    # exc_info set.

return write result = application(environ, start_response) try: for data in result: if data: # don't send headers until body appears write(data) if not headers_sent: write('') # send headers now if body was empty finally: if hasattr(result, 'close'): result.close()


有任何问题可以发邮件到 kongyifei (at) gmail.com 讨论