說起較大型系統的源碼閱讀,算上目前正在進行的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']
status = manager.run_command(command, **options.__dict__)

到這里,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