python-3.x - os.kill 与 process.terminate 在 aiohttp 中

标签 python-3.x aiohttp

我有这段代码

import asyncio
from aiohttp import web
from multiprocessing import Process
import time

def block():
    i = 0
    while i < 5:
        print('running')
        time.sleep(1)
        i += 1


async def start(request):
    process = Process(target=block)
    process.start()
    print('process started')
    request.app['p'] = process
    return web.Response(status=200)

async def stop(request):
    if request.app['p'].is_alive():
        print('stop process')
        request.app['p'].terminate()
        return web.Response(status=200)

app = web.Application()
app.router.add_route('GET', '/start', start)
app.router.add_route('GET', '/stop', stop)
web.run_app(app, host='127.0.0.1', port=8888)

当我在 /start 上发送 HTTP GET 时,我可以看到 running 每秒都像预期的那样在终端上输出。但是,当我在 /stop 上发送 HTTP GET 时,running 流在循环完成之前不会停止。 此外,当循环结束时,整个应用程序就死掉了。

这是一个输出:

❯ python3 server.py
======== Running on http://127.0.0.1:8888 ========
(Press CTRL+C to quit)
process started
running
running
running
stop process
running
running

❯

有趣的是,这个请求处理程序确实可以在不停止整个应用程序的情况下工作。

async def stop2(request):
    if request.app['p'].is_alive():
        print('stop2 process')
        os.kill(request.app['p'].pid, signal.SIGKILL)
        return web.Response(status=200)

那么在 python/aiohttp 中使用 os.killprocess.terminate 有什么区别?

最佳答案

区别在于terminate() 发送SIGTERM 信号,而kill() 发送您指定的信号。

默认情况下,Aiohttp 会在启动时为 SIGTERMSIGINT 注册处理程序,以便正常关闭。在生产环境中,您通常不会在控制台中运行您的应用程序(因此您不能使用 Ctrl+C 禁用它),而是使用一些进程主管,如 Systemd 或 Docker。要停止此类应用程序,主管通常会发送 SIGTERM。您可以使用 arg handle_signals=False 禁用默认处理程序到 run_app() 调用,但是当您决定关闭服务器时,这些信号中的任何一个都会被立即杀死。

当您在 UNIX 上使用 multiprocessing 模块时,您的子进程是通过 fork() 调用创建的 - 这意味着,在创建时,它几乎是主进程的完整副本- 它有一个父信号处理程序的副本。使用 subprocess 模块有助于实现这一点——您将拥有全新的 python 进程,无需父处理程序。或者你可以只恢复默认处理程序,如果你将这些行添加到 block() 调用的开头:

signal.set_wakeup_fd(-1)
signal.signal(signal.SIGTERM, signal.SIG_DFL)

通常,信号仅由一个进程使用(您在 kill() 中指定其 pid)。但是使用 aiohttp 会发生一些神奇的事情。 asyncio 的底层实现使用signal.set_wakeup_fd() 函数来处理信号,将信号写入套接字。这使得信号只是异步事件循环的标准事件。但这是您的应用程序中断的地方。不仅你的工作进程没有停止(因为 aiohttp 中断了信号)。由于套接字是在 fork() 调用之前创建的,因此它在您的主进程和所有子进程之间共享。因此,您的子进程的任何 SIGTERMSIGINT 信号都将由主进程处理。在 /stop 请求之后,您的网络服务器已经死了——甚至在循环结束之前。

关于python-3.x - os.kill 与 process.terminate 在 aiohttp 中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50781181/

相关文章:

python - 删除了 __pycache__ 和 __init__.py

python - Websocket 连接的 Pytest 测试

python - PyYAML 转储无效的 YAML,除非参数包含数组

python - 使用 python -bb (bytes_warning=2) 覆盖一个文件

python - 我们可以设置可以在 session 中修改的模块级属性吗

python - aiohttp 处理程序中的后台任务

Python 3.5 aiohttp 即使在使用 async/await 时也会阻塞

Python循环遍历列表以在asyncio中获取api调用并保存结果

python - aiohttp Websocket 客户端和 HTTP 服务器

python - 不会为自定义用户模型保存配置文件数据