大家好,幾天沒跟新了,在上一篇,我們簡單介紹了mochiweb這個項目,以及下載,編譯,創建mochiweb的源碼以及示例。今天繼續跟大家來分析mochiweb這個項目,跟之前分析cowboy的方法一樣,我們找到切入口點,來一一分析每個函數的功能,作用,下面看下上一篇創建的示例都有哪些文件,如下圖:
這里除了keepalive.erl 是我從官方example考過來以外,其他為上一篇創建的例子的源碼。
我不一一介紹這幾個文件了,參考我一直給大家推薦的Erlang OTP 設計原理。好好看幾遍,你就對Erlang應用程序有一定的了解了。
我們從運行程序的 mochiweb_example:start/0 開始看吧,代碼如下:
%% @spec start() -> ok %% @doc Start the mochiweb_example server. start() -> mochiweb_example_deps:ensure(), ensure_started(crypto), application:start(mochiweb_example).
這個函數三行代碼,第一行先跳過,一會我們重點看下。
第二行啟動crypto,mochiweb_example:ensure_started/1,代碼如下:
ensure_started(App) -> case application:start(App) of ok -> ok; {error, {already_started, App}} -> ok end.
很簡單,判斷下該應用程序是否啟動,如果沒啟動就啟動,如果已經啟動了,就提示 {error, {already_started, App}} 錯誤。
第三行,啟動mochiweb_example,很簡單,其實這里也可以修改成這樣:ensure_started(mochiweb_example),因為上面那個函數本來也就是啟動應用程序用的。不知道作者為什么生成代碼,不用上面的函數,不過關系不大。
現在可以重點看下:mochiweb_example_deps:ensure/0 這個函數了,代碼如下:
%% @spec ensure(Module) -> ok %% @doc Ensure that all ebin and include paths for dependencies %% of the application for Module are on the code path. ensure(Module) -> code:add_paths(new_siblings(Module)), code:clash(), ok. %% @spec ensure() -> ok %% @doc Ensure that the ebin and include paths for dependencies of %% this application are on the code path. Equivalent to %% ensure(?Module). ensure() -> ensure(?MODULE).
從上面代碼可以知道 mochiweb_example_deps:ensure/0 調用 mochiweb_example_deps:ensure/1 函數,並傳遞參數 ?MODULE,而mochiweb_example_deps:ensure/1 函數,就三行代碼也很簡單,我們先看下 mochiweb_example_deps:new_siblings/1 函數:
%% @spec new_siblings(Module) -> [Dir] %% @doc Find new siblings paths relative to Module that aren't already on the %% code path. new_siblings(Module) -> Existing = deps_on_path(), SiblingEbin = filelib:wildcard(local_path(["deps", "*", "ebin"], Module)), Siblings = [filename:dirname(X) || X <- SiblingEbin, ordsets:is_element( filename:basename(filename:dirname(X)), Existing) =:= false], lists:filter(fun filelib:is_dir/1, lists:append([[filename:join([X, "ebin"]), filename:join([X, "include"])] || X <- Siblings])).
從這個函數的注釋,我們能大概知道,查找相對於Module模塊不在代碼路徑下的新的路徑。看下具體邏輯,首先是 mochiweb_example_deps:deps_on_path/0 函數,代碼如下:
%% @spec deps_on_path() -> [ProjNameAndVers] %% @doc List of project dependencies on the path. deps_on_path() -> F = fun (X, Acc) -> ProjDir = filename:dirname(X), case {filename:basename(X), filename:basename(filename:dirname(ProjDir))} of {"ebin", "deps"} -> [filename:basename(ProjDir) | Acc]; _ -> Acc end end, ordsets:from_list(lists:foldl(F, [], code:get_path())).
這個函數,有不少的系統函數,我們先看下這幾個函數:
函數:filename:dirname/1,返回當前文件名所在的目錄,大家可以查下erlang doc,地址:http://www.erlang.org/doc/man/filename.html#dirname-1 我截了個圖,方便大家看吧,如下圖:
函數filename:basename/1,大概意思是:如果它不包含任何目錄分隔符,返回最后一部分的文件名或文件名本身。同樣查下erlang doc,地址:http://www.erlang.org/doc/man/filename.html#basename-1,如下圖:
函數:code:get_path/0,返回當前代碼路徑。 erlang doc 地址:http://www.erlang.org/doc/man/code.html#get_path-0,如下圖:
我在shell上嘗試打印出完整的列表(使用rp命令),一共是58項,如下圖:
函數:lists:foldl/3,對List中的每一項執行函數Fun,Acc0作為該函數第二個參數。 erlang doc 地址:http://www.erlang.org/doc/man/lists.html#foldl-3,如下圖:
我們做個練習如下:
函數: ordsets:from_list/1,對List排序,erlang doc 地址:http://www.erlang.org/doc/man/ordsets.html#from_list-1,如下圖:
示例如下:
理解完這幾個系統函數,我們來整理下 mochiweb_example_deps:deps_on_path/0 這個函數的作用。首先是定義了匿名函數F,接受2個參數,X為調用 code:get_path/0 返回的列表的每一項項,Acc用來保存對每一項處理的結果。而處理每一項的邏輯也很簡單,這里我們用Debugger跟蹤下其中一項的處理過程,如下圖:
圖一:
圖二:
圖三:
根據上面三個圖,我們就能更加清楚這個函數的作用了。
好了,這一篇就到這里,這些系統函數需要多動手練習還會熟悉。希望大家也多多動手,下一篇,我們繼續從mochiweb_example_deps:new_siblings/1 函數往下看。
謝謝大家的收看。