说起较大型系统的源码阅读,算上目前正在进行的Swift,也就只有两次经验(去年的上半年有阅读过学习过Openfire的源码)。虽说还是菜鸟级别啦,但两次也可以总结经验嘛:P,哈哈~

我的这个经验呢,就是对于这种服务器端的源码,最好首先对系统的“启动过程”和请求到来时的“data flow”进行一遍跟踪阅读,了解程序的运作流程以及各个关键类、方法之间的关系,然后再从这条主线进行各个分叉流程的细致学习。这种方式一来目的性比较强,代码比较容易看得下去,二来不会有越看越茫然的感觉,总体上是逐渐清晰了解细节的过程。

好啦,废话说完咯,进入正题。这两周开始了swift源码(swift 1.8.0)的学习,首先就按照以上所述的流程进行了一遍粗浅的阅读,现在总结一下。

 

Swift启动流程=========================================================

在ubuntu上部署完毕swift后,我们知道可以执行脚本 startmain 启动swift,那么这条命令执行后,swift究竟做了哪些启动相关的工作呢?

1. 概况

startmain 脚本实际上是执行了以下这条命令:



swift-init main start



即为swift执行初始化工作,而该命令实际上又分别执行了swift-proxy-server、swift-account-server、swift-container-server、swift-object-server 这四个swift bin中的python命令。接下来,让我们看一下在swift代码中都做了些什么。

2. swift-init

swift-init 是启动swift的脚本,改脚本可以跟几个参数:swift-init [主体] [动作]。在“swift-init main start”中main就对应主体,动作即为start。

1)swift-init main start 执行后,首先看swift-init代码 line61~line62,在这里对命令和参数进行了分割,从而决定接下来的执行任务:



command = args[- 1]     #command此处为start
servers = args[:-1]     #servers此处为main



 

2)在swift-init代码line 70,创建了Manager的实例,Manager是直接管理各个servers的类:



manager = Manager(servers, run_dir=options.run_dir)     # 这里执行了Manager的__init__



 

3)由于在上一步中,swift-init创建了Manager实例,因此调用了Manager的__init__初始化方法,在__init__方法中,我们可以看到对命令参数servers(这里是“main”)的处理(line 137)。



elif server == 'main':
    server_names.update(MAIN_SERVERS)



其中MAIN_SERVERS为manager.py中定义的全局数组,指明了swift中servers的启动脚本(line 37)



MAIN_SERVERS = ['proxy-server', 'account-server', 'container-server', 'object-server']



swift-proxy -server、swift-account-server、swift-container-server、swift-object-server



 



4)Manager实例创建完毕后,回到swift-init中,在line 72,调用了manager的run_command方法,继而获取了start命令:



status = manager.run_command(command, **options.__dict__)



 



5)在manager.py的run_cmmand方法中(line 162),最终执行了start方法,即执行了swift-proxy -server、swift-account-server、swift-container-server、swift-object-server的__main__,也就是start方法中调用的launch方法打印了这堆东西:




swift 处理耗时操作 swift流程_swift


到这里,swift中的各个servers就被启动起来了,接下来让我们以proxy-server为例,看看在这些swift-xx-server中都做了些什么。

 

3. swift-xx-server的启动

在swift-xx-server中最终调用了相应的xx.server.py中相应类的__init__(以proxy为例):


run_wsgi(conf_file, 'proxy-server', default_port=8080 , **options)


我们可以看到swift的HTTP交互是基于WSGI实现的,并且它使用了paste deploy进行配置文件的管理,从而通过xx-server.conf配置文件load相应的wsgi app。在run_wsgi方法中,主要执行了以下四个步骤:

1)run_wsgi方法根据参数load配置文件

2)根据配置文件绑定的端口创建socket,将socket与server绑定

swift 处理耗时操作 swift流程_配置文件_02

3)run_server方法调用proxy的server的__init__方法

4)__init__方法根据配置文件初始化server的内部状态,从而gets the server ready to handle requests

 

至此,Swift中的各个servers就已经stand by啦!只待request的到来!OK,那么接下来让我们来看一下当一个request到来时,swift中的data flow又是怎样的吧 : )

 

 

Swift Data Flow=========================================================

在上文中呢,我已经提到过swift的HTTP交互框架是基于wsgi实现的,并且使用paste deploy实现通过conf来load app,因此如果你希望可以更好的了解swift的交互流程和wsgi-style application标准,那么建议你先学习一下 wsgi标准 以及 paste deploy 的配置文件含义。

首先,来看一张俺画滴图!这张图基本上就把整个请求过程的data flow说清楚啦!(原谅我无耻的打了水印,哈哈哈哈哈)

swift 处理耗时操作 swift流程_swift_03

让我们按照图中的流程来解释吧。

1. Send a Request

首先,用户也就是Client向swift发出一个request,该请求通过proxy-server.conf中配置的bind_port进入swift;

 

2. WSGI middleware: Pipeline

这里先给出一份proxy-server.conf的配置内容,我们来结合这个paste deploy配置文件进行pipeline的说明:


[DEFAULT]
bind_port = 8080
user = root
log_facility = LOG_LOCAL1
eventlet_debug = true

[pipeline:main]
pipeline = healthcheck cache tempauth proxy-logging proxy-server

[app:proxy-server]
use = egg:swift#proxy
allow_account_management = true
account_autocreate = true

[filter:tempauth]
use = egg:swift#tempauth
user_admin_admin = admin .admin .reseller_admin
user_test_tester = testing .admin
user_test2_tester2 = testing2 .admin
user_test_tester3 = testing3

[filter:healthcheck]
use = egg:swift#healthcheck

[filter:cache]
use = egg:swift#memcache

[filter:proxy-logging]
use = egg:swift#proxy_logging


请求需要经过wsgi pipeline中的层层过滤器才能最终到达请求的应用:proxy-server。请注意配置文件中加粗的部分:

[pipeline:main]表明这里是application的入口,pipeline代表一些列的filters,main则类似各种编程语言中的main方法指明入口。

pipeline = filter1 filter2 ... filterN application 的最后一个元素必须是一个符合wsgi的callable对象,具体到这个例子中,表明请求必须经过healthcheck、cache tempauth、proxy-logging这四个filter的过滤后,才能到达proxy-server。如果请求没有通过任何一个过滤器,则请求将会被返回相应的错误信息,不能到达proxy-server。

这些filters和最后的app都必须是符合WSGI标准的callable application,在程序执行时,依次调用它们的__call__方法,让我们以healthcheck为例,来看看它的__call__:


def __call__(self, env, start_response):
        req = Request(env)
        try:
            if req.path == '/healthcheck':
                handler = self.GET
                if self.disable_path and os.path.exists(self.disable_path):
                    handler = self.DISABLED
                return handler(req)(env, start_response)
        except UnicodeError:
            # definitely, this is not /healthcheck
            pass
        return self.app(env, start_response)


我们可以看到,这个方法必须包含两个参数(这里是类中的方法,因此还有一个self)env和start_response,env是wsgi中的环境变量字典,start_response用于产生response的结果,这个方法返回的self.app(env, start_response)的这个app其实就是它的下一个filter:cache,cache也会以相同的方式执行,直到调用proxy-server,proxy-server负责产生相应的Response并返回。

 

3. Proxy-Server.__call__

假设Client发出的这个request非常的合法,他通过了所有的filters,现在终于到达了proxy-server,那接下来又会发生什么呢?

proxy-server这个application实际上对应swift源码proxy包下的server.py中的Application类,如同我们在上文中说到的,这里会调用Application.__call__。在Application.__call__中,对请求做了一些校验,合法后,就调用它的handle_request方法,顾名思义的来handle这个request。

我们知道在swift中请求可以针对account、container和object,显然它们所对应的控制器是不一样的,因此在handle_request方法中又继续调用了get_controller方法。

get_congroller方法根据request path中的http://xxxx/account[container/object]/xxx的子路径来判断这个request想要请求的部分,从而获取对应的controller(AccountController、ContainerController、ObjectController)。细心的你一定会检查一下swift源码中的account包、container包、obj包中的server.py吧,没错AccountController、ContainerController、ObjectController就分别位于这些server.py中!

 

4. Controllers

现在,我们已经确定了这个request对应的controller啦!那么它的请求动作又是什么呢?HEAD?GET?其实这一切已经在HTTP Verb中定义了,因此在handle_request方法中会继续调用controller实例中对应的XXX方法(如HEAD/PUT/GET/POST/DELETE),这些方法最终对这个request做出处理,产生响应,返回相应的Request!

 

5. 返回Request

最后这个Request就沿着原路一层层的返回到Client。Swift也继续该干嘛干嘛等待它来自四面八法的Requests。

 

至此,一个比较outline的请求处理过程就算介绍完毕了。我在这里省略了很多的细节和校验工作,只是为了让大家跟我一样,在diving into swift source code前先知道它的水大概有多深,路线大概是个什么样子的。至于每一步的细节,和精准的角度,还需要在后续一步步的来看,一步步的充实、学习。

 

 

 

==================================全文完结分割啦啦啦啦======================================

 

本文写于慌乱之际,说慌乱,是因为我对Python实在是一知半解,很多地方都是现学的(比如WSGI、pastedeploy),再加上这是对swift源码第一遍的粗浅了解,所以难免有些不正确的地方,麻烦各位有什么想法的话多多交流,一起diving啦 =D