年假不能白休,时间不能浪费,看了 erlang 程序设计的 gen_server 章节,为了更好的理解、掌握于是上手写一个名称(键值)服务器。这个 lzy_name_svc 服务器是基于 otp gen_server 写成的,在底层键值被保存在了 erlang 的进程字典里,并且用于存储字典的进程是可以替换的,可以通过 lzy_name_svc:start/1 启动服务时指定,缺省情况保存在“当前” erlang 进程中。闲话少叙,代码贴上。
-module(lzy_name_svc).
-behaviour(gen_server).
-export([init/0, init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-export([start/0, start/1, stop/0, save/2, load/1, load_all/0, remove/1, remove_all/0]).
%% Interface functions.
start() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
start(Args) ->
gen_server:start_link({local, ?MODULE}, ?MODULE, Args, []).
stop() ->
gen_server:call(?MODULE, stop).
%% @spec save(Key, Value) -> OldValue.
save(Key, Value) ->
gen_server:call(?MODULE, {save, Key, Value}).
%% @spec load(Key) -> Value.
load(Key) ->
gen_server:call(?MODULE, {load, Key}).
%% @spec load_all() -> [{Key, Value}].
load_all() ->
gen_server:call(?MODULE, {load_all}).
%% @spec remove(Key) -> Value.
remove(Key) ->
gen_server:call(?MODULE, {remove, Key}).
%% @spec remove_all() -> [{Key, Value}].
remove_all() ->
gen_server:call(?MODULE, {remove_all}).
%% Callback functions.
init([]) ->
{ok, local};
init([{isolation, NameServer}]) ->
{ok, {isolation, NameServer}}.
handle_call({save, Key, Value}, _From, NameServer) ->
{reply, do_save(Key, Value, NameServer), NameServer};
handle_call({load, Key}, _From, NameServer) ->
{reply, do_load(Key, NameServer), NameServer};
handle_call({load_all}, _From, NameServer) ->
{reply, do_load_all(NameServer), NameServer};
handle_call({remove, Key}, _From, NameServer) ->
{reply, do_remove(Key, NameServer), NameServer};
handle_call({remove_all}, _From, NameServer) ->
{reply, do_remove_all(NameServer), NameServer};
handle_call({stop}, _From, NameServer) ->
{stop, normal, stopped, NameServer}.
%% Default implement.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%% Private functions.
do_save(Key, Value, {isolation, NameServer}) ->
NameServer ! {self(), save, Key, Value},
receive
Msg -> Msg
end;
do_save(Key, Value, _) ->
erlang:put(Key, Value).
do_load(Key, {isolation, NameServer}) ->
NameServer ! {self(), load, Key},
receive
Msg -> Msg
end;
do_load(Key, _) ->
erlang:get(Key).
do_load_all({isolation, NameServer}) ->
NameServer ! {self(), load_all},
receive
Msg -> Msg
end;
do_load_all(_) ->
erlang:get().
do_remove(Key, {isolation, NameServer}) ->
NameServer ! {self(), remove, Key},
receive
Msg -> Msg
end;
do_remove(Key, _) ->
erlang:erase(Key).
do_remove_all({isolation, NameServer}) ->
NameServer ! {self(), remove_all},
receive
Msg -> Msg
end;
do_remove_all(_) ->
erlang:erase().
上面这段代码就是 lzy_name_svc 名称服务了,有些地方写得有点冗余,呵呵。
为了能够替换字典进程来测试验证名称服务功能,还写了一个超简单的 foo_svc 服务,用来和 lzy_name_svc 通信完成进程字典存取。
-module(foo_svc).
-export([start/0, load_all/0, server_pid/0]).
start() ->
register(fs, spawn(fun() -> loop() end)).
load_all() ->
fs ! {self(), load_all},
receive
Msg -> Msg
end.
server_pid() ->
fs ! { self(), server_pid},
receive
Msg -> Msg
end.
loop() ->
receive
{From, save, Key, Value} ->
From ! erlang:put(Key, Value),
loop();
{From, load, Key} ->
From ! erlang:get(Key),
loop();
{From, load_all} ->
From ! erlang:get(),
loop();
{From, remove, Key} ->
From ! erlang:erase(Key),
loop();
{From, remove_all} ->
From ! erlang:erase(),
loop();
{From, server_pid} ->
From ! self(),
loop()
end.
下面的代码就是创建和调用服务的相关代码了,一起贴上来。第一段是以缺省方式启动了 lzy_name_svc 服务,并向存取 abc -> 123 名称。
C:\Program Files\erl5.6.4\usr>..\bin\erl -sname server
Eshell V5.6.4 (abort with ^G)
(server@lzy)1> c(lzy_name_svc).
{ok,lzy_name_svc}
(server@lzy)2> c(foo_svc.erl).
{ok,foo_svc}
(server@lzy)3> lzy_name_svc:start().
{ok,<0.47.0>}
(server@lzy)4> lzy_name_svc:save(abc, 123).
undefined
(server@lzy)5> lzy_name_svc:load(abc).
123
(server@lzy)6> lzy_name_svc:load(efg).
undefined
(server@lzy)7> lzy_name_svc:load_all().
[{abc,123},
{'$ancestors',[<0.35.0>]},
{'$initial_call',{gen,init_it,
[gen_server,<0.35.0>,<0.35.0>,
{local,lzy_name_svc},
lzy_name_svc,[],[]]}}]
(server@lzy)8> lzy_name_svc:remove(abc).
123
(server@lzy)9> lzy_name_svc:load(abc).
undefined
下面这段是启动 foo_svc 服务,用它创建的进程来专门存储名称数据,是通过 lzy_name_svc:start/1 传入的 PID。
C:\Program Files\erl5.6.4\usr>..\bin\erl -sname server
Eshell V5.6.4 (abort with ^G)
(server@lzy)1> foo_svc:start().
true
(server@lzy)2> NameSvcPid = foo_svc:server_pid().
<0.37.0>
(server@lzy)3> lzy_name_svc:start([{isolation, NameSvcPid}]).
{ok,<0.40.0>}
(server@lzy)4> lzy_name_svc:save(abc, 123).
undefined
(server@lzy)5> lzy_name_svc:load(abc).
123
(server@lzy)6> foo_svc:load_all().
[{abc,123}]
(server@lzy)7> lzy_name_svc:remove_all().
[{abc,123}]
(server@lzy)8> foo_svc:load_all().
[]
上边的两段都是在同一机器上的同一 erlang 节点上完成服务调用的,下面这段代码是 lzy_name_svc 服务基于上边状态时,在同一机器的另外了个 erlang 节点上通过 rpc 库完成服务调用的。
C:\Program Files\erl5.6.4\usr>..\bin\erl -sname client1
Eshell V5.6.4 (abort with ^G)
(client1@lzy)1> rpc:call(server@lzy, lzy_name_svc, save, [abc, 123]).
undefined
(client1@lzy)2> rpc:call(server@lzy, foo_svc, load_all, []).
[{abc,123}]
呵呵,挺入门的,就当做为学习过程的记录吧。看好 erlang。
在学习的过程中,有一个事情比较不解,就是对于
字典进程的
“热替换”
。
我想本应该是可以通过 gen_server behaviour 用于“热代码替换”的 code_change 方法完成的,但试了几次都达不到目的,服务倒是跑的正常,可是字典进程就是不能热替换,code_change 正常返回,可是名称数据却还是原有字典进程的。测试验证代码如下:
C:\Program Files\erl5.6.4\usr>..\bin\erl -sname server
Eshell V5.6.4 (abort with ^G)
(server@lzy)1> lzy_name_svc:start().
{ok,<0.37.0>}
(server@lzy)2> lzy_name_svc:save(abc, 123).
undefined
(server@lzy)3> lzy_name_svc:load_all().
[{abc,123},
{'$ancestors',[<0.35.0>]},
{'$initial_call',{gen,init_it,
[gen_server,<0.35.0>,<0.35.0>,
{local,lzy_name_svc},
lzy_name_svc,[],[]]}}]
(server@lzy)4> foo_svc:start().
true
(server@lzy)5> NameSvcPid = foo_svc:server_pid().
<0.41.0>
(server@lzy)6> foo_svc:load_all().
[]
(server@lzy)7> lzy_name_svc:code_change(foo, NameSvcPid, foo).
{ok,<0.41.0>}
(server@lzy)8> lzy_name_svc:load_all().
[{abc,123},
{'$ancestors',[<0.35.0>]},
{'$initial_call',{gen,init_it,
[gen_server,<0.35.0>,<0.35.0>,
{local,lzy_name_svc},
lzy_name_svc,[],[]]}}]
还请哪位 erlang guru 指点~
// 2009.02.07 22:52 添加 ////
这里提供了该名称服务的新迭代版本。
gen_server tasting 之超简单名称服务(续)
添加了如下功能:
- 使用 otp 监控树保证服务可靠性。
- 添加日志功能,记录警告事件。
- 将名称服务打包为 application。
- 开放 socket 服务,使用 vsns://verb
/param
自定义协议对外提供访问支持。
// 2009.03.07 13:30 添加 ////
作者:lzy.je
出处:http://lzy.iteye.com
本文版权归作者所有,只允许以摘要和完整全文两种形式转载,不允许对文字进行裁剪。未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
分享到:
相关推荐
NULL 博文链接:https://lzy.iteye.com/blog/324962
NULL 博文链接:https://lzy.iteye.com/blog/327399
some basic knowledge on statistics and good for beginners to know the history of sta.
女士品茶英文版 The Lady Tasting Tea How Statistics Revolutionized Science in the Twentieth Century
虚拟品酒 :wine_glass: 虚拟品酒是一种应用程序,可提供分步说明,以在家里进行专业的品酒。... 科技栈 :desktop_computer: JavaScript React Express.js PostgreSQL 安装指南 :keyboard: 分叉并克隆此仓库 ...
##Beer Tasting App 一个 AngularJS 应用程序,用于与多个品尝者和啤酒评分进行啤酒品尝。
Create React App入门 该项目是通过引导的。 可用脚本 在项目目录中,可以运行: yarn start 在开发模式下运行应用程序。 打开在浏览器中查看它。 如果您进行编辑,则页面将重新加载。 您还将在控制台中看到任何...
语言:日本語 添加源预览按钮到Google代码JAM记分牌添加链接以在Google Code Jam记分牌上预览源代码。 可以省略下载,提取和打开操作,因此您可以看到和轻松地研究人们的代码。... 报告,如错误,推特:@ororog。...
自述 此自述文件通常会记录启动和运行应用程序所需的任何步骤。 您可能想要涵盖的内容: ...服务(作业队列、缓存服务器、搜索引擎等) 部署说明 … 如果您不打算运行rake doc:app请随意使用不同的标记语言。
关于LaravelLaravel是一个具有表达力,优雅语法的Web应用程序框架。 我们认为,发展必须是一种令人愉快的,富有创造力的经历,才能真正实现。 Laravel减轻了许多Web项目中使用的常见任务,从而减轻了开发过程中的...
tasting table covering important topics in both areas. Both of these subject areas are growing exponentially. As it introduces both deep learning and NLP with an emphasis on implementation, this book ...
1、替换文本中的taste 为 tasting Yesterday when I was young 昨日当我年少轻狂 The taste of life was sweet 生命的滋味是甜的 As rain upon my tongue #将文件读取到内存中 with open("./fileread.txt","r",...
The tasting of life was sweet 生命的滋味是甜的 As rain upon my tongue tasting I lived by night and shunned the naked light of day tasting123 And only now I see how the time ran away tasting tasting 将...
This book aims to bring newcomers to natural language processing (NLP) and deep learning to a tasting table covering important topics in both areas. Both of these subject areas are growing ...
This book aims to bring newcomers to natural language processing (NLP) and deep learning to a tasting table covering important topics in both areas. Both of these subject areas are growing ...
Tasting Dagger 2 on Android Clean Architecture…Dynamic Parameters in Use Cases Demo video of this sample Clean architecture Architectural approach Architectural reactive approach Local Development...
质量评估 (QE) 是机器翻译中缺失的部分之一:它的目标是在不访问参考翻译的情况下评估翻译系统的质量。 我们展示了OpenKiwi ,这是一个基于 Pytorch 的开源框架,它实现了 WMT 2015-18 共享任务中最好的 QE 系统,...
Chapter 1, Tasting Functional Style in C#, introduces the functional programming approach by discussing its concepts and the comparison between functional and imperative programming. We also try to ...
韦恩乔伊Winenjoy est应用程序倒入了vin quisdégustationsde vin。 应用éééééééveloppéavec le框架Nativescript。 设计,学习和效率方面的成就:无可比拟。 Evaluz chaqueétapede ladégustationd'un ...