php网站开发接口文档,上海建筑建材业网电话,wordpress 知名网站,海外专用服务器在开发django应用的过程中#xff0c;使用开发者模式启动服务是特别方便的一件事#xff0c;只需要 python manage.py runserver 就可以运行服务#xff0c;并且提供了非常人性化的autoreload机制#xff0c;不需要手动重启程序就可以修改代码并看到反馈。刚接触的时候觉得… 在开发django应用的过程中使用开发者模式启动服务是特别方便的一件事只需要 python manage.py runserver 就可以运行服务并且提供了非常人性化的autoreload机制不需要手动重启程序就可以修改代码并看到反馈。刚接触的时候觉得这个功能比较人性化也没觉得是什么特别高大上的技术。后来有空就想着如果是我来实现这个autoreload会怎么做想了很久没想明白总有些地方理不清楚看来第一反应真是眼高手低了。于是就专门花了一些时间研究了django是怎样实现autoreload的每一步都看源码说话不允许有丝毫的想当然 1、runserver命令。在进入正题之前其实有一大段废话是关于runserver命令如何执行的和主题关系不大就简单带一下命令行键入 python manage.py runserver 后django会去寻找runserver这个命令的执行模块最后落在django\contrib\staticfiles\management\commands\runserver.py模块上 #django\contrib\staticfiles\management\commands\runserver.py
from django.core.management.commands.runserver import \
Command as RunserverCommandclass Command(RunserverCommand):help Starts a lightweight Web server for development and also serves static files. 而这个Command的执行函数在这 #django\core\management\commands\runserver.py
class Command(BaseCommand):def run(self, **options):Runs the server, using the autoreloader if neededuse_reloader options[use_reloader]if use_reloader:autoreload.main(self.inner_run, None, options)else:self.inner_run(None, **options) 这里有关于use_reloader的判断。如果我们在启动命令中没有加--noreload程序就会走autoreload.main这个函数如果加了就会走self.inner_run直接启动应用。其实从autoreload.main的参数也可以看出它应该是对self.inner_run做了一些封装autoreload的机制就在这些封装当中下面我们继续跟。 PS: 看源码的时候发现django的command模式还是实现的很漂亮的值得学习。 2、autoreload模块。看autoreload.main(): #django\utils\autoreload.py:
def main(main_func, argsNone, kwargsNone):if args is None:args ()if kwargs is None:kwargs {}if sys.platform.startswith(java):reloader jython_reloaderelse:reloader python_reloaderwrapped_main_func check_errors(main_func)reloader(wrapped_main_func, args, kwargs) 这里针对jpython和其他python做了区别处理先忽略jpythoncheck_errors就是把对main_func进行错误处理也先忽略。看python_reloader: #django\utils\autoreload.py:
def python_reloader(main_func, args, kwargs):if os.environ.get(RUN_MAIN) true:thread.start_new_thread(main_func, args, kwargs)try:reloader_thread()except KeyboardInterrupt:passelse:try:exit_code restart_with_reloader()if exit_code 0:os.kill(os.getpid(), -exit_code)else:sys.exit(exit_code)except KeyboardInterrupt:pass 第一次走到这里时候环境变量中RUN_MAIN变量不是true 甚至都没有所以走else 看restart_with_reloader: #django\utils\autoreload.py:
def restart_with_reloader():while True:args [sys.executable] [-W%s % o for o in sys.warnoptions] sys.argvif sys.platform win32:args [%s % arg for arg in args]new_environ os.environ.copy()new_environ[RUN_MAIN] trueexit_code os.spawnve(os.P_WAIT, sys.executable, args, new_environ)if exit_code ! 3:return exit_code 这里首先起一个while循环, 内部先把RUN_MAIN改成了true然后用os.spawnve方法开一个子进程subprocess看看os.spawnve的说明 #os.pydef spawnve(mode, file, args, env):spawnve(mode, file, args, env) - integerExecute file with arguments from args in a subprocess with thespecified environment.If mode P_NOWAIT return the pid of the process.If mode P_WAIT return the processs exit code if it exits normally;otherwise return -SIG, where SIG is the signal that killed it. return _spawnvef(mode, file, args, env, execve) 其实就是再调一遍命令行又走了一遍 python manage.py runserver。 接着看restart_with_reloader里的while循环需要注意的是while循环退出的唯一条件是exit_code!3。 如果子进程不退出就一直停在 os.spawnve这一步; 如果子进程退出而退出码不是3while就被终结了如果是3继续循环重新创建子进程。从这个逻辑可以猜想autoreload的机制当前进程主进程其实啥也不干就监视子进程的运行状况子进程才是真正干事儿的如果子进程以exit_code3退出应该由于检测到了文件修改就再启动一遍子进程新代码自然就生效了如果子进程以exit_code!3退出主进程也结束整个django程序就算跪了。这只是猜想下面接着来验证。 3、子进程。上面其实有一个疑问既然是重新启动了一次为什么子进程不会接着生成子进程原因就在于RUN_MAIN这个环境变量主进程中把它改成了true子进程走到python_reloader函数的时候 #django\utils\autoreload.py:
def python_reloader(main_func, args, kwargs):if os.environ.get(RUN_MAIN) true:thread.start_new_thread(main_func, args, kwargs)try:reloader_thread()except KeyboardInterrupt:passelse:try:exit_code restart_with_reloader()if exit_code 0:os.kill(os.getpid(), -exit_code)else:sys.exit(exit_code)except KeyboardInterrupt:pass if条件满足了和主进程走了不一样的逻辑分支。在这里首先去开一个线程运行main_func就是上文的 Command.inner_run。这里的thread模块是这么import的 #django\utils\autoreload.py:
from django.utils.six.moves import _thread as thread 这里six模块的作用是兼容各种python版本 [codeblock six]
#django\utils\six.py
class _SixMetaPathImporter(object):
A meta path importer to import six.moves and its submodules.This class implements a PEP302 finder and loader. It should be compatible
with Python 2.5 and all existing versions of Python3
官网说明
# https://pythonhosted.org/six/
Six: Python 2 and 3 Compatibility Library
Six provides simple utilities for wrapping over differences between Python 2 and Python 3. It is intended to support codebases that work on both Python 2 and 3 without modification. six consists of only one Python file, so it is painless to copy into a project. 所以如果程序想在python2和python3上都能跑且鲁邦six是重要的工具。之后抽个时间看下sixmark一下。 然后再开一个reloader_thread [codeblock autoreload_reloader_thread]
#django\utils\autoreload.py:
def reloader_thread():ensure_echo_on()if USE_INOTIFY:fn inotify_code_changedelse:fn code_changedwhile RUN_RELOADER:change fn()if change FILE_MODIFIED:sys.exit(3) # force reloadelif change I18N_MODIFIED:reset_translations()time.sleep(1) ensure_echo_on()其实还没看明白貌似是针对类unix系统文件处理的先略过USE_INOTIFY也是系统文件操作相关的变量根据 inotify 是否可用选择检测文件变化的方法。while循环每隔1秒检测一下文件状态如果是普通文件有变化进程退出退出码为3主进程一看退出码是3就重启子进程。。。。这样就和上面连上了如果不是普通文件变化而是I18N_MODIFIED.mo后缀的文件变化二进制库文件之类的那就 reset_translations 大概意思是把已加载过的库缓存清理掉下次重新加载。 以上就是autoreload机制的流程。其中还是有些细节不是特别清楚比如不同操作系统文件变化的检测但都是很细节的东西了不涉及主流程。看完这些我又问了自己一遍如果是让我设计autoreload机制会怎样搞。现在我的答案是直接把 django\utils\autoreload.py 文件拿来用啊。其实这是很独立的一个模块而且特别通用完全可以作为通用的autoreload解决方案我还自己写个毛啊。转载于:https://www.cnblogs.com/beeler/p/7382917.html