os.kill vs aiohttp中的process.terminate

时间:2018-06-10 06:40:37

标签: 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)

那么使用os.killprocess.terminate与python / aiohttp之间的区别是什么?

1 个答案:

答案 0 :(得分:3)

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

默认情况下,Aiohttp在启动时为SIGTERMSIGINT注册处理程序,以便正常关闭。在生产环境中,您通常不在控制台中运行应用程序(因此无法使用Ctrl + C禁用它),而是使用某些进程管理器,如Systemd或Docker。要停止此类应用程序,主管通常会发送SIGTERM。您可以使用arg handle_signals=Falserun_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()函数来处理信号,这使得信号被写入套接字。这使得信号只是asyncio事件循环的标准事件。但这是您的应用程序中断的地方。不仅你的工作进程没有停止(因为aiohttp中断了信号)。由于套接字是在fork()调用之前创建的,因此它在主进程和所有子进程之间共享。因此,您的子进程的任何SIGTERMSIGINT信号都将由主进程处理。在/stop请求之后,您的网络服务器已经死了 - 甚至在循环结束之前。