https://blog.csdn.net/a519640026/article/details/76157976

请求从 Nginx 到 uwsgi 到 django 交互概览

作为python web开发,我们首先要弄清楚,到底一个请求过来,发生了什么事,请求的传递是怎么样完成的,由nginx是怎么转发到uwsgi, uwsgi又是怎样把请求传给到我们的框架(django or falsk)由我们自己写的代码处理,返回数据给客户端的。因此我作了以下一个粗略的流程图:

WSGI 协议

从上面的图看得出 wsgi server (比如uwsgi) 要和 wsgi application(比如django )交互,uwsgi需要将过来的请求转给django 处理,那么uwsgi 和 django的交互和调用就需要一个统一的规范,这个规范就是WSGI WSGI(Web Server Gateway Interface) ,WSGI是 Python PEP333中提出的一个 Web 开发统一规范

Web 应用的开发通常都会涉及到 Web 框架(django, flask)的使用,各个 Web 框架内部由于实现不同相互不兼容,给用户的学习,使用和部署造成了很多麻烦。

正是有了WSGI这个规范,它约定了wsgi server 怎么调用web应用程序的代码,web 应用程序需要符合什么样的规范,只要 web 应用程序和 wsgi server 都遵守 WSGI 协议,那么,web 应用程序和 wsgi server就可以随意的组合。 比如uwsgi+django , uwsgi+flask, gunicor+django, gunicor+flask 这些的组合都可以任意组合,因为他们遵循了WSGI规范。

WSGI 标准

“server” 或 “gateway” 端

“application” 或 “framework” 端

为了方便理解,我们可以把server具体成 uwsgi, application具体成django

这里可以看到,WSGI 服务器需要调用应用程序的一个可调用对象,这个可调用对象(callable object)可以是一个函数,方法,类或者可调用的实例,总之是可调用的。

下面是一个 callable object 的示例,这里的可调用对象是一个函数:

defsimple_app(environ, start_response):"""Simplest possible application object"""status= '200 OK'response_headers= [('Content-type', 'text/html')]

start_response(status, response_headers)return ['Hello World']

这里,我们首先要注意,这个对象接收两个参数:

environ:请求的环境变量,它是一个字典,包含了客户端请求的信息,如 HTTP 请求的首部,方法等信息,可以认为是请求上下文,

start_response:一个用于发送HTTP响应状态(HTTP status )、响应头(HTTP headers)的回调函数。在返回内容之前必须先调用这个回掉函数

上面的 start_response 这个回调函数的作用是用于让 WSGI Server 返回响应的 HTTP 首部和 HTTP 状态码。这个函数有两个必须的参数,返回的状态码和返回的响应首部组成的元祖列表。返回状态码和首部的这个操作始终都应该在响应 HTTP body 之前执行。

还需要注意的是,最后的返回结果,应该是一个可迭代对象,这里是将返回的字符串放到列表里。如果直接返回字符串可能导致 WSGI 服务器对字符串进行迭代而影响响应速度。

当然,这个函数是一个最简单的可调用对象,它也可以是一个类或者可调用的类实例。

WSGI 实例

wsgi application 的代码 app.py

defapplication(env, start_response):

start_response('200 OK', [('Content-Type', 'text/html'), ('X-Coder', 'Cooffeeli')])return ['

你好!!世界

']

wsgi server 代码 wsgi_server.py

我们可以借助 python 的 wsgiref 库运行一个 WSGI 服务器(当然这个 WSGI 服务器同时也是 Web 服务器),用它来运行我们的 application

1 from wsgiref.simple_server importmake_server2 from app importapplication3

4 #启动 WSGI 服务器

5 httpd =make_server (6 'localhost',7 9000,8 application #这里指定我们的 application object)

9 )10 #开始处理请求

11 httpd.handle_request()12

python wsgiref_server.py

运行上面的程序,并访问 http://localhost:9000 , 将返回此次请求所有的首部信息。

这里,我们利用 environ 字典,获取了请求中所有的变量信息,构造成相应的内容返回给客户端。

environ 这个参数中包含了请求的首部,URL,请求的地址,请求的方法等信息。可以参考 PEP3333来查看 environ 字典中必须包含哪些 CGI 变量。

自己实现WSGI Server

既然我们知道了WSGI的规范,我们完全可以自己实现一个WSGI Server

根据这个规范,我们可以总结WSGI Server需要实现以下功能:

监听端口,接收请求

接受HTTP请求后,解析HTTP协议

根据HTTP内容,生成env参数,该参数包括HTTP,wsgi信息,可以看作是请求上下文

实现一个start_response函数,作为调用application的参数,用作application回调函数,负责http相应头

实现代码: WSGIServer.py

1 #!/usr/bin/env python

2 #-*- coding: utf-8 -*-

3 importsocket4 importsys5 importStringIO6 from app importapplication7 from datetime importdatetime8

9 classWSGIServer(object):10

11 def __init__(self, server_address):12 """初始构造函数, 创建监听socket"""

13 self.listen_sock =socket.socket(socket.AF_INET, socket.SOCK_STREAM)14 self.listen_sock.bind(server_address)15 self.listen_sock.listen(5)16 (host, port) =self.listen_sock.getsockname()17 self.server_port =port18 self.server_name =socket.getfqdn(host)19

20

21 defset_application(self, application):22 """设置wsgi application, 供server 调用"""

23 self.application =application24

25

26 defget_environ(self):27 """构造WSGI环境变量,传给application的env参数"""

28 self.env ={29 'wsgi.version': (1, 0),30 'wsgi.url_scheme': 'http',31 'wsgi.errors': sys.stderr,32 'wsgi.multithread': False,33 'wsgi.run_once': False,34 'REQUEST_METHOD': self.request_method,35 'PATH_INFO': self.request_path,36 'SERVER_NAME': self.server_name,37 'SERVER_PORT': str(self.server_port),38 'wsgi.input': StringIO.StringIO(self.request_data),39 }40 returnself.env41

42

43 defstart_response(self, http_status, http_headers):44 """构造WSGI响应, 传给application的start_response"""

45 self.http_status =http_status46 self.http_headers =dict(http_headers)47 headers ={48 'Date': datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT'),49 'Server': 'WSGIServer 1.0'

50 }51 self.http_headers.update(headers)52

53

54 defparse_request(self, text):55 """获取http头信息,用于构造env参数"""

56 request_line =text.splitlines()[0]57 request_info = request_line.split(' ')58 (self.request_method,59 self.request_path,60 self.request_version) =request_info61

62

63 defget_http_response(self, response_data):64 """完成response 内容"""

65 res = 'HTTP/1.1 {status} \r\n'.format(status=self.http_status)66 for header inself.http_headers.items():67 res += '{0}: {1} \r\n'.format(*header)68

69 res += '\r\n'

70

71 res_body = ''

72 for val inresponse_data:73 res_body +=val74

75 res +=res_body76

77 returnres78

79

80 defhandle_request(self):81 """处理请求"""

82 #初始版本,只接受一个请求

83 conn, addr =self.listen_sock.accept()84

85 #获取http 请求的request内容

86 self.request_data = conn.recv(1024)87 self.parse_request(self.request_data)88

89 #构造调用application需要的两个参数 env, start_response

90 env =self.get_environ()91 start_response =self.start_response92

93 #调用application, 并获取需要返回的http response内容

94 response_data =self.application(env, start_response)95

96 #获取完整http response header 和 body, 通过socket的sendall返回到客户端

97 res =self.get_http_response(response_data)98 conn.sendall(res)99

100 #脚本运行完毕也会结束

101 conn.close()102

103

104 defmake_server(server_address, application):105 """创建WSGI Server 负责监听端口,接受请求"""

106 wsgi_server =WSGIServer(server_address)107 wsgi_server.set_application(application)108

109 returnwsgi_server110

111

112 SERVER_ADDRESS = (HOST, PORT) = '', 8124

113 wsgi_server =make_server(SERVER_ADDRESS, application)114 wsgi_server.handle_request()115

上面的 WSGI 服务器运行过程为:

初始化,创建套接字,绑定端口

接收客户端请求

解析 HTTP 协议

构造 WSGI 环境变量(environ)

调用 application

回调函数 start_response 设置好响应的状态码和首部

返回信息

WSGI SERVER---> WSGI APPLICATION

至此, wsgi server -> wsgi application 的交互讲解完毕, 下面我们继续看nginx->uwsgi交互过程

启动 uwsgi

上面说了我们自己实现WSGI Server的过程,现在我们用uwsgi 来作为Server

运行监听请求uwsgi

uwsgi --http :9090 --wsgi-file foobar.py --master --processes 4 --threads 2

执行这个命令会产生4个uwsgi进程(每个进程2个线程),1个master进程,当有子进程死掉时再产生子进程,1个 the HTTP router进程,一个6个进程。

这个Http route进程的地位有点类似nginx,(可以认为与nginx同一层)负责路由http请求给worker, Http route进程和worker之间使用的是uwsgi协议

FastCgi协议, uwsgi协议, http协议有什么用?

在构建 Web 应用时,通常会有 Web Server (nginx)和 Application Server(wsgi server eg:uwsgi) 两种角色。其中 Web Server 主要负责接受来自用户的请求,解析 HTTP 协议,并将请求转发给 Application Server,Application Server 主要负责处理用户的请求,并将处理的结果返回给 Web Server,最终 Web Server 将结果返回给用户。

由于有很多动态语言和很多种 Web Server,他们彼此之间互不兼容,给程序员造成了很大的麻烦。因此就有了 CGI/FastCGI ,uwsgi 协议,定义了 Web Server 如何通过输入输出与 Application Server 进行交互,将 Web 应用程序的接口统一了起来。

总而言之, 这些协议就是进程交互的一种沟通方式。

举个例子:美国人和中国人沟通必须要有一个公共的语言:英语, 这时候英语就是两个人沟通的协议, 不然,一个说英语(uwsgi协议), 一个说中文(fastcgi协议)是肯定会乱码的,处理不成功的。用同一个协议,大家都知道该如何解析过来的内容。

所以,nginx 和 uwsgi交互就必须使用同一个协议,而上面说了uwsgi支持fastcgi,uwsgi,http协议,这些都是nginx支持的协议,只要大家沟通好使用哪个协议,就可以正常运行了。

将uwsgi 放在nginx 后面

将uwsgi 放在nginx后面,让nginx反向代理请求到uwsgi

uwsgi 原生支持HTTP, FastCGI, SCGI,以及特定的uwsgi协议, 性能最好的明显时uwsgi, 这个协议已经被nginx支持。

所以uwsgi 配置使用哪个协议,nginx 要使用对应协议

#使用http协议

uwsgi --http-socket 127.0.0.1:9000 --wsgi-file app.py

#nginx配置

lcation /{

proxy_pass127.0.0.1:9000;

}

更多协议

[uwsgi]#使用uwsgi协议 socket, uwsgi-socket 都是uwsgi协议#bind to the specified UNIX/TCP socket using default protocol#UNIX/TCP 意思时可以UNIX: xx.sock, 或者 TCP: 127.0.0.1:9000 他们是都可以的#UNIX 没有走TCP协议,不是面向连接, 而是直接走文件IO#nginx 使用uwsgi_pass

socket = 127.0.0.1:9000socket= /dev/shm/owan_web_uwsgi.sock

uwsgi-socket = /dev/shm/owan_web_uwsgi.sock#nginx 使用 uwsgi_pass /dev/shm/owan_web_uwsgi.sock;

#使用fastcgi协议 fastcgi-socket#bind to the specified UNIX/TCP socket using FastCGI protocol#nginx 就可以好象PHP那样配置 使用fastcgi_pass

fastcgi-socket = /dev/shm/owan_web_uwsgi.sock#nginx 使用fastcgi_pass /dev/shm/owan_web_uwsgi.sock;

#使用http协议 http-socket#bind to the specified UNIX/TCP socket using HTTP protocol#nginx 使用proxy_pass#原来proxy_pass 是http协议,但不一定要用TCP#proxy_pass http://unix:/dev/shm/owan_web_uwsgi.sock;

http-socket = /dev/shm/owan_web_uwsgi.sock#nginx 使用 proxy_pass /dev/shm/owan_web_uwsgi.sock;

chdir= /data/web/advance_python/uwsgi/wsgi-file =app.py

processes= 4threads= 2master=true

结束 :

至此,nginx ->uwsgi ->web 框架 以及 WSGI的相关知识已经讲解完了。 需要补充的是,我们自己实现的WSGI Server只能支持一个请求,在之后的日子,我会再写一些教程,关于socket IO 复用 和线程池 让我们自己写server支持多请求,多并发的功能

python uwsgi_python nginx+uwsgi+WSGI 处理请求详解相关推荐

  1. python的get和post方式请求详解

    来自:http://blog.csdn.net/jianhong1990/article/details/8090204 1.使用get方式时,url类似如下格式: index.jsp?id=100& ...

  2. Nginx正则表达式之匹配操作符详解

    2019独角兽企业重金招聘Python工程师标准>>> Nginx正则表达式之匹配操作符详解 nginx可以在配置文件中对某些内置变量进行判断,从而实现某些功能.例如:防止rewri ...

  3. Nginx 自编译及编译参数详解

    Nginx Nginx(发音同"engine X")是异步框架的网页服务器,也可以用作反向代理.负载平衡器和HTTP缓存.该软件由俄罗斯程序员伊戈尔·赛索耶夫开发并于2004年首次 ...

  4. nginx配置文件及工作原理详解

    nginx配置文件及工作原理详解 1 nginx配置文件的结构 2 nginx工作原理 1 nginx配置文件的结构 1)以下是nginx配置文件默认的主要内容: #user nobody; #配置用 ...

  5. python 3.x 爬虫基础---http headers详解

    python 3.x 爬虫基础 python 3.x 爬虫基础---http headers详解 python 3.x 爬虫基础---Urllib详解 python 3.x 爬虫基础---Requer ...

  6. nginx下gzip配置参数详解

    这篇文章主要介绍了nginx下gzip配置参数详解,本文同时给出了配置例子,以及一些注意事项,需要的朋友可以参考下 Nginx自带的有gzip模块 http://wiki.nginx.org/Ngin ...

  7. Nginx开启Gzip压缩配置详解

    Nginx开启Gzip压缩配置详解 最近生产上发生了一些问题,原先所有的静态资源文件都是经过gzip压缩的,然而这几天突然都没有压缩了,经过一顿排查,发现是Nginx的配置有问题,借此机会详细了解了N ...

  8. python菜单怎么做_Python 城市菜单详解(超详解)

    print("--------城市查询系统---------") print("--------按数值进行查询--------") menu={"内蒙 ...

  9. python gil 解除_详解Python中的GIL(全局解释器锁)详解及解决GIL的几种方案

    先看一道GIL面试题: 描述Python GIL的概念, 以及它对python多线程的影响?编写一个多线程抓取网页的程序,并阐明多线程抓取程序是否可比单线程性能有提升,并解释原因. GIL:又叫全局解 ...

最新文章

  1. 迪士尼研究院等将人造“神经纤维”用于软体机器人,赋予其“本体感知能力”!...
  2. oracle的clob赋值_JAVA处理Oracle数据库CLOB读写操作
  3. Hdu 1217 最短路.cpp
  4. python算方差_python计算均值方差
  5. mac的ideal终端中mvn命令不生效 commang not found
  6. ABAP新的关键字:BASE
  7. oracle clob截取_Oracle数据库设计规范建议
  8. C++ Primer Plus 随记(第八章)
  9. GPU Architect Functional Verification
  10. 程序员经常遇见的9大困难你造么?
  11. linux环境下的TIME_WAIT和CLOSE_WAIT问题解决方法
  12. 25-70K*14薪| 梅卡曼德视觉算法、C++软件开发工程师等职位招聘
  13. python arp 网关_python使用arp欺骗伪造网关的方法
  14. 【原理】 进程调度算法
  15. pink老师 JS p61课后作业
  16. 计算机界一些有意思的命名,程序员们也都很有情调嘛!
  17. 网站文章采集与伪原创技巧
  18. 手机ssh发送文件到服务器,使用ssh传输文件
  19. 5000多字深度分析:从电视剧《鱿鱼游戏》看国内SaaS行业
  20. Gartner发布2022年中国智慧城市和可持续发展技术成熟度曲线

热门文章

  1. python 数据库模块_MySQl 数据库 之 python模块 pymysql 简单介绍
  2. Tensorflow——placeholder(矩阵运算小实例)
  3. OpenCV彩色目标跟踪
  4. tf.nn.rnn_cell.DropoutWrapper用法细节案例1
  5. 试验笔记 - Eclipse的.class反编译插件
  6. PAT Basic 1011
  7. python爬虫(四)_urllib2库的基本使用
  8. linux常用命令整理1
  9. 团队作业4——第一次项目冲刺(Alpha版本)
  10. SABAPDEMOS 这个包下好多好玩儿的,可以看看