主要内容:
- 1.WSGI
- 2.uWSGI
- 3.uwsgi
1. WSGI
1.1 WSGI相关概述
引子: wsgi server (比如uWSGI) 要和 wsgi application(比如django )交互,uwsgi需要将过来的请求转给django 处理,那么uWSGI 和 django的交互和调用就需要一个统一的规范,这个规范就是WSGI WSGI(Web Server Gateway Interface)
- WSGI,全称 Web Server Gateway Interface,或者 Python Web Server Gateway Interface ,是为 Python 语言定义的 Web 服务器和 Web 应用程序或框架之间的一种简单而通用的接口。自从 WSGI 被开发出来以后,许多其它语言中也出现了类似接口。
- WSGI 的官方定义是,the Python Web Server Gateway Interface。从名字就可以看出来,这东西是一个Gateway,也就是网关。网关的作用就是在协议之间进行转换。
- WSGI 是作为 Web 服务器与 Web 应用程序或应用框架之间的一种低级别的接口,以提升可移植 Web 应用开发的共同点。WSGI 是基于现存的 CGI 标准而设计的
1.2 定义一个简版的WSGI 接口
- 一个 application函数
from wsgiref.simple_server import make_server def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) return [b'<h1>Hello, web!</h1>'] httpd = make_server('', 8080, application) print('Serving HTTP on port 8080...') # 开始监听HTTP请求: httpd.serve_forever()
- 相关分析
注意: 整个application()函数本身没有涉及到任何解析HTTP的部分,也就是说,底层代码不需要我们自己编写, 我们只负责在更高层次上考虑如何响应请求就可以了。 application()函数必须由WSGI服务器来调用。有很多符合WSGI规范的服务器,我们可以挑选一个来用。 Python内置了一个WSGI服务器,这个模块叫wsgiref application()函数就是符合WSGI标准的一个HTTP处理函数,它接收两个参数: //environ:一个包含所有HTTP请求信息的dict对象; //start_response:一个发送HTTP响应的函数。 在application()函数中,调用:start_response('200 OK', [('Content-Type', 'text/html')]) 就发送了HTTP响应的Header,注意Header只能发送一次,也就是只能调用一次start_response()函数。 start_response()函数接收两个参数,一个是HTTP响应码,一个是一组list表示的HTTP Header,每个Header用一个包含两个str的tuple表示。 通常情况下,都应该把Content-Type头发送给浏览器。其他很多常用的HTTP Header也应该发送。 然后,函数的返回值b'<h1>Hello, web!</h1>'将作为HTTP响应的Body发送给浏览器。 有了WSGI,我们关心的就是如何从environ这个dict对象拿到HTTP请求信息,然后构造HTML,通过start_response()发送Header,最后返回Body。
1.3 WSGI 的作用
WSGI有两方:“服务器”或“网关”一方,以及“应用程序”或“应用框架”一方。服务方调用应用方,提供环境信息,以及一个回调函数(提供给应用程序用来将消息头传递给服务器方),并接收Web内容作为返回值。
所谓的 WSGI中间件同时实现了API的两方,因此可以在WSGI服务和WSGI应用之间起调解作用:从WSGI服务器的角度来说,中间件扮演应用程序,而从应用程序的角度来说,中间件扮演服务器。“中间件”组件可以执行以下功能:
重写环境变量后,根据目标URL,将请求消息路由到不同的应用对象。
允许在一个进程中同时运行多个应用程序或应用框架。
负载均衡和远程处理,通过在网络上转发请求和响应消息。
进行内容后处理,例如应用XSLT样式表
2. uWSGI
1.1 uWSGI的相关概述
uWSGI是一个Web服务器,它实现了WSGI协议、uwsgi、http等协议。Nginx中HttpUwsgiModule的作用是与uWSGI服务器进行交换。
- WSGI是一种通信协议。
- uwsgi同WSGI一样是一种通信协议。
- 而uWSGI是实现了uwsgi和WSGI两种协议的Web服务器。
1.2 uWSGI配置的的理解
(1) 通信接口: http/http-socket/socket
socket 方式: socket = 0.0.0.0:8000
- 现在大部分web服务器(如nginx)支持uwsgi,这是这三种方式最高效的一种形式,socket通信速度会比http快 ,
- 注: 指定socket协议,运行django,只能与nginx结合时
http 方式: http = 0.0.0.0:8000
上面两个图都是http方式,使用http启动uwsgi,系统会额外启动一个http进程,从级别上来说,它和nginx是同一级别的,所以客户端和uwsgi通信,完全可以绕过nginx,不需要额外进行一个转发(如第二张图一样),但很显然,这是并不是一个很明智但选择,这样会失去了nginx很多优秀的功能。
因此: 如果你没用nginx,只想自己启动一个http界面,用这个
--- 另外 还有一方式,但是在uWSGI的配置文件中并未展示出: 具体使用待定---
http -socket 方式 http-socket=127.0.0.1:8000
http-socket方式,这个适用于web服务器不支持uwsgi时。
(2) processes/workers.
表示开启多进程,根据你的应有开启合适的进程数,在一些参考资料上,可能会看到processes = 2 * cpucores或者workers = 2 * cpucores,如果应有比较简单,这样设置一般可以。如果想更合理,官方提供了uwsgitop去获得一个较为合理的值。
(3)threads & enable-threads
python中存在GIL,实际上不存在真正意义上的多线程,但是否需要,这个就根据各自但需求设定了。结合processes:
- processes=2
- threads=4
表示2个进程,每个进程中有4个线程。
- 由于GIL的存在,uwsgi索性默认不支持多线程,不对GIL进行初始化。但如果希望程序中的线程发挥作用,需要加入enable-threads=True;
- 但如果已经在多线程模式(使用 threads 选项)下,那么uWSGI将会自动启用线程支持。
(4)uid & gid & chmod-socket
uwsgi不建议使用root权限去启动uwsgi实例。可以通过root用户去运行uwsgi文件,当通过uid和gid去修改用户(移除root权限)。并且,如果你使用的是socket的通信方式,最好加上chmod-socket字段,在linux下,socket的启动方式,套接字类似文件,你必须保证有权限去读取它。
- chmod-socket=664
- uid=1000
- gid=1000
(5)master
意味着启动一个master主进程来管理其他进程,建议启动这个进程,在管理的时候比较方便;如果kill这个master进程,相当于关闭所有的uwsgi进程
----------- 内容摘自: https://blog.csdn.net/l_vip/article/details/81487608 -------------------------------------------
3. uwsgi
概述: uwsgi是服务器和服务端应用程序的通信协议,规定了怎么把请求转发给应用程序和返回
关于 uwsgi的理解
本文根据代码阅读以及参照多种文档,描述了uwsgi的启动多进程+多线程工作原因,以及thunder_lock参数的作用:
- uwsgi是用c语言写的一个webserver,可以启动多个进程,进程里面可以启动多个线程来服务。进程分为主进程和worker进程,worker里面可以有多个线程。
- 一开始进入main函数,启动这个就是主进程了,uwsgi_setup函数(主进程)里面针对选项参数做一些处理,执行环境设置,执行一些hook,语言环境初始化(python),如果没有设置延迟加载app,则app在主进程加载;如果设置了延迟加载,则在每一个worker进程中都会加载一次。uwsgi_setup函数还执行了插件的初始化(当前我只关心http、python:http是gateway类型的插件,这种插件是向外暴露http服务的,python的requests类型的插件,用来服务请求的)、tcp socket的绑定与监听(这个是指http与work之间的通信,且它的端口是自动产生的)。最后uwsgi主进程fork了指定worker进程用来接收(accept)请求。虽然在setup中就fork了子进程,但是现在还没有开始accept。
- wsgi_run函数就是真正的开始执行了,这个函数分为俩个部分,主进程执行部分和worker进程执行部分。主进程执行部分是一个无限循环,他可以执行特定的hook以及接收信号等,总之是用来管理worker进程以及一些定时或者事件触发任务。worker部分:注册型号处理函数,执行一些hook,循环接收(accept)请求。在启动woker时可以根据--threads参数指定要产生的线程个数,否则只在当前进程启动一个线程。这些线程循环接收请求并处理。
- 在worker中接收请求的函数wsgi_req_accept有一个锁:thunder_lock,这个锁用来串行化accept,防止“惊群”现象:参考这里
- 惊群现象出现在这样的情况:主进程绑定并监听socket,然后调用fork,在各个子进程进行accept。无论任何时候,只要有一个连接尝试连接,所有的子进程都将被唤醒,但只有一个会连接成功,其他的会得到一个EAGAIN的错误,浙江导致巨大的CPU资源浪费,如果在进程中使用线程,这个问题被再度放大。一个解决方法是串行化accept,在accept前防止一个锁。
-------- 以上内容摘自: https://blog.csdn.net/pzqingchong/article/details/79522215 -------