- 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_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,或者尋找其他的解決方案