我是在 python 中处理信号的新手。我想使用 fastapi 运行服务器。在服务器中,有一个耗时的服务(代码中的url:“/run”)可能会被外部杀死(url:“/stop”)。
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from pathos.multiprocessing import ProcessingPool
import os
import uvicorn
import signal
from sub import run_loop
app = FastAPI()
def run_py(**kwargs):
i = kwargs['i']
try:
out = ProcessingPool().map(run, [[i]])[0]
except SystemError as err:
out = 114514
return out
def run(args):
re_fig = run_loop(args[0])
return re_fig
@app.post("/run")
def run_main():
i = 1
out = run_py(i=i)
return JSONResponse({"code": 200, "message": out})
@app.post("/stop")
def stop():
with open("./pid.txt", "r") as f:
pid = int(f.readline())
os.kill(pid, signal.SIGTERM)
if __name__ == "__main__":
uvicorn.run(app='test:app', host="0.0.0.0", port=1145, reload=False)
我在子进程中设置了一个 SIGTERM 处理程序:
import signal
import os
import time
def loop(i):
pid = os.getpid()
with open("./pid.txt", "w") as f:
f.write(str(pid))
while i < 30:
i += 1
print(f"sleep {i}")
return i
def run_loop(i):
def sigterm_handler(_signo, _stack_frame):
print("stop!!!!!!!!!!!!")
raise SystemError
signal.signal(signal.SIGTERM, sigterm_handler)
i = loop(i)
return i
当我通过向“/stop”发送请求来触发
sigterm_handler
时,我期望仅停止子进程循环并引发SystemError
,以便可以在主进程中将out
设置为114514。好吧,这已经实现了,但是整个服务器也停止了:
INFO: Started server process [108395]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:1145 (Press CTRL+C to quit)
sleep 2
sleep 3
sleep 4
sleep 5
sleep 6
stop!!!!!!!!!!!!
INFO: 127.0.0.1:57480 - "POST /stop HTTP/1.1" 200 OK
INFO: 127.0.0.1:57476 - "POST /run HTTP/1.1" 200 OK
INFO: Shutting down
INFO: Waiting for application shutdown.
INFO: Application shutdown complete.
INFO: Finished server process [108395]
如何只杀死子进程而不终止整个服务器?
您可以将多处理的start_method更改为spawn方法,您的子进程将变得清晰(没有继承的信号处理程序,线程池和其他东西)。
@app.on_event("startup")
async def on_startup():
multiprocessing.set_start_method('spawn')