builtin Python 的 next 函数在 Gunicorn 服务器中的工作原理
下面是来自 gunicorn.workers.sync.SyncWorker
类的 self.handle
方法中的代码片段:
def handle(self, listener, client, addr):
req = None
try:
if self.cfg.is_ssl:
client = sock.ssl_wrap_socket(client, self.cfg)
parser = http.RequestParser(self.cfg, client, addr)
req = next(parser)
self.handle_request(listener, req, client, addr)
这个方法执行了这一行:parser = http.RequestParser(self.cfg, client, addr)
,这行代码返回了一个 gunicorn.http.parser.RequestParser
对象,这个对象是从 gunicorn.http.parser.Parser
继承而来的,具体内容如下:
class Parser(object):
mesg_class = None
def __init__(self, cfg, source, source_addr):
self.cfg = cfg
if hasattr(source, "recv"):
self.unreader = SocketUnreader(source)
else:
self.unreader = IterUnreader(source)
self.mesg = None
self.source_addr = source_addr
# request counter (for keepalive connetions)
self.req_count = 0
def __iter__(self):
return self
def __next__(self):
# Stop if HTTP dictates a stop.
if self.mesg and self.mesg.should_close():
raise StopIteration()
# Discard any unread body of the previous message
if self.mesg:
data = self.mesg.body.read(8192)
while data:
data = self.mesg.body.read(8192)
# Parse the next request
self.req_count += 1
self.mesg = self.mesg_class(self.cfg, self.unreader, self.source_addr, self.req_count)
if not self.mesg:
raise StopIteration()
return self.mesg
next = __next__
上面的 gunicorn.http.parser.Parser
对象定义了两个特殊的方法:self.__iter__
和 self.__next__
,这两个方法是用来处理可迭代对象和迭代器的。不过,parser
这个局部变量,存储了返回的对象,它是 gunicorn.http.parser.RequestParser
的一个实例,而不是一个迭代器,这一点通过 pdb
(Python 调试器)的输出得到了确认:<class 'gunicorn.http.parser.RequestParser'>
。在将返回的对象赋值给 parser
后,方法接着执行了这一行:req = next(parser)
,并将 parser
作为参数传入。
问题:
内置函数 next()
不应该接受第一个参数作为迭代器吗?parser
不是一个迭代器,而是 gunicorn.http.parser.RequestParser
类的一个实例。以下是 Python 中 next()
的文档:
next(iterator)
next(iterator, default)
Retrieve the next item from the iterator by calling its __next__() method. If default is given, it is returned if the iterator is exhausted, otherwise StopIteration is raised.
我运行了 pdb
来生成堆栈跟踪,以确认当执行这一行:req=next(parser)
时实际发生了什么。它进入了 gunicorn.http.parser.Parser
的 self.__next__
方法。self.__iter__
方法本该返回一个迭代器,但根本没有被调用,实际上我把它注释掉了,代码依然可以正常工作。那么,self.__iter__
方法真的有必要吗?
堆栈跟踪:
/home/humbulani/django/env/bin/gunicorn(8)<module>()
-> sys.exit(run())
/home/humbulani/django/env/lib/python3.10/site-packages/gunicorn/app/wsgiapp.py(71)run()
-> WSGIApplication("%(prog)s [OPTIONS] [APP_MODULE]").run()
/home/humbulani/django/env/lib/python3.10/site-packages/gunicorn/app/base.py(264)run()
-> super().run()
/home/humbulani/django/env/lib/python3.10/site-packages/gunicorn/app/base.py(74)run()
-> Arbiter(self).run()
/home/humbulani/django/env/lib/python3.10/site-packages/gunicorn/arbiter.py(226)run()
-> self.manage_workers()
/home/humbulani/django/env/lib/python3.10/site-packages/gunicorn/arbiter.py(602)manage_workers()
-> self.spawn_workers()
/home/humbulani/django/env/lib/python3.10/site-packages/gunicorn/arbiter.py(673)spawn_workers()
-> self.spawn_worker()
/home/humbulani/django/env/lib/python3.10/site-packages/gunicorn/arbiter.py(640)spawn_worker()
-> worker.init_process()
/home/humbulani/django/env/lib/python3.10/site-packages/gunicorn/workers/base.py(144)init_process()
-> self.run()
/home/humbulani/django/env/lib/python3.10/site-packages/gunicorn/workers/sync.py(126)run()
-> self.run_for_one(timeout)
/home/humbulani/django/env/lib/python3.10/site-packages/gunicorn/workers/sync.py(70)run_for_one()
-> self.accept(listener)
/home/humbulani/django/env/lib/python3.10/site-packages/gunicorn/workers/sync.py(32)accept()
-> self.handle(listener, client, addr)
/home/humbulani/django/env/lib/python3.10/site-packages/gunicorn/workers/sync.py(140)handle()
-> req = next(parser) # self.msg attribute
> /home/humbulani/django/env/lib/python3.10/site-packages/gunicorn/http/parser.py(37)__next__()
-> if self.mesg and self.mesg.should_close():
非常感谢你的回复。
1 个回答
next
的功能非常简单;你可以想象它的实现大概是这样的:
_sentinel = object()
def next(itr, default=_sentinel):
try:
return itr.__next__()
except StopIteration:
if default is _sentinel:
raise
return default
所以 next(parser)
其实就是调用 parser.__next__
,因为 parser
是一个迭代器:它是一个类(Parser
)的实例,而这个类定义了 __next__
方法。
next
不接受的情况是,如果你传入一个不是迭代器的类的实例。通过定义 __iter__
,Parser
也恰好(就像其他所有迭代器一样)成为了一个可迭代对象。你可以是一个可迭代对象而不一定是迭代器(比如 list
),但反过来就不行。