builtin Python 的 next 函数在 Gunicorn 服务器中的工作原理

0 投票
1 回答
26 浏览
提问于 2025-04-14 17:52

下面是来自 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.Parserself.__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 个回答

0

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),但反过来就不行。

撰写回答