【问题标题】:Python AIOHTTP.web server multiprocessing load-balancer?Python AIOHTTP.web 服务器多处理负载平衡器?
【发布时间】:2019-12-06 17:33:41
【问题描述】:

我目前正在使用 aiohttp 模块开发一个网络应用程序。我正在使用:

aiohttp.webasynciouvloopaiohttp_sessionaiohttp_securityaiomysqlaioredis

我已经针对它运行了一些基准测试,虽然它们非常好,但我还是忍不住想要更多。我知道 Python 本质上是单线程的。 AIOHTTP 使用异步作为非阻塞,但我假设它没有利用所有 CPU 内核是否正确?

我的想法: 在多处理模式下通过concurrent.futures 运行我的 aiohttp.web 代码的多个实例。每个进程将在不同的端口上为站点提供服务。然后我会在他们面前放一个负载均衡器。 MySQL 和 Redis 可用于在必要时共享状态,例如会话。

问题:给定一个具有多个 CPU 内核的服务器,这会带来预期的性能提升吗?如果是这样,为了避免问题,是否有任何特定的模式可以追求?我想不出这些 aio 模块所做的任何事情都需要只有一个线程,尽管我可能是错的。

注意:这不是我提出的主观问题。模块当前绑定到一个线程/进程,或者没有 - 可以从多处理模块 + 负载平衡器中受益,或者不能。

【问题讨论】:

  • 你需要使用concurrent.futures吗?为什么不使用 gunicorn 或任何其他简化运行多个应用程序实例的工具?
  • 为什么不将gunicornuvicorn 等一些异步工作者一起使用?
  • 我真的很喜欢 gelonida 在他们的回答中所做的,因为它使用了 Linux 非常干净的端口共享功能,并且不依赖于外部应用程序来工作。尽管使用了已弃用的 AIOHTTP 功能,但它提示了这个问题:stackoverflow.com/questions/59719022/…

标签: python python-3.x multiprocessing load-balancing aiohttp


【解决方案1】:

你说得对,asyncio 只使用一个 CPU。 (一个事件循环只使用一个线程,因此只使用一个 CPU)

我不能说您的整个项目是受网络限制还是受 CPU 限制。 你必须尝试。

您可以使用 nginx 或 haproxy 作为负载均衡器。

您甚至可以尝试完全不使用负载平衡器。我从未尝试过将此功能用于负载平衡,就像故障转移系统的概念证明一样。 使用新内核时,多个进程可以监听同一个端口(使用SO_REUSEPORT 选项时),我猜是内核会进行循环。

这里有一个小链接指向一篇比较典型 nginx 配置与具有 SO_REUSEPORT 功能的 nginx 设置的性能的文章:

https://blog.cloudflare.com/the-sad-state-of-linux-socket-balancing/

似乎SO_REUSEPORT 可能会相当均匀地分配 CPU 费用,但可能会增加响应时间的变化。不确定这与您的设置是否相关,但我想我让您知道了。

于 2020 年 2 月 4 日添加:

我添加 2019-12-09 的解决方案有效,但触发了弃用警告。

当我有更多的时间和时间自己测试它时,我会在此处发布改进的解决方案。暂时你可以在AIOHTTP - Application.make_handler(...) is deprecated - Adding Multiprocessing找到它

于 2019 年 12 月 9 日添加:

这是一个 HTTP 服务器的小例子,它可以在同一个套接字上启动多次侦听。 内核将分发任务。不过,我从未检查过这是否有效。

reuseport.py:

import asyncio
import os
import socket
import time
from aiohttp import web


def mk_socket(host="127.0.0.1", port=8000, reuseport=False):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    if reuseport:
        SO_REUSEPORT = 15
        sock.setsockopt(socket.SOL_SOCKET, SO_REUSEPORT, 1)
    sock.bind((host, port))
    return sock

async def handle(request):
    name = request.match_info.get('name', "Anonymous")
    pid = os.getpid()
    text = "{:.2f}: Hello {}! Process {} is treating you\n".format(
        time.time(), name, pid)
    time.sleep(0.5)  # intentionally blocking sleep to simulate CPU load
    return web.Response(text=text)

if __name__ == '__main__':
    host = "127.0.0.1"
    port=8000
    reuseport = True
    app = web.Application()
    sock = mk_socket(host, port, reuseport=reuseport)
    app.add_routes([web.get('/', handle),
                    web.get('/{name}', handle)])
    loop = asyncio.get_event_loop()
    coro = loop.create_server(
        protocol_factory=app.make_handler(),
        sock=sock,
        )
    srv = loop.run_until_complete(coro)
    loop.run_forever()

还有一种测试方法:

./reuseport.py & ./reuseport.py & 
sleep 2 # sleep a little so servers are up
for n in 1 2 3 4 5 6 7 8 ; do wget -q http://localhost:8000/$n -O - & done

输出可能如下所示:

1575887410.91: Hello 1! Process 12635 is treating you
1575887410.91: Hello 2! Process 12633 is treating you
1575887411.42: Hello 5! Process 12633 is treating you
1575887410.92: Hello 7! Process 12634 is treating you
1575887411.42: Hello 6! Process 12634 is treating you
1575887411.92: Hello 4! Process 12634 is treating you
1575887412.42: Hello 3! Process 12634 is treating you
1575887412.92: Hello 8! Process 12634 is treating you

【讨论】:

  • 多个进程如何监听同一个端口?不是每个都需要自己的端口吗?
  • 这正是我要说的。如果使用特定选项打开,新的 Linux 内核允许多个进程在同一个端口上侦听。当然,只有一个进程将用于一个传入连接。它是一种内置负载平衡的内核。
  • 它叫什么?我找不到任何关于它的信息。这会很酷,但并不那么重要,因为在 aiohttp 中,我可以为每个进程使用单独的端口。这样做没有什么大的缺点。
  • @xendi 我增强了我的答案。选项是 SO_REUSEPORT
  • 这很好,但有一个已弃用的警告:DeprecationWarning: Application.make_handler(...) is deprecated, use AppRunner API instead protocol_factory=app.make_handler(),。你能解决它,我会接受吗?
【解决方案2】:

我认为最好不要重新发明轮子并使用文档中提出的解决方案之一: https://docs.aiohttp.org/en/stable/deployment.html#nginx-supervisord

【讨论】:

  • gelonida 的解决方案很棒。他设法让多处理工作共享同一个端口,而不需要像 nginx 这样的东西。我已经在我的应用程序前面运行了一个 Web 服务器,但是与他的解决方案相比,从 shell 跨多个端口生成多个进程有点可疑。他使用了一个已弃用的 aiohttp 功能,但应该将其替换。我打开赏金是为了修复已弃用的代码(请参阅那里的 cmets)
猜你喜欢
  • 2012-07-15
  • 1970-01-01
  • 2011-04-07
  • 2013-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多