真正项目开发使用还是需要自己写app程序,把esockd作为第三方的依赖库使用。
一、前期准备
1、新建文件夹myerlserver
然后输入终端命令
./rebar create-app appid=myerlserver
./rebar create template=simplesrv srvid=start_server
./rebar create template=simplesrv srvid=echo_server
手动新建rel文件夹
../rebar create-node nodeid=myerlserver
2、rebar.config文件修改为:
{deps, [
{esockd, ".*", {git, "git://github.com/emqtt/esockd.git", {tag, "v4.2"}}}
]}.
{sub_dirs, ["rel"]}.
其中“v4.2”来自https://github.com/emqtt/esockd里面定义的Tags字符串,可以是“4.0”,“v4.1”和“v4.2”等等关键字。
3、reltool.config文件修改为:
%% -*- mode: erlang -*-
%% ex: ft=erlang
{sys, [
{lib_dirs, ["../deps"]},
{erts, [{mod_cond, derived}, {app_file, strip}]},
{app_file, strip},
{rel, "myerlserver", "1",
[
kernel,
stdlib,
sasl,
goldrush,
lager,
gen_logger,
esockd,
myerlserver
]},
{rel, "start_clean", "",
[
kernel,
stdlib
]},
{boot_rel, "myerlserver"},
{profile, embedded},
{incl_cond, derived},
{excl_archive_filters, [".*"]}, %% Do not archive built libs
{excl_sys_filters, ["^bin/(?!start_clean.boot)",
"^erts.*/bin/(dialyzer|typer)",
"^erts.*/(doc|info|include|lib|man|src)"]},
{excl_app_filters, ["\.gitignore"]},
{app, myerlserver, [{mod_cond, app}, {incl_cond, include}, {lib_dir, ".."}]}
]}.
{target_dir, "myerlserver"}.
{overlay, [
{mkdir, "log/sasl"},
{copy, "files/erl", "\{\{erts_vsn\}\}/bin/erl"},
{copy, "files/nodetool", "releases/\{\{rel_vsn\}\}/nodetool"},
{copy, "myerlserver/bin/start_clean.boot",
"\{\{erts_vsn\}\}/bin/start_clean.boot"},
{copy, "files/myerlserver", "bin/myerlserver"},
{copy, "files/myerlserver.cmd", "bin/myerlserver.cmd"},
{copy, "files/start_erl.cmd", "bin/start_erl.cmd"},
%% Following line may be safely removed in new projects
{copy, "files/install_upgrade.escript", "bin/install_upgrade.escript"},
{copy, "files/sys.config", "releases/\{\{rel_vsn\}\}/sys.config"},
{copy, "files/vm.args", "releases/\{\{rel_vsn\}\}/vm.args"}
]}.
./rebar get-deps
二、源文件
myerlserver_sup.erl
-module(myerlserver_sup).
-behaviour(supervisor).
%% API
-export([start_link/0]).
%% Supervisor callbacks
-export([init/1]).
%% Helper macro for declaring children of supervisor
-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).
%% ===================================================================
%% API functions
%% ===================================================================
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
%% ===================================================================
%% Supervisor callbacks
%% ===================================================================
init([]) ->
{ok, { {one_for_one, 5, 10}, [?CHILD(start_server, worker)]} }.
start_server.erl
-module(start_server).
-behaviour(gen_server).
-define(SERVER, ?MODULE).
%% ------------------------------------------------------------------
%% API Function Exports
%% ------------------------------------------------------------------
-export([start_link/0]).
%% ------------------------------------------------------------------
%% gen_server Function Exports
%% ------------------------------------------------------------------
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
%% ------------------------------------------------------------------
%% API Function Definitions
%% ------------------------------------------------------------------
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
%% ------------------------------------------------------------------
%% gen_server Function Definitions
%% ------------------------------------------------------------------
init(Args) ->
echo_server:start(),
{ok, Args}.
handle_call(_Request, _From, State) ->
{reply, ok, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%% ------------------------------------------------------------------
%% Internal Function Definitions
%% ------------------------------------------------------------------
echo_server.erl
-module(echo_server).
-include("../deps/esockd/include/esockd.hrl").
-behaviour(gen_server).
%% start
-export([start/0, start/1]).
-export([start_link/1]).
%% gen_server Function Exports
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-record(state, {conn}).
-define(TCP_OPTIONS, [
binary,
{packet, raw},
{buffer, 1024},
{reuseaddr, true},
{backlog, 512},
{nodelay, false}]).
-define(TCP_PORT, 5000).
%% API
start() ->
start(?TCP_PORT).
start([Port]) when is_atom(Port) ->
start(list_to_integer(atom_to_list(Port)));
start(Port) when is_integer(Port) ->
%% application:start(sasl),
%% ok = esockd:start(),
Access = application:get_env(esockd, access, [{allow, all}]),
SockOpts = [{access, Access},
{acceptors, 32},
{max_clients, 1000000},
{sockopts, ?TCP_OPTIONS}],
MFArgs = {?MODULE, start_link, []},
esockd:open(echo, Port, SockOpts, MFArgs).
%% eSockd Callbacks
start_link(Conn) ->
{ok, proc_lib:spawn_link(?MODULE, init, [Conn])}.
init(Conn) ->
{ok, Conn1} = Conn:wait(),
Conn1:setopts([{active, once}]),
gen_server:enter_loop(?MODULE, [], #state{conn = Conn1}).
handle_call(_Request, _From, State) ->
{reply, ok, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info({tcp, Sock, Data}, State = #state{conn = ?ESOCK(Sock) = Conn}) ->
{ok, PeerName} = Conn:peername(),
io:format("~s - ~s~n", [esockd_net:format(peername, PeerName), Data]),
Conn:send(Data),
Conn:setopts([{active, once}]),
{noreply, State};
handle_info({tcp_error, Sock, Reason}, State = #state{conn = ?ESOCK(Sock)}) ->
io:format("tcp_error: ~s~n", [Reason]),
{stop, {shutdown, {tcp_error, Reason}}, State};
handle_info({tcp_closed, Sock}, State = #state{conn = ?ESOCK(Sock)}) ->
io:format("tcp_closed~n"),
{stop, normal, State};
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
Gen_server行为分析与实践
enter_loop(Module, Options, State)
enter_loop(Module, Options, State, ServerName)
enter_loop(Module, Options, State, Timeout)
enter_loop(Module, Options, State, ServerName, Timeout)
Options = [Option]
Option = {debug,Dbgs}
Dbgs = [trace | log | statistics | {log_to_file,FileName} | {install,{FuncState}}]
ServerName = {local,Name} | {global,GlobalName} | {via,Module,ViaName}
Timeout = int() | infinity
该函数的功能是让一个已经存在的进程成为gen_server进程,通过请求进程让它进入gen_server循环成为gen_server进程,该普通进程的创建必须通过proc_lib模块(实例3.4)。
这个函数更加的有用对于要进行比gen_server还要复杂的初始化时。
Module, Options and ServerName 的意义与gen_server:start_link一样.然而,如果Servername被指定这个进程被调用前一定要相应的先注册。
State and Timeout与回调函数的Module:init一样。
Failure: 如果请求进程不是一个由proc_lib创建的进程或使用了没有注册的ServerName.
Module:init(Args) -> Result
Types:
Result = {ok,State} | {ok,State,Timeout} | {ok,State,hibernate}| {stop,Reason} | ignore
State = term()
Timeout = int()>=0 | infinity
通过start或start_link初始化一个新的进程。
若初始化成功返回{ok,State} | {ok,State,Timeout} | {ok,State,hibernate}。State是gen_server的内部状态;Timeout指进程初始化后等待接受请求的时间限制,超过时间限制将向handle_info发送请求为timeout的信息,默认是infinity;hibernate指可通过调用proc_lib:hibernate/3使进程进入冬眠状态从而进行GC,当有消息请求该进程,处理该请求,然后冬眠并进行GC.注意应该小心使用'hibernate',主要针对空闲时间比较长的进程,因为至少有两个GC回收器,对于请求比较平凡的进程,资源的消耗高。
如果初始化失败将返回{stop,Reason} | ignore
注解:并不是简单的通过spawn在子进程启动函数中启动一个进程然后返回{ok, Pid}就可以让子进程拥有出错自动重启的功能。实际上,需要使用proc_lib:spawn_link或者proc_lib:start_link启动子进程,才能在子进程出错退出时让supervisor自动重启它。proc_lib:start_link和proc_lib:spawn_link的不同之处在于,前者是同步创建子进程,后者是异步创建子进程,proc_lib:start_link调用后会阻塞,直到子进程初始化完毕,调用proc_lib:init_ack后才返回。而proc_lib:spawn_link一调用就会立即返回子进程ID。
三、编译和发布
./rebar compile
./rebar generate
ulimit -n 100000
./myerlserver console
其实也可以把几个命令联合一起执行,例如:
./rebar clean compile generate
./rebar compile generate
本文rebar的使用参考了文章:用 rebar 来构建、编译、测试、发布 Erlang 应用程序
完整的工程源码请访问:http://download.csdn.net/download/libaineu2004/10048006