4.5.9. 以nitrogen_2.0.4为例建立工程框架¶
- 说明:
把所有[appname]换成你你自己的app的名字
首先建立一个基本的工程框架¶
首先进入项目根目录,建立如下基本文件目录:
mkdir apps/ deps/ rel/ etc/
- 增加
rebar
文件: 源码生成:
git clone https://github.com/basho/rebar.git cd rebar make
直接下载文件:
wget http://cloud.github.com/downloads/basho/rebar/rebar
从别的项目(如:riak)复制
注:最好是用riak发布项目里面的rebar文件(用前面介绍的方法[wget或源码]可能有问题),会比较稳定
- 增加
增加
rebar.config
文件,并在rebar.config
文件中增加如下内容:%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- {sub_dirs, ["rel"]}. %%需要编译的子目录 {require_otp_vsn, "R13B04|R14"}. %%要求当前机器的 erlang 版本为 R13B04|R14 {cover_enabled, true}. %%如果 ebin 目录下存在 beam 文件,允许覆盖 {erl_opts, [debug_info, fail_on_warning]}. %%编译时检查是否有 warning,如果有将编译报错 {deps, [ {mochiweb, "1.5.*", {git, "git://github.com/mochi/mochiweb.git", {tag, "1.5.0"}}}, {nitrogen_core, "2.1.*", {git, "git://github.com/nitrogen/nitrogen_core.git", "HEAD"}}, %%nitrogen 框架依赖项目 {nprocreg, "0.2.*", {git, "git://github.com/nitrogen/nprocreg.git", "HEAD"}}, %%nitrogen 框架依赖项目 {simple_bridge, "1.2.*", {git, "git://github.com/nitrogen/simple_bridge.git", "HEAD"}}, %%nitrogen 框架依赖项目 {sync, "0.1.*", {git, "git://github.com/rustyio/sync.git", "HEAD"}} %%nitrogen 框架依赖项目 ]}.
增加
Makefile
文件, 添加如下内容(如果编译有问题,把前面的空格重新打一遍):VERSION=0.1.0 #首先定义版本 .PHONY: deps doc # 默认为获取依赖 OTP 项目,然后进行编译 all: deps compile help: @echo @echo "Usage: " @echo "./make {compile|clean}" @echo @echo "./make {rel|package}" @echo @echo #编译相关项目,在编译之前先查看依赖项目是否已经存在 compile:deps ./rebar compile #获取 OTP 项目 deps: ./rebar get-deps clean: ./rebar clean #清除依赖项目 distclean: clean ./rebar delete-deps #测试所有项目 test: ./rebar eunit dialyzer: compile @dialyzer -Wno_return -c ebin doc : @./rebar doc skip_deps=true * 编译项目(编译的过程会把信赖项目加载进来):: make
使用 nitrogen 做一个网站¶
首先创建基本目录结构(框架根目录):
mkdir site; cd site mkdir ebin;mkdir src;mkdir include;mkdir static;mkdir templates使用 rebar 创建一个 otp 项目:
../rebar create-app appid=[appname]修改文件
appname_sup.erl
:
增加对
wf.hrl
文件的包含:-include_lib("nitrogen_core/include/wf.hrl").%%包含 wf.hrl 文件增加一个循环接收请求消息的函数:
%% 循环接收消息的函数 loop(Req) -> {ok, DocRoot} = application:get_env(mochiweb, document_root), RequestBridge = simple_bridge:make_request(mochiweb_request_bridge, {Req, DocRoot}), ResponseBridge = simple_bridge:make_response(mochiweb_response_bridge, {Req, DocRoot}), nitrogen:init_request(RequestBridge, ResponseBridge), nitrogen:run().导出 loop 函数:
-export ([loop/1]).%%导出 loop 函数修改
init/1
函数为:init([]) -> %% Start the Process Registry... application:start(nprocreg), %% Start Mochiweb... application:load(mochiweb), {ok, BindAddress} = application:get_env(mochiweb, bind_address), {ok, Port} = application:get_env(mochiweb, port), {ok, ServerName} = application:get_env(mochiweb, server_name), {ok, DocRoot} = application:get_env(mochiweb, document_root), io:format("Starting Mochiweb Server (~s) on ~s:~p, root: '~s'~n", [ServerName, BindAddress, Port, DocRoot]), % Start Mochiweb... Options = [ {name, ServerName}, {ip, BindAddress}, {port, Port}, {loop, fun [appname]_sup:loop/1} ], mochiweb_http:start(Options), {ok, { {one_for_one, 5, 10}, []} }.增加一个 index.erl 文件,内容如下:
-module (index). -compile(export_all). -include_lib("nitrogen_core/include/wf.hrl"). main() -> body(). title() -> "Hello,Welcome to My World". body() -> #container_12 { body=[#grid_8 { alpha=true, prefix=2, suffix=2, omega=true,body=inner_body() }]}. inner_body() -> [ #h1 { text="Hello,Welcome to Nitrogen World" }, #p{}, "Run <b>./bin/dev help</b> to see some useful developer commands." ].修改 rebar.config 文件, 使之可以编译 site 目录:
{sub_dirs, ["rel","site"]}. %%需要编译的子目录增加一个 mochiweb 配置文件:
etc/mochiweb.config
,内容如下:[{mochiweb, [ {bind_address, "0.0.0.0"}, {port, 8000}, {server_name, [appname]}, {document_root, "./site/static"} ]}].在项目根目录增加文件:
start.sh
文件,并执行命令chmod +x start.sh
,内容如下:#!/bin/sh cd `dirname $0` exec erl -name 'appname@127.0.0.1' -pa $PWD/site/ebin -pa $PWD/apps/*/ebin \ $PWD/deps/*/ebin -boot start_sasl -config "etc/mochiweb.config" \ -eval "application:load(mochiweb)" -eval "application:start([appname])"启动应用:
./start.sh打开浏览器,输入:http://127.0.0.1:8000, 在浏览器中显示:
Hello,Welcome to nitrogen world -:).
工程项目发布¶
说明
rebar 工具的打包功能使用的 erlang 类库是 reltool,所以发布版本之前需要首先在
rel
目录新建一个reltool.config
, 这个文件很复杂,幸运的是 rebar 工具提供生成这个文件的功能
使用 rebar 工具生成版本发布配置文件:
cd rel ../rebar create-node nodeid=[appname]命令执行后生成如下文件(重要文件
reltool.config
):|-- files | |-- app.config | |-- erl | |-- [appname] | |-- nodetool | `-- vm.args `-- reltool.config注:在
reltool.config
文件中, overlay标签 十分重要,除了涉及到相关 erlang OTP 项目类库文件外,在一个发布版本中还需要有一些提交设定的参数以及静态文件,例如 css 文件等
- 对文件
reltool.config
进行修改:
增加erlang格式的头:
%% -*- mode: erlang -*-修改配置节:
{lib_dirs, []} => {lib_dirs, ["../deps","../apps","../site"]}修改 rel 版本号:
{rel, "[appname]", "1" ... => {rel, "[appname]", "0.1.0" ...修改包含的应用列表(举例如下[根据实际情况改变]):
{rel, "[appname]", "0.1.0", [ kernel, stdlib, sasl, inets, crypto, runtime_tools, mnesia, os_mon ]},要对包含的 OTP 进行打包压缩,建议加上这个选项:
{excl_sys_filters, ["^bin/.*", "^erts.*/bin/(dialyzer|typer)"]}, {excl_archive_filters, [".*"]},%%(这行是增加的) 不要对包含的OTP进行打包压缩,建议加上这个选项 {app, sasl, [{incl_cond, include}]}打包如下otp项目:
{app, sasl, [{incl_cond, include}]}, {app, eunit, [{incl_cond, include}]},%%建议打包上 eunit OTP 项目 {app, os_mon, [{incl_cond, include}]},%% 打包 os_mon OTP 项目 {app, mochiweb, [{incl_cond, include}]}, %% 打包 mochiweb OTP 项目 {app, nitrogen_core, [{incl_cond, include}]}, %% 打包 nitrogen_core OTP 项目 {app, nprocreg, [{incl_cond, include}]}, %% 打包 nprocreg OTP 项目 {app, simple_bridge, [{incl_cond, include}]}, %% 打包 simple_bridge OTP 项目 {app, sync, [{incl_cond, include}]} %% 打包 sync OTP 项目
- 在
rel
目录下增加overlay
目录, 并在其下创建子目录:
执行如下命令:
mkdir overlay; cd overlay mkdir common erts; mkdir -p mochiweb/etc cd common; mkdir bin etc site; mkdir -p log/sasl cd site; mkdir ebin include src static templates cd ../../.. tree执行后目录
rel
目录下结构为:. ├── files │ ├── app.config │ ├── erl │ ├── [appname] │ ├── nodetool │ └── vm.args ├── overlay │ ├── common │ │ ├── bin │ │ ├── etc │ │ ├── log │ │ │ └── sasl │ │ └── site │ │ ├── ebin │ │ ├── include │ │ ├── src │ │ ├── static │ │ └── templates │ ├── erts │ └── mochiweb │ └── etc └── reltool.config 16 directories, 6 files将 files 目录下的文件复制如下目录中:
cp files/app.config overlay/common/etc; cp files/vm.args overlay/common/etc cp files/erl overlay/erts; cp files/nodetool overlay/erts cp files/[appname] overlay/common/bin cp ../etc/mochiweb.config overlay/mochiweb/etc这时的目录结构树如下:
├── files │ ├── app.config │ ├── erl │ ├── [appname] │ ├── nodetool │ └── vm.args ├── overlay │ ├── common │ │ ├── bin │ │ │ └── [appname] │ │ ├── etc │ │ │ ├── app.config │ │ │ └── vm.args │ │ ├── log │ │ │ └── sasl │ │ └── site │ │ ├── ebin │ │ ├── include │ │ ├── src │ │ ├── static │ │ └── templates │ ├── erts │ │ ├── erl │ │ └── nodetool │ └── mochiweb │ └── etc │ └── mochiweb.config └── reltool.config 16 directories, 12 files修改 reltool.config 文件中 overlay配置段 ( 把overlay配置段删除 ):
{overlay, [ %% {mkdir, "fold/subfold"} %创建目录 %% Copy common files... {copy, "./overlay/common/*"}, %% Copy start files... {copy, "./overlay/erts/*", "{{erts_vsn}}/bin"}, {copy, "../site/ebin", "site"}, {copy, "../site/include", "site"}, {copy, "../site/static", "site"}, {copy, "../site/templates", "site"}, %% Copy mochiweb config files... {copy, "overlay/mochiweb/*"} ]}.将 rebar 文件 复制到 rel 目录下:
cp ../rebar ./修改 overlay/common/bin/[appname] 文件,使之可以读取多个 config 文件:
找到:
CMD="$BINDIR/erlexec -boot $RUNNER_BASE_DIR/releases/$APP_VSN/$BOOTFILE \ -embedded -config $RUNNER_ETC_DIR/app.config -args_file \ $RUNNER_ETC_DIR/vm.args -- ${1+"$@"}"修改为:
CONFIG="" for f in `ls $RUNNER_ETC_DIR/*.config` do CONFIG="$CONFIG -config $f"; done CMD="$BINDIR/erlexec -boot $RUNNER_BASE_DIR/releases/$APP_VSN/$BOOTFILE \ -embedded $CONFIG -args_file $RUNNER_ETC_DIR/vm.args -- ${1+"$@"}"修改 overlay/common/etc/vm.args 文件, 增加 如下配置项:
## Include .beam files for site. -pa ./site/ebin ## Include .beam files for dependencies. -pa ./deps/*/ebin ## Include .beam files for dependencies. -pa ./apps/*/ebin ## Run code at startup. -eval "application:start([appname])"生成版本:
./rebar generate启动发布的工程项目:
./[appname]/bin/[appname] console在浏览器中输入网址:http://127.0.0.1:8000,显示:
Hello,Welcome to nitrogen world -:).