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