ERLANG OTP源码分析 – code_server
继续上一次的sys模块升级的话题,我们再讨论一下升级基本原理,了解一下code和code_server模块。
简单升级
上文也说到过,如果升级未改动 State 里的内容,你可以这样升级。
在集群的每个节点内(当然也可以通过 rpc)
如果你想省略后 2,3 步,可以利用mochiweb的 reloader 模块进行自动加载。
升级的原理
结合code_server代码来研究一下升级的原理。
erlang 可以同时允许两套代码同时加载运行在虚拟机内部运行, 一套被成为老版本代码 , 一套被成为当前版本代码,也成为最新代码。
升级调度的命令code模块其实是code_server模块的接口模块,上面的 purge, load_file 实际上都是向code_server模块发送相应的话 call 消息。
code_server是一个gen_serverlike 的进程,但却没有使用behaviour,因为是 kernel 里的模块吧。
code:purge
code:purge 底层调度的是 codeserver:dopurge 函数,它的作用就是杀掉在调用老代码的进程,并清空老代码,之后就只有当前代码了。
do_purge(Mod) -> do_purge(processes(), to_atom(Mod), false). do_purge([P|Ps], Mod, Purged) -> case erlang:check_process_code(P, Mod) of true -> Ref = erlang:monitor(process, P), exit(P, kill), receive {\'DOWN\',Ref,process,_Pid,_} -> ok end, do_purge(Ps, Mod, true); false -> do_purge(Ps, Mod, Purged) end; do_purge([], Mod, Purged) -> catch erlang:purge_module(Mod), Purged.
erlang:checkprocesscode 就是用来判断进程是否有在调用老代码的函数。
code:load_file
code:loadfile 最后调度的是 erlang:loadmodule, 该函数做了两步事情:
结合例子更好说明问题:
我们用例子来证明一下。是否会杀掉调用老代码的进程。
-module(e3). -compile(export_all). start() -> register(?MODULE, spawn(?MODULE, loop, [])). switch() -> ?MODULE ! code_switch. compile() -> compile:file(?MODULE), code:purge(?MODULE), code:load_file(?MODULE). msg() -> ?MODULE ! hello. loop() -> receive code_switch -> ?MODULE:loop(); _ -> io:format("~p~n", ["bb"]), loop() end.
我们开始实验,启动进程后,两次加载 e3 这个模块。
1> c(e3). {ok,e3} 2> e3:start(). true 3> whereis(e3). <0.41.0> 4> c(e3). {ok,e3} 5> whereis(e3). <0.41.0> 6> c(e3). {ok,e3} 7> whereis(e3). undefined
可以看到第二次的加载 c(e3) 杀掉了 e3 这个进程, 从代码可以知道 e3 一直在 receive 等待外界的消息, 一直调用者老代码, 所以被杀。
Fully qualified function calls`
热升级的目的就是让老代码的程序能运行新的代码,要触发这个切换,最关键一步必须使用完全限定的函数调用,即上面的 ?MODULE:loop 这样会触发虚拟机寻找当前代码继续运行。
上面的场景,代码就是停留在 receive 这块,连续发生了两次升级,老的进程被杀死, 如何避免这种情况呢,尽量不要在 用户模块里挂起,让程序一旦发生挂起时都停留在系统模块里。
例如上个例子如果使用gen_server则 receive 在gen_server这个模块,程序占用的模块就不是 e3 这个模块, 而 gen 进程调用 callback 函数都使用 Mod:handle_call 这类Fully qualified function calls的调用方法,所以加载过新代码之后, gen 进程一接收到消息,老程序立马就走到当前代码上了。
网络类的应用,尽量不要使用 {active, false}, 这样 Socket 会阻塞在用户模块的的 gen_tcp:recv 里,如果这个 Socket 在升级之后没有任何数据交互(未离开过用户模块),那么下次升级这个连接将被杀掉,这个也是我们要避免的。
升级之前,我们可以运行如下的程序可以提前发现即将被杀掉的进程。
[Pid|| Pid <- processes(), true =:= erlang:check_process_code(Pid, Module)]
code_server其它函数都比较简单
未完待续
参考
建议继续学习:
- Erlang match_spec引擎介绍和应用 (阅读:4535)
- whatsapp深度使用Erlang有感 (阅读:4581)
- php-erlang (阅读:4312)
- gen_tcp调用进程收到{empty_out_q, Port}消息奇怪行为分析 (阅读:3544)
- hibernate使用注意事项 (阅读:3222)
- Erlang linkin driver用port_control方式时的一些经验分享 (阅读:2968)
- Erlang如何限制节点对集群的访问之net_kernel:allow (阅读:2979)
- ERLANG OTP源码分析 – gen_server (阅读:2859)
- erlang学习手记 (阅读:2697)
- gen_tcp容易误用的一点解释 (阅读:2638)
扫一扫订阅我的微信号:IT技术博客大学习
- 作者:hoterran 来源: 运维和开发
- 标签: code_server ERLANG
- 发布时间:2012-08-09 23:46:56
- [44] 界面设计速成
- [39] 视觉调整-设计师 vs. 逻辑
- [39] Oracle MTS模式下 进程地址与会话信
- [35] 程序员技术练级攻略
- [35] IOS安全–浅谈关于IOS加固的几种方法
- [35] 如何拿下简短的域名
- [33] 图书馆的世界纪录
- [33] android 开发入门
- [32] 【社会化设计】自我(self)部分――欢迎区
- [30] 读书笔记-壹百度:百度十年千倍的29条法则