大多数服务器都执行一个固定的程序,如果要修改服务器的行为,就必须先停止服务器,再用修改后的代码重启它。而要修改这个服务器的行为,不用停止它,只需要发送一个包含新代码的消息,它就会提取新代码,然后用新代码和老的会话数据继续工作。这一过程被称为热代码交换。
-module(name_server1).
-export([init/0,add/2,find/1,handle/2]).
-import(server3,[rpc/2]).
%%客户端方法
add(Name,Place)->rpc(name_server,{add,Name,Place}).
find(Name)->rpc(name_server,{find,Name}).
%%回调方法
init()->dict:new().
handle({add,Name,Place},Dict)->{ok,dict:store(Name,Place,Dict)};
handle({find,Name},Dict)->{dict:find(Name,Dict),Dict}.
如果向服务器发送一个交换代码消息,它就会把回调模块改为消息里包含的新模块。
我们可以演示这一点,做法是用某个回调模块启动server3,然后动态交换这个回调模块、不能用name_server作为回调模块,因为服务器名已被硬编译进这个模块里了。因而将制作一个名为name_server1的副本,然后在里面修改服务器的名称。
-module(new_name_server).
-export([init/0,add/2,all_names/0,delete/1,find/1,handle/2]).
-import(server3,[rpc/2]).
%%接口
all_names()->rpc(name_server,allNames).
add(Name,Place)->rpc(name_server,{add,Name,Place}).
delete(Name)->rpc(name_server,{delete,Name}).
find(Name)->rpc(name_server,{find,Name}).
%%回调方法
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({find,Name},Dict)->{dict:find(Name,Dict),Dict}.
编写新的回调模块
-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.
于是在这里适时更换了回调模块,这就是动态代码升级。
Erang程序设计(第2版)【瑞典】Joe Armstrong著 牛化成译–299页至301页