- On Benchmarks
- gunicorn Overview
- Basic Running
- Configuration
- Notable HTTP Features
- PEX bundling
- Process Titles
- Project Support Overview
- Conclusion
在最后一部分中,WSGI的基础知识以及通过werkzeug的一些有用的增强。现在,有许多实施WSGI的服务器可以用于更多的生产水平环境。在本文中,我将把Gunicorn视为WSGI服务器解决方案之一。虽然我最初的计划是在一篇文章中介绍所有WSGI服务器,但我发现描述每个服务器的内容都有足够的内容用于一篇文章。考虑到这一点,本系列的未来帖子将引入WSGI服务器解决方案作为他们自己的专门文章。
在基准上
在这里和将来的服务器帖子中不会有太多基准测试。软件正在不断发展,现在可能会在一周内变成非常表现的人。 WSGI服务器在更多的生产环境中也可以成为集群的一部分,也可以多样化以满足不同的应用需求。即使在绩效指标很重要的情况下,也应在更接近部署的更具控制的环境中进行测量。
gunicorn概述
Gunicorn是一款纯Python WSGI的服务器,可在前叉工具模型以及other alternatives上运行。纯粹是Python,它加载了WSGI应用程序via module import。纯Python体系结构还意味着与PyPy进行更轻松的集成,从而在长期运行过程中提供JIT优化。
基本运行
可以通过pip install gunicorn
安装枪支。给定一个简单的WSGI应用程序:
wsgi_test.py
def application(env, start_response):
data = b'Hello World'
status = '200 OK'
response_headers = [
('Content-Type', 'text/plain'),
('Content-Length', str(len(data))),
]
start_response(status, response_headers)
return [data]
枪支将被这样执行:
$ gunicorn --workers=2 wsgi_test:application
[2023-08-23 05:00:33 +0000] [13126] [INFO] Starting gunicorn 21.2.0
[2023-08-23 05:00:33 +0000] [13126] [INFO] Listening at: http://127.0.0.1:8000 (13126)
[2023-08-23 05:00:33 +0000] [13126] [INFO] Using worker: sync
[2023-08-23 05:00:33 +0000] [13176] [INFO] Booting worker with pid: 13176
[2023-08-23 05:00:33 +0000] [13177] [INFO] Booting worker with pid: 13177
其中wsgi_test.py
没有扩展名作为模式,然后是:
,然后是callable的名称(在这种情况下为application
函数)。
配置
可以将Python文件用于配置。配置从设置described in the documentation中撤出。它还可以用作标准的Python文件,这意味着您可以执行以下操作:
gunicorn.config.py
import multiprocessing
bind = "127.0.0.1:8000"
workers = multiprocessing.cpu_count() * 2 + 1
wsgi_app = "wsgi_test:application"
然后可以检查配置以确保其有效:
$ gunicorn --check-config -c gunicorn.config.py
终于使用--config
/-c
选项和配置文件的名称运行Gunicorn:
$ gunicorn -c gunicorn.config.py
[2023-08-23 02:50:37 +0000] [18928] [INFO] Starting gunicorn 21.2.0
[2023-08-23 02:50:37 +0000] [18928] [INFO] Listening at: http://127.0.0.1:8000 (18928)
[2023-08-23 02:50:37 +0000] [18928] [INFO] Using worker: sync
[2023-08-23 02:50:37 +0000] [18984] [INFO] Booting worker with pid: 18984
[2023-08-23 02:50:37 +0000] [18985] [INFO] Booting worker with pid: 18985
[2023-08-23 02:50:37 +0000] [18986] [INFO] Booting worker with pid: 18986
[2023-08-23 02:50:37 +0000] [18987] [INFO] Booting worker with pid: 18987
[2023-08-23 02:50:37 +0000] [18988] [INFO] Booting worker with pid: 18988
[2023-08-23 02:50:37 +0000] [18989] [INFO] Booting worker with pid: 18989
[2023-08-23 02:50:37 +0000] [18990] [INFO] Booting worker with pid: 18990
[2023-08-23 02:50:37 +0000] [18991] [INFO] Booting worker with pid: 18991
[2023-08-23 02:50:37 +0000] [18992] [INFO] Booting worker with pid: 18992
著名的HTTP功能
在这里,我将查看支持分解输入,块输出和范围标题的能力。这些功能可能取决于您的用例。
块的输入支持
枪支支持通过wsgi.input_terminated
的块输入,如此简单应用所示:
wsgi_chunked_input
def application(environ, start_response):
input = environ['wsgi.input']
with open('test.json', 'wb') as stream_fp:
stream_fp.write(input.read())
status = '200 OK'
body = b'Hello World\n'
response_headers = [
('Content-Type', 'text/plain'),
('Content-Length', str(len(body))),
]
start_response(status, response_headers)
return [body]
发送25MB JSON文件还带回:
$ curl -v -H "Transfer-Encoding: chunked" -d @large-file.json http://127.0.0.1:8000
* Trying 127.0.0.1:8000...
* Connected to 127.0.0.1 (127.0.0.1) port 8000 (#0)
> POST / HTTP/1.1
> Host: 127.0.0.1:8000
> User-Agent: curl/7.74.0
> Accept: */*
> Transfer-Encoding: chunked
> Content-Type: application/x-www-form-urlencoded
> Expect: 100-continue
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 100 Continue
* Signaling end of chunked upload via terminating chunk.
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: gunicorn
< Date: Tue, 22 Aug 2023 17:47:21 GMT
< Connection: close
< Content-Type: text/plain
< Content-Length: 12
<
Hello World
* Closing connection 0
在服务器端很好地显示:
$ ls -lh test.json
-rw-r--r-- 1 john doe 25M Aug 22 18:47 test.json
响应支撑块
块的响应也可以工作,只需要按modified example添加的Transfer-Encoding: chunked
标头:
class TestIter(object):
def __iter__(self):
lines = [b'line 1\n', b'line 2\n']
for line in lines:
yield line
def app(environ, start_response):
status = '200 OK'
response_headers = [
('Content-type', 'text/plain'),
('Transfer-Encoding', "chunked"),
]
start_response(status, response_headers)
return TestIter()
在通过卷发产生时,会产生:
$ curl -iv --raw -H "Transfer-Encoding: chunked" http://127.0.0.1:8000/
* Trying 127.0.0.1:8000...
* Connected to 127.0.0.1 (127.0.0.1) port 8000 (#0)
> GET / HTTP/1.1
> Host: 127.0.0.1:8000
> User-Agent: curl/7.74.0
> Accept: */*
> Transfer-Encoding: chunked
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Server: gunicorn
Server: gunicorn
< Date: Tue, 22 Aug 2023 21:40:12 GMT
Date: Tue, 22 Aug 2023 21:40:12 GMT
< Connection: close
Connection: close
< Transfer-Encoding: chunked
Transfer-Encoding: chunked
< Content-type: text/plain
Content-type: text/plain
<
7
line 1
7
line 2
0
* Closing connection 0
范围支持
范围没有明确的包装器,需要一个辅助功能,例如werkzeug.http.parse_range_header。要通过的价值将通过HTTP_RANGE
获得:
from werkzeug.http import parse_range_header
def application(environ, start_response):
range = parse_range_header(environ['HTTP_RANGE'])
start, end = range.ranges[0]
with open('large-file.json', 'rb') as stream_fp:
stream_fp.seek(start)
data = stream_fp.read(end - start)
status = '200 OK'
response_headers = [
('Content-type', 'application/json')
]
start_response(status, response_headers)
return [data]
当卷曲与卷发时:
$ curl -v -r 1200-1299 http://127.0.0.1:8000/
* Trying 127.0.0.1:8000...
* Connected to 127.0.0.1 (127.0.0.1) port 8000 (#0)
> GET / HTTP/1.1
> Host: 127.0.0.1:8000
> Range: bytes=1200-1299
> User-Agent: curl/7.74.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: gunicorn
< Date: Wed, 23 Aug 2023 10:59:27 GMT
< Connection: close
< Transfer-Encoding: chunked
< Content-type: application/json
<
* Closing connection 0
pt"},"message":"Fix main header height on mobile","distinct":true,"url":"https://api.github.com/repo
PEX捆绑
作为纯粹的python,也可以使用pex(可执行文件)包装枪。例如,我将创建一个简单的WSGI应用程序wsgi_test.py
以及简化的gunicorn.config.py
:
wsgi_test.py
def application(env, start_response):
data = b'Hello World'
status = '200 OK'
response_headers = [
('Content-Type', 'text/plain'),
('Content-Length', str(len(data))),
]
start_response(status, response_headers)
return [data]
gunicorn.config.py
bind = "127.0.0.1:8000"
workers = 2
wsgi_app = "wsgi_test:application"
我将它们放在专用的文件夹中,以便易于复制。然后需要打包可执行文件:
$ pex gunicorn -c gunicorn -o wsgi_app.pex --python pypy --inject-args "--config gunicorn.config.py"
因此,第一部分是要包括的模块。我们将包括gunicorn
,因为这是我们想要运行WSGI应用程序的内容。下一个是一个入口点,即setup.py of the project中定义的console_script
枪支。这将允许所产生的可执行文件运行,就像我们在命令行上运行gunicorn
一样。 --python pypy
将pypy设置为要使用的python二进制。您需要确保这与目标计算机上可用的Python匹配。 --inject-args
使得始终将配置文件参数传递,因此不必在执行时写出它们。最后,-o wsgi_app.pex
是要输出的可执行文件。如果我将其复制到安装了PYPY的另一个Linux系统:
$ scp server:~/projects/pex_example/* .
$ ./wsgi_app.pex
[2023-08-23 05:00:33 +0000] [13126] [INFO] Starting gunicorn 21.2.0
[2023-08-23 05:00:33 +0000] [13126] [INFO] Listening at: http://127.0.0.1:8000 (13126)
[2023-08-23 05:00:33 +0000] [13126] [INFO] Using worker: sync
[2023-08-23 05:00:33 +0000] [13176] [INFO] Booting worker with pid: 13176
[2023-08-23 05:00:33 +0000] [13177] [INFO] Booting worker with pid: 13177
尽管没有在目标机器上安装枪支,但一切都在运行。请注意,这是由于配置文件指向wsgi_test:application
而起作用。要将其与可执行文件一起捆绑在一起,您需要通过setup.py
/pyproject.toml
样式构建系统安装它。然后它可以像:
一样捆绑
$ pex wsgi_test gunicorn -c gunicorn -o wsgi_app.pex --python pypy --inject-args "--config gunicorn.config.py"
过程标题
安装steproctitle将允许Gunicorn以使其更容易管理的方式命名其工作过程。只需通过pip install setproctitle
安装并查看过程列表将显示类似的内容:
28928 pts/2 00:00:01 gunicorn: maste
28984 pts/2 00:00:00 gunicorn: worke
28985 pts/2 00:00:00 gunicorn: worke
28986 pts/2 00:00:00 gunicorn: worke
28987 pts/2 00:00:00 gunicorn: worke
28988 pts/2 00:00:00 gunicorn: worke
28989 pts/2 00:00:00 gunicorn: worke
28990 pts/2 00:00:00 gunicorn: worke
28991 pts/2 00:00:00 gunicorn: worke
28992 pts/2 00:00:00 gunicorn: worke
可以轻松区分主工作和工作过程。
项目支持概述
这探讨了如何在考虑生产环境中使用的人维护项目。与任何软件评估一样,请务必在接近您期望生产环境的环境中测试事物。
文档
Gunicorn的主要网站有一个简单的documentation page。对于更广泛的文档,有一个full docs site。文档本身的井井有条。多次文档允许指向特定版本或版本指针,例如latest
和stable
。
来源可维护性
最后一个提交表明写作大约1个月大。有274期和85个开放率。最近的大量活动似乎是一个人的努力。鉴于代码基础置换性的大小,如果需要出现,则相当合理。
结论
我认为整体包装很好。如果您想快速进行原型原型,那么起床并运行非常简单。多种工人类型的可用性意味着如果进行生产部署,您需要评估每个工人的选择以找到最合适的选择(除非龙卷风工人可能不是一个好主意,因为WSGI是一个较小的奖励,而不是主要的重点)。至于源存储库,主要拥有一个人的维护者确实有一个关注点。确定一些共同维护者和虫子牧马人确实可以帮助该项目。