读源码-Gunicorn篇-3-配置
本节说明
上一节我们梳理了 gunicorn
服务启动的流程,本章节我们来研究一下它的配置是如何处理的。
来源
从官方文档中我们可以得知,gunicorn
会从 5 个地方读取配置数据,按优先级从低到高分别是:
- 从环境变量中读取
- 从框架特定的配置文件中读取(目前仅支持
Paster
应用) - 从当前工作目录中的
gunicorn.conf.py
文件中读取,或通过命令行参数指定这个文件 - 从名称为
GUNICORN_CMD_ARGS
的环境变量中读取 - 命令行启动时指定的参数配置
这几个位置读取到的配置优先级依次增加,不同位置配置的相同参数,优先级高的会覆盖优先级低的。
需要注意的是如果像 GUNICORN_CMD_ARGS
和命令行中都指定了配置文件的路径,则只会解析使用命令行中指定的文件,而不是都解析后进行覆盖合并。
了解了配置的来源及优先级,我们来看下 gunicorn
是怎么做的。
加载
在上一节中,我们看到在创建 WSGIApplication
对象时,调用了 BaseApplication.do_load_config
方法,今天我们就从这个方法入手,看下 gunicorn
是如何处理配置的。
1 | def do_load_config(self): |
可以看到 do_load_config
中先加载了默认配置,之后又加载了用户配置。加载默认配置就是简单地生成了一个 Config
对象保存到 cfg
属性中,而 Config
对象中处理了所有的默认配置,然后加载了环境变量,如下:
1 | def __init__(self, usage=None, prog=None): |
之后执行了 load_config
来加载用户配置,这个方法最终会调用 Application.load_config
。
1 | def load_config(self): |
在 load_config
方法中,先生成了一个解析参数的 parser
,之后调用 init
方法去读了 Paster
应用的配置,然后又解析了环境变量 GUNICORN_CMD_ARGS
里的参数。
之后就是判断命令行参数中有没有指定配置文件,如果没有那 GUNICORN_CMD_ARGS
中有没有指定,如果也没有再看有没有默认的配置文件。这几个按顺序先有哪个加载哪个,后续的就不再处理了。都加载完成后,先将环境变量中读到的配置覆盖到默认配置,之后再将命令行读到的配置进行覆盖。
至此,所有的配置就加载完成了!可以看出,gunicorn
就是按照文档所写的顺序对配置进行覆盖的。
默认参数的实现与加载
我们再来看下默认配置是如何处理的。
在 config.py
文件中,gunicorn
设计了一个基类 Setting
,其他所有的配置项比如像 ConfigFile
都继承自这个类,同时还实现了一个 SettingMeta
类,这个类是个元类,我们看下它的实现:
1 | class SettingMeta(type): |
在 config.py
文件中还有这样一行:
1 | Setting = SettingMeta('Setting', (Setting,), {}) |
这行代码是全局的,在导入 config.py
文件时就会执行,它会将 SettingMeta
作为 Setting
类的元类重新生成一个 Setting
类对象,而所有的配置项类(也就是 Setting
的子类)都是在这行后边定义的,保证了所有配置项继承的都是 修改过
的 Setting
类,这样这些配置项类在创建的时候,就都会走一遍由 SettingMeta
创建的流程。
那么 SettingMeta
类做了什么呢?它做了下面几件事:
- 对每个配置项类添加了一个序号
order
用于后续对配置项的分组和排序 - 给每个配置项类挂上一个
validator
并设置为静态方法 - 将每个配置项类添加到全局的已知配置列表
KNOWN_SETTINGS
中
这样做有什么用呢?
我们看,在 Config.__init__
方法中,调用了 make_settings
,它从 KNOWN_SETTINGS
读取所有的配置项类,之后使用名称作为 key
创建了所有配置项类的词典,然后在 Config.parser
方法中,将每个配置项作为一个参数添加到 parser
的支持参数列表,再用这个 parse
去解析了每个来源的参数,之后再按顺序一个个 set
到 Config
对象的 settings
中,完成所有配置项的解析。
这样在后续如果需要支持新的配置项,只需要写一个新的配置项类,继承 Setting
类即可,因为它的元类是 SettingMeta
,它会自动被加入到全局的支持参数的列表,会自动被加入到 parser
中进行解析,能保证服务运行起来时,Config
对象中一定能获取到这个配置项的 key
,即使它未配置。
而这一切,都不用再多写其它的东西,只需要继承 Setting
类就好了。
结尾
好了,这一节我们讨论了 gunicorn
的配置是如何加载的,以及配置项的实现,基本上就这么多。
下一个章节,我们来看一下 worker
进程是如何加载的,worker
进程的生命周期,以及当一个请求进来时,是如何交给 worker
进行处理的。
就这样。