Erlang的热部署做的很完善,参见Release
Handling,这篇文章只关心最基本的模块更新。模块是Erlang程序组织的最基本单元。如下代码就是一个最简单的hello模块(为了说明问题,我们添加了一个init函数):
-module(hello).
-export([init/0, hello/1]).
init() ->
Db = dict:new(),
dict:store(name, "jzh", Db).
hello(Db) ->
Value = dict:fetch(name, Db),
io:format("hello: ~p!~n", [Value]).
运行一下:
1> c(hello).
{ok,hello}
2> Db = hello:init().
{dict,1,16,16,8,80,48,
{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]}
{{[],[],[],[],[],
[[name,106,122,104]],
[],[],[],[],[],[],[],[],[],[]}}}
3> hello:hello(Db).
hello: "jzh"!
ok
(c命令的作用是编译和加载指定模块)
hello:hello打印出hello的字样,但是,假如我们现在希望输出hi,而不是hello,要怎么做呢?Java里面一般需要重新编译和运行程序,但是先前的运行状态也会丢失(dict中存储的name),需要重新运行init方法。
我们将hello改成hi,并在同一个控制台直接调用hello:hello(未调用hello:init),看看结果是怎么样的:
4> c(hello).
{ok,hello}
5> hello:hello(Db).
hi: "jzh"!
ok
可以看到,代码更新后打印出hi字样(保存在dict中的name,并没有因为新代码的加载而失效)。
从上面的示例可以看到,Erlang可以在运行时进行代码的替换,而不会影响到运行时状态(数据)。在Java中要完成类似的功能比较难,那Erlang背后实现这种功能的原理是什么呢?原来,在任何时候,一个模块都可以有两个版本加载到运行时系统,一个旧版本,一个当前版本。在加载一个模块时,运行时系统会先检查这个模块是否已经有一份代码存在,如果存在,则把已存在的代码作为旧的代码,新加载的代码作为当前版本。如果有第三份代码加载进系统,则原来的旧版本代码会被清理掉,跟此版本代码相关的所有进程都会被结束;原来的当前代码变为当前的旧版本代码,第三份代码成为当前版本。
函数调用有两种方式:一种完全限定调用(Mod:Func),包括模块内、模块之间这种方式的调用,以及模块之间通过import指令导入其它模块,再直接通过函数名调用;一种直接调用(Func),只存在于模块内的函数调用。完全限定的函数调用总是引用当前版本代码。旧版本代码只可能通过直接调用的方式引用到。
-module(m).
-export([loop/0]).
loop() ->
receive
code_switch ->
m:loop();
Msg ->
...
loop()
end.
如上代码,如果有代码更新,并且进程没有收到code_switch消息,那么进程就会通过loop调用一直在引用旧版本代码;而收到code_switch消息后,进程便通过m:loop引用到当前版本代码。
(这一块,《Erlang编程指南》软件升级一章,幕后一节对原理讲的有点复杂)
从虚拟机实现角度来看,这两者的区别在哪里呢?看一下如下代码:
-module(test).
-import(hello, [hello/0]).
test2() ->
ok.
test() ->
test2(),
test:test2(),
hello(),
hello:hello(),
ok.
我们来看看test函数生成的opcode:
i_call_f test:test2/0
i_call_ext_e test:test2/0
i_call_ext_e hello: hello/0
i_call_ext_e hello: hello/0
可以看出,直接调用test2生成的opcode是i_call_f,而其它的全限定调用(包括hello()调用)都生成的是i_call_ext_e指令。这两者有什么区别?i_call_f指令会直接跳到相应函数的入口(进程生成后,地址确定)并执行,而i_call_ext_e会根据模块导出函数列表中的地址跳转(详见[$OTP_SRC/erts/emulator/beam/beam_emu.c -->
process_main)。这个地址在模块更新时会更新为当前版本代码导出函数的地址(详见[$OTP_SRC/erts/emulator/beam/beam_bif_load.c -->
load_module_2] ,导出函数地址通过[$OTP_SRC/erts/emulator/beam/export.h]中Export结构的address定义)。从这里也可以看出来,除非有进程在引用旧版本的代码,模块更新后,其它进程是无法再通过任何方式引用到旧版本的代码(模块导出函数列表中的地址已更新)。
分享到:
相关推荐
Erlang 中的Module级别热部署,可以看看
NULL 博文链接:https://langzhe.iteye.com/blog/682425
NULL 博文链接:https://langzhe.iteye.com/blog/1123218
描述erlang的设计,非常实用,•原书名: Programming Erlang: Software for a Concurrent World
搭建自己的erlang服务器(基于RabbitMQ改进)(一)
视频+例子,这是才litaocheng文章和fengyu的帮助下完成的
erlang入门电子书 erlang编程 Introducing Erlang,作者Simon.St.Laurent
erlang 安装包
Erlang及其应用Erlang及其应用Erlang及其应用
自己逐字翻译的,有些不顺当,比英文版适应初学
erlang25.0 windows版本
erlang otp25 win安装包
ErlangB和ErlangC计算工具(exe可执行文件+excel两个) ErlangB和ErlangC计算工具(exe可执行文件+excel两个)
erlang22最新下载包 erlang22.1.tar.gz erlang22最新下载包 erlang22最新下载包
(494页带目录的高清扫描版) 这是一本讲解Erlang编程语言的入门指南,内容通俗...内容涉及模块、函数、类型、递归、错误和异常、常用数据结构、并行编程、多处理、OTP、事件处理,以及所有Erlang的重要特性和强大功能。
erlang 中文基础教程erlang 中文基础教程
有些同学想把Erlang数据通过term_to_binary函数封包后以二制进形式存入数据库,然后用PHP读取并解包成PHP数组。 为了解决上面的这种应用场合中遇到的问题, 参考peb(Php-Erlang Bridge)扩展写了这个类似erlang:...
Erlang并发编程,Erlang程序设计,Erlang中文手册。 学习erlang的好资料。 Erlang是一个结构化,动态类型编程语言,内建并行计算支持。最初是由爱立信专门为通信应用设计的,比如控制交换机或者变换协议等,因此...