码迷,mamicode.com
首页 > 其他好文 > 详细

再说说erlang的模块热更新

时间:2014-11-17 10:51:31      阅读:237      评论:0      收藏:0      [点我收藏+]

标签:des   style   blog   http   io   ar   使用   sp   for   

前面的文章有讲过erlang热更新,只是大概介绍,现在再深入一点讲erlang的模块热更新。erlang的热更新是模块级别的,就是一个模块一个模块更新的。

热更新是什么,就是在不停止系统的情况下对运行的代码进行替换。

如何进行热更新?

c(Mod) ->
	compile:file(Mod),
	code:purge(Mod),
	code:load_file(Mod).

以上就是shell c(Mod) 的主要代码,3个步骤:编译新的代码,清除旧代码,加载新代码

同样, l(Mod) 的主要代码如下,少了编译过程:

l(Mod) ->
	code:purge(Mod),
	code:load_file(Mod).

热更新原理

erlang文档有说明:

The code of a module can exist in two variants in a system: current and old. When a module is loaded into the system for the first time, the code becomes ‘current‘. If then a new instance of the module is loaded, the code of the previous instance becomes ‘old‘ and the new instance becomes ‘current‘.

意思是,erlang每个模块都能保存2份代码,当前版本‘current‘和旧版本‘old‘,当模块第一次被加载时,代码就是‘current‘版本。如果有新的代码被加载,‘current‘版本代码就变成了‘old‘版本,新的代码就成了‘current‘版本

这样,就算代码在热更新,有进程在调用这个模块,执行的代码也不会受影响。热更新后,这个进程执行的代码没有改变,只不过代码被标记成‘old‘版本。而新的进程调用这个模块时,只会访问‘current‘版本的代码。而‘old‘版本的代码如果没有进程再访问,就会在下次热更新被系统清除掉。

erlang用两个版本共存的方法来保证任何时候总有一个版本可用,这样,对外服务就不会停止。

热更新问题

有个问题,如果‘old‘版本一直都有进程在调用,在此期间,代码热再更新了会发生什么情况?

热更新时,如果模块存在‘old‘版本代码,erlang会kill掉所有调用这个‘old‘版本代码的进程,然后移除掉‘old‘版本代码,‘current‘版本变成了‘old‘版本,新的代码就成了‘current‘版本。

热更新问题重现

-module(t).
-compile(export_all).

start() ->
	Pid = spawn(fun() -> do_loop() end),
	register(t, Pid).
	
do_loop() ->
	receive
		Msg ->
			io:format("~p~n", [Msg])
	end,
	do_loop().

结果如下:

7> t:start().
true
8> erlang:monitor(process, whereis(t)).  %%进程监控
#Ref<0.0.0.56>
9> whereis(t).
<0.40.0>
10> l(t).                                %%第1次热更
{module,t}
11> whereis(t).
<0.40.0>
12> l(t).                                %%第2次热更
{module,t}
13> whereis(t).
undefined
14> flush().
Shell got {‘DOWN‘,#Ref<0.0.0.56>,process,<0.40.0>,killed}
ok
热更新2次后,进程就被kill掉了。(想知道在哪被kill,可在code_server中do_purge/3找到,参考[1])

解决热更新问题

如果进程一直在自己loop里面,就会一直跑着‘old‘版本的代码,这样的后果就是新的代码没有被使用,而且在下一次热更新时进程会被系统kill掉。

怎么解决这个问题,erlang文档还是能找到答案:

To change from old code to current code, a process must make a fully qualified function call. Example:

-module(m).
-export([loop/0]).

loop() ->
    receive
        code_switch ->
            m:loop();
        Msg ->
            do_something(),
            loop()
    end.

就是在热更新后,给这个进程发消息 code_switch ,这样进程会调用 m:loop() 

这里,loop()和m:loop()有什么区别呢?

erlang根据模块划分,函数分本地调用和外部调用,其中,本地调用是调用本模块内的函数,函数可以不导出,调用形式为 Atom(Args) ;外部调用就是调用别的模块函数,函数必须导出,调用形式为 Module:Function(Args).

在erlang VM中,进程调用模块的过程是先加载这个模块当前版本的代码再执行,如果进程一直都是本地调用,那么所有操作都是在进程当前运行的代码中完成。换句话,这个过程中进程不会去加载新的代码。打破这种局面的就是外部调用。

bubuko.com,布布扣

看这张图的时候先把绿色的内容去掉,进程就一直都是本地调用,加入绿色内容后,进程会重新加载这个模块的代码再运行。

那么有些同学会好奇,既然这样,erlang为何还要本地调用,直接全部都外部调用就好了?

外部调用的开销比本地调用大很多。

erlang VM的基本调度单位是进程,每个进程都是隔离的,不共享内存。每次外部调用时,erlang要从导出函数表中找到这个函数,然后引用到这个函数的代码,这个过程比起本地调用上下文地址跳转代价昂贵得多。

今晚先到这里,有点困了,找时间再谈谈模块加载。 good night !


参考:http://blog.csdn.net/mycwq/article/details/41175237

http://learnyousomeerlang.com/designing-a-concurrent-application#hot-code-loving

http://www.erlang.org/doc/reference_manual/code_loading.html#id86381

再说说erlang的模块热更新

标签:des   style   blog   http   io   ar   使用   sp   for   

原文地址:http://blog.csdn.net/mycwq/article/details/41175237

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!