Django 源码阅读(4):Django中自动加载机制

  • django 监听文件变化实现自动重载,流程大致是:django服务启动的同时,会启动两个进程,一个负责监控文件的变化,一个是主进程,如果文件发生变化,则会将退出当前进程,重新启动一个子进程。

两个进程

  • 重启的设置在 run_with_reloader 函数中,该函数判断是否设置了 RUN_MAIN 为 True,但开始是没有这个环境变量的,因此程序走 else 代码块,而在 restart_with_reloader 中,就设置了这个环境变量 new_environ = {**os.environ, DJANGO_AUTORELOAD_ENV: 'true'}
  • 在新的环境变量中,用 subprocess.call 启动新子进程,而这个子进程运行的正是当前的命令(python manage.py runserver),现在 RUN_MAIN 为 True 了,执行 start_django(reloader, main_func, *args, **kwargs) ,也就是启动了一个 server。如果子进程不退出,就一直停在 subprocess.call 这一步; 如果退出码不是 3,子进程退出,while 就被终结了;如果是 3,继续循环,重新创建子进程。
  • 在此可以得出,django 的 autoreload 机制中,主进程其实也没做什么事,就是监控子进程的运行,如果子进程退出码是 3,继续创建子进程。
#django/utils/autoreload.py
DJANGO_AUTORELOAD_ENV = 'RUN_MAIN'
...
def restart_with_reloader():
    new_environ = {**os.environ, DJANGO_AUTORELOAD_ENV: 'true'}
    args = get_child_arguments()
    while True:
        exit_code = subprocess.call(args, env=new_environ, close_fds=False)
        if exit_code != 3:
            return exit_code
            
def run_with_reloader(main_func, *args, **kwargs):
    signal.signal(signal.SIGTERM, lambda *args: sys.exit(0))
    try:
        if os.environ.get(DJANGO_AUTORELOAD_ENV) == 'true':
            reloader = get_reloader()
            logger.info('Watching for file changes with %s', reloader.__class__.__name__)
            start_django(reloader, main_func, *args, **kwargs)
        else:
            exit_code = restart_with_reloader()
            sys.exit(exit_code)
    except KeyboardInterrupt:
        pass

实际问题

  • 当项目中用到了supervisord来守护django的进程,发现使用项目重启的时候,特别的慢?
  • 但supervisord启动django的时候监控的是两个进程中的一个,所以实际上supervisord是无效的,同时还有可能会与主进程起冲突,占用端口。

解决方案

  • 取消django的reload ,针对django的web服务可以添加 –noreload参数,即使用reload即可
  • 不使用supervisord,或者寻找其他的解决方案

你可能感兴趣的:(Django源码阅读)