一個完整的Erlang應用

http://blog.chinaunix.net/uid-25876834-id-3308693.htmlcss

這裏介紹構建一個完整的Erlang/OTP應用的例子,最後還給出了一個在實際生成環境中,如何啓動應用的才能方便運維人員維護和部署的實例。例子摘自 《Manning.Erlang.and.OTP.in.Action》(國內目前已經有中文版發行)。html

關於該應用例子中,代碼和配置文件中的基本的概念的解釋我這裏就省略了。
 
一個完整而簡單的基於tcp的rpc 應用爲例,一個應用包括:
 
一、元數據文件 tcp_rpc.app

點擊(此處)摺疊或打開shell

  1. {application,tcp_rpc,
  2.  [{description,"RPC Server for Erlang and OTP in action"},
  3.   {vsn, "0.1.0"},
  4.   {modules,[tr_app,
  5.             tr_sup,
  6.             tr_server]},
  7.   {registered, [tr_sup]},
  8.   {applications, [kernel, stdlib]},
  9.   {mod, {tr_app, []}}
  10. ]}.
二、應用啓動模塊 tr_app.erl

點擊(此處)摺疊或打開windows

  1. -module(tr_app).
  2. -behaviour(application).
  3. %% --------------------------------------------------------------------
  4. %% Include files
  5. %% --------------------------------------------------------------------
  6. %% --------------------------------------------------------------------
  7. %% Behavioural exports
  8. %% --------------------------------------------------------------------
  9. -export([
  10.      start/2,
  11.      stop/1
  12.         ]).
  13. %% --------------------------------------------------------------------
  14. %% Internal exports
  15. %% --------------------------------------------------------------------
  16. %% -export([]).
  17. %% ====================================================================!
  18. %% External functions
  19. %% ====================================================================!
  20. %% --------------------------------------------------------------------
  21. %% Func: start/2
  22. %% Returns: {ok, Pid} |
  23. %% {ok, Pid, State} |
  24. %% {error, Reason}
  25. %% --------------------------------------------------------------------
  26. start(_Type, _StartArgs) -> 
  27.     case tr_sup:start_link() of
  28.     {ok, Pid} -> 
  29.      {ok, Pid};
  30.     Error ->
  31.      {error,Error}
  32.     end.
  33. %% --------------------------------------------------------------------
  34. %% Func: stop/1
  35. %% Returns: any
  36. %% --------------------------------------------------------------------
  37. stop(_State) ->
  38.     ok.
三、頂層監督模塊 tr_sup.erl

點擊(此處)摺疊或打開服務器

  1. -module(tr_sup).
  2. -behaviour(supervisor).
  3. -export([start_link/0]).
  4. -export([
  5.      init/1
  6.         ]).
  7. -define(SERVER, ?MODULE).
  8. start_link() ->
  9.     supervisor:start_link({local, ?SERVER}, ?MODULE, []).
  10. init([]) ->
  11.     Server = {tr_server, 
  12.              {tr_server,start_link,[]},
  13.              permanent, 2000, worker, [tr_server]},
  14.     {ok,{
  15.          {one_for_one,0,1}, 
  16.          [Server]
  17.         }
  18.     }.
四、服務器模塊 tr_server.erl

點擊(此處)摺疊或打開cookie

  1. %%%-------------------------------------------------------------------
  2. %%% @author <龍二>
  3. %%% @copyright (C) 2012, 
  4. %%% @doc RPC over TCP server. This module defines a server process that 
  5. %%% listens for incoming TCP connections and allows the user to
  6. %%% execute RPC commands via that TCP stream.
  7. %%% @end
  8. %%% Created : 2 Jul 2012 by <emacs>
  9. %%%-------------------------------------------------------------------
  10. -module(tr_server).
  11. -include_lib("eunit/include/eunit.hrl"). %% Eunit單元測試
  12. -behaviour(gen_server).
  13. %% API
  14. -export([
  15.          start_link/1,
  16.          start_link/0,
  17.          get_count/0,
  18.          stop/0
  19.          ]).
  20. %% gen_server callbacks
  21. -export([init/1,handle_call/3,handle_cast/2,handle_info/2,
  22.          terminate/2,code_change/3]).
  23. -define(SERVER,?MODULE).
  24. -define(DEFAULT_PORT,1055).
  25. -record(state,{port,lsock,request_count = 0}).
  26. %%%===================================================
  27. %%% API
  28. %%%===================================================
  29. %%----------------------------------------------------
  30. %% @doc Starts the server.
  31. %%
  32. %% @spec start_link(Port::integer()) -> {ok,Pid}
  33. %% where
  34. %% Pid = pid()
  35. %% @end
  36. %%----------------------------------------------------
  37. start_link(Port) ->
  38.     gen_server:start_link({local,?SERVER},?MODULE,[Port],[]).
  39. %% @spec start_link() -> {ok,Pid}
  40. %% @doc Calls 'start_link(Port)' using the default port
  41. start_link() ->
  42.     start_link(?DEFAULT_PORT).
  43. %%----------------------------------------------------
  44. %% @doc Fetches the number of requests made to this server
  45. %% @spec get_count() -> {ok,Count}
  46. %% where
  47. %% Count = integer()
  48. %% @end
  49. %%----------------------------------------------------
  50. get_count() ->
  51.     gen_server:call(?SERVER,get_count).
  52. %%----------------------------------------------------
  53. %% @doc Stops the server
  54. %% @spec stop() -> ok
  55. %% @end
  56. %%----------------------------------------------------
  57. stop() ->
  58.     gen_server:cast(?SERVER,stop).
  59. %%%===================================================
  60. %%% gen_server callbacks
  61. %%%===================================================
  62. init([Port]) ->
  63.     {ok,LSock} = gen_tcp:listen(Port,[{active,true}]),
  64.     %% 0 is timeout value
  65.     %% a atom msg 'timeout' will be generate
  66.     %% handle_info/2 will handle this msg immediately when init/1 finished
  67.     {ok,#state{port = Port,lsock = LSock},0}. 
  68. handle_call(get_count,_From,State) ->
  69.     {reply,{ok,State#state.request_count},State}.
  70. handle_cast(stop,State) ->
  71.     {stop,normal,State}.
  72. %% handle_info/2: handle tcp,port,timeout msg 
  73. handle_info({tcp,Socket,RawData},State) ->
  74.     do_rpc(Socket,RawData),
  75.     RequestCount = State#state.request_count,
  76.     {noreply,State#state{request_count = RequestCount + 1}};
  77. handle_info(timeout,#state{lsock = LSock} = State) ->
  78.     {ok,_Sock} = gen_tcp:accept(LSock),
  79.     {noreply,State}.
  80. terminate(_Reason,_State) ->
  81.     ok.
  82. code_change(_OldVsn,State,_Extra) ->
  83.     {ok,State}.
  84. %%%===================================================
  85. %%% Internal functions
  86. %%%===================================================
  87. do_rpc(Socket,RawData) ->
  88.     try
  89.         {M,F,A} = split_out_mfa(RawData),
  90.         Request = apply(M,F,A),
  91.         gen_tcp:send(Socket,io_lib:fwrite("~p~n",[Request]))
  92.     catch
  93.         _Class:Err ->
  94.             gen_tcp:send(Socket,io_lib:fwrite("~p~n",[Err]))
  95.     end.
  96. split_out_mfa(RawData) ->
  97.      MFA = re:replace(RawData,"\r\n$","",[{return,list}]),
  98.     {match,[M,F,A]} =
  99.         re:run(MFA,
  100.              "(.*):(.*)\s*\\((.*)\s*\\)\s*.\s*$",
  101.              [{capture,[1,2,3],list},ungreedy]),
  102.     {list_to_atom(M),list_to_atom(F),args_to_terms(A)}.
  103. args_to_terms(RawArgs) ->
  104.     {ok,Toks,_Line} = erl_scan:string("[" ++ RawArgs ++ "]. ",1),
  105.     {ok,Args} = erl_parse:parse_term(Toks),
  106.     Args.
五、啓動整個應用的開啓模塊 tr.erl

點擊(此處)摺疊或打開app

  1. -module(tr).
  2. -export([start/0]).
  3. %%
  4. %% API Functions
  5. %%
  6. %% 啓動應用
  7. start() ->
  8.     case application:start(tcp_rpc) of
  9.         ok ->
  10.             ok;
  11.         Msg ->
  12.             {failur, Msg}
  13.     end.
  14. %% 關閉應用
  15. stop() ->
  16. case application:stop(tcp_rpc) of
  17. ok ->
  18. ok;
  19. Msg ->
  20. {failure, Msg}
  21. end.
 
六、啓動腳本 start_tr.sh (windows下爲start_tr.bat)

點擊(此處)摺疊或打開運維

  1. erl -pa ebin -name tr@127.0.0.1 -setcookie tr -s tr start
  2. chmod +x start_tr.sh
  3. windows下的話,須要將erl這個程序加入到環境變量,不然就要使用絕對路徑來應用erl程序
  
七、編譯代碼能夠經過:
erlc -o ebin src/*.erl
或者利用Emakefile

點擊(此處)摺疊或打開tcp

  1. { ["src/*"]
  2.     , [     
  3.         {outdir, "./ebin"}]
  4. }.

配合 erl -make 來編譯這個應用。單元測試

 
八、啓動。由於咱們寫了啓動腳本和啓動模塊,因此不須要進入到erlang shell 裏面啓動這個應用,能夠直接經過執行 start_tr.sh 來啓動。
 
這裏給出一個完整又簡單的例子,但願能夠對剛接觸OTP知識的人有幫助。
  1.     , [     
  2.         {outdir, "./ebin"}]
  3. }.
配合 erl -make 來編譯這個應用。
 
八、啓動。由於咱們寫了啓動腳本和啓動模塊,因此不須要進入到erlang shell 裏面啓動這個應用,能夠直接經過執行 start_tr.sh 來啓動。
 
這裏給出一個完整又簡單的例子,但願能夠對剛接觸OTP知識的人有幫助。
相關文章
相關標籤/搜索