`
AvinDev
  • 浏览: 113025 次
社区版块
存档分类
最新评论

Erlang 中的Module级别热部署

阅读更多
Java里面要实现Code Replacement,也就是什么热部署,通常是使用ClassLoader机制。不久前看到了一篇Google的Paper,里面讲解的C++代码热部署更为复杂。
在Erlang里面,实现Code Replacement其实很简单,最方便的方法可以参考 《Erlang Reference Manual》的12.3:

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

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


这种简单的HelloWorld例子,不能满足我的疑问。在一个更加复杂的应用里面,比如有多个Process,部分Process的代码更换后,其他没有更新的Process会怎样呢?下面做个试验。
实例代码:codereload.erl

-module(codereload).
-export([main/0, master_loop/2, worker_loop/0]).
-define(VERSION, "0.1").

main() ->
    process_flag(trap_exit, true),
    Pid1 = spawn(?MODULE, worker_loop, []),
    Pid2 = spawn(?MODULE, worker_loop, []),
    spawn(fun() -> register(master, self()), master_loop(Pid1, Pid2) end).


master_loop(Pid1, Pid2) ->
    io:format("Pid1, Pid2 is alive ~p ~p~n",[is_process_alive(Pid1), is_process_alive(Pid2)]),
    receive
	refresh ->
	    io:format("Master code reload~n"),
	    Pid1 ! refresh,
	    Pid2 ! refresh,
	    codereload:master_loop(Pid1, Pid2);
	Any ->
	    io:format("Master ~p receive message: ~p~n", [?VERSION, Any]),
	    Pid1 ! Any,
	    Pid2 ! Any,
	    master_loop(Pid1, Pid2)
    end.

worker_loop() ->
    receive
	refresh ->
	    io:format("Worker code ~p reload~n", [self()]),
	    codereload:worker_loop();
	Any ->
	    io:format("Worker ~p at ~p also receive message: ~p~n", [?VERSION, self(), Any]),
	    worker_loop()
    end.


程序里面有三个进程,一个Master和两个Worker,下面使用分布式进程通信来实现Code Replacement。

使用
引用
erl -sname foo@localhost
erl -sname bar@localhost

启动erlang shell

启动codereload:
引用
(foo@localhost)1> codereload:main().
Pid1, Pid2 is alive true true
<0.39.0>


发一个"HI"过去看看:
引用
(bar@localhost)1> {master, 'foo@localhost'} ! "HI".
"HI"


嗯,收到了
引用
(foo@localhost)2> Master "0.1" receive message: "HI"
(foo@localhost)2> Pid1, Pid2 is alive true true
(foo@localhost)2> Worker "0.1" at <0.37.0> also receive message: "HI"
(foo@localhost)2> Worker "0.1" at <0.38.0> also receive message: "HI"



好了,将源程序改一下:
-define(VERSION, "0.1"). --> -define(VERSION, "0.2").

Pid2 ! refresh, --> %Pid2 ! refresh,

这里改了版本号,还有注释掉发送给Pid2的更新通知

先编译新版本
引用
(foo@localhost)2> c(codereload).
{ok,codereload}


发送更新通知:
引用
(bar@localhost)2> {master, 'foo@localhost'} ! refresh.
refresh


收到了:
引用
(foo@localhost)3> Master code reload
(foo@localhost)3> Worker code <0.37.0> reload
(foo@localhost)3> Worker code <0.38.0> reload
(foo@localhost)3> Pid1, Pid2 is alive true true


这时候master, Pid1, Pid2应该都是v0.2的版本,看看是不是这样:
引用
(bar@localhost)3> {master, 'foo@localhost'} ! "HI".
"HI"


引用
(foo@localhost)3> Master "0.2" receive message: "HI"
(foo@localhost)3> Pid1, Pid2 is alive true true
(foo@localhost)3> Worker "0.2" at <0.37.0> also receive message: "HI"
(foo@localhost)3> Worker "0.2" at <0.38.0> also receive message: "HI"


OK,改成了 "0.2" 了,代码替换成功。事情还没有完,留意上面将 Pid2 ! refresh 给注释掉了么,这时候更新会怎样呢?先修改程序:
-define(VERSION, "0.2"). --> -define(VERSION, "0.3").

%Pid2 ! refresh, --> Pid2 ! refresh,


再次编译:
引用
(foo@localhost)3> c(codereload).
{ok,codereload}


发送更新通知:
引用
(bar@localhost)7> {master, 'foo@localhost'} ! refresh.
refresh


引用
(foo@localhost)4> Master code reload
(foo@localhost)4> Worker code <0.37.0> reload
(foo@localhost)4> Pid1, Pid2 is alive true true


可见,现在只有Pid1收到了更新通知进行了更新,会产生怎样的结果呢:
引用
(bar@localhost)8> {master, 'foo@localhost'} ! "HI".
"HI"


引用
(foo@localhost)4> Master "0.3" receive message: "HI"
(foo@localhost)4> Pid1, Pid2 is alive true true
(foo@localhost)4> Worker "0.3" at <0.37.0> also receive message: "HI"
(foo@localhost)4> Worker "0.2" at <0.38.0> also receive message: "HI"

噢,Pid2还停留在v0.2的代码上,可见各个进程的代码是独立的。

发送更新通知,让Pid2加载v0.3的代码:
引用
(bar@localhost)8> {master, 'foo@localhost'} ! refresh.
refresh


引用
(foo@localhost)4> Master code reload
(foo@localhost)4> Worker code <0.37.0> reload
(foo@localhost)4> Worker code <0.38.0> reload
(foo@localhost)4> Pid1, Pid2 is alive true true


这回Pid2更新到新版本了:
(bar@localhost)9> {master, 'foo@localhost'} ! "HI".
"HI"


引用
(foo@localhost)4> Master "0.3" receive message: "HI"
(foo@localhost)4> Pid1, Pid2 is alive true true
(foo@localhost)4> Worker "0.3" at <0.37.0> also receive message: "HI"
(foo@localhost)4> Worker "0.3" at <0.38.0> also receive message: "HI"



看到头晕了么?我也晕了,还有一种情况呢。在上面,
引用
(foo@localhost)4> Master "0.3" receive message: "HI"
(foo@localhost)4> Pid1, Pid2 is alive true true
(foo@localhost)4> Worker "0.3" at <0.37.0> also receive message: "HI"
(foo@localhost)4> Worker "0.2" at <0.38.0> also receive message: "HI"

这步,如果不输入
引用
{master, 'foo@localhost'} ! refresh.


而是再次编译:
引用
(foo@localhost)4> c(codereload).
{ok,codereload}


发送个消息过去看看:
(bar@localhost)9> {master, 'foo@localhost'} ! "HI".
"HI"


引用
(foo@localhost)5> Master "0.3" receive message: "HI"
(foo@localhost)5> Pid1, Pid2 is alive true false
(foo@localhost)5> Worker "0.3" at <0.37.0> also receive message: "HI"

噢,怎么回事,is_process_alive(Pid2)返回false了,进程怎么挂了?

《Erlang Reference Manual》的12.3:
引用
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'.

Bot old and current code is valid, and may be evaluated concurrently. Fully qualified function calls always refer to current code. Old code may still be evaluated because of processes lingering in the old code.

If a third instance of the module is loaded, the code server will remove (purge) the old code and any processes lingering in it will be terminated. Then the third instance becomes 'current' and the previously current code becomes 'old'.


可见,Erlang里面,模块的代码只有新旧两个版本,当时Pid1对应于v0.3(Current),Pid2对应于v0.2(Old),当进行编译之后,Pid1的代码就是Old,而Pid2就被强制终止了。


对于以守护进程形式启动的Erlang进程,就不能在shell里面直接编译了,如果在外部编译,这个进程是不认的。我想到的一个方法就是使用rpc来调用编译:
rpc:call('foo@localhost', shell_default, c, [codereload]).


对于热部署,Erlang还有一种更强大的形式,就是使用OTP的Release Handling,我也不会,以后再学习了。

p.s:对本文有任何异议,欢迎拍板:)
分享到:
评论

相关推荐

    Erlang应用部署与热代码替换--理解2

    本篇将深入探讨Erlang应用的部署与热代码替换。 一、Erlang应用部署 在Erlang环境中,应用通常被打包成一个`.app`文件,包含应用元数据,以及一个或多个beam文件(编译后的Erlang代码)。部署Erlang应用的步骤如下...

    erlang代码热替换与应用部署

    本文将深入探讨Erlang中的代码热替换(Code Replacement)技术及其在应用部署中的重要性。 代码热替换是Erlang的一大特色,它允许在不中断运行服务的情况下更新和替换正在运行的代码。这一特性使得Elang系统可以在...

    erlang 中文基础教程

    在Erlang Shell中,使用`c(module_name).`命令编译指定的模块,如`c(tut).`。若编译成功,会返回`{ok,module_name}`的信息;若编译失败,系统会给出错误报告,帮助开发者定位并修正问题。 通过以上介绍,我们可以...

    Erlang并发编程,Erlang程序设计,Erlang中文手册

    Erlang并发编程,Erlang程序设计,Erlang中文手册。 学习erlang的好资料。  Erlang是一个结构化,动态类型编程语言,内建并行计算支持。最初是由爱立信专门为通信应用设计的,比如控制交换机或者变换协议等,因此...

    erlang中文基础教程

    用户可以在 Erlang Shell 中输入命令,例如数学运算符号,函数调用等。Erlang Shell 提供了一个交互式的环境,用户可以实时查看输出结果。 2. 顺序编程 顺序编程是 Erlang 编程语言的基础,用户可以使用 Erlang ...

    硝烟中的erlang

    本书不适用于初学者。从大多数教程、书籍以及培训课程上所学到的知识,还不能用来运维、 诊断以及调试生产环境中的Erlang 系统。在程序员学习新的语言和环境时,...道一些如何在生产环境中部署Erlang的最佳实践F 18 F。

    Erlang中文手册.pdf

    - **1.3.1 进程**:Erlang的进程与其他语言中的线程类似,但它们是轻量级的,并且通过消息传递进行通信。 - **1.3.2 信息传递**:进程间通信是通过消息传递完成的,Erlang提供了简单的机制来实现这一点。 - **1.3.3 ...

    <27>erlang record

    标题中的“&lt;27&gt;erlang record”可能指的是Erlang编程语言中的Record特性,它是一种数据结构,类似于结构体或者哈希表,用于组织和操作数据。在Erlang中,Record提供了一种方便的方式来定义和访问具有固定字段的数据...

    erlang lru cache module

    在Erlang中,这个模块可以帮助开发者高效地存储和检索数据,同时在内存资源紧张时自动清理过期或不常访问的数据。 在Erlang LRU Cache模块中,主要有以下几个关键概念和功能: 1. **键值对(Key-Value Pair)**:...

    erlang 中文,chm参考文档

    2. **分布式Erlang**:如何在多台机器上部署和管理Erlang集群,实现跨节点的进程通信。 3. **行为模式**:如GenServer、GenEvent和Supervisor,它们提供了一种组织和管理Erlang进程的标准方式。 4. **性能优化**:...

    erlang 中文乱码

    在IT行业中,Erlang是一种强大的并发编程语言,主要用于构建高可用性、容错性和分布式系统。当我们在处理中文字符时,特别是在Erlang环境中,可能会遇到“中文乱码”的问题。这个问题通常与字符编码、文件读写以及...

    erlang深度分析.pdf

    以上总结了Erlang深度分析的主要知识点,涵盖了虚拟机、性能分析、编码实践、分布式系统开发、内存管理、高可用性设计、网络通信、热部署、并发模型、本地接口设计、系统监控以及社区资源等多个方面。这些知识能够...

    erlang编程 Introducing Erlang

    Erlang中的链接(Linking)和监控(Monitoring)机制允许进程间建立关系,以便在另一进程崩溃时得到通知。链接用于追踪相关进程的状态,而监控则可以观察进程的生存状态。 ### 5. 消息传递 Erlang的进程间通信主要...

    Erlang编程规则——中文翻译版本

    Erlang术语部分提到了Erlang程序的模块化设计,强调了模块(module)、函数(function)和进程(process)的基本概念。模块是Erlang程序中代码封装的基本单位,其中函数是执行操作的最小单元。模块包含的函数数量不宜过多...

    erlang中dns解析

    在Erlang编程语言中,DNS(Domain Name System)解析是一项关键功能,它允许程序将域名转换为IP地址,反之亦然。Erlang提供了强大的工具来处理网络通信,包括DNS解析。这篇博客(虽然链接不可用)可能讨论了如何在...

Global site tag (gtag.js) - Google Analytics