`
lzy.je
  • 浏览: 148586 次
  • 性别: 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 之超简单名称服务(续)

    NULL 博文链接:https://lzy.iteye.com/blog/324962

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

    NULL 博文链接:https://lzy.iteye.com/blog/327399

    Lady_tasting_tea

    some basic knowledge on statistics and good for beginners to know the history of sta.

    The Lady Tasting Tea

    女士品茶英文版 The Lady Tasting Tea How Statistics Revolutionized Science in the Twentieth Century

    virtual-wine-tasting-app

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

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

    ##Beer Tasting App 一个 AngularJS 应用程序,用于与多个品尝者和啤酒评分进行啤酒品尝。

    wwsc-beer-tasting

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

    Code Jam Tasting-crx插件

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

    tasting.info

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

    tasting-bracket

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

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

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

    1、替换文本中的taste 为 tasting Yesterday when I was young 昨日当我年少轻狂 The taste of life was sweet 生命的滋味是甜的 As rain upon my tongue #将文件读取到内存中 with open("./fileread.txt","r",...

    python 文件操作删除某行的实例

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

    Natural Language Processing with PyTorch 1st Edition (2019)

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

    Build Intelligent Language Applications Using Deep Learning 【epub】

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

    Android代码-Android轻架构

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

    OpenKiwi:PyTorch 中的开源机器翻译质量估计

    质量评估 (QE) 是机器翻译中缺失的部分之一:它的目标是在不访问参考翻译的情况下评估翻译系统的质量。 我们展示了OpenKiwi ,这是一个基于 Pytorch 的开源框架,它实现了 WMT 2015-18 共享任务中最好的 QE 系统,...

    Functional C#[January 2017].pdf

    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.app:笔记品酒的移动应用程序

    韦恩乔伊Winenjoy est应用程序倒入了vin quisdégustationsde vin。 应用éééééééveloppéavec le框架Nativescript。 设计,学习和效率方面的成就:无可比拟。 Evaluz chaqueétapede ladégustationd'un ...

Global site tag (gtag.js) - Google Analytics