实现代码热更新
在前面编写的过程中,我们发现我们每次编写新的模块或者修改老的模块 都需要去关闭服务器,编译代码,再打开服务器,但实际工作中,Erlang是可以实现代码的热更新的,这里我们就来编写代码实现代码的热更新
1.创建src/mo/hotfix目录,创建hotfix_sup.erl 文件
-module(hotfix_sup).
-behaviour(supervisor).
-export([start_link/0, init/1, start/0]).
%%%---------------------------------------------------------------------------------
%% des:启动热更新模块
%%%---------------------------------------------------------------------------------
start() ->
%% 在主监控树下启动热更新模块的监控进程
supervisor:start_child(server_sup:get_sup(), #{
id => ?MODULE,
start => {?MODULE, start_link, []},
restart => permanent,
shutdown => 5000,
type => supervisor,
modules => [?MODULE]
}).
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
AChild = #{id => 'srv_hotfix',
start => {'srv_hotfix', start_link, []},
restart => permanent,
shutdown => 2000,
type => worker,
modules => ['srv_hotfix']},
{ok, {#{strategy => one_for_one,
intensity => 5,
period => 30},
[AChild]}
}.
2.创建srv_hotfix.erl 文件
-module(srv_hotfix).
-behaviour(gen_server).
%%%-------------------------------------include-------------------------------------
%%%---------------------------------------------------------------------------------
%%%------------------------------------- define ------------------------------------
-define(SERVER, ?MODULE).
-define(BEAM_DIR, "../ebin").
%%%---------------------------------------------------------------------------------
%%%------------------------------------- record ------------------------------------
-record(srv_hotfix_state, {
%% 已热更新文件 {模块名, 文件md5值}
beam_list = []
}).
%%%---------------------------------------------------------------------------------
-export([start_link/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-export([load_beam_list/0, hotfix/0]).
%%%================================== gen_server func ==================================
%%%---------------------------------------------------------------------------------
%% des:start_link
%%%---------------------------------------------------------------------------------
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
%%%---------------------------------------------------------------------------------
%% des:init
%%%---------------------------------------------------------------------------------
init([]) ->
%% 1.热更新服务配置 比如说多少秒自动更新之类的 这里我们先忽略 手动来控制更新
%% 2.加载本次启动已经加载在shell文件中的模块 并记录到状态中,这里我们要指定路径,不要把所有的模块都加载进来
BeamList = load_beam_list(),
{ok, #srv_hotfix_state{beam_list = BeamList}}.
%%%---------------------------------------------------------------------------------
%% des:handle_call
%%%---------------------------------------------------------------------------------
handle_call(_Request, _From, State = #srv_hotfix_state{}) ->
{reply, ok, State}.
%%%---------------------------------------------------------------------------------
%% des:handle_cast
%%%---------------------------------------------------------------------------------
handle_cast({hot}, State = #srv_hotfix_state{}) ->
try
BeamList = State#srv_hotfix_state.beam_list,
%% 循环对比文件的md5值 如果有变化则加载到内存中 并记录到状态中
NewBeamList = check_beam_load(BeamList),
{noreply, State#srv_hotfix_state{beam_list = NewBeamList}}
catch
Err ->
io:format("hotfix error: ~p~n", [Err]),
{noreply, State}
end;
%%%---------------------------------------------------------------------------------
%% des:handle_cast
%%%---------------------------------------------------------------------------------
handle_cast(_Request, State = #srv_hotfix_state{}) ->
{noreply, State}.
%%%---------------------------------------------------------------------------------
%% des:handle_info
%%%---------------------------------------------------------------------------------
handle_info(_Info, State = #srv_hotfix_state{}) ->
{noreply, State}.
%%%---------------------------------------------------------------------------------
%% des:terminate
%%%---------------------------------------------------------------------------------
terminate(_Reason, _State = #srv_hotfix_state{}) ->
ok.
%%%---------------------------------------------------------------------------------
%% des:code_change
%%%---------------------------------------------------------------------------------
code_change(_OldVsn, State = #srv_hotfix_state{}, _Extra) ->
{ok, State}.
%%%================================== export func ==================================
%%%---------------------------------------------------------------------------------
%% des:
%%%---------------------------------------------------------------------------------
hotfix() ->
gen_server:cast(?SERVER, {hot}).
%%%=================================== local func ==================================
%%%---------------------------------------------------------------------------------
%% des:
%%%---------------------------------------------------------------------------------
load_beam_list() ->
%% 获取目录下所有的.beam文件
BeamFiles = filelib:wildcard("*.beam", ?BEAM_DIR),
%% 遍历每个.beam文件 加载到内存中 并记录到状态中
Fun = fun(BeamFile) ->
%% 获取模块的md5值
BeamPath = filename:join(?BEAM_DIR, BeamFile),
{ok, {Mod, Md5}} = beam_lib:md5(BeamPath),
%% 记录到状态中
{Mod, Md5}
end,
lists:map(Fun, BeamFiles).
%%%---------------------------------------------------------------------------------
%% des:
%%%---------------------------------------------------------------------------------
check_beam_load(BeamList) ->
BeamFiles = filelib:wildcard("*.beam", ?BEAM_DIR),
check_beam_load_(BeamFiles, BeamList, []).
check_beam_load_([BeamFile | T], BeamList, NewBeamList) ->
%% 获取模块的md5值
BeamPath = filename:join(?BEAM_DIR, BeamFile),
case beam_lib:md5(BeamPath) of
{ok, {Mod, Md5}} ->
case lists:keyfind(Mod, 1, BeamList) of
{Mod, OldMd5} = BeamInfo ->
case Md5 =:= OldMd5 of
true ->
check_beam_load_(T, BeamList, [BeamInfo | NewBeamList]);
false ->
case code:soft_purge(Mod) of
true ->
code:load_file(Mod),
io:format("reload module: ~p~n", [BeamFile]),
check_beam_load_(T, BeamList, [{Mod, Md5} | NewBeamList]);
false ->
io:format("soft_purge module: ~p failed~n", [BeamFile]),
check_beam_load_(T, BeamList, [BeamInfo | NewBeamList])
end
end;
false ->
code:load_file(Mod),
io:format("load new module: ~p~n", [BeamFile]),
check_beam_load_(T, BeamList, [{Mod, Md5} | NewBeamList])
end;
_ ->
%% 加载失败 记录到状态中
io:format("get_mod_md5 failed: ~p~n", [BeamFile]),
check_beam_load_(T, BeamList, [BeamFile | NewBeamList])
end;
check_beam_load_([], _, NewBeamList) ->
NewBeamList.

在server.erl的start方法中添加代码
%%启动热更新模块
hotfix_sup:start()
编译代码,启动游戏服

3 测试一下
在test目录在增加host_test.erl
-module(hot_test).
%% API
-export([test1/0]).
%%%================================== export func ==================================
%%%---------------------------------------------------------------------------------
%% des:
%%%---------------------------------------------------------------------------------
test1() ->
io:format("hot_test test1~n").
编译,然后再shell中调用srv_hotfix:hotfix()

411

被折叠的 条评论
为什么被折叠?



