0%

Erlang-发布版本升级-3-升级发布版本工具讲解

为了不加班,所以程序员一般都会开发一些便捷的工具来提高开发效率,下面就让我来给大家讲讲升级发布版本工具:rebar3_appup_plugin

发布

看前缀就知道,这是一个rebar3的插件,如果对rebar3不熟悉的请先移步去官网看看文档.

那我们先来创建一个rebar3的项目:

1
rebar3 new release rus_rel && cd rus_rel

接下来,当然是添加这个插件到rebar.confg里面:

1
2
3
4
5
6
7
8
9
10
11
12
13
{erl_opts, [debug_info]}.
{plugins, [
rebar3_appup_plugin
]}.
{deps, []}.
{relx, [
{release, { rus_rel, "0.1.0" }, [rus_rel,sasl]},
{sys_config, "./config/sys.config"},
{vm_args, "./config/vm.args"},
{dev_mode, false},
{include_erts, false},
{extended_start_script, true}
]}.

记得relx的配置dev_mode要修改为false(此处是演示,正式设置的时候当然是设置profiles里面的设置)

我们需要增加一个gen_server模块

rus_gen_server.erl :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
-module(rus_gen_server).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-export([
start_link/0
]).
-record(state, {}).
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
init([]) ->
{ok, #state{}}.
handle_cast(_Info, State) ->
{noreply, State}.
handle_call(_Info, _From, State) ->
{reply, ok, State}.
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, State) ->
{ok, State}.
code_change(OldVsn, State, Extra) ->
io:format("~p ~p ~p~n", [OldVsn, State, Extra]),
{ok, State}.

然后我们在sup里面增加这个模块作为子进程,之后我们会在第二版修改这个进程的内部状态

rus_rel_sup.erl :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
-module(rus_rel_sup).
-behaviour(supervisor).
%% API
-export([start_link/0]).
%% Supervisor callbacks
-export([init/1]).
-define(SERVER, ?MODULE).
%%====================================================================
%% API functions
%%====================================================================
start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
%%====================================================================
%% Supervisor callbacks
%%====================================================================
%% Child :: {Id,StartFunc,Restart,Shutdown,Type,Modules}
init([]) ->
Children = [
{rus_gen_server, {rus_gen_server, start_link, []}, permanent, 5000, worker, [rus_gen_server]}
],
{ok, { {one_for_all, 0, 1}, Children} }.

然后我们来发布一个版本:

1
rebar3 release tar

就这么简单,我们0.1.0的版本就发布好了,接下来运行起来:

1
2
3
4
5
$ tar zxf rus_rel-0.1.0.tar.gz // 解压
$ ./bin/rus start // 运行
$ ./bin/rus_rel versions
Installed versions:
* 0.1.0 permanent

ok, 准备工作完成

发布版本升级

接下来我们修改gen_server模块state的数据结构,增加一个字段id

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
-module(rus_gen_server).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-export([
start_link/0
]).
-record(state, {id}).
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
init([]) ->
{ok, #state{}}.
handle_cast(_Info, State) ->
{noreply, State}.
handle_call(_Info, _From, State) ->
{reply, ok, State}.
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, State) ->
{ok, State}.
code_change(OldVsn, {state}, Extra) ->
io:format("~p ~p ~p~n", [OldVsn, State, Extra]),
{ok, #state{id = 1}}.

然后别忘记了修改版本号

rus_rel.app.src修改vsn为0.2.0

1
rebar.config的{ rus_rel, “0.1.0” }修改为{ rus_rel, “0.2.0” }

然后先发布一下:

rebar3 release
之后再运行插件:

1
rebar3 appup generate

这个插件需要之前的版本也在本地目录下面,因为它的运行机制是通过对比两个rel文件夹的beam, 然后根据各自不同自动生成.appup 文件:

1
2
3
4
5
6
7
%% appup generated for rus_rel by rebar3_appup_plugin (2017/09/18 14:46:53)
{ "0.2.0",
[{ "0.1.0",
[{update,rus_gen_server,{advanced,[]},brutal_purge,brutal_purge,[]}] }],
[{ "0.1.0",
[{update,rus_gen_server,{advanced,[]},brutal_purge,brutal_purge,[]}] }]
}.

然后我们再执行relup 和 打包:

1
rebar3 relup tar

这样我们就获得了一个带有更新指令的发布,然后将这个发布包发到运行根目录下的releases文件夹下面, 并执行更新命令:

1
2
3
4
5
6
7
8
9
$ ./bin/rus_rel upgrade 0.2.0
Release 0.2.0 not found, attempting to unpack releases/rus_rel-0.2.0.tar.gz
Unpacked successfully: "0.2.0"
Installed Release: 0.2.0
Made release permanent: "0.2.0"
$ ./bin/rus_rel versions
Installed versions:
* 0.2.0 permanent
* 0.1.0 old

ok,整个更新就完成了,你可以attach进去看一下进程的内部状态,发现已经更新了.

appup插件说明

支持功能:

原理:通过对比两个rel文件夹的beam, 然后根据各自不同自动生成.appup 文件.
支持.appup.src 生成.appup 文件.
当gen_server 存在了-state_record(state),插件会自动注入state变更代码到beam里, More info.
支持根据两个版本sup模块init的返回的规格定义不同,生成相应的升级指令
自动生成模块之间的依赖关系.
使用注意事项:


两个版本必须编译参数里必须都有debug_info ,因为插件需要debug_info 的信息, 担心源码安全的话, 可以通过在项目下增加.erlang.crypt 文件来保护源码.
如果需要手动修改.appup 文件, 则需要编写.appup.src , 但是.appup.src 生成.appup 文件后,插件不会再生成.appup文件,因此建议先用rebar3 appup generate 出 .appup文件,然后再以这个文件的基础去修改,然后作为.appup.src.