Rails的启动
前言
? 本文主要是針對(duì)Ruby On Rails 2.0.2的源代碼進(jìn)行分析,學(xué)習(xí)與研究。所使用的工具是NetBean 6.1 Beta,WEBRick,SciTE,ruby-debug-base(0.10.0),ruby-debug-ide(0.1.10)。Ruby版本為1.8.6。
? 應(yīng)該怎么分析總結(jié),是開始最令人頭痛的事,Ruby是面向?qū)ο蟮恼Z言,從對(duì)象的層次記錄吧,似乎一切都不那么直觀,一個(gè)龐大的系統(tǒng)擺在眼前,整理一個(gè)類圖,繼承關(guān)系圖。。。有點(diǎn)牛啃南瓜,無從下口的感覺。最后,決定打算從Ruby的本質(zhì)-解釋語言下手,從解釋器的角度出發(fā),跟著解釋器的步伐,從細(xì)微入手,一步一步深入Rails,以達(dá)到從局部到整體,了解學(xué)習(xí)的目的。所以,最終,我決定從源代碼執(zhí)行順序的角度去分析Rails。
? 為方便起見,我直接使用NetBean的調(diào)試環(huán)境,使用Ruby自帶的WEBRick,從接觸Rails最基本的ruby script/server開始,首先來看Rails是怎么啟動(dòng)起來的。
-
Rails的啟動(dòng)
? ruby script/server,應(yīng)該是搞rails的同學(xué)們耳熟能詳?shù)拿盍恕erver腳本主要執(zhí)行兩個(gè)的過程:1.啟動(dòng)Rails;2.啟動(dòng)web服務(wù)器(當(dāng)然,我這里是啟動(dòng)WEBRick了)。我們就從這里入手,看看Rails是怎么樣被啟動(dòng)起來的。
? 前面我說過,我將從源代碼執(zhí)行順序的角度去分析,所以,讓我們先來看一看Rails啟動(dòng)時(shí),核心源代碼的執(zhí)行順序,具體見下圖(為了使得分析簡(jiǎn)單明了,抓住關(guān)鍵本質(zhì)所在,我只把個(gè)人認(rèn)為與啟動(dòng)有關(guān)的源代碼列出來,執(zhí)行過程中,其他類似關(guān)于ActiveSupport中關(guān)于core的extension之類的代碼就不列出):
? boot.rb
? 源代碼路徑:RAILS_ROOT/config/boot.rb
? 這個(gè)代碼文件是Rails的啟動(dòng)入口,完成的功能是:首先判斷Rails是否啟動(dòng),如果未啟動(dòng)則先執(zhí)行一個(gè)“預(yù)初始化”(preinitialize)過程,然后選擇一種啟動(dòng)方式(Vendor/Gem),執(zhí)行相應(yīng)類上的run方法。主方法boot!代碼如下:
? 其中,與初始化過程是執(zhí)行RAILS_ROOT/config目錄下面的preinitializer.rb(如果存在的話)。這個(gè)過程的目的是在加載environment.rb文件執(zhí)行執(zhí)行一些初始化工作。參見:http://yudionrails.com/2008/1/7/what-s-new-in-edge-rails-pre-environment-load-hook。此源代碼中包含一個(gè)module Rails,此模塊下面包括三個(gè)類:VendorBoot,GemBoot,他們都繼承自Boot類,分別代表是通過Vendor還是Gem的方式啟動(dòng)Rails(如果RAILS_ROOT/vender/下面存在名為rails的目錄,則以Vendor方式啟動(dòng)Rails,否則,從Gem啟動(dòng)Rails)。當(dāng)使用Gem方式啟動(dòng)Rails的話,還有一個(gè)重要的功能就是判斷加載哪個(gè)版本的Rails,當(dāng)然,正如我們所知,environment.rb中的RAILS_GEM_VERSION起了作用。總得來說,boot代碼邏輯較簡(jiǎn)單,沒有什么費(fèi)解的東西,下面給出這個(gè)文件的整個(gè)執(zhí)行邏輯流程圖:
initialize.rb
? 源代碼路徑:gems/rails-2.0.2/lib/initializer.rb (Gem方式啟動(dòng))
??????????????????? RAILS_ROOT/vendor/rails/railties/lib/initializer.rb(Vendor方式啟動(dòng))
? 雖然兩種不同啟動(dòng)方式執(zhí)行的源代碼不同,但是他們完成的功能都大同小異,都對(duì)Rails執(zhí)行必要的配置以及初始化。我們先來看看上一步--執(zhí)行boot.rb代碼的最后一步(還記得pick_boot.run么?),具體代碼如下:
?
Ruby代碼 ?當(dāng)完成了選擇一個(gè)boot方式后,會(huì)執(zhí)行相應(yīng)Boot對(duì)象的run方法,那么run方法首先載入初始化器,VendorRoot通過如下方式載入:
Ruby代碼 ?GemRoot通過如下方式載入:
Ruby代碼 ?OK。初始化器載入完成,Boot的run方法立即執(zhí)行初始化器對(duì)象的類方法run,注意這里的run方法參數(shù)是:set_load_path符號(hào)。好了,下面,我們可以深入到initialize.rb里面去看個(gè)究竟了。? initialize.rb中定義了一個(gè)module Rails,其中包括了此代碼文件中最重要的兩個(gè)類:Configuration和Initializer。從類名我們就可以很清晰的了解到,Initializer類完成Rails的初始化工作,當(dāng)然這個(gè)過程需要各種各樣的配置,參數(shù),這些則由Configuration提供。那么首先來看看Configuration提供了Rails所需的哪些配置參數(shù),詳見下表:
| 配置名(accessor名) | 具體描述 |
| frameworks | 會(huì)被載入的Rails框架組件列表,會(huì)包括action_controller,action_view等 |
| load_paths | 附加的load路徑列表,app/controller;app/models等Rails項(xiàng)目下的目錄 |
| load_once_paths | Rails只會(huì)load一次的目錄,似乎目前版本的Rails未用到這個(gè)參數(shù) |
| log_path | 日志文件的路徑,根據(jù)目前的環(huán)境(development,test,production)決定 |
| log_level | Rails日志器的日志級(jí)別(info,debug) |
| view_path | view的目錄路徑,默認(rèn)路徑是app/view了 |
| controller_paths | controller的目錄路徑,默認(rèn)路徑是app/controller |
| cache_classes | 是否對(duì)類進(jìn)行緩存。目前未使用(一直是false) |
| whiny_nils | true/false,當(dāng)設(shè)置為true的話,當(dāng)你在Rails中調(diào)用一個(gè)nil方法的時(shí)候,將會(huì)得到警告 |
| plugins | 載入的插件列表,默認(rèn)為空 |
| plugin_paths | 插件路徑,默認(rèn)是RAILS_ROOT/vendor/plugins目錄 |
| plugin_locators | 插件的定位器,默認(rèn)是Plugin::FileSystemLocator |
| plugin_loader | 插件的載入器,默認(rèn)是Plugin::Loader |
| database_configuration_file | 數(shù)據(jù)庫配置文件,默認(rèn)位于RAILS_ROOT/config/database.yml |
? 繞了一圈,現(xiàn)在讓我們回到Initializer類的run方法(由boot.rb調(diào)用:Rails::Initializer.run(:set_load_path)),十分簡(jiǎn)單:
Ruby代碼 ?現(xiàn)在我們姑且不管block(boot.rb調(diào)用他的時(shí)候確實(shí)也沒有關(guān)聯(lián)一個(gè)block),接下來的工作是生成一個(gè)新的Configuration對(duì)象,并賦給Initializer的構(gòu)造函數(shù)(然后由Initializer對(duì)象保存該配置對(duì)象),然后執(zhí)行initializer上的command方法,默認(rèn)情況是執(zhí)行process方法,這里通過boot.rb的調(diào)用,將執(zhí)行set_load_path方法。在這里值得注意的是,新生成的Configuration對(duì)象的所有配置參數(shù)都是默認(rèn)值,例如:frameworks參數(shù)通過如下方法得到默認(rèn)值:
Ruby代碼 ?controller_path參數(shù)通過如下方法得到默認(rèn)值:
?
Ruby代碼 ?其實(shí),所有的這些配置都不是定死的,我們可以在enviroment.rb中重新定義他們,象下面這樣:
Ruby代碼 ?到這里,initializer.rb的介紹暫時(shí)結(jié)束,只是簡(jiǎn)單的執(zhí)行了set_load_path方法設(shè)置load路徑。接下來,執(zhí)行流程回到了script/server:
Ruby代碼 ?該執(zhí)行第二句了,下面輪到server.rb出場(chǎng)了。
? server.rb
源代碼路徑:gems/rails-2.0.2/lib/commands/server.rb
? server.rb主要完成兩個(gè)功能:1.加載active_support;2.加載web服務(wù)器。
? 加載active_support十分簡(jiǎn)單,只是通過源代碼開始的第一句:
加載web服務(wù)器相比復(fù)雜一些。首先,Rails會(huì)試圖加載FastCGI,然后會(huì)試圖加載mongrel,如下代碼所示:
Ruby代碼 ?最終,會(huì)通過defined?(Mongrel)和defined?(FCGI)來決定使用哪種服務(wù)器。當(dāng)然,本文前面提到了將使用WEBRick作為web服務(wù)器,這里最終加載的服務(wù)器當(dāng)然是webrick。server.rb的最后一句代碼:?
Ruby代碼 ?在這里#{server}當(dāng)然是webrick了,所以,接下來執(zhí)行的將是webrick.rb
? webrick.rb
源代碼路徑:gems/rails-2.0.2/lib/commands/servers/webrick.rb
? webrick.rb完成如下幾個(gè)主要功能:1.加載Ruby自帶的webrick庫;2.加載environment.rb;3.加載webrick_server.rb;4.執(zhí)行DispatchServlet(在webrick_server.rb中定義)的類方法dispatch。整個(gè)過程都是順序完成的,所以,示意的源代碼可以如下所示:
? environment.rb
? 源代碼路徑:RAILS_ROOT/config/environment.rb
? 回到了我們熟悉的environment.rb中,在這里我們可以對(duì)Rails運(yùn)行的環(huán)境進(jìn)行配置,這里不做過多闡述,可以參與相關(guān)Rails文檔。
? webrick_server.rb
源代碼路徑:gems/rails-2.0.2/lib/webrick_server.rb
? 這是Rails啟動(dòng)所執(zhí)行的最后一個(gè)源代碼文件。我前面提到了一點(diǎn),此源代碼文件中定義了DispatchServlet,這是一個(gè)自定義的dispatch servlet,用于將瀏覽器的請(qǐng)求dispatch到相應(yīng)的controller,action上。因?yàn)檫@里我只打算介紹Rails的啟動(dòng),所以,我們只用關(guān)注如下的代碼即可:
這是一個(gè)經(jīng)典的啟動(dòng)WEBRick服務(wù)器的方式,不用過多闡述,可以參考webrick的相關(guān)文檔。當(dāng)然,webrick_server.rb文件中還有一個(gè)相當(dāng)重要的類CGI,并且,DispatchServlet類中還有一些重要的方法,我們暫時(shí)可以將他們擱在一旁。以后進(jìn)一步分析Rails的時(shí)候再講解。? OK。至此,Rails就正式上馬了,web服務(wù)器也啟動(dòng)起來了,接下來的事情,當(dāng)然是等著web服務(wù)器將request轉(zhuǎn)發(fā)給Rails進(jìn)行處理了,按照國際慣例,這當(dāng)然是下文分解的事了~
轉(zhuǎn)自:http://www.iteye.com/topic/170683
總結(jié)
- 上一篇: 【Java多线程】轻松搞定Java多线程
- 下一篇: 【已补蓝奏云链接】PyTorch中MNI