年假不能白休,时间不能浪费,看了 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
本文版权归作者所有,只允许以摘要和完整全文两种形式转载,不允许对文字进行裁剪。未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
分享到:
相关推荐
本篇博客“gen_server tasting 之超简单名称服务(续)”主要探讨了如何使用gen_server来构建一个简单的名称服务系统,这是一个在分布式系统中常见的需求,用于存储和查找服务或资源的名称与地址的对应关系。...
在"gen_server tasting 之超简单名称服务(再续)"这篇博文中,作者可能会探讨如何利用`gen_server`来实现一个简单的名称服务系统,这个系统可能是为了方便在分布式环境中查找和管理资源。 名称服务在分布式系统中...
《Lady_tasting_tea》这本书通过讲述统计学的历史发展脉络,介绍了统计学如何成为20世纪科学研究的重要工具。书中不仅涉及基本的统计学知识,还穿插了一些历史上著名人物的故事,使读者能够更好地理解统计学的发展...
这本书的标题为《女士品茶》,它描述了统计学如何在20世纪科学领域中引发了一场革命。统计学是一门收集、分析、解释以及呈现数据的科学,它在科学研究中扮演了核心角色。书中通过“女士品茶”这个比喻,反映了20世纪...
"tasting"应用的控制器可能包含处理品尝者评分和反馈的逻辑,而服务可能用于保存和加载这些信息,或者与后端API通信。 ### 路由与状态管理 AngularJS 提供了内置的`$routeProvider`或第三方的`ui-router`库来管理...
虚拟品酒 :wine_glass: 虚拟品酒是一种应用程序,可提供分步说明,以在家里进行专业的品酒。... 科技栈 :desktop_computer: JavaScript React Express.js PostgreSQL 安装指南 :keyboard: 分叉并克隆此仓库 ...
Tasting,此处现在分词短语作原因状语,表示烤火鸡尝起来美味。 4. 题目考查连词的使用。"It is almost five years _________ we saw each other last time." 正确答案为B. since,句型"It is/has been + 时间 + ...
Create React App入门 该项目是通过引导的。 可用脚本 在项目目录中,可以运行: yarn start 在开发模式下运行应用程序。 打开在浏览器中查看它。 如果您进行编辑,则页面将重新加载。 您还将在控制台中看到任何...
Tasting。 12. "Our school trip was _____ because of the heavy rain." 这里考察动词短语辨析。"call off"表示取消,符合题意,答案是B. called off。 13. "The museum _____ in the northeast of the city." ...
语言:日本語 添加源预览按钮到Google代码JAM记分牌添加链接以在Google Code Jam记分牌上预览源代码。 可以省略下载,提取和打开操作,因此您可以看到和轻松地研究人们的代码。... 报告,如错误,推特:@ororog。...
自述 此自述文件通常会记录启动和运行应用程序所需的任何步骤。 您可能想要涵盖的内容: ...服务(作业队列、缓存服务器、搜索引擎等) 部署说明 … 如果您不打算运行rake doc:app请随意使用不同的标记语言。
- 句子"_____ delicious, the food was soon sold out."中,"tasting"是现在分词作连接动词,表示食物尝起来美味,所以正确答案是C. Tasting。 - 句子"My parents have always made me _____ about myself, even ...
关于LaravelLaravel是一个具有表达力,优雅语法的Web应用程序框架。 我们认为,发展必须是一种令人愉快的,富有创造力的经历,才能真正实现。 Laravel减轻了许多Web项目中使用的常见任务,从而减轻了开发过程中的...
假设我们要将所有出现的"taste"替换为"tasting": ```python with open("./fileread.txt","r",encoding="utf-8") as f: lines = f.readlines() with open("./fileread.txt","w",encoding="utf-8") as f_w: for ...
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 ...
- "tasting good and sweet" 中 "tasting" 是 "taste" 的现在分词,表主动,此处表示苹果尝起来味道好。 2. 动词不定式:如 "to finish the homework",动词不定式在这里表示目的,即“想要他按时完成作业”。 3....
在这个项目中,HTML(超文本标记语言)是核心,它是构建网页结构的基础语言,允许开发者通过标签来定义页面内容和布局。 HTML(HyperText Markup Language)是一种标记语言,它由一系列的元素组成,每个元素都有其...
- 现在分词作状语:`tasting good` 在句中作原因状语,表示食物尝起来味道好。 - 名词复数:`good manners` 表示“良好的礼貌”。 2. **单句改错** - 第一句去掉介词`for`,因为`for the first time`在这里是...