ÔÚÕâ¸ö°æ±¾ÖУ¬ÐèÒªÌṩÈçϹ¦ÄÜ£º
- ʹÓà otp µÄ supervisor ¼à¿ØÊ÷£¬±£Ö¤·þÎñ¿É¿¿ÐÔ¡£
- Ìí¼ÓÈÕÖ¾¹¦ÄÜ£¬Í¨¹ý¶¨ÖÆ sasl alarm_handler À´¼Ç¼¾¯¸æÊ¼þ¡£
- ½«Ãû³Æ·þÎñ´ò°üΪ application£¬ÔÝÇҽРvsns °É£¬very stabilization name server ºÇºÇ¡£
- ¿ª·Å socket ·þÎñ £¨Ê¹Óðë×èÈûµÄ»ìºÏģʽ£©£¬Ê¹Óà vsns://verb /param ×Ô¶¨ÒåÐÒé¶ÔÍâÌṩ·ÃÎÊÖ§³Ö¡£
×îÖÕÑéÖ¤ÐԵŦÄܲâÊÔÓÃÀýÈçÏ£¬Ö÷ÒªµÄ²âÊÔ´úÂëλÓÚ test/0 ·½·¨ÖУ¬ÆäÉϵö·½·¨¶¼ÓÃÓÚ socket ͨÐÅ£º
- -module(vsns_tcp_client).
- -author(lzy).
- -email(lzy.dev@gmail.com).
- -date("2009.02.06").
- -vsn(0.11).
- -compile(export_all).
- conn() ->
- {ok, Socket} = gen_tcp:connect("localhost", 8304,
- [binary, {packet, 2}, {reuseaddr, true}, {active, once}]),
- Socket.
- eval(Socket, Args, AssertVal) ->
- ok = gen_tcp:send(Socket, Args),
- receive
- {tcp, _, AssertVal} ->
- io:format("Ok. ~p = ~p.~n", [Args, AssertVal]);
- {tcp_closed, _} ->
- case Args of
- <<"vsns://kernel_oops">> ->
- io:format("Ok. kernel_oops = tcp_closed.~n");
- _Other ->
- io:format("Connection abort by server.~n")
- end;
- Other ->
- io:format("Assert faild. ~p != ~p.~n", [Other, AssertVal])
- end,
- inet:setopts(Socket, [{active, once}]).
- close(Socket) ->
- gen_tcp:close(Socket).
- test() ->
- S = conn(),
- eval(S, <<"vsns://remove_all">>, <<"ack">>),
- eval(S, <<"vsns://save/abc/123">>, <<"">>),
- eval(S, <<"vsns://save/abc/456">>, <<"123">>),
- eval(S, <<"vsns://save/abc/789">>, <<"456">>),
- eval(S, <<"vsns://load_all">>, <<"ack">>),
- eval(S, <<"vsns://remove/abc">>, <<"789">>),
- eval(S, <<"vsns://remove/not_value">>, <<"">>),
- eval(S, <<"foo">>, <<"unknow">>),
- eval(S, <<"vsns://kernel_oops">>, <<"">>),
- ok = close(S),
- pass.
- %% File end.
ʵ¼ÊʵÏÖ supervisor ¼à¿ØÊ÷¡¢ÈÕÖ¾ºÍ¾¯¸æÊ¼þ¹¦ÄܵĹý³Ì£¬Ò²ÊÇѧϰ ¡¶Erlang ³ÌÐòÉè¼Æ¡·µÄ¹ý³Ì¡£
Ê×ÏÈ£¬ÎªÃû³Æ·þÎñÌí¼Ó¼à¿Ø½ø³Ì¡£erlang otp ¼à¿ØÊ÷ºÜ¼òµ¥£¬Ö»ÐèҪʵÏÖÒ»¸ö supervisor behaviour module Ìṩ¸ø otp supervisor Ä£¿é¾Í¿ÉÒÔ£¬Ç°Ãæ°æ±¾µÄÃû³Æ·þÎñÊÇͨ¹ý erlang shell Æô¶¯µÄ£¬ÔÚÒÔºó½«ÓÉÕâ¸ö¼à¿Ø½ø³ÌÀ´Æô¶¯Ëý£¬Ö÷ÒªµÄÆô¶¯´úÂëÔÚ init/1 ·½·¨ÖУ¬¼à¿ØÄ£¿é´úÂëÈçÏ£º
- -module(name_server_sup).
- -author(lzy).
- -email(lzy.dev@gmail.com).
- -date("2009.02.04").
- -vsn(0.1).
- -behaviour(supervisor).
- %% gen_supervisor behaviour callback functions.
- -export([init/1]).
- %% Interface functions.
- -export([start/0, start_in_shell/0, start_link/1]).
- start() ->
- spawn(fun() -> supervisor:start_link({local, ?MODULE}, ?MODULE, _Arg = []) end).
- start_in_shell() ->
- {ok, Pid} = supervisor:start_link({local, ?MODULE}, ?MODULE, _Arg = []),
- unlink(Pid).
- start_link(Args) ->
- supervisor:start_link({local, ?MODULE}, ?MODULE, Args).
- init([]) ->
- gen_event:swap_handler(alarm_handler, {alarm_handler, swap}, {vsns_alarm_handler, foo}),
- {ok, {
- {one_for_one, 3, 10},
- [{
- vsns_name_server,
- {name_server, start_link, []},
- permanent,
- 1,
- worker,
- [name_server]
- }]
- }}.
- %% File end.
ÓÐÁËÕâ¸ö name_server_sup ¾Í²»Å name_server ±ÀÀ£ÁË£¬supervisor ½ø³Ì»á¸ºÔðÖØÐÂÆô¶¯£¬¶ÔÓÚÃèÊö¼à¿Ø²ßÂÔµÄÊý¾Ý½á¹¹¿É²Î¿¼ erlang doc¡£ÆäÖÐµÄ vsns_alarm_handler ÊǶ¨ÖƵľ¯¸æÊ¼þ´¦ÀíÄ£¿é£¬¸ºÔ𽫷þÎñÖеı¨¾¯¼Ç¼µ½ erlang sasl ÈÕÖ¾ÖУ¬ºóÆÚ¿ÉÒÔʹÓà rb ¹¤¾ßÀ´²é¿´´¦Àí¡£½ÓÏÂÀ´¾ÍÊǾ¯¸æÈÕÖ¾´¦ÀíÄ£¿é´úÂ룺
- -module(vsns_alarm_handler).
- -author(lzy).
- -email(lzy.dev@gmail.com).
- -date("2009.02.04").
- -vsn(0.11).
- -behaviour(gen_event).
- %% gen_event behaviour callback functions.
- -export([init/1, handle_event/2, handle_call/2, handle_info/2, terminate/2, code_change/3]).
- init(Args) ->
- io:format("vsns_alarm_handler init : ~p.~n", [Args]),
- {ok, Args}.
- handle_event({set_alarm, {remove_all, From}}, _State) ->
- error_logger:error_msg("vsns depot clear by ~p started.~n.", [From]),
- {ok, _State};
- handle_event({clear_alarm, {remove_all, From}}, _State) ->
- error_logger:error_msg("vsns depot clear by ~p done.~n.", [From]),
- {ok, _State};
- handle_event(Event, State) ->
- error_logger:error_msg("unmatched event: ~p.~n", [Event, State]),
- {ok, State}.
- handle_call(_Req, State) ->
- {ok, State, State}.
- handle_info(_Info, State) ->
- {ok, State}.
- terminate(_Reason, _State) ->
- ok.
- code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
- %% File end.
¹é¸ùµ½µ×£¬¾ÍÊÇͨ¹ý error_logger:error_msg µ÷ÓÃÀ´¼Ç¼ÈÕÖ¾¡£µ±È»»¹Éæ¼°µ½ erlang sasl µÄÅäÖãº
- %% file name: sasl_log.config
- %% auther: lzy
- %% email: lzy.dev@gmail.com
- %% date: 2009.02.04
- %% version: 0.1
- [{sasl, [
- {sasl_error_logger, false},
- {errlog_type, error},
- {error_logger_mf_dir, "./logs"},
- %% 10M per log file.
- {error_logger_mf_maxbytes, 1048760},
- {error_logger_mf_maxfiles, 5}
- ]}].
- %% File end.
¸ÃÅäÖÃÎļþ¿ÉÒÔͨ¹ý erlang shell µÄ Æô¶¯Æô¶¯²ÎÊýÖ¸¶¨¡£-boot start_sasl -config .\sasl_log¡£ÔÙ½ÓÏÂÀ´¾ÍÊÇ´ò°ü vsns application£¬ÕâÐèÒªÒ»¸ö application ÃèÊöÎļþºÍÒ»¸ö application behavior Ä£¿é£¬ºÜ¼òµ¥¾ßÌåÅäÖòÎÊýÓïÒâ¿É²Î¿¼ erlang doc¡£
- %% file name: vsns.app
- %% auther: lzy
- %% email: lzy.dev@gmail.com
- %% date: 2009.02.05
- %% version: 0.1
- {
- application, vsns,
- [
- {description, "very stabilization name service."},
- {vsn, "1.0a"},
- {modules, [vsns_app, vsns_supervisor, name_server, vsns_alarm_handler]},
- {registered, [vsns_supervisor, name_server]},
- {applications, [kernel, stdlib]},
- {mod, {vsns_app, []}},
- {start_phases, []}
- ]
- }.
- %% File end.
- -module(vsns_app).
- -author(lzy).
- -email(lzy.dev@gmail.com).
- -date("2009.02.05").
- -vsn(0.1).
- -behavior(application).
- -export([start/2, stop/1]).
- start(_Type, Args) ->
- name_server_sup:start_link(Args).
- stop(_State) ->
- void.
- %% File end.
¾¹ýÕâÑùµÄ°ü×°£¬¾Í¿ÉÒÔͨ¹ý application:start(vsns) µ÷ÓÃÀ´Æô¶¯ vsns ·þÎñ¡£Í¨¹ý appmon ¹¤¾ß¿ÉÒÔ¿´µ½ÈçϽø³ÌÊ÷£º
µ½ÕâÀÎÒÃǾͿÉÒÔͨ¹ý erlang À´Ê¹Óà vsns ÁË¡£
- C:\Program Files\erl5.6.4\usr\lzy_app\vsns>..\..\..\bin\erl.exe -sname vsns +P 1
- 02400 -smp enable +S 1 -boot start_sasl -config sasl_log
- Eshell V5.6.4 (abort with ^G)
- (vsns@srclzy)1> application:start(vsns).
- vsns_alarm_handler init : {foo,{alarm_handler,[]}}.
- name_server starting.
- ok
- (vsns@srclzy)2> name_server:save(abc, 123).
- undefined
- (vsns@srclzy)3> name_server:load_all().
- [{abc,123}]
×îºó»¹ÐèÒªÒ»¸ö socket tcp ·þÎñÆ÷£¬À´½« vsns ±©Â¶³öÀ´£¬ÔÊÐíÆäËü client À´Ê¹Ó÷þÎñ¡£otp ÖÐûÓÐÀàËÆµÄ socket server behavior£¬µ«¿ÉÒÔͨ¹ý gen_server À´ÊµÏÖ£¬µ±È»ÉõÖÁ¿ÉÒÔʵÏÖÒ»¸ö·Ç otp Ïà¹ØµÄ socket ·þÎñÆ÷¡£ÕâÀï Serge Aleynikov ʵÏÖÁËÒ»¸öºÜºÃ tcp ·þÎñÆ÷£¬»ùÓÚÓÐÏÞ״̬»úģʽÀ´´¦ÀíÇëÇó£¬ÔÚ´Ë×öÁ˺ܺõIJûÊö£ºBuilding a Non-blocking TCP server using OTP principles £¬²»¹ý¿ÖÅÂÐèÒª´úÀíÀ´´ò¿ªÁ¬½Ó¡£ÔÚËû¸ø³öµÄ´úÂëÖУ¬ÎÒÌí¼ÓÁ˼¸ÐдúÂ룬½« socket server ÌṩµÄ·þÎñÊÇ×öΪ¿ÉÅäÖõģ¬Í¨¹ý application »·¾³À´ÅäÖà socket server ʹÓÃµÄ gen_fsm behaviour module£¬´óԼλÓÚ tcp_server_app Ä£¿éµÄ 15 ºÍ 27 ÐС£
- -module(tcp_server_app).
- ... ...
- -define(DEF_SERVICE, tcp_echo_fsm).
- ... ...
- start(_Type, _Args) ->
- ListenPort = get_app_env(listen_port, ?DEF_PORT),
- ServiceMod = get_app_env(service_mod, ?DEF_SERVICE),
- supervisor:start_link({local, ?MODULE}, ?MODULE, [ListenPort, ServiceMod]).
- ... ...
ÔÚ saleyn_tcp_server ÖÐÌṩµÄÊÇ echo ·þÎñ¡£ÎªÁ˽« saleyn_tcp_server ·þÎñÖ¸¶¨³É vsns£¬³ýÁËÉÏÃæµÄÐÞ¸ÄÍ⣬ʣϾÍÖ»ÐèҪʵÏÖÒ»¸öµ÷Óà vsns µÄ gen_fsm behaviour module ÁË£¬´úÂëºÜ¼òµ¥£¬ÊÇ»ùÓÚ tcp_echo_fsm Ð޸ĵÃÀ´µÄ£¬ºÇºÇ¡£
- -module(vsns_tcp_fsm).
- -author(lzy).
- -email(lzy.dev@gmail.com).
- -date("2009.02.06").
- -vsn(0.1).
- -remark("vsns_tcp_fsm used by saleyn_tcp_server appliction to support vsns socket server.").
- -remark("It referenced from saleyn_tcp_server/tcp_echo_fsm module.").
- -behaviour(gen_fsm).
- -export([start_link/0, set_socket/2]).
- %% gen_fsm callbacks
- -export([init/1, handle_event/3,
- handle_sync_event/4, handle_info/3, terminate/3, code_change/4]).
- %% FSM States
- -export([
- 'WAIT_FOR_SOCKET'/2,
- 'WAIT_FOR_DATA'/2
- ]).
- -record(state, {
- socket, % client socket
- addr % client address
- }).
- -define(TIMEOUT, 120000).
- %%%------------------------------------------------------------------------
- %%% API
- %%%------------------------------------------------------------------------
- %%-------------------------------------------------------------------------
- %% @spec (Socket) -> {ok,Pid} | ignore | {error,Error}
- %% @doc To be called by the supervisor in order to start the server.
- %% If init/1 fails with Reason, the function returns {error,Reason}.
- %% If init/1 returns {stop,Reason} or ignore, the process is
- %% terminated and the function returns {error,Reason} or ignore,
- %% respectively.
- %% @end
- %%-------------------------------------------------------------------------
- start_link() ->
- gen_fsm:start_link(?MODULE, [], []).
- set_socket(Pid, Socket) when is_pid(Pid), is_port(Socket) ->
- gen_fsm:send_event(Pid, {socket_ready, Socket}).
- %%%------------------------------------------------------------------------
- %%% Callback functions from gen_server
- %%%------------------------------------------------------------------------
- %%-------------------------------------------------------------------------
- %% Func: init/1
- %% Returns: {ok, StateName, StateData} |
- %% {ok, StateName, StateData, Timeout} |
- %% ignore |
- %% {stop, StopReason}
- %% @private
- %%-------------------------------------------------------------------------
- init([]) ->
- process_flag(trap_exit, true),
- {ok, 'WAIT_FOR_SOCKET', #state{}}.
- %%-------------------------------------------------------------------------
- %% Func: StateName/2
- %% Returns: {next_state, NextStateName, NextStateData} |
- %% {next_state, NextStateName, NextStateData, Timeout} |
- %% {stop, Reason, NewStateData}
- %% @private
- %%-------------------------------------------------------------------------
- 'WAIT_FOR_SOCKET'({socket_ready, Socket}, State) when is_port(Socket) ->
- % Now we own the socket
- inet:setopts(Socket, [binary, {packet, 2}, {reuseaddr, true}, {active, once}]),
- {ok, {IP, _Port}} = inet:peername(Socket),
- {next_state, 'WAIT_FOR_DATA', State#state{socket=Socket, addr=IP}, ?TIMEOUT};
- 'WAIT_FOR_SOCKET'(Other, State) ->
- error_logger:error_msg("State: 'WAIT_FOR_SOCKET'. Unexpected message: ~p\n", [Other]),
- %% Allow to receive async messages
- {next_state, 'WAIT_FOR_SOCKET', State}.
- %% Notification event coming from client
- 'WAIT_FOR_DATA'({data, Data}, #state{socket=S} = State) ->
- ok = handle_data(S, string:tokens(binary_to_list(Data), "/")),
- inet:setopts(S, [{active, once}]),
- {next_state, 'WAIT_FOR_DATA', State, ?TIMEOUT};
- 'WAIT_FOR_DATA'(timeout, State) ->
- error_logger:error_msg("~p Client connection timeout - closing.\n", [self()]),
- {stop, normal, State};
- 'WAIT_FOR_DATA'(Data, State) ->
- io:format("~p Ignoring data: ~p\n", [self(), Data]),
- {next_state, 'WAIT_FOR_DATA', State, ?TIMEOUT}.
- %%-------------------------------------------------------------------------
- %% Func: handle_event/3
- %% Returns: {next_state, NextStateName, NextStateData} |
- %% {next_state, NextStateName, NextStateData, Timeout} |
- %% {stop, Reason, NewStateData}
- %% @private
- %%-------------------------------------------------------------------------
- handle_event(Event, StateName, StateData) ->
- {stop, {StateName, undefined_event, Event}, StateData}.
- %%-------------------------------------------------------------------------
- %% Func: handle_sync_event/4
- %% Returns: {next_state, NextStateName, NextStateData} |
- %% {next_state, NextStateName, NextStateData, Timeout} |
- %% {reply, Reply, NextStateName, NextStateData} |
- %% {reply, Reply, NextStateName, NextStateData, Timeout} |
- %% {stop, Reason, NewStateData} |
- %% {stop, Reason, Reply, NewStateData}
- %% @private
- %%-------------------------------------------------------------------------
- handle_sync_event(Event, _From, StateName, StateData) ->
- {stop, {StateName, undefined_event, Event}, StateData}.
- %%-------------------------------------------------------------------------
- %% Func: handle_info/3
- %% Returns: {next_state, NextStateName, NextStateData} |
- %% {next_state, NextStateName, NextStateData, Timeout} |
- %% {stop, Reason, NewStateData}
- %% @private
- %%-------------------------------------------------------------------------
- handle_info({tcp, Socket, Bin}, StateName, #state{socket=Socket} = StateData) ->
- % Flow control: enable forwarding of next TCP message
- inet:setopts(Socket, [{active, once}]),
- ?MODULE:StateName({data, Bin}, StateData);
- handle_info({tcp_closed, Socket}, _StateName,
- #state{socket=Socket, addr=Addr} = StateData) ->
- error_logger:info_msg("~p Client ~p disconnected.\n", [self(), Addr]),
- {stop, normal, StateData};
- handle_info(_Info, StateName, StateData) ->
- {noreply, StateName, StateData}.
- %%-------------------------------------------------------------------------
- %% Func: terminate/3
- %% Purpose: Shutdown the fsm
- %% Returns: any
- %% @private
- %%-------------------------------------------------------------------------
- terminate(_Reason, _StateName, #state{socket=Socket}) ->
- (catch gen_tcp:close(Socket)),
- ok.
- %%-------------------------------------------------------------------------
- %% Func: code_change/4
- %% Purpose: Convert process state when code is changed
- %% Returns: {ok, NewState, NewStateData}
- %% @private
- %%-------------------------------------------------------------------------
- code_change(_OldVsn, StateName, StateData, _Extra) ->
- {ok, StateName, StateData}.
- handle_data(S, ["vsns:", "save", Key, Value]) ->
- gen_tcp:send(S, list_to_binary(swap_undefined(name_server:save(Key, Value))));
- handle_data(S, ["vsns:", "load", Key]) ->
- gen_tcp:send(S, list_to_binary(swap_undefined(name_server:load(Key))));
- handle_data(S, ["vsns:", "load_all"]) ->
- name_server:load_all(),
- gen_tcp:send(S, <<"ack">>); % list_to_binary(name_server:load_all())
- handle_data(S, ["vsns:", "remove", Key]) ->
- gen_tcp:send(S, list_to_binary(swap_undefined(name_server:remove(Key))));
- handle_data(S, ["vsns:", "remove_all"]) ->
- name_server:remove_all(),
- gen_tcp:send(S, <<"ack">>); % list_to_binary(name_server:remove_all())
- handle_data(S, ["vsns:", "kernel_oops"]) ->
- gen_tcp:send(S, list_to_binary(name_server:kernel_oops()));
- handle_data(S, _Data) ->
- gen_tcp:send(S, <<"unknow">>).
- swap_undefined(undefined) ->
- "";
- swap_undefined(Other) ->
- Other.
- % File end.