引子
最近项目引入了apscheduler模块做定时任务,在flask本身的调试server中跑的好好的,但是部署到生产环境,就各种报504错误。于是上网搜了一些解决方案,虽然临时解决了,但总觉得心里不放心。加之之前用uwsgi的时候也有一些问题,一直都是临时解决没有深究。这回想接着这个机会好好读一下官方文档,研究一下uwsgi。
初识wsgi
官方文档说uWSGI这个名字中的"WSGI"部分是为了致敬同名的Python标准。WSGI称为Web服务器网关接口(Python Web Server Gateway Interface),是为Python语言定义的Web服务器和Web应用程序或框架之间的一种简单而通用的接口。WSGI不是服务器,不是模块,不是框架,它是一种通信规范,描述了web server如何与web application通信的规范。要实现WSGI协议,必须同时实现web server和web application,当前运行在WSGI协议之上的web框架有Bottle,Flask,Django。这些框架都自带了WSGI server,但是性能都不好,自带的web server更多的是测试用途,发布时则使用生产环境的WSGI server或者联合nginx做uwsgi。
也就是说,WSGI就像一座桥梁,一边连着web服务器,另一边连着用户应用。
WSGI有两方面:“服务器”或“网关”一方,以及“应用程序”或“应用框架”一方。服务方调用应用方,提供环境信息以及一个回调函数(提供给应用程序来将消息头传递给服务器方),并接收web内容作为返回值。
所谓的WSGI中间件同时实现了API的两方,因此可以在WSGI服务和WSGI应用之间起到调解作用:从WSGI服务器的角度来说,中间件扮演应用程序,而从应用程序的角度来说,中间件扮演服务器。
编写WSGI application
WSGI接口要求应用或框架的开发者实现一个函数,就可以响应HTTP请求。来看一个最简单版本的hello.py:
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return [b'<h1>Hello, World!</h1>']
上面的application()函数就是符合WSGI标准的一个HTTP处理函数,它接收两个参数:
- environ:一个包含所有HTTP请求信息的dict对象
- start_response:一个发送HTTP响应的函数
start_response()函数接收两个参数,一个是HTTP响应码,一个是一组list标识的HTTP Header,每个Header用一个包含两个str的tuple表示。
然后函数的返回值b'<h1>Hello, World!</h1>'作为HTTP响应的Body发送给浏览器。
编写WSGI server
有了应用,还需要一个符合WSGI协议的服务器来响应HTTP请求并调用上面写好的WSGI application。Python内置了一个WSGI服务器,这个模块叫做wsgiref,它是用纯Python编写的WSGI服务器的参考实现。所谓“参考实现”是指完全符合WSGI标准,但是不考虑任何运行效率,仅供开发和测试使用。
下面编写一个server.py,负责启动WSGI服务器,加载application()函数
from wsgiref.simple_server import make_server
from hello import application
# 创建一个服务器,IP地址为空,端口是8000,处理函数是application:
httpd = make_server('', 8000, application)
print('Serving HTTP on port 8000....')
# 开始监听HTTP请求
httpd.serve_forever()
测试运行
确保hello.py和server.py位于同一目录下,然后再命令行输入python server.py来启动服务器:
启动成功后,打开浏览器,输入http://ip:8000/,就可以看到结果。
在命令行里可以看到wsgiref打印的log信息:
从environ中获取数据
以application的角度看,WSGI服务将用户的输入放入到environ参数中传给application。下面例子从url中获取数据:
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
body = '<h1>Hello, %s!</h1>' % (environ['PATH_INFO'][1:] or 'World')
return [body.encode('utf-8')]
这个例子可以获取url
WSGI数据流总结
WSGI的数据流总结如下:
- 浏览器发送HTTP请求到 WSGI server
- WSGI server调用WSGI app的函数,将用户数据以dict对象的形式放到environ参数中。
- application函数调用start_response函数将HTTP响应码和HTTP header传回给WSGI server
- application函数的返回值作为HTTP响应的body返回