erlang中的代码热替换实践——动态升级尾递归中的用来记录状态的Record

最近在项目开发中,遇到了一个问题,就是如何在erlang中对模块进行代码热替换的时候,同时修改进程中尾递归所传递的用于记录状态的Record信息。

典型的程序结构如下:

-record(stat, {ver=?VER, counter=0}).

loop(#stat{ver=_Ver, counter=Counter}=Stat) ->
    receive
        {ver} ->
            io:format("~p @ ~p ver~n", [?MODULE, Stat]),
            ?MODULE:loop(Stat#stat{counter=Counter+1});
        _Any ->
            ?MODULE:loop(Stat#stat{counter=Counter+1})
    after
        1000 ->
            ?MODULE:loop(Stat#stat{counter=Counter+1})
    end.

问题为:用一种方式实现当stat记录的格式发生变化时,代码热替换仍能正常进行。

首先,在上面的结构中,如果修改了stat的字段的个数,当执行
code:purge(?MODULE).
code:delete(?MODULE).
之后,当前正在执行的进程肯定会退出的,因为在尾递归?MODULE:loop调用的时候,会迁移到新的代码去执行,但传入的Stat参数却是旧的代码中的record结构。

解决这个问题的关键,就是实现在代码升级的时候,将旧的结构转换成新的结构,然后再调用新的代码执行。

下面是我的一种实现的方式

module_upgrade({stat, _Ver, Counter}=OldStat) ->   
    NewStat = #stat{ver=?VER, counter=Counter},
    io:format("~p @ ~p module_upgrade to ~p ~n", [?MODULE, OldStat, NewStat]),
    NewStat;
module_upgrade(OldStat) ->
    io:format("~p @ ~p module_upgrade no change ~n", [?MODULE, OldStat]),
    OldStat.

loop(#stat{ver=_Ver, counter=Counter}=Stat) ->
    receive
        {ver} ->
            io:format("~p @ ~p ver~n", [?MODULE, Stat]),
            ?MODULE:loop(Stat#stat{counter=Counter+1});
        {upgrade} ->   
            io:format("~p @ ~p upgrade~n", [?MODULE, Stat]),
            code:purge(?MODULE),
            code:delete(?MODULE),
            NewStat = ?MODULE:module_upgrade(Stat),
            io:format("~p @ ~p upgrade ready~n", [?MODULE, NewStat]),
            ?MODULE:loop(NewStat);
        _Any ->
            ?MODULE:loop(Stat#stat{counter=Counter+1})
    after
        1000 ->
            ?MODULE:loop(Stat#stat{counter=Counter+1})
    end.

其中module_upgrade是用来进行record记录结构转换的,当在loop中收到{upgrade}消息时,首先通过code:purge(?MODULE)和code:delete(?MODULE)加载最新的代码,然后通过?MODULE:module_upgrade(Stat)将当前代码所使用的record结构转换成新的结构,(注意module_upgrade函数的参数是用 tuple 表示的旧的结构,而不是record,这是一个关键的地方),然后?MODULE:loop(NewStat)将新的结构传入函数调用,同时执行新的代码。

例如:

当把stat记录的格式从
-record(stat, {ver=?VER, counter=0}).
修改为
-record(stat, {ver=?VER, counter=0, name="MULIN"}).
时,module_upgrade可以按照上面的例子中直接写。

如果反过来的话,就需要这样了:
module_upgrade({stat, _Ver, Counter, Name}=OldStat) ->   
    NewStat = #stat{ver=?VER, counter=Counter},
    io:format("~p @ ~p module_upgrade to ~p ~n", [?MODULE, OldStat, NewStat]),
    NewStat;
module_upgrade(OldStat) ->
    io:format("~p @ ~p module_upgrade no change ~n", [?MODULE, OldStat]),
    OldStat.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值