首先介绍一下keystone的整体代码框架,就是:
Paste + PasteDeploy + Routes + WebOb
文章整体以ubuntu14.04操作系统为平台,如果为CentOS或其他系统相关文件目录可能会有区别,请注意。
1、WSGI入口
Keystone的入口其实就是WSGI的入口,WSGI可以通过两种方式进行部署。Apache和evenlet,自然keystone也同时支持这两种方式。
对于Apache部署而言,他的文件保存在/etc/apache2/sites-available/目录下。其中,wsgi-keystone.conf是一个mod_wsgi的示例配置文件。
keystone.py则是WSGI应用程序的入口文件。文件位置为:httpd/keystone.py也就是我们要找的入口文件之一。这个文件比较简单,从中可以看到,关键的一行代码就是:
application = wsgi_server.initialize_application(name)
从中看到他创建了WSGI入口所需要使用的application对象。
而对于eventlet而言,他的部署主要是通过keystone-all命令。从setup.cfg中看到该命令的入口是:
keystone-all = keystone.cmd.all:main
找到keystone/cmd/all.py中的main函数,其中的内容证明了我们上面所述:
def main():
eventlet_server.run(possible_topdir)
上面的main()函数主要作用是根据配置文件possible_topdir去启动一个eventlet_server。对于eventlet先介绍到这里。
下面的重点以httpd/keystone.py文件作为入口来看。
2、Paste和PasteDeploy
上述讲到keystone.py主要调用了initialize_application(name)函数来载入整个WSGI应用。这里用到了Paste和PasteDeploy库。
到keystone/server/wsgi.py中查看具体的函数。
其中config.find_paste_config()用来查找PasteDeploy需要用到的WSGI配置文件,这个文件在源码中是etc/keystone-paste.ini文件。keystone_service.loadapp()函数内部则调用了paste.deploy.loadapp()函数来加载WSGI应用,如何加载则使用了刚才提到的keystone-paste.ini文件,这个文件也是看懂整个程序的关键。
3、Paste.ini
对于整个openstack而言经常会看到ini文件,下面来具体解析一下这个文件。
他由多个section组成,每个section的格式为[type:name]。
不同type的section的不同作用也是理解这个文件的关键,主要是有这么几个type:
Composite:用于将http请求分发给制定的app。这个的关键字use = egg:Paste#urlmap标示到Paste模块的egg-info中寻找urlmap对应的函数。其中不同URL path前缀,把请求路由给不同的app。比如/v2.0=public_api,标示以/v2.0开头的url会路由给public_api处理。路由的对象即为其他section的名字,类型必须为app或者pipline。
Pipline:将一系列filter和app串联起来。其中的关键字为其他section的名字。其中有一个规则为最后一项必须是一个app,而其他项都为filter。
Filter:实现一个过滤器中间件,过滤请求和响应。其中的关键字表明调用哪个函数来获取这个filter中间件。比如:
[filter:debug]
paste.filter_factory = keystone.common.wsgi:Debug.factory
调用的即为keystone/common/wsgi.py中的classDebug(Middleware):。
从中可以看到这个类是以WSGI中间件的方式实现。
App:表示具体的一个app,为一个标准的WSGI application。其中的关键字也为调用哪个函数来获取这个app。
整一套keystone有两个app,admin和main。在实际部署过程中,httpd/keystone.py会重命名为admin.py或main.py。其中admin来监听35357端口,main来监听5000端口。
所以总结来说paste.ini中这一大堆配置的作用就是把我们用Python写的WSGI application和middleware串起来,规定好HTTP请求处理的路径。
在上述的application =wsgi_server.initialize_application(name)存在一个name变量,这个变量来确定入口,在Keystone的paste.ini中,请求必须先由[composite:main]或者[composite:admin]处理,所以在keystone项目中,name的值必须是main或者admin。
举一个例子: POST http://localhost:35357/v3/auth/tokens
发到keystone获取一个token时是如何操作的?大致流程就是解析这个URL:
(1) hostname:35357 这一部分是由Web服务器处理的,比如Apache。然后,请求会被转到WSGI的入口,也就是httpd/keystone.py中的application对象来处理。
(2) application对象是根据paste.ini中的配置来处理的。这里会先由[composite:admin]来处理(一般是admin监听35357端口,main监听5000端口)。
(3) [composite:admin]发现请求的path是/v3开头的,于是就把请求转发给[pipeline:api_v3]去处理,转发之前,会把/v3这个部分去掉。
(4) [pipeline:api_v3]收到请求,path是/auth/tokens,然后开始调用各个filter来处理请求。最后会把请求交给[app:service_v3]进行处理。
(5) [app:service_v3]收到请求,path是/auth/tokens,http请求的方法为POST然后交给最终的WSGI app去处理。
4、中间件(Middleware)
Middleware是WSGI架构中重要的组成部分。同时具备Server和application的角色。
从代码上来讲,上述的中间件实际是调用Middleware作为基类的。这边主要是一个__call__()方法。
@webob.dec.wsgify()
def __call__(self, request):
try:
response =self.process_request(request)
if response:
return response
response =request.get_response(self.application)
return self.process_response(request,response)
except exceptin.Error as e:
从代码中可以看到,他的实现主要是接受一个request对象,然后返回一个response对象。然后使用WebOB模块的装饰器webob.dec.wsgify()将它变成标准的WSGI application接口。这里的request和response对象分别是webob.Request和webob.Response。
5、Routes
上述的URL还剩下/auth/tokens来实现URL路由。这边就要使用Routes模块。
Routes模块是用Python实现的类似Rails的URL路由系统,它的主要功能就是把path映射到对应的动作。
Routes模块的一般用法是创建一个Mapper对象,然后调用该对象的connect()方法把path和method映射到一个controller的某个action上,这里controller是一个自定义的类实例,action是表示controller对象的方法的字符串。一般调用的时候还会指定映射哪些方法,比如GET或者POST之类的。
总结一下:整个keystone的代码流程其实就是解析整个URL的过程。通过ini文件进行解析,根据不同的version找到不同的factory,路径为keystone/service.py。然后找到具体的执行内容。整个keystone根据文件名来进行区分,比较方便查找。找到之后根据各个controller.py
core.pyrouters.py三个文件来进行具体的处理。其中routers.py实现URL路由,把URL和controller.py中的action对应起来。Controllers.py中的action调用core.py中的底层接口实现RESTfulAPI承诺的功能。Core.py中定义了Manager类和Driver类。其中Manager负责基于不同的Backend Driver对请求进一步处理。