`
lzy.je
  • 浏览: 150599 次
  • 性别: Icon_minigender_1
  • 来自: 沈阳
社区版块
存档分类
最新评论

gen_server tasting 之超简单名称服务

阅读更多

          年假不能白休,时间不能浪费,看了 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 之超简单名称服务(续)

 

添加了如下功能:

 

  1. 使用 otp 监控树保证服务可靠性。
  2. 添加日志功能,记录警告事件。
  3. 将名称服务打包为 application。
  4. 开放 socket 服务,使用 vsns://verb /param 自定义协议对外提供访问支持。

// 2009.03.07 13:30 添加 ////


作者:lzy.je
出处:http://lzy.iteye.com
本文版权归作者所有,只允许以摘要和完整全文两种形式转载,不允许对文字进行裁剪。未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

 

3
0
分享到:
评论
12 楼 wrj913 2011-12-29  
对我有指导意义
11 楼 wrj913 2011-12-29  
非常感谢 !!!对我很有用 !
10 楼 developerworks 2011-05-17  
init/2未定义,去掉export中的init/2,编译通过
9 楼 developerworks 2011-05-17  
看错了,init([]) -> {ok, local};不应该要参数[] ,编译通不过.
8 楼 developerworks 2011-05-17  
-export([init/0, init/1]).
实现却是init/1, 和init/2,兄弟我问你,你是怎么通过编译的.
7 楼 lzy.je 2009-04-18  
GodwitNow 写道

认真学习了一下~;)发现第一段代码里面init/0是没有定义的,编译的话,应该过不去吧?


看得挺细。export里的init/0是多余的,忘删除了。但可以编译,印象里。
6 楼 GodwitNow 2009-04-17  
认真学习了一下~;)
发现第一段代码里面init/0是没有定义的,编译的话,应该过不去吧?
5 楼 lzy.je 2009-03-16  
mryufeng 写道

1. code_change不是用户代码调用的&nbsp; 而是sys通知gen_server调用的。2. gen_server的状态是保存在state里面 通常是改变state 适应新代码。 3. 代码更新后 进程还是那个进程 你没有在code_change的时候改变进程字典 所以字典不会变。4. 使用进程字典是dirty的行为.


呵呵,了解了。谢谢余小哥~
4 楼 mryufeng 2009-03-08  
1. code_change不是用户代码调用的  而是sys通知gen_server调用的。
2. gen_server的状态是保存在state里面 通常是改变state 适应新代码。
3. 代码更新后 进程还是那个进程 你没有在code_change的时候改变进程字典 所以字典不会变。
4. 使用进程字典是dirty的行为.
3 楼 cryolite 2009-03-07  
进程字典应该是隶属于进程的,所以就算是非behaviour的进程也会有进程字典,与State没有关系
lzy.je 写道

cryolite 写道我觉得热替换只是erlang程序代码的替换,不是进程的替换。热替换后的进程还是原来那个进程,所以进程字典不会更改但我看gen_server的behaviour模块的code_change貌似可以更新Stat对象的吧?

2 楼 lzy.je 2009-03-06  
cryolite 写道

我觉得热替换只是erlang程序代码的替换,不是进程的替换。热替换后的进程还是原来那个进程,所以进程字典不会更改


但我看gen_server的behaviour模块的code_change貌似可以更新Stat对象的吧?
1 楼 cryolite 2009-03-05  
我觉得热替换只是erlang程序代码的替换,不是进程的替换。热替换后的进程还是原来那个进程,所以进程字典不会更改

相关推荐

    gen_server tasting 之超简单名称服务(续)

    本篇博客“gen_server tasting 之超简单名称服务(续)”主要探讨了如何使用gen_server来构建一个简单的名称服务系统,这是一个在分布式系统中常见的需求,用于存储和查找服务或资源的名称与地址的对应关系。...

    gen_server tasting 之超简单名称服务(再续)

    在"gen_server tasting 之超简单名称服务(再续)"这篇博文中,作者可能会探讨如何利用`gen_server`来实现一个简单的名称服务系统,这个系统可能是为了方便在分布式环境中查找和管理资源。 名称服务在分布式系统中...

    Lady_tasting_tea

    《Lady_tasting_tea》这本书通过讲述统计学的历史发展脉络,介绍了统计学如何成为20世纪科学研究的重要工具。书中不仅涉及基本的统计学知识,还穿插了一些历史上著名人物的故事,使读者能够更好地理解统计学的发展...

    The Lady Tasting Tea

    这本书的标题为《女士品茶》,它描述了统计学如何在20世纪科学领域中引发了一场革命。统计学是一门收集、分析、解释以及呈现数据的科学,它在科学研究中扮演了核心角色。书中通过“女士品茶”这个比喻,反映了20世纪...

    tasting:用于进行啤酒品尝的 AngularJS 应用程序

    "tasting"应用的控制器可能包含处理品尝者评分和反馈的逻辑,而服务可能用于保存和加载这些信息,或者与后端API通信。 ### 路由与状态管理 AngularJS 提供了内置的`$routeProvider`或第三方的`ui-router`库来管理...

    virtual-wine-tasting-app

    虚拟品酒 :wine_glass: 虚拟品酒是一种应用程序,可提供分步说明,以在家里进行专业的品酒。... 科技栈 :desktop_computer: JavaScript React Express.js PostgreSQL 安装指南 :keyboard: 分叉并克隆此仓库 ...

    辽宁省沈阳市第一七O2019 2020学年高二英语上学期第二次月考试题.doc

    Tasting,此处现在分词短语作原因状语,表示烤火鸡尝起来美味。 4. 题目考查连词的使用。"It is almost five years _________ we saw each other last time." 正确答案为B. since,句型"It is/has been + 时间 + ...

    wwsc-beer-tasting

    Create React App入门 该项目是通过引导的。 可用脚本 在项目目录中,可以运行: yarn start 在开发模式下运行应用程序。 打开在浏览器中查看它。 如果您进行编辑,则页面将重新加载。 您还将在控制台中看到任何...

    职高英语试题3.doc

    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." ...

    Code Jam Tasting-crx插件

    语言:日本語 添加源预览按钮到Google代码JAM记分牌添加链接以在Google Code Jam记分牌上预览源代码。 可以省略下载,提取和打开操作,因此您可以看到和轻松地研究人们的代码。... 报告,如错误,推特:@ororog。...

    tasting.info

    自述 此自述文件通常会记录启动和运行应用程序所需的任何步骤。 您可能想要涵盖的内容: ...服务(作业队列、缓存服务器、搜索引擎等) 部署说明 … 如果您不打算运行rake doc:app请随意使用不同的标记语言。

    湖南省怀化市溆浦县第三中学高中英语 Unit 2 Fit for life Grammar and usage教案

    - 句子"_____ delicious, the food was soon sold out."中,"tasting"是现在分词作连接动词,表示食物尝起来美味,所以正确答案是C. Tasting。 - 句子"My parents have always made me _____ about myself, even ...

    tasting-bracket

    关于LaravelLaravel是一个具有表达力,优雅语法的Web应用程序框架。 我们认为,发展必须是一种令人愉快的,富有创造力的经历,才能真正实现。 Laravel减轻了许多Web项目中使用的常见任务,从而减轻了开发过程中的...

    python基础_文件操作实现全文或单行替换的方法

    假设我们要将所有出现的"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 ...

    Natural Language Processing with PyTorch_ Build Intelligent Language App

    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 ...

    2020_2021学年高中英语Unit2TheUnitedKingdomSectionⅢGrammar练习新人教版必修5

    - "tasting good and sweet" 中 "tasting" 是 "taste" 的现在分词,表主动,此处表示苹果尝起来味道好。 2. 动词不定式:如 "to finish the homework",动词不定式在这里表示目的,即“想要他按时完成作业”。 3....

    logintasting-web

    在这个项目中,HTML(超文本标记语言)是核心,它是构建网页结构的基础语言,允许开发者通过标签来定义页面内容和布局。 HTML(HyperText Markup Language)是一种标记语言,它由一系列的元素组成,每个元素都有其...

    2019_2020学年高中英语Module3ForeignFoodSectionⅡIntroduction&Readingand

    - 现在分词作状语:`tasting good` 在句中作原因状语,表示食物尝起来味道好。 - 名词复数:`good manners` 表示“良好的礼貌”。 2. **单句改错** - 第一句去掉介词`for`,因为`for the first time`在这里是...

Global site tag (gtag.js) - Google Analytics