Swift 啟動流程 & Data Flow


說起較大型系統的源碼閱讀,算上目前正在進行的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']
在Manager.__init__最后add servers的時候,就創建了Swift中的四個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中的各個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綁定


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說清楚啦!(原諒我無恥的打了水印,哈哈哈哈哈)

讓我們按照圖中的流程來解釋吧。

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM