Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

* add config example in mcache.app.

* refactor handling of pendings in mcache_client.
* fix some incorrect external module dependencies.
  • Loading branch information...
commit f21345969377db78eba5c606f90f4b26a5ec03a2 1 parent 406e796
@echou authored
View
1  .gitignore
@@ -1,3 +1,4 @@
+*.dump
ebin
priv
lib/*/obj
View
45 lib/mcache/src/mcache.app.src
@@ -1,12 +1,41 @@
% vim:syn=erlang
{application, mcache,
- [{description, "memcached client application"},
- {vsn, "%VSN%"},
- {modules, [%MODULES%]},
- {registered, []},
- {mod, {mcache_app, []}},
- {env, []},
- {applications, [kernel,stdlib]}
- ]
+ [ {description, "memcached client application"},
+ {vsn, "%VSN%"},
+ {modules, [ %MODULES% ]},
+ {registered, []},
+ {mod, {mcache_app, []}},
+ {applications, [kernel,stdlib]},
+ {env,
+ [ {pools, [ [ {name, generic},
+ {connection_count, 10},
+ {servers,
+ [
+ {"1.0.0.1", 256},
+ {"1.0.0.2", 256},
+ {"1.0.0.3", 256}
+ ]
+ }
+ ],
+ [ {name, foobar},
+ {connection_count, 20},
+ {servers,
+ [
+ {"1.0.0.1", 256},
+ {"1.1.0.1", 512},
+ {"1.1.0.2", 256}
+ ]
+ }
+ ]
+ ]
+ },
+ {expires,
+ [ {example.foo, {generic, 300}},
+ {example.bar, {generic, {10, hours}}}
+ ]
+ }
+ ]
+ }
+ ]
}.
View
6 lib/mcache/src/mcache_app.erl
@@ -48,12 +48,12 @@ init([]) ->
% supervisor local functions
specs(Specs) ->
lists:foldl(fun({Module, Count}, Acc) ->
- Acc ++ app_util:sup_child_spec(Module, fun one_spec/2, Count)
+ Acc ++ mcache_util:sup_child_spec(Module, fun one_spec/2, Count)
end, [], Specs).
one_spec(mcache_config, Id) ->
- PoolsConfig = app_util:get_app_env(pools, []),
- ExpiresConfig = app_util:get_app_env(expires, []),
+ PoolsConfig = mcache_util:get_app_env(pools, []),
+ ExpiresConfig = mcache_util:get_app_env(expires, []),
{Id, {mcache_config, start_link, [ {PoolsConfig, ExpiresConfig} ]}, permanent, 2000, worker, []};
one_spec(mcache_client_sup, Id) ->
{Id, {mcache_client_sup, start_link, []}, permanent, infinity, supervisor, []};
View
238 lib/mcache/src/mcache_client.erl
@@ -7,7 +7,7 @@
-export([start_link/1]).
-export([init/1,handle_call/3,handle_cast/2,handle_info/2,code_change/3,terminate/2]).
--export([mc_get/2, ab_get/2, ab_mget/2, mc_set/5, ab_set/5, mc_delete/2, ab_delete/2]).
+-export([mc_get/2, ab_get/2, mc_mget/2, ab_mget/2, mc_set/5, ab_set/5, mc_delete/2, ab_delete/2]).
-include_lib("kernel/src/inet_int.hrl").
@@ -16,10 +16,14 @@
-define(CONNECT_TIMEOUT, 1000).
-define(RECONNECT_AFTER, 2000).
--record(state, {addr, seq=0, buffer, pendings, sock=not_connected, connecting, mgets}).
+-record(state, {addr, seq=0, buffer, pendings, sock=not_connected, connecting}).
+
-record(req, {opcode=get, data_type=0, cas=0, extra= <<>>, key= <<>>, body= <<>>}).
-record(resp, {seq, status, opcode, data_type=0, cas=0, extra= <<>>, key= <<>>, body= <<>>}).
+-record(pending, {from, time}).
+-record(mget_pending, {from, time, items}).
+
start_link({Host, Port}) ->
gen_server:start_link(?MODULE, [{Host, Port}], []).
@@ -36,12 +40,11 @@ init([{Host, Port}=Server]) ->
seq=0,
buffer= <<>>,
pendings=dict:new(),
- mgets=dict:new(),
connecting={Sock, Ref}}}.
% HANDLE_CALL
-handle_call({mc, Req}, _From, #state{sock=not_connected}=State) ->
+handle_call({mc, _Req}, _From, #state{sock=not_connected}=State) ->
{reply, {error, not_connected}, State};
handle_call({mc, Req}, From, State) ->
@@ -63,27 +66,27 @@ handle_call({mc_ab, Req}, From, State) ->
handle_call(_Req, _From, State) ->
{noreply, State}.
-send_wrapper({mget, Keys}=Req, From, #state{sock=Sock, seq=Seq, pendings=Ps, mgets=Ms}=State) ->
+send_wrapper({mget, Keys}=Req, From, #state{sock=Sock, seq=Seq, pendings=Pendings}=State) ->
case (catch send_req(Sock, Seq, Req)) of
true ->
- ItemDict = lists:foldl(fun(K, Acc) -> dict:store(K, undefined, Acc) end, dict:new(), Keys),
- {ok, State#state{seq=Seq+1,
- pendings=dict:store(Seq, {From, erlang:now()}, Ps),
- mgets=dict:store(Seq, ItemDict, Ms)}};
+ Items = lists:foldl(fun(K, Dict) -> dict:store(K, undefined, Dict) end, dict:new(), Keys),
+ P = #mget_pending{from=From, time=erlang:now(), items=Items},
+ {ok, State#state{seq=Seq+1, pendings=dict:store(Seq,P,Pendings)}};
_ ->
{not_sent, State}
end;
-send_wrapper(Req, From, #state{sock=Sock, seq=Seq, pendings=Ps}=State) ->
+send_wrapper(Req, From, #state{sock=Sock, seq=Seq, pendings=Pendings}=State) ->
case (catch send_req(Sock, Seq, Req)) of
true ->
- {ok, State#state{seq=Seq+1, pendings=dict:store(Seq, {From, erlang:now()}, Ps)}};
+ P = #pending{from=From, time=erlang:now()},
+ {ok, State#state{seq=Seq+1, pendings=dict:store(Seq,P,Pendings)}};
_ ->
{not_sent, State}
end.
% HANDLE_CAST
-handle_cast({mc, Req}, #state{sock=not_connected}=State) ->
+handle_cast({mc, _Req}, #state{sock=not_connected}=State) ->
{noreply, State};
handle_cast({mc, Req}, #state{sock=Sock, seq=Seq}=State) ->
@@ -105,30 +108,27 @@ handle_info({inet_async, Sock, Ref, Status}, #state{connecting={Sock, Ref}}=Stat
ok ->
{noreply, State#state{sock=Sock, connecting=undefined}};
_ ->
- gen_tcp:close(Sock),
- erlang:send_after(?RECONNECT_AFTER, self(), reconnect),
- {noreply, State#state{sock=not_connected, connecting=undefined}}
+ {noreply, socket_close(State), hibernate}
end;
-handle_info({tcp_closed, Sock}, #state{sock=Sock, connecting=undefined}=State) ->
- erlang:send_after(?RECONNECT_AFTER, self(), reconnect),
- {noreply, State#state{sock=not_connected, connecting=undefined,buffer= <<>>}, hibernate};
+handle_info({tcp_closed, Sock}, #state{sock=Sock}=State) ->
+ {noreply, socket_close(State), hibernate};
handle_info(reconnect, #state{addr={Host, Port}}=State) ->
{ok, Sock, Ref} = async_connect(Host, Port, ?SOCK_OPTS, ?CONNECT_TIMEOUT),
{noreply, State#state{sock=not_connected, connecting={Sock, Ref}}, hibernate};
-handle_info({tcp, Sock, Data}, #state{sock=Sock,buffer=Buf,pendings=Pendings, mgets=Mgets}=State) ->
- {NewBuf, Resps} = do_parse(<<Buf/binary,Data/binary>>, []),
+handle_info({tcp, Sock, Data}, #state{sock=Sock,buffer=Buf,pendings=Pendings}=State) ->
+ {NewBuf, Resps} = do_parse_packet(<<Buf/binary,Data/binary>>, []),
case Resps of
[] ->
{noreply, State#state{buffer=NewBuf}};
[_|_] ->
- {NewPendings, NewMgets} = lists:foldl(fun handle_resp/2, {Pendings, Mgets}, Resps),
- {noreply, State#state{buffer=NewBuf,pendings=NewPendings,mgets=NewMgets}}
+ NewPendings = lists:foldl(fun handle_one_resp/2, Pendings, Resps),
+ {noreply, State#state{buffer=NewBuf,pendings=NewPendings}}
end;
-handle_info(Msg, State) ->
+handle_info(_Msg, State) ->
%error_logger:info_msg("handle_info: ~p~n", [Msg]),
{noreply, State}.
@@ -137,80 +137,69 @@ handle_info(Msg, State) ->
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
-terminate(_Reason, State) ->
+terminate(_Reason, _State) ->
ok.
+socket_close(#state{sock=not_connected, pendings=Pendings}=State) ->
+ flush_pendings(Pendings, {error, closed}),
+ erlang:send_after(?RECONNECT_AFTER, self(), reconnect),
+ State#state{connecting=undefined, pendings=dict:new(), buffer= <<>>};
+socket_close(#state{sock=Sock}=State) when is_port(Sock) ->
+ catch gen_tcp:close(Sock),
+ socket_close(State#state{sock=not_connected}).
% Handles mget sequences (getkq, getkq, ..., noop)
-handle_resp(#resp{opcode=getkq, seq=Seq}=Resp, {Ps, Ms}) ->
- NewMs = case dict:find(Seq, Ms) of
- error -> % not found
- Ms;
- {ok, ItemDict} ->
- case process_resp(Resp) of
- {ok, {Key, Value}} ->
- dict:store(Seq, dict:store(Key, Value, ItemDict), Ms);
- _ ->
- Ms
- end
- end,
-
- {Ps, NewMs};
-
-handle_resp(#resp{opcode=noop,seq=Seq}=Resp, {Ps, Ms}) ->
- case dict:find(Seq, Ms) of
- error ->
- Ms;
- {ok, ItemDict} ->
- case dict:find(Seq, Ps) of
- {ok, {From, _Time}} ->
- gen_server:reply(From, {mget, ItemDict});
- error ->
- ok
- end
+handle_one_resp(#resp{opcode=getkq, seq=Seq}=Resp, Pendings) ->
+ case dict:find(Seq, Pendings) of
+ {ok, #mget_pending{items=Items}=P} ->
+ case parse_resp(Resp) of
+ {ok, {Key, Value}} ->
+ NewItems = dict:store(Key, Value, Items),
+ dict:store(Seq, P#mget_pending{items=NewItems}, Pendings);
+ _ ->
+ Pendings
+ end;
+ _ -> % normal pendings
+ Pendings
+ end;
+
+handle_one_resp(#resp{opcode=noop,seq=Seq}, Pendings) ->
+ case dict:find(Seq, Pendings) of
+ {ok, #mget_pending{from=From, items=Items}} ->
+ gen_server:reply(From, {mget, Items});
+ _ ->
+ ok
end,
+ dict:erase(Seq, Pendings);
- {dict:erase(Seq, Ps), dict:erase(Seq, Ms)};
-handle_resp(#resp{seq=Seq}=Resp, {Ps, Ms}) ->
- case dict:find(Seq, Ps) of
- error ->
- ignore;
- {ok, {From, _Time}} ->
- Result = case (catch process_resp(Resp)) of
+handle_one_resp(#resp{seq=Seq}=Resp, Pendings) ->
+ case dict:find(Seq, Pendings) of
+ {ok, #pending{from=From}} ->
+ Result = case (catch parse_resp(Resp)) of
{ok, Any} -> Any;
{'EXIT', Reason} -> {error, Reason};
Status -> Status
end,
- gen_server:reply(From, Result)
+ gen_server:reply(From, Result);
+ _ ->
+ ok
end,
- {dict:erase(Seq, Ps), Ms}.
-
-% PUBLIC APIS
-
-mc_get(Server, Key) ->
- gen_server:call(get_client_pid(Server), {mc, {getk, Key}}).
-
-ab_get(Server, Key) ->
- gen_server:call(get_client_pid(Server), {mc_ab, {getk, Key}}).
-
-mc_mget(Server, Keys) ->
- gen_server:call(get_client_pid(Server), {mc, {mget, Keys}}).
-
-ab_mget(Server, Keys) ->
- gen_server:call(get_client_pid(Server), {mc_ab, {mget, Keys}}).
-
-mc_set(Server, Key, Value, Flags, Expiry) ->
- gen_server:call(get_client_pid(Server), {mc, {set, Key, Value, Flags, Expiry}}).
-
-ab_set(Server, Key, Value, Flags, Expiry) ->
- gen_server:cast(get_client_pid(Server), {mc, {set, Key, Value, Flags, Expiry}}).
-
-mc_delete(Server, Key) ->
- gen_server:call(get_client_pid(Server), {mc, {delete, Key}}).
-
-ab_delete(Server, Key) ->
- gen_server:cast(get_client_pid(Server), {mc, {delete, Key}}).
+ dict:erase(Seq, Pendings).
+
+flush_pendings(Pendings, Result) ->
+ dict:fold(
+ fun(_Seq, #pending{from=From}, Any) ->
+ gen_server:reply(From, Result),
+ Any;
+ (_Seq, #mget_pending{from=From, items=Items}, Any) ->
+ gen_server:reply(From, {mget, Items}),
+ Any;
+ (_, _, Any) ->
+ Any
+ end,
+ nil,
+ Pendings).
% internal apis
@@ -235,12 +224,14 @@ async_connect({A,B,C,D}=_Addr, Port, Opts, Time) ->
Error ->
Error
end;
- {ok, _} = Any ->
+ {ok, _} ->
exit(badarg)
end.
-do_parse(<<16#81, Opcode, KeyLen:16, ExtraLen, DataType, Status:16, TotalBodyLen:32, Seq:32, CAS:64, TotalBody:TotalBodyLen/binary, Rest/binary>>, Acc) ->
- <<Extra:ExtraLen/binary, Key:KeyLen/binary, Body/binary>> = TotalBody,
+do_parse_packet(<<16#81, Opcode, KeyLen:16, ExtraLen, DataType, Status:16, TotalBodyLen:32, Seq:32, CAS:64,
+ Extra:ExtraLen/binary, Key:KeyLen/binary, Rest/binary>>, Acc) ->
+ BodyLen = TotalBodyLen - ExtraLen - KeyLen,
+ <<Body:BodyLen/binary, Rest1/binary>> = Rest,
Resp= #resp{seq=Seq,
opcode=mcache_proto:opcode(Opcode),
status=mcache_proto:status(Status),
@@ -249,8 +240,8 @@ do_parse(<<16#81, Opcode, KeyLen:16, ExtraLen, DataType, Status:16, TotalBodyLen
extra=Extra,
key=Key,
body=Body},
- do_parse(Rest, [Resp|Acc]);
-do_parse(Data, Acc) ->
+ do_parse_packet(Rest1, [Resp|Acc]);
+do_parse_packet(Data, Acc) ->
{Data, lists:reverse(Acc)}.
send_req(Sock, Seq, {mget, Keys}) ->
@@ -271,8 +262,8 @@ send_req(Sock, Seq, {flush, Expiry}) ->
send_req(Sock, Seq, flush) ->
do_send_req(Sock, Seq, #req{opcode=flush});
-send_req(Sock, Seq, {Op, Key}) when Op=:=get;Op=:=getk;Op=:=getkq ->
- do_send_req(Sock, Seq, #req{opcode=Op, key=Key});
+send_req(Sock, Seq, {getk, Key}) ->
+ do_send_req(Sock, Seq, #req{opcode=getk, key=Key});
send_req(Sock, Seq, {Op, Key, Value, Flags, Expiry}) when Op=:=set;Op=:=add;Op=:=replace ->
do_send_req(Sock, Seq, #req{opcode=Op, key=Key, body=Value, extra= <<Flags:32, Expiry:32>>});
@@ -282,10 +273,11 @@ send_req(Sock, Seq, {Op, Key, Delta, Initial, Expiry}) when Op=:=incr;Op=:=decr
-process_resp(#resp{opcode=noop}) ->
+parse_resp(#resp{opcode=noop}) ->
{ok, noop};
-process_resp(#resp{opcode=get, status=Status, extra=Extra, body=Value}) ->
+% not used !!
+parse_resp(#resp{opcode=get, status=Status, extra=Extra, body=Value}) ->
case Status of
ok ->
<<Flags:32>> = Extra,
@@ -293,35 +285,38 @@ process_resp(#resp{opcode=get, status=Status, extra=Extra, body=Value}) ->
_ -> {ok, undefined}
end;
-process_resp(#resp{opcode=getk, status=Status, extra=Extra, key=Key, body=Value}) ->
+parse_resp(#resp{opcode=getk, status=Status, extra=Extra, key=Key, body=Value}) ->
case Status of
ok ->
<<Flags:32>> = Extra,
{ok, {Key, {Value, Flags}}};
- _ -> {ok, {Key, undefined}}
+ _ ->
+ {ok, {Key, undefined}}
end;
-process_resp(#resp{opcode=getkq, status=Status, extra=Extra, key=Key, body=Value}) ->
+parse_resp(#resp{opcode=getkq, status=Status, extra=Extra, key=Key, body=Value}) ->
case Status of
ok ->
<<Flags:32>> = Extra,
{ok, {Key, {Value, Flags}}};
- _ -> {ok, {Key, undefined}}
+ _ ->
+ {ok, {Key, undefined}}
end;
-process_resp(#resp{opcode=incr, status=ok, body= <<Value:64>>}) ->
+parse_resp(#resp{opcode=incr, status=ok, body= <<Value:64>>}) ->
{ok, Value};
-process_resp(#resp{opcode=decr, status=ok, body= <<Value:64>>}) ->
+parse_resp(#resp{opcode=decr, status=ok, body= <<Value:64>>}) ->
{ok, Value};
-process_resp(#resp{opcode=version, status=ok, body=Body}) ->
+parse_resp(#resp{opcode=version, status=ok, body=Body}) ->
{ok, binary_to_list(Body)};
-process_resp(#resp{opcode=_, status=Status}) ->
+parse_resp(#resp{opcode=_, status=Status}) ->
Status.
-gen_req(Seq, #req{opcode=Opcode,
+
+gen_one_req(Seq, #req{opcode=Opcode,
data_type=DataType,
cas=CAS,
extra=Extra,
@@ -344,9 +339,36 @@ gen_req(Seq, #req{opcode=Opcode,
Key,
Body].
-do_send_req(Sock, Seq, [_,_|_]=Reqs) ->
- erlang:port_command(Sock, [ gen_req(Seq, Req) || Req <- Reqs ]);
-do_send_req(Sock, Seq, [Req]) ->
- erlang:port_command(Sock, gen_req(Seq, Req));
+do_send_req(Sock, Seq, [_|_]=Reqs) ->
+ erlang:port_command(Sock, [ gen_one_req(Seq, Req) || Req <- Reqs ]);
do_send_req(Sock, Seq, Req) ->
- erlang:port_command(Sock, gen_req(Seq, Req)).
+ erlang:port_command(Sock, gen_one_req(Seq, Req)).
+
+
+% =======================================================
+% PUBLIC API
+% =======================================================
+
+mc_get(Server, Key) ->
+ gen_server:call(get_client_pid(Server), {mc, {getk, Key}}).
+
+ab_get(Server, Key) ->
+ gen_server:call(get_client_pid(Server), {mc_ab, {getk, Key}}).
+
+mc_mget(Server, Keys) ->
+ gen_server:call(get_client_pid(Server), {mc, {mget, Keys}}).
+
+ab_mget(Server, Keys) ->
+ gen_server:call(get_client_pid(Server), {mc_ab, {mget, Keys}}).
+
+mc_set(Server, Key, Value, Flags, Expiry) ->
+ gen_server:call(get_client_pid(Server), {mc, {set, Key, Value, Flags, Expiry}}).
+
+ab_set(Server, Key, Value, Flags, Expiry) ->
+ gen_server:cast(get_client_pid(Server), {mc, {set, Key, Value, Flags, Expiry}}).
+
+mc_delete(Server, Key) ->
+ gen_server:call(get_client_pid(Server), {mc, {delete, Key}}).
+
+ab_delete(Server, Key) ->
+ gen_server:cast(get_client_pid(Server), {mc, {delete, Key}}).
View
42 lib/mcache/src/mcache_config.erl
@@ -33,16 +33,14 @@ init({Pools, Expires}=Opts) ->
handle_call({update_expires, Info}, _From, #state{expires=OldExpires, initial_expires=InitialExpires}=State) ->
NewExpires = case Info of
restore ->
- % 恢复初始配置
+ % restore to initial config
InitialExpires;
{assign, Expires} ->
- % 设置新一套配置
+ % set a new set of config
Expires;
{delete, Class} ->
- % 删除某个Class的配置
proplists:delete(Class, OldExpires);
{set, Class, {PoolName, Expiry}} ->
- % 设置某个Class配置
Expires1 = proplists:delete(Class, OldExpires),
[{Class, {PoolName, Expiry}}|Expires1];
_ ->
@@ -113,9 +111,10 @@ parse_pools(Pools) ->
io:format("[MCACHE_CONFIG] Generating mcache_continuum module ...~n", []),
Continuums = lists:map(
fun(PoolConfig) ->
- Name = proplists:get_value(name, PoolConfig),
- Servers = proplists:get_value(servers, PoolConfig),
- {Name, Servers}
+ Name = proplists:get_value(name, PoolConfig, generic),
+ Servers = proplists:get_value(servers, PoolConfig, []),
+ Servers1 = [normalize_server(S) || S <- Servers],
+ {Name, Servers1}
end,
Pools),
{NewPools, _} = mcache_continuum_gen:gen(Continuums, gb_trees),
@@ -133,10 +132,35 @@ parse_pools(Pools) ->
{ok, PoolConfig} = dict:find(Name, PoolsDict),
ConnectionCount = proplists:get_value(connection_count, PoolConfig, 10),
lists:foreach(
- fun({Host,Port}=Addr) ->
- io:format("[MCACHE_CONFIG] Starting mcache clients (~p) to ~p ...~n", [ConnectionCount, Addr]),
+ fun({{A,B,C,D}=Host,Port}) ->
+ io:format("[MCACHE_CONFIG] Starting ~p mcache clients at [~p, ~p.~p.~p.~p:~p] ...~n", [ConnectionCount, Name, A,B,C,D,Port]),
mcache_client_sup:start_child(Name, {Host, Port}, ConnectionCount)
end,
Servers)
end,
NewPools).
+
+normalize_server({{_,_,_,_}=Addr, Port, Weight}) ->
+ {Addr, Port, Weight};
+normalize_server({Addr, Port, Weight}) when is_list(Addr) ->
+ {Addr1, _} = normalize_addr(Addr),
+ {Addr1, Port, Weight};
+normalize_server({Addr, Weight}) ->
+ {Addr1, Port1} = normalize_addr(Addr),
+ {Addr1, Port1, Weight}.
+
+normalize_addr(Addr) when is_list(Addr) ->
+ case string:tokens(Addr, ":") of
+ [IP] ->
+ {ok, Addr1} = inet:getaddr(IP, inet),
+ {Addr1, 11211};
+ [IP,PortStr|_] ->
+ {ok, Addr1} = inet:getaddr(IP, inet),
+ Port1 = case string:to_integer(PortStr) of
+ {Port, []} -> Port;
+ _ -> 11211
+ end,
+ {Addr1, Port1};
+ _ ->
+ {{127,0,0,1}, 11211}
+ end.
View
13 lib/mcache/src/mcache_test.erl
@@ -37,6 +37,19 @@ test_mget(P, R, memcached_api) ->
stresstest:start("test_mget", P, R, F).
+gen_continuum(Mode) ->
+ Servers = [
+ {{10,0,1,1}, 11211, 600},
+ {{10,0,1,2}, 11211, 300},
+ {{10,0,1,3}, 11211, 200},
+ {{10,0,1,4}, 11211, 350},
+ {{10,0,1,5}, 11211, 1000},
+ {{10,0,1,6}, 11211, 800},
+ {{10,0,1,7}, 11211, 950},
+ {{10,0,1,8}, 11211, 100}
+ ],
+ mcache_continuum_gen:gen([{generic, Servers},{foobar, Servers}], Mode).
+
validate_continuum(Mode) ->
Servers = [
View
27 lib/mcache/src/mcache_util.erl
@@ -4,7 +4,9 @@
%%%=========================================================================
-author('thijsterlouw@gmail.com').
--export([now/0, hash/2, floor/1, ceiling/1, get_app_env/2, make_atom_name/2, make_atom_name/3, reload_config/1]).
+-compile([inline, native, {hipe, o3}]).
+
+-export([now/0, hash/2, floor/1, ceiling/1, get_app_env/2, make_atom_name/2, make_atom_name/3, reload_config/1, sup_child_spec/3, sup_child_spec/2]).
-export([gb_trees_find/4]).
now() ->
@@ -53,6 +55,29 @@ make_atom_name(First, Second, Third) ->
list_to_atom(lists:flatten(io_lib:format("~p_~p_~p", [First, Second, Third]))).
+format_atom(Format, Args) ->
+ list_to_atom(lists:flatten(io_lib:format(Format, Args))).
+ % }}}
+
+% 是否在本node中启动该进程, 如果不设置,缺省为启动
+module_enabled(Module) ->
+ module_enabled(Module, 1).
+
+module_enabled(Module, DefaultCount) ->
+ Key = format_atom("enable_~p", [Module]),
+ get_app_env(Key, DefaultCount).
+
+sup_child_spec(Module, Fun, Count) ->
+ N = module_enabled(Module, Count),
+ lists:map(fun(I) ->
+ % Id = format_atom("~p_~p", [Module, I]),
+ Fun(Module, {Module, I})
+ end, lists:seq(0, N-1)).
+
+sup_child_spec(Module, Fun) ->
+ sup_child_spec(Module, Fun, 1).
+
+
%supply a list of application names to restart (as atoms).
%function will only reload config for apps already started
reload_config(AppNames) ->
Please sign in to comment.
Something went wrong with that request. Please try again.