【发布时间】:2021-08-18 12:19:34
【问题描述】:
我目前正在尝试了解 Django 在接收 SIGTERM 时的信号处理。
背景资料
我有一个运行在 Docker 容器中的应用程序,其请求可能需要长时间运行。当 Docker 想要停止一个容器时,它首先发送一个 SIGTERM 信号,等待一段时间,然后发送一个 SIGKILL。通常,在 SIGTERM 上,您会停止接收新请求,并希望当前正在运行的请求在 Docker 决定发送 SIGKILL 之前完成。 但是,在我的应用程序中,我想保存已尝试过的请求,并发现这比立即完成请求更重要。所以我更喜欢在 SIGTERM 上关闭当前请求,这样我就可以优雅地结束它们(并保存它们的状态),而不是等待 SIGKILL。
我的尝试
我的理论是您可以为 SIGTERM 注册一个信号侦听器,该侦听器执行sys.exit(),从而引发SystemExit 异常。然后我想在我的请求处理程序中捕获该异常,并保存我的状态。作为第一个实验,我为 Django 开发服务器创建了一个模拟项目。
我在Appconfig.ready()函数中注册了信号:
import signal
import sys
from django.apps import AppConfig
import logging
logger = logging.getLogger(__name__)
def signal_handler(signal_num, frame):
sys.exit()
class TesterConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'tester'
def ready(self):
logger.info('starting ready')
signal.signal(signal.SIGTERM, signal_handler)
并创建了一个捕获异常和基本异常的请求处理程序:
import logging
import sys
import time
from django.http import HttpResponse
def handler(request):
try:
logger.info('start')
while True:
time.sleep(1)
except Exception:
logger.info('exception')
except BaseException:
logger.info('baseexception')
return HttpResponse('hallo')
但是当我启动开发服务器用户python manage.py runserver,然后使用kill -n 15 <pid> 发送终止信号时,不会记录“baseexception”消息(确实会记录“start”)。
完整代码可以在here找到。
我的问题
我的假设是 SIGTERM 信号是在主线程中处理的,所以 sys.exit() 调用是在主线程中处理的。因此,在运行请求处理程序的线程中不会引发异常,也不会捕获任何内容。
如何更改我的代码以在请求处理程序线程中引发SystemError?我需要来自该线程的一些信息来记录,所以我不能直接在信号处理程序中“记录”一些东西。
【问题讨论】: