环境说明
- 虚拟机 双核 4G
- ubuntu16.04
- python3.6.6
什么是WSGI
WSGI是Web服务器网关接口。它是一个规范,描述了Web服务器如何与Web应用程序通信,以及Web应用程序如何链接在一起以处理一个请求。
WSGI是一个详细描述的Python标准 PEP 3333。
一个简单的WSGI例子
代码
from wsgiref.simple_server import make_server
def demo_app(environ, start_response):
# 回调 状态码、响应头
start_response("200 OK", [('Content-Type', 'text/plain; charset=utf-8')])
# 返回内容,注意返回内容应该是一个可迭代的bytes, python2.x是str
# 详情请看PEP-3333
return [b"Hello World!"]
if __name__ == '__main__':
with make_server('', 8000, demo_app) as httpd:
httpd.serve_forever()
说明
这里有两个很重要的参数environ和start_response
- environ: 字典类型的客户端请求信息
- start_response: 回调函数,用于返回HTTP响应状态、返回头信息等
访问
http://localhost:8000/
如果看到Hello World! 就正常
封装成类
为了方便扩展我把上面的例子封装成类, 运行效果和上面一致
from wsgiref.simple_server import make_server
class WSGIApplication:
def __call__(self, environ, start_response):
start_response("200 OK", [('Content-Type', 'text/plain; charset=utf-8')])
yield b"Hello World!"
if __name__ == '__main__':
with make_server('', 8000, WSGIApplication) as httpd:
httpd.serve_forever()
封装environ
为了方便请求我把environ封装成一个类
class HTTPRequest:
def __init__(self, environ):
# 可以通过environ.items()看下所有内容
# 可以把url参数,from表单都写到这里
self.method = environ['REQUEST_METHOD'] # 请求方法
self.PATH_INFO = environ['PATH_INFO'] # 请求url
self.status_code = 200
添加请求类
这里仿tornado写一个RequestHandler类, 把常用的GET POST PUT DELETE 都写在里面
class RequestHandler:
def __init__(self, application, request, environ):
self.application = application # wsgi对象
self.request = request # environ封装后的实例对象
self.environ = environ # environ
def get(self, *args, **Kwargs):
"""get 请求方法"""
raise HTTPError(405)
添加路由
class WSGIApplication:
def __init__(self, handlers):
self.handlers = handlers # 路由列表
self.route_handlers = {} # 路由字典,通过url查找调用类
self.add_handlers()
def add_handlers(self):
for handler in self.handlers:
self.route_handlers[handler[0]] = handler[1]
客户端对应写法
class IndexHandler(RequestHandler):
def get(self):
"""重写get方法"""
return b'Hello world!'
if __name__ == '__main__':
# 添加路由
app = WSGIApplication([('/', IndexHandler)])
添加WSGI适配器
class ServerAdapter:
def __init__(self, host='127.0.0.1', port=9000, **kwargs):
self.host = host
self.port = int(port)
self.options = kwargs
def __repr__(self):
return "%s (%s:%d)" % (self.__class__.__name__, self.host, self.port)
def run(self, handler):
pass
class WSGIRefServer(ServerAdapter):
def run(self, handler):
with make_server(self.host, self.port, handler) as httpd:
httpd.serve_forever()
class BjoernServer(ServerAdapter):
def run(self, handler):
"""
bjoern是一个C语言编写高性能WSGI服务器
https://github.com/jonashaag/bjoern
:param handler:
:return:
"""
from bjoern import run
run(handler, self.host, self.port)
def run(server=WSGIRefServer, host='localhost', port=9000, app=None):
httpd = server(host=host, port=port)
httpd.run(app)
测试代码
from apiserver import RequestHandler, WSGIApplication, run
class IndexHandler(RequestHandler):
def get(self):
print(self.get_argument('name'))
return {'msg': 'get index'}
def post(self):
print(self.get_body_argument())
return 'post index'
def put(self):
print(self.get_json_argument())
return {'msg': 'put index'}
if __name__ == '__main__':
app = WSGIApplication([('/', IndexHandler)])
run(app=app)
测试
相比之下bjoern比自带的wsgiref快60多倍
# wsgiref
~$ wrk -t4 -c1000 -d30s http://127.0.0.1:9000/
Running 30s test @ http://127.0.0.1:9000/
4 threads and 1000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 13.79ms 105.37ms 1.80s 97.79%
Req/Sec 503.30 327.59 1.12k 59.09%
3440 requests in 30.09s, 540.86KB read
Socket errors: connect 0, read 3440, write 0, timeout 0
Requests/sec: 114.32
Transfer/sec: 17.97KB
# bjoern
~$ wrk -t4 -c1000 -d30s http://127.0.0.1:9000/
Running 30s test @ http://127.0.0.1:9000/
4 threads and 1000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 137.30ms 33.55ms 462.43ms 75.77%
Req/Sec 1.87k 0.87k 3.86k 75.34%
213376 requests in 30.05s, 30.41MB read
Requests/sec: 7101.14
Transfer/sec: 1.01MB
完整代码
https://github.com/ilyq/wsgi-server-py
参考
http://wsgi.tutorial.codepoint.net/#
https://wsgi.readthedocs.io/en/latest/
https://www.letiantian.me/2015-09-10-understand-python-wsgi/
网友评论