微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

WSGI,uwsgi,uWSGI梳理

Wsgi,uwsgi,uWsgi梳理

由于项目用的是uWsgi部署,想要了解uWsgi的工作流程,理清其在整个项目中的角色定位。在看了很多所谓技术文章之后脑子越发混沌,但最终在uWsgi的文档中找到了我所需的所有答案。

uWsgi中文文档的翻译虽然很欢乐,但还是免不了浓重的翻译腔,英文文档的表达更为清晰。

本文涵括

定性

Wsgi

Wsgi规定了web服务器与应用程序如何相互作用,是一种设计规范,也可称为编程接口。

符合这种规范设计的web服务器可称为Wsgi server,而符合这种规范设计的应用程序则可以被所有的Wsgi server调用运行,例如Django,Flask。

uWsgi

uWsgi就是一种符合Wsgi的设计规范的web服务器,支持实现了HTTP, uwsgi等协议,至于用在哪里,后面会涉及到。

uwsgi

uwsgi是uWsgi自己实现的一种传输协议,它用于与Nginx,apache等上游服务器通讯,它很大一部分作用是代替http协议与Nginx等服务器传输数据。

注意

请注意切勿将uwsgi, uWsgi, Wsgi张冠李戴。

概览图

它们之间的关系可以用下图表示

在这里插入图片描述


目前看不懂没关系,现在只是看个大概,下面我们从自己实现一个web服务器的角度来理清这些东西究竟是什么,有什么用。

简陋的web服务器

用一下代码可实现最简陋的web服务器

import socket

ip_port = ('127.0.0.1', 80)
back_log = 10
buffer_size = 1024

alldata = "<h1>Hello World</h1>"

def main():
    webserver = socket.socket(socket.AF_INET, socket.soCK_STREAM)
    webserver.bind(ip_port)
    webserver.listen(back_log)

    while True:
        conn, addr = webserver.accept()
        recvdata = conn.recv(buffer_size)
        conn.sendall(bytes("HTTP/1.1 201 OK\r\n\r\n", "utf-8"))  # 响应头
        conn.sendall(bytes(alldata, "utf-8"))
        conn.close()
        
if __name__ == '__main__':
    main()

这就是一个最简陋的web服务器,但它并没有实际应用意义,它监听本地80端口,接受一个连接后返回一段写好的data。在它身上我们看不到概览图中任何模块的影子。

现在我们开始根据实际需求拓展功能,一点点靠近现实的web服务器。

解析请求数据,处理业务,返回响应,实现http协议支持

作为web服务器,你应该根据相应请求,做出业务处理,返回响应。

而第一步就是解析请求数据,最后一步则是返回响应,用什么格式解析请求,返回响应呢。目前我们监听的是80端口,毫无疑问,我们与client交流使用的协议是http协议。

那么用伪代码表示如下

......
conn, addr = webserver.accept()
recvdata = conn.recv(buffer_size)
# 根据http协议解析请求数据,获取相应参数
data = parse_request(recvdata)
#根据获取的数据做出相应的处理逻辑
......
......
#返回数据
response_data = ......
#根据http协议处理响应的数据,并通过连接返回。
response = parse_response(response_data)
conn.sendall(bytes("HTTP/1.1 201 OK\r\n\r\n", "utf-8"))  # 响应头
conn.sendall(bytes(alldata, "utf-8"))
conn.close()

如此我们实现了web服务器http协议支持。完成的模块如下图所示。

在这里插入图片描述


目前这个服务器至少有了一点实际意义,能通过支持http协议来完成一些业务处理。

业务处理与服务器解耦,使用Wsgi规范编写代码

业务处理逻辑与服务器其他功能分离

现在有更多服务器的项目来临,它们的业务需求不尽相同,但对于服务器来说,监听端口,支持相应协议等功能是必不可少的,本着复用(偷懒)的原则,我们分离web服务器与业务处理逻辑,将业务处理逻辑抽象为一个应用,即app。

服务器解析完请求,将请求数据传入并调用app完成业务处理,app返回响应的数据,服务器再根据协议包装数据返回响应。

那么对于每一个项目,你只需要着力编写app即可。

使用Wsgi规范

你的同事也本着复用(偷懒)的原则,想要用你的web服务器调用他的app。但你们事先并没有沟通过,你的服务器与他的app由于参数,调用形式等原因并不兼容。

但如果你们都根据Wsgi的规范来编写服务器与app,那么你的web服务器就可以严丝合缝地调用他的app。

Wsgi简介

简单看看Wsgi如何规范server和app的。

  • Wsgi协议主要包括server和application两部分,server负责接受客户端请求并进行解析,然后将其传入application,客户端处理请求并将响应头和正文返回服务器。
  • 从application的角度来说,它应当是一个调用的对象(实现了__call__ 函数方法或者类),它接受两个参数:environ和start_response,其主要作用就是根据server传入的environ字典来生成一个“可迭代的”http报文并返回给server
  • 从server的角度来说,其主要工作是解析http请求,生成一个environ字典并将其传递给可调用的application对象;另外,server还要实现一个start_response函数,其作用是生成响应头,start_response作为参数传入application中并被其调用

其数据流如下

在这里插入图片描述

该简介节选自

python从小白到入门:10分钟搞懂WSGI协议

截至目前,我们已完成如下模块

在这里插入图片描述


我们这个服务器的功能与uWsgiuwsgi --http :80 --wsgi-file app.py的模式下运行的功能相似。即对外接受http请求,业务处理,返回http响应。

请注意uWsgi--http--http-socket选项是完全不同的工作模式。

--http选项的工作模式下,前者会创建一个额外的进程,转发请求到一系列的worker进程 ,与apache或者Nginx的定位相似,而后者是令worker为原生使用http协议处理请求。

--http-socket会在使用Nginx/apache等服务器作为上游服务器的架构中,随后会涉及到。

选择Nginx作为上游服务器

uWsgi虽然也能处理静态资源处理,但能力远不如高效的Nginx,且生产环境下一般需要其为集群实现负载均衡的功能。所以我们可以选择选择Nginx作为上游服务器用作处理静态资源以及实现负载均衡。

那么自然而然地,就能想到直接使用Nginx的proxy_pass功能来向uWsgi转发Http包。而proxy_pass模式下与uWsgi的沟通是使用http协议的。uWsgi这边,需要用--http-socket模式启动,Nginx这边需要设置好代理配置,如此Nginx就能向uWsgi转发相应的请求了。

至此,我们已完成如下模块。

在这里插入图片描述

开发uwsgi协议代替http协议用以Nginx与uWsgi沟通

Nginx的proxy_pass代理http请求给uWsgi的架构,有一个小缺点,就是http解析了两次(Nginx与uWsgi各一次),然后uWsgi就开发出一个uwsgi协议,该协议解析比http解析快,只要上游服务器兼容uwsgi协议,那么上游服务器(如Nginx)就可以与uWsgi通过uwsgi协议传输数据。该情况下,http只在上游服务器解析一次,效率更高一点。

当然,这也得上游服务器兼容uwsgi协议才可以,常用的Nginx,Apache都兼容uwsgi协议,而Lighttpd则认为uwsgi协议是重复造轮子,建议使用FastCGI,当然,uWsgi也是支持FastCGI的。

如果要使用uwsgi协议,则需用-socket参数代替--http-socket,上游服务器也应做相应的配置(如Nginx要用uwsgi_pass代替proxy_pass)。

最终我们常用的架构为什么如下图一般,就一清二楚了。

在这里插入图片描述


如有纰漏,欢迎斧正

参考文献

uWSGI项目文档

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐