mochiweb reloader 实现代码热更新

在用 mochiweb 的时候有一个 reloader 模块用来实现代码热更新,在自己的项目中也想加进去用一用,于是把这个东西搞了出来。

我的程序是用 rebar 配置的,用的 rebar generate 来运行,配置 reloader 之后,rebar compile generate 之后却不生效,于是在 reloader 的源码中找答案。

init([]) ->
    {ok, TRef} = timer:send_interval(timer:seconds(1), doit),
    {ok, #state{last = stamp(), tref = TRef}}.

doit(From, To) ->
    [case file:read_file_info(Filename) of
         {ok, #file_info{mtime = Mtime}} when Mtime >= From, Mtime < To ->
             reload(Module);
         {ok, _} ->
             unmodified;
         {error, enoent} ->
             %% The Erlang compiler deletes existing .beam files if
             %% recompiling fails.  Maybe it's worth spitting out a
             %% warning here, but I'd want to limit it to just once.
             gone;
         {error, Reason} ->
             io:format("Error reading ~s's file info: ~p~n",
                       [Filename, Reason]),
             error
     end || {Module, Filename} <- code:all_loaded(), is_list(Filename)].

这2个函数足以看出 reloader 的逻辑,是每秒检查一次当前加载的 beam 文件,如果有更新,就 reload。“有更新”的判断标准是更新发生在 From 和 To 这 2 个时间之间,这 2 个时间也就是定时器的间隔时间,为 1 秒。

在 reloader 里面加了调试日志,把 From To Mtime 都输出出来,结果如下:

doit {_,{10,19,57}} {_,{10,19,58}} {_,{10,13,34}}
doit {_,{10,19,58}} {_,{10,19,59}} {_,{10,13,34}}
doit {_,{10,19,59}} {_,{10,20,0}}  {_,{10,19,50}}
doit {_,{10,20,0}}  {_,{10,20,1}}  {_,{10,19,50}}

这样问题就已经确定了,reloader 检测到了文件的更改,但由于 generate 的延迟,文件的更新操作没有在更新的那一秒之间被 reloader 检测到,检测到的时间已经是 From 之前了,所以没有执行更新操作。

解决方案:

reloader 只是开发的时候测试使用,所以在开发调试阶段就不使用  rebar generate 来运行程序,改为用 Bash 脚本

erl -pa ebin -s demo -s reloader