为了不加班,所以程序员一般都会开发一些便捷的工具来提高开发效率,下面就让我来给大家讲讲升级发布版本工具: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} }.
|
然后我们来发布一个版本:
就这么简单,我们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
之后再运行插件:
这个插件需要之前的版本也在本地目录下面,因为它的运行机制是通过对比两个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 和 打包:
这样我们就获得了一个带有更新指令的发布,然后将这个发布包发到运行根目录下的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.