这是我的:
-module(myserver).
-export([start/0, init/0]).
start() ->
erlang:spawn_link(?MODULE, init, []).
init() ->
State = undefined, % You may want to do something at startup
loop(State).
% if something went wrong comment above line and uncomment below line:
% exit(element(2, catch loop(State))).
loop(MyState) ->
Msg =
receive
Any ->
Any
end,
handle_message(Msg, MyState).
% We got a message for becoming something:
handle_message({become, Mod, InitArgument}, _) ->
% Also our callback may want to do something at startup:
CallbackState = Mod:init(InitArgument),
loop({Mod, CallbackState});
% We got a message and we have a callback:
handle_message(Other, {Mod, CallbackState}) ->
case Mod:handle_message(Other, CallbackState) of
stop ->
loop(undefined);
NewCallbackState ->
loop({Mod, NewCallbackState})
end;
% We got a message and we Don't have a callback:
handle_message(Other, undefined) ->
io:format("Don't have any callback for handling ~p~n", [Other]),
loop(undefined).
我还为我的服务器编写了一个简单的counter 程序:
-module(counter).
-export([init/1, handle_message/2]).
init(Start) ->
Start.
handle_message(inc, Number) ->
Number + 1;
handle_message(dec, Number) ->
Number - 1;
handle_message({From, what_is}, Number) ->
From ! Number;
handle_message(stop, _) ->
stop;
handle_message(Other, Number) ->
io:format("counter got unknown message ~p~n", [Other]),
Number.
让我们测试一下:
Eshell V10.1 (abort with ^G)
1> S = myserver:start().
<0.79.0>
2> S ! hello.
Don't have any callback for handling hello
hello
3> S ! {become, counter, 10}.
{become,counter,10}
4> S ! hi.
counter got unknown message hi
hi
5> S ! inc.
inc
6> S ! dec.
dec
7> S ! dec.
dec
8> S ! {self(), what_is}.
{<0.77.0>,what_is}
9> flush().
Shell got 9
ok
10> S ! stop.
stop
11> S ! inc.
Don't have any callback for handling inc
inc
我们应该怎么做才能完成它?
如您所见,这不是生产就绪代码,我们应该:
- 有办法设置初始化超时。
- 有办法设置进程生成选项。
- 有办法在本地或全局注册进程或使用自定义进程注册表。
- 在
try catch 中调用回调函数。
- 确保消息回复是针对当前消息传递的,而不是针对我们的进程之前发送的其他消息! (
gen 模块提供的内容为 call)。
- 当我们的 starter 进程死亡时杀死我们自己,如果 starter 链接到我们就不要成为僵尸进程!
- 在每个回调的最后调用一个函数,如果有的话让他们清理这些东西(你可以将它命名为
terminate)。
- 兼容OTP
sys模块,所以我们应该定义它的回调函数。见sys callback functions。然后我们可以将我们的进程切换到调试模式,查看它的 I/O,在重新加载代码时改变它的状态等等。
请注意,proc_lib 和 gen 模块可以帮助您完成大部分工作。