erlang服务器编程思想和实现
2012-02-20 16:32
183 查看
刚刚读了一本书,读到最后书里写到,本节是本书的精华说在,如果读者读一遍不能理解,那就多读两遍,有必要的话读100遍也在所不惜,不求倒背如流,但求运用自如。下面会写到4个服务器程序,他们以server1,server2...这样的方式命名,好戏即将开始深吸一口气,go!!
1.server1 原始服务器程序
server1.erl
这段代码是erlang的标准服务器程序,下面写一个回调函数来运行测试。
name_server.erl
首先看后面的代码1.他负责回调服务器程序,处理其他进程发货来的请求2.相当于定义接口。
如下运行程序:
如果读者细心可以发现,这段代码里面并没有spawn这样的创建进程的命令,不错,这个服务器程序完全是顺序性代码,也许有人质疑这样做的意义,不要急这才刚刚开始
2.server2:支持事务的服务器程序
接下来要向大家展示的是,当服务器宕机,客户端回或得到异常,然后做相应的处理。
server2.erl
想对于之前的版本,多了注册函数和创建进程函数,可以理解的认为是增加了并行操作,但是我之前做过一些小项目而言,erlang的进程的确有他特殊的地方,这里会在之后的文章详细讲解,这段代码实际上是提供了“事务机制”,在handler发生异常时,回调用loop(Name, Mod, OldState)在之前的状态下进行循环,否则会在新的状态下进行运行
流程是怎样的呢:当服务端宕机,服务器会给客户端发送crash消息,说明服务器异常,然后客户端接收到消息后,可以做相应处理(在我们的项目中用到的是重连,例子中是退出),然后调用loop并带入变量OldState说明本次服务处理异常,同时不影响其他连接到服务器的客户端。
要测试的话,与server1完全相同只需要改变import时改为server2即可。
3.server3:支持热带吗替换的服务器程序
server3.erl
工作流程:
当服务器收到swap_code消息后,发送给客户端确认消息,然后用新的模块Mod作为参数进行循环,这样下次处理request时,就会调用新的模块代码了。
name_server1.erl
下面举例说明
先用name_server1回调模块server3
如果这时,我想知道服务器中所有存储的名字,但是没有相应的api,我们该如何处理,这是我们可以再写一个模块如下
new_name_server.
编译程序,然后服务器程序替换回调函数:
之后就可以在服务器重用心的函数了:
可以看到服务器不用处理更改任何代码,就能完成热代码的替换,这就是回调函数的好处,这就是erlang编程思想的好处。
4.server4:同时支持事务和热代码替换
知道了如何支持事务和热代码替换,那么如何把两个融合在意思呢?
server4.erl
哈哈!不敢相信自己的眼睛吧,erlang写的服务器仅仅只有几十行,同时支持热代码和事务,是不是位置心动了。精彩的还在后头。。。
5.server5:压轴好戏 prefect ending
我们已经拥有了支持事务和热代码替换的服务器,下面让我们更进一步,让客户端自定义自己属于哪种服务,处理哪种程序。
当开启start()方法是,服务器只管等待{become,F}消息,是自己编程一个F服务器,F由客户端指定。
这里不做更多的测试相比大家也能理解,这里学习的不是一种编程语言,而是一种编程思想。这么几天学习,也感谢erlang带给我的快乐。
总结:
写好gen_server的三个要点:
一、确定回调模块名称:比如我们要写一个简单的通信服务,模块命名为,tcp_server。
二、写接口函数:为了更容易使用,要给tcp_server模块写几个接口,例如
0.start(启动服务进程) 1.connection 2.send 3.rece 4.close 。。。。。
三、写回调函数。。。
来自http://blog.csdn.net/lengzijian 玩命写博客
回调思想。你记住了么。
1.server1 原始服务器程序
server1.erl
-module(server1). -export([start/2, rpc/2]). start(Name, Mod) -> register(Name, spawn(fun() -> loop(Name, Mod, Mod:init()) end)). rpc(Name, Request) -> Name ! {self(), Request}, receive {Name, Response} -> Response end. loop(Name, Mod, State) -> receive {From, Request} -> {Response, State1} = Mod:handle(Request, State), From ! {Name, Response}, loop(Name, Mod, State1) end.
这段代码是erlang的标准服务器程序,下面写一个回调函数来运行测试。
name_server.erl
-module(name_server). -export([init/0, add/2, whereis/1, handle/2]). -import(server1, [rpc/2]). %% client routines add(Name, Place) -> rpc(name_server, {add, Name, Place}). whereis(Name) -> rpc(name_server, {whereis, Name}). %% callback routines init() -> dict:new(). handle({add, Name, Place}, Dict) -> {ok, dict:store(Name, Place, Dict)}; handle({whereis, Name}, Dict) -> {dict:find(Name, Dict), Dict}.
首先看后面的代码1.他负责回调服务器程序,处理其他进程发货来的请求2.相当于定义接口。
如下运行程序:
erlc *.erl
Eshell V5.6.5 (abort with ^G) 1> server1:start(name_server, name_server). true 2> nameserver:add("lengzijian","go to school"). ** exception error: undefined function nameserver:add/2 3> name_server:add("lengzijian","go to school"). ok 4> name_server:whereis("lengzijian"). {ok,"go to school"} 5>
如果读者细心可以发现,这段代码里面并没有spawn这样的创建进程的命令,不错,这个服务器程序完全是顺序性代码,也许有人质疑这样做的意义,不要急这才刚刚开始
2.server2:支持事务的服务器程序
接下来要向大家展示的是,当服务器宕机,客户端回或得到异常,然后做相应的处理。
server2.erl
-module(server2). -export([start/2, rpc/2]). start(Name, Mod) -> register(Name, spawn(fun() -> loop(Name,Mod,Mod:init()) end)). rpc(Name, Request) -> Name ! {self(), Request}, receive {Name, crash} -> exit(rpc); {Name, ok, Response} -> Response end. loop(Name, Mod, OldState) -> receive {From, Request} -> try Mod:handle(Request, OldState) of {Response, NewState} -> From ! {Name, ok, Response}, loop(Name, Mod, NewState) catch _:Why -> log_the_error(Name, Request, Why), %% send a message to cause the client to crash From ! {Name, crash}, %% loop with the *original* state loop(Name, Mod, OldState) end end. log_the_error(Name, Request, Why) -> io:format("Server ~p request ~p ~n" "caused exception ~p~n", [Name, Request, Why]).
想对于之前的版本,多了注册函数和创建进程函数,可以理解的认为是增加了并行操作,但是我之前做过一些小项目而言,erlang的进程的确有他特殊的地方,这里会在之后的文章详细讲解,这段代码实际上是提供了“事务机制”,在handler发生异常时,回调用loop(Name, Mod, OldState)在之前的状态下进行循环,否则会在新的状态下进行运行
流程是怎样的呢:当服务端宕机,服务器会给客户端发送crash消息,说明服务器异常,然后客户端接收到消息后,可以做相应处理(在我们的项目中用到的是重连,例子中是退出),然后调用loop并带入变量OldState说明本次服务处理异常,同时不影响其他连接到服务器的客户端。
要测试的话,与server1完全相同只需要改变import时改为server2即可。
3.server3:支持热带吗替换的服务器程序
server3.erl
-module(server3). -export([start/2, rpc/2, swap_code/2]). start(Name, Mod) -> register(Name, spawn(fun() -> loop(Name,Mod,Mod:init()) end)). swap_code(Name, Mod) -> rpc(Name, {swap_code, Mod}). rpc(Name, Request) -> Name ! {self(), Request}, receive {Name, Response} -> Response end. loop(Name, Mod, OldState) -> receive {From, {swap_code, NewCallBackMod}} -> From ! {Name, ack}, loop(Name, NewCallBackMod, OldState); {From, Request} -> {Response, NewState} = Mod:handle(Request, OldState), From ! {Name, Response}, loop(Name, Mod, NewState) end.
工作流程:
当服务器收到swap_code消息后,发送给客户端确认消息,然后用新的模块Mod作为参数进行循环,这样下次处理request时,就会调用新的模块代码了。
name_server1.erl
-module(name_server1). -export([init/0, add/2, whereis/1, handle/2]). -import(server3, [rpc/2]). %% client routines add(Name, Place) -> rpc(name_server, {add, Name, Place}). whereis(Name) -> rpc(name_server, {whereis, Name}). %% callback routines init() -> dict:new(). handle({add, Name, Place}, Dict) -> {ok, dict:store(Name, Place, Dict)}; handle({whereis, Name}, Dict) -> {dict:find(Name, Dict), Dict}.
下面举例说明
先用name_server1回调模块server3
Eshell V5.6.5 (abort with ^G) 1> server3:start(name_server, name_server1). true 2> name_server:add("lengzijian","go to school"). ok 3> name_server:add("lengzijian1","at work"). ok
如果这时,我想知道服务器中所有存储的名字,但是没有相应的api,我们该如何处理,这是我们可以再写一个模块如下
new_name_server.
-module(new_name_server). -export([init/0, add/2, all_names/0, delete/1, whereis/1, handle/2]). -import(server3, [rpc/2]). %% interface all_names() -> rpc(name_server, allNames). add(Name, Place) -> rpc(name_server, {add, Name, Place}). delete(Name) -> rpc(name_server, {delete, Name}). whereis(Name) -> rpc(name_server, {whereis, Name}). %% callback routines init() -> dict:new(). handle({add, Name, Place}, Dict) -> {ok, dict:store(Name, Place, Dict)}; handle(allNames, Dict) -> {dict:fetch_keys(Dict), Dict}; handle({delete, Name}, Dict) -> {ok, dict:erase(Name, Dict)}; handle({whereis, Name}, Dict) -> {dict:find(Name, Dict), Dict}.
编译程序,然后服务器程序替换回调函数:
4> c(new_name_server). {ok,new_name_server} 5> server3:swap_code(name_server,new_name_server). ack
之后就可以在服务器重用心的函数了:
6> new_name_server:all_names(). ["lengzijian1","lengzijian"]
可以看到服务器不用处理更改任何代码,就能完成热代码的替换,这就是回调函数的好处,这就是erlang编程思想的好处。
4.server4:同时支持事务和热代码替换
知道了如何支持事务和热代码替换,那么如何把两个融合在意思呢?
server4.erl
-module(server4). -export([start/2, rpc/2, swap_code/2]). start(Name, Mod) -> register(Name, spawn(fun() -> loop(Name,Mod,Mod:init()) end)). swap_code(Name, Mod) -> rpc(Name, {swap_code, Mod}). rpc(Name, Request) -> Name ! {self(), Request}, receive {Name, crash} -> exit(rpc); {Name, ok, Response} -> Response end. loop(Name, Mod, OldState) -> receive {From, {swap_code, NewCallbackMod}} -> From ! {Name, ok, ack}, loop(Name, NewCallbackMod, OldState); {From, Request} -> try Mod:handle(Request, OldState) of {Response, NewState} -> From ! {Name, ok, Response}, loop(Name, Mod, NewState) catch _: Why -> log_the_error(Name, Request, Why), From ! {Name, crash}, loop(Name, Mod, OldState) end end. log_the_error(Name, Request, Why) -> io:format("Server ~p request ~p ~n" "caused exception ~p~n", [Name, Request, Why]).
哈哈!不敢相信自己的眼睛吧,erlang写的服务器仅仅只有几十行,同时支持热代码和事务,是不是位置心动了。精彩的还在后头。。。
5.server5:压轴好戏 prefect ending
我们已经拥有了支持事务和热代码替换的服务器,下面让我们更进一步,让客户端自定义自己属于哪种服务,处理哪种程序。
-module(server5). -export([start/0, rpc/2]). start() -> spawn(fun() -> wait() end). wait() -> receive {become, F} -> F() end. rpc(Pid, Q) -> Pid ! {self(), Q}, receive {Pid, Reply} -> Reply end.
当开启start()方法是,服务器只管等待{become,F}消息,是自己编程一个F服务器,F由客户端指定。
这里不做更多的测试相比大家也能理解,这里学习的不是一种编程语言,而是一种编程思想。这么几天学习,也感谢erlang带给我的快乐。
总结:
写好gen_server的三个要点:
一、确定回调模块名称:比如我们要写一个简单的通信服务,模块命名为,tcp_server。
二、写接口函数:为了更容易使用,要给tcp_server模块写几个接口,例如
0.start(启动服务进程) 1.connection 2.send 3.rece 4.close 。。。。。
三、写回调函数。。。
来自http://blog.csdn.net/lengzijian 玩命写博客
回调思想。你记住了么。
相关文章推荐
- erlang服务器编程思想和实现
- 用socket实现服务器的编程总结(转载)
- java网络socket编程(五)之Socket扩展2--实现重定向服务器
- linux下socket编程实现一个服务器连接多个客户端
- 如何编程实现修改本机的IP地址DNS服务器等设置
- HTML5+javascriptOO编程思想的写法实现键盘游戏
- Linux 网络编程——并发服务器的三种实现模型
- java网络编程(6)——实现一个服务器把小写转大写
- Android与web服务器数据交互编程---2-如何实现手机与web的通信---server实现
- Linux 网络编程基础---------------客户端/服务器的简单实现
- linux下socket编程实现一个服务器连接多个客户端
- 使用面向对象的编程思想实现js代码
- iOS之推送通知-本地-服务器3.服务器端实现: 如果要编写内容提供者的推送服务程序,需要进行SSL认证编程,以及构建APNS数据包,数据包分为3个主要部分:Command(命令)、deviceTo
- TCP并发式服务器编程实现
- 运用Java中socket编程实现简单的服务器客户端收发信息程序
- java中break,continue,标签实现goto效果(编程思想)
- 【Linux网络编程】并发服务器的三种实现模型
- Linux 网络编程基础(一) ---------------客户端/服务器的简单实现
- Linux下socket编程实现客户机服务器通信的例子
- Socket编程 消息传送 TCP协议(窗口实现) 服务器