1. django1.8源码分析 -- WSGI是如何启动的
我们都知道django提供一个WSGI服务器用于开发调试。那么django是如何启动的一个WSGI服务器呢?
入口:
python manage.py runserver
整个django的WSGI服务器就只有这一条语句:
utility.execute()
让我们看一下这个方法都做了什么。
1.1. 解析settings.py
try:
settings.INSTALLED_APPS #这里实际上真正想执行的是LazySettings中的_setup
except ImproperlyConfigured as exc:
# 如果django没有设置
# ENVIRONMENT_VARIABLE = "DJANGO_SETTINGS_MODULE"
# 会触发异常跳到这里
self.settings_exception = exc
# A handful of built-in management commands work without settings.
# Load the default settings -- where INSTALLED_APPS is empty.
if subcommand in no_settings_commands:
settings.configure() # configure也处理了settings.py这里应该是django留了一个钩子给用户定制的
首先需要看一下LazySettings(),用于读取settings.py,通过:
self._wrapped = Settings(settings_module)
self._wrapped就成了settings.py的包装。之后查询settings中的配置,只能从self._wrapped去查询了。
1.2. 导入app和app中的models.py
if settings.configured:
# Start the auto-reloading dev server even if the code is broken.
# The hardcoded condition is a code smell but we can't rely on a
# flag on the command class because we haven't located it yet.
if subcommand == 'runserver' and '--noreload' not in self.argv:
try:
autoreload.check_errors(django.setup)()
except Exception:
# The exception will be raised later in the child process
# started by the autoreloader. Pretend it didn't happen by
# loading an empty list of applications.
apps.all_models = defaultdict(OrderedDict)
apps.app_configs = OrderedDict()
apps.apps_ready = apps.models_ready = apps.ready = True
# In all other cases, django.setup() is required to succeed.
else:
django.setup()
这里,只有最后一句有用django.setup(),看看都做了什么。配置log子系统,没什么好讲的。 主要是这个,apps.populate(settings.INSTALLED_APPS) django的注释是这么说的:
Loads application configurations and models.
This method imports each application module and then each model module.
It is thread safe and idempotent, but not reentrant.
做了这样几件事情: app_config = AppConfig.create(entry) # 生成了一个AppConfig实例
self.app_configs[app_config.label] = app_config # 将所有的app实例放到一个order_dict中维护。
app_config.import_models(all_models) # 导入models.py
<!-- 这里出现了两个类,Apps 和 AppConfig. -->
1.3. 真正干活了
if subcommand == 'help':
if '--commands' in args:
sys.stdout.write(self.main_help_text(commands_only=True) + '\n')
elif len(options.args) < 1:
sys.stdout.write(self.main_help_text() + '\n')
else:
self.fetch_command(options.args[0]).print_help(self.prog_name, options.args[0])
# Special-cases: We want 'django-admin --version' and
# 'django-admin --help' to work, for backwards compatibility.
elif subcommand == 'version' or self.argv[1:] == ['--version']:
sys.stdout.write(django.get_version() + '\n')
elif self.argv[1:] in (['--help'], ['-h']):
sys.stdout.write(self.main_help_text() + '\n')
else:
self.fetch_command(subcommand).run_from_argv(self.argv)
help,version之类都没什么用,看最后一个分支。
self.fetch_command(subcommand).run_from_argv(self.argv) 我们只关注为什么runserver之后,django启动了一个WSGI 服务器。所以subcommand == 'runserver'。 self.fetch_command(subcommand)做了一些搜索遍历操作,就不详细讲了,最后找到了这个: https://github.com/django/django/blob/1.8.18/django/contrib/staticfiles/management/commands/runserver.py#L7
这里面最注重要的是 self.execute(*args, **cmd_options) 后面的步骤略复杂。 最后调用的是:
def run(addr, port, wsgi_handler, ipv6=False, threading=False):
server_address = (addr, port)
if threading:
httpd_cls = type(str('WSGIServer'), (socketserver.ThreadingMixIn, WSGIServer), {})
else:
httpd_cls = WSGIServer
httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
if threading:
# ThreadingMixIn.daemon_threads indicates how threads will behave on an
# abrupt shutdown; like quitting the server by the user or restarting
# by the auto-reloader. True means the server will not wait for thread
# termination before it quits. This will make auto-reloader faster
# and will prevent the need to kill the server manually if a thread
# isn't terminating correctly.
httpd.daemon_threads = True
httpd.set_app(wsgi_handler)
httpd.serve_forever()
其中比较重要的两个对象分别是:
- class WSGIHandler(base.BaseHandler)这是WSGI的客户端。
- class BaseCommand(object):以及它的子类class Command(BaseCommand和class Command(BaseCommand,这是command的封装。
完!