使用Erlang实现游戏服务器-05

实现代码热更新

在前面编写的过程中,我们发现我们每次编写新的模块或者修改老的模块 都需要去关闭服务器,编译代码,再打开服务器,但实际工作中,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()
在这里插入图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值