spec/1
This commit is contained in:
parent
0c13490092
commit
855152f653
|
@ -36,7 +36,7 @@
|
|||
%% API Function Exports
|
||||
-export([start_link/2, session/1, info/1, kick/1]).
|
||||
|
||||
%% SUB/UNSUB Asynchronously, called by plugins.
|
||||
%% SUB/UNSUB Asynchronously. Called by plugins.
|
||||
-export([subscribe/2, unsubscribe/2]).
|
||||
|
||||
%% gen_server Function Exports
|
||||
|
@ -243,7 +243,7 @@ with_session(Fun, State = #client_state{proto_state = ProtoState}) ->
|
|||
Fun(emqttd_protocol:session(ProtoState)),
|
||||
hibernate(State).
|
||||
|
||||
%% receive and parse tcp data
|
||||
%% Receive and parse tcp data
|
||||
received(<<>>, State) ->
|
||||
hibernate(State);
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ init([]) ->
|
|||
|
||||
%% CM Pool Sup
|
||||
MFA = {?CM, start_link, [emqttd_stats:statsfun('clients/count', 'clients/max')]},
|
||||
PoolSup = emqttd_pool_sup:spec(pool_sup, [?CM, hash, erlang:system_info(schedulers), MFA]),
|
||||
PoolSup = emqttd_pool_sup:spec([?CM, hash, erlang:system_info(schedulers), MFA]),
|
||||
|
||||
{ok, {{one_for_all, 10, 3600}, [PoolSup]}}.
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ handle_request('GET', "/status", Req) ->
|
|||
AppStatus =
|
||||
case lists:keysearch(emqttd, 1, application:which_applications()) of
|
||||
false -> not_running;
|
||||
{value, _Ver} -> running
|
||||
{value, _Val} -> running
|
||||
end,
|
||||
Status = io_lib:format("Node ~s is ~s~nemqttd is ~s",
|
||||
[node(), InternalStatus, AppStatus]),
|
||||
|
@ -78,7 +78,7 @@ handle_request('POST', "/mqtt/publish", Req) ->
|
|||
%% MQTT Over WebSocket
|
||||
%%------------------------------------------------------------------------------
|
||||
handle_request('GET', "/mqtt", Req) ->
|
||||
lager:info("Websocket Connection from: ~s", [Req:get(peer)]),
|
||||
lager:info("WebSocket Connection from: ~s", [Req:get(peer)]),
|
||||
Upgrade = Req:get_header_value("Upgrade"),
|
||||
Proto = Req:get_header_value("Sec-WebSocket-Protocol"),
|
||||
case {is_websocket(Upgrade), Proto} of
|
||||
|
|
|
@ -35,10 +35,8 @@ merge(Defaults, Options) ->
|
|||
lists:foldl(
|
||||
fun({Opt, Val}, Acc) ->
|
||||
case lists:keymember(Opt, 1, Acc) of
|
||||
true ->
|
||||
lists:keyreplace(Opt, 1, Acc, {Opt, Val});
|
||||
false ->
|
||||
[{Opt, Val}|Acc]
|
||||
true -> lists:keyreplace(Opt, 1, Acc, {Opt, Val});
|
||||
false -> [{Opt, Val}|Acc]
|
||||
end;
|
||||
(Opt, Acc) ->
|
||||
case lists:member(Opt, Acc) of
|
||||
|
|
|
@ -28,14 +28,18 @@
|
|||
-behaviour(supervisor).
|
||||
|
||||
%% API
|
||||
-export([spec/2, start_link/3, start_link/4]).
|
||||
-export([spec/1, spec/2, start_link/3, start_link/4]).
|
||||
|
||||
%% Supervisor callbacks
|
||||
-export([init/1]).
|
||||
|
||||
-spec spec(list()) -> supervisor:child_spec().
|
||||
spec(Args) ->
|
||||
spec(pool_sup, Args).
|
||||
|
||||
-spec spec(any(), list()) -> supervisor:child_spec().
|
||||
spec(Id, Args) ->
|
||||
{Id, {?MODULE, start_link, Args},
|
||||
spec(ChildId, Args) ->
|
||||
{ChildId, {?MODULE, start_link, Args},
|
||||
transient, infinity, supervisor, [?MODULE]}.
|
||||
|
||||
-spec start_link(atom(), atom(), mfa()) -> {ok, pid()} | {error, any()}.
|
||||
|
@ -47,7 +51,7 @@ start_link(Pool, Type, MFA) ->
|
|||
start_link(Pool, Type, Size, MFA) ->
|
||||
supervisor:start_link({local, sup_name(Pool)}, ?MODULE, [Pool, Type, Size, MFA]).
|
||||
|
||||
sup_name(Pool) ->
|
||||
sup_name(Pool) when is_atom(Pool) ->
|
||||
list_to_atom(atom_to_list(Pool) ++ "_pool_sup").
|
||||
|
||||
init([Pool, Type, Size, {M, F, Args}]) ->
|
||||
|
|
|
@ -63,16 +63,17 @@ name(Id) ->
|
|||
%% @end
|
||||
%%------------------------------------------------------------------------------
|
||||
submit(Fun) ->
|
||||
Worker = gproc_pool:pick_worker(pooler),
|
||||
gen_server:call(Worker, {submit, Fun}, infinity).
|
||||
gen_server:call(worker(), {submit, Fun}, infinity).
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
%% @doc Submit work to pooler asynchronously
|
||||
%% @end
|
||||
%%------------------------------------------------------------------------------
|
||||
async_submit(Fun) ->
|
||||
Worker = gproc_pool:pick_worker(pooler),
|
||||
gen_server:cast(Worker, {async_submit, Fun}).
|
||||
gen_server:cast(worker(), {async_submit, Fun}).
|
||||
|
||||
worker() ->
|
||||
gproc_pool:pick_worker(pooler).
|
||||
|
||||
%%%=============================================================================
|
||||
%%% gen_server callbacks
|
||||
|
|
|
@ -40,9 +40,9 @@
|
|||
-copy_mnesia({mnesia, [copy]}).
|
||||
|
||||
%% API Exports
|
||||
-export([start_link/3]).
|
||||
-export([start_link/4]).
|
||||
|
||||
-export([create/1, subscribe/1, subscribe/2,
|
||||
-export([create/2, subscribe/1, subscribe/2,
|
||||
unsubscribe/1, unsubscribe/2, publish/1]).
|
||||
|
||||
%% Local node
|
||||
|
@ -56,7 +56,7 @@
|
|||
-compile(export_all).
|
||||
-endif.
|
||||
|
||||
-record(state, {pool, id}).
|
||||
-record(state, {pool, id, statsfun}).
|
||||
|
||||
-define(ROUTER, emqttd_router).
|
||||
|
||||
|
@ -123,26 +123,33 @@ cache_env(Key) ->
|
|||
%% @doc Start one pubsub server
|
||||
%% @end
|
||||
%%------------------------------------------------------------------------------
|
||||
-spec start_link(Pool, Id, Opts) -> {ok, pid()} | ignore | {error, any()} when
|
||||
-spec start_link(Pool, Id, StatsFun, Opts) -> {ok, pid()} | ignore | {error, any()} when
|
||||
Pool :: atom(),
|
||||
Id :: pos_integer(),
|
||||
StatsFun :: fun(),
|
||||
Opts :: list(tuple()).
|
||||
start_link(Pool, Id, Opts) ->
|
||||
gen_server2:start_link({local, name(Id)}, ?MODULE, [Pool, Id, Opts], []).
|
||||
start_link(Pool, Id, StatsFun, Opts) ->
|
||||
gen_server2:start_link({local, name(Id)}, ?MODULE, [Pool, Id, StatsFun, Opts], []).
|
||||
|
||||
name(Id) ->
|
||||
list_to_atom("emqttd_pubsub_" ++ integer_to_list(Id)).
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
%% @doc Create Topic.
|
||||
%% @doc Create Topic or Subscription.
|
||||
%% @end
|
||||
%%------------------------------------------------------------------------------
|
||||
-spec create(Topic :: binary()) -> ok | {error, Error :: any()}.
|
||||
create(Topic) when is_binary(Topic) ->
|
||||
-spec create(topic | subscription, binary()) -> ok | {error, any()}.
|
||||
create(topic, Topic) when is_binary(Topic) ->
|
||||
Record = #mqtt_topic{topic = Topic, node = node()},
|
||||
case mnesia:transaction(fun add_topic/1, [Record]) of
|
||||
{atomic, ok} -> ok;
|
||||
{aborted, Error} -> {error, Error}
|
||||
end;
|
||||
|
||||
create(subscription, {SubId, Topic, Qos}) ->
|
||||
case mnesia:transaction(fun add_subscription/2, [SubId, {Topic, Qos}]) of
|
||||
{atomic, ok} -> ok;
|
||||
{aborted, Error} -> {error, Error}
|
||||
end.
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
|
@ -233,12 +240,13 @@ match(Topic) when is_binary(Topic) ->
|
|||
%%% gen_server callbacks
|
||||
%%%=============================================================================
|
||||
|
||||
init([Pool, Id, Opts]) ->
|
||||
init([Pool, Id, StatsFun, Opts]) ->
|
||||
?ROUTER:init(Opts),
|
||||
?GPROC_POOL(join, Pool, Id),
|
||||
{ok, #state{pool = Pool, id = Id}}.
|
||||
{ok, #state{pool = Pool, id = Id, statsfun = StatsFun}}.
|
||||
|
||||
handle_call({subscribe, {SubId, SubPid}, TopicTable}, _From, State) ->
|
||||
handle_call({subscribe, {SubId, SubPid}, TopicTable}, _From,
|
||||
State = #state{statsfun = StatsFun}) ->
|
||||
%% Add routes first
|
||||
?ROUTER:add_routes(TopicTable, SubPid),
|
||||
|
||||
|
@ -247,11 +255,13 @@ handle_call({subscribe, {SubId, SubPid}, TopicTable}, _From, State) ->
|
|||
|
||||
case mnesia:transaction(fun add_topics/1, [Topics]) of
|
||||
{atomic, _} ->
|
||||
StatsFun(topic),
|
||||
if_subscription(
|
||||
fun(_) ->
|
||||
%% Add subscriptions
|
||||
Args = [fun add_subscriptions/2, [SubId, TopicTable]],
|
||||
emqttd_pooler:async_submit({mnesia, async_dirty, Args})
|
||||
emqttd_pooler:async_submit({mnesia, async_dirty, Args}),
|
||||
StatsFun(subscription)
|
||||
end),
|
||||
{reply, {ok, [Qos || {_Topic, Qos} <- TopicTable]}, State};
|
||||
{aborted, Error} ->
|
||||
|
@ -262,14 +272,16 @@ handle_call(Req, _From, State) ->
|
|||
lager:error("Bad Request: ~p", [Req]),
|
||||
{reply, {error, badreq}, State}.
|
||||
|
||||
handle_cast({unsubscribe, {SubId, SubPid}, Topics}, State) ->
|
||||
handle_cast({unsubscribe, {SubId, SubPid}, Topics}, State = #state{statsfun = StatsFun}) ->
|
||||
%% Delete routes first
|
||||
?ROUTER:delete_routes(Topics, SubPid),
|
||||
|
||||
%% Remove subscriptions
|
||||
if_subscription(
|
||||
fun(_) ->
|
||||
Args = [fun remove_subscriptions/2, [SubId, Topics]],
|
||||
emqttd_pooler:async_submit({mnesia, async_dirty, Args})
|
||||
emqttd_pooler:async_submit({mnesia, async_dirty, Args}),
|
||||
StatsFun(subscription)
|
||||
end),
|
||||
{noreply, State};
|
||||
|
||||
|
@ -320,20 +332,36 @@ add_subscriptions(undefined, _TopicTable) ->
|
|||
ok;
|
||||
add_subscriptions(SubId, TopicTable) ->
|
||||
lists:foreach(fun({Topic, Qos}) ->
|
||||
%%TODO: this is not right...
|
||||
Subscription = #mqtt_subscription{subid = SubId, topic = Topic, qos = Qos},
|
||||
mnesia:write(subscription, Subscription, write)
|
||||
add_subscription(SubId, {Topic, Qos})
|
||||
end,TopicTable).
|
||||
|
||||
add_subscription(SubId, {Topic, Qos}) ->
|
||||
Subscription = #mqtt_subscription{subid = SubId, topic = Topic, qos = Qos},
|
||||
Pattern = #mqtt_subscription{subid = SubId, topic = Topic, qos = '_'},
|
||||
Records = mnesia:match_object(subscription, Pattern, write),
|
||||
case lists:member(Subscription, Records) of
|
||||
true ->
|
||||
ok;
|
||||
false ->
|
||||
[delete_subscription(Record) || Record <- Records],
|
||||
insert_subscription(Subscription)
|
||||
end.
|
||||
|
||||
insert_subscription(Record) ->
|
||||
mnesia:write(subscription, Record, write).
|
||||
|
||||
remove_subscriptions(undefined, _Topics) ->
|
||||
ok;
|
||||
remove_subscriptions(SubId, Topics) ->
|
||||
lists:foreach(fun(Topic) ->
|
||||
Pattern = #mqtt_subscription{subid = SubId, topic = Topic, qos = '_'},
|
||||
[mnesia:delete_object(subscription, Subscription, write)
|
||||
|| Subscription <- mnesia:match_object(subscription, Pattern, write)]
|
||||
Records = mnesia:match_object(subscription, Pattern, write),
|
||||
[delete_subscription(Record) || Record <- Records]
|
||||
end, Topics).
|
||||
|
||||
delete_subscription(Record) ->
|
||||
mnesia:delete_object(subscription, Record, write).
|
||||
|
||||
%%%=============================================================================
|
||||
%%% Trace Functions
|
||||
%%%=============================================================================
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
-include("emqttd.hrl").
|
||||
|
||||
%% API Function Exports
|
||||
-export([start_link/1, aging/1, setstats/1]).
|
||||
-export([start_link/2, aging/1]).
|
||||
|
||||
%% gen_server Function Exports
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
|
@ -42,7 +42,7 @@
|
|||
|
||||
-record(aging, {topics, time, tref}).
|
||||
|
||||
-record(state, {aging :: #aging{}}).
|
||||
-record(state, {aging :: #aging{}, statsfun}).
|
||||
|
||||
-define(SERVER, ?MODULE).
|
||||
|
||||
|
@ -56,9 +56,9 @@
|
|||
%% @doc Start pubsub helper.
|
||||
%% @end
|
||||
%%------------------------------------------------------------------------------
|
||||
-spec start_link(list(tuple())) -> {ok, pid()} | ignore | {error, any()}.
|
||||
start_link(Opts) ->
|
||||
gen_server2:start_link({local, ?SERVER}, ?MODULE, [Opts], []).
|
||||
-spec start_link(fun(), list(tuple())) -> {ok, pid()} | ignore | {error, any()}.
|
||||
start_link(StatsFun, Opts) ->
|
||||
gen_server2:start_link({local, ?SERVER}, ?MODULE, [StatsFun, Opts], []).
|
||||
|
||||
%%------------------------------------------------------------------------------
|
||||
%% @doc Aging topics
|
||||
|
@ -68,19 +68,11 @@ start_link(Opts) ->
|
|||
aging(Topics) ->
|
||||
gen_server2:cast(?SERVER, {aging, Topics}).
|
||||
|
||||
setstats(topic) ->
|
||||
emqttd_stats:setstats('topics/count', 'topics/max',
|
||||
mnesia:table_info(topic, size));
|
||||
setstats(subscription) ->
|
||||
emqttd_stats:setstats('subscriptions/count', 'subscriptions/max',
|
||||
mnesia:table_info(subscription, size)).
|
||||
|
||||
%%%=============================================================================
|
||||
%%% gen_server callbacks
|
||||
%%%=============================================================================
|
||||
|
||||
init([Opts]) ->
|
||||
|
||||
init([StatsFun, Opts]) ->
|
||||
mnesia:subscribe(system),
|
||||
|
||||
AgingSecs = proplists:get_value(route_aging, Opts, 5),
|
||||
|
@ -90,13 +82,15 @@ init([Opts]) ->
|
|||
|
||||
{ok, #state{aging = #aging{topics = dict:new(),
|
||||
time = AgingSecs,
|
||||
tref = AgingTref}}}.
|
||||
tref = AgingTref},
|
||||
statsfun = StatsFun}}.
|
||||
|
||||
start_tick(Secs) ->
|
||||
timer:send_interval(timer:seconds(Secs), {clean, aged}).
|
||||
|
||||
handle_call(_Request, _From, State) ->
|
||||
{reply, ok, State}.
|
||||
handle_call(Req, _From, State) ->
|
||||
lager:error("Unexpected Request: ~p", [Req]),
|
||||
{reply, {error, unsupported_request}, State}.
|
||||
|
||||
handle_cast({aging, Topics}, State = #state{aging = Aging}) ->
|
||||
#aging{topics = Dict} = Aging,
|
||||
|
@ -123,7 +117,7 @@ handle_info({clean, aged}, State = #state{aging = Aging}) ->
|
|||
|
||||
NewAging = Aging#aging{topics = dict:from_list(Dict1)},
|
||||
|
||||
{noreply, State#state{aging = NewAging}, hibernate};
|
||||
noreply(State#state{aging = NewAging});
|
||||
|
||||
handle_info({mnesia_system_event, {mnesia_down, Node}}, State) ->
|
||||
Pattern = #mqtt_topic{_ = '_', node = Node},
|
||||
|
@ -132,7 +126,7 @@ handle_info({mnesia_system_event, {mnesia_down, Node}}, State) ->
|
|||
R <- mnesia:match_object(topic, Pattern, write)]
|
||||
end,
|
||||
mnesia:async_dirty(F),
|
||||
{noreply, State};
|
||||
noreply(State);
|
||||
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State}.
|
||||
|
@ -147,6 +141,9 @@ code_change(_OldVsn, State, _Extra) ->
|
|||
%%% Internal Functions
|
||||
%%%=============================================================================
|
||||
|
||||
noreply(State = #state{statsfun = StatsFun}) ->
|
||||
StatsFun(topic), {noreply, State, hibernate}.
|
||||
|
||||
try_clean(ByTime, List) ->
|
||||
try_clean(ByTime, List, []).
|
||||
|
||||
|
|
|
@ -42,16 +42,22 @@ start_link() ->
|
|||
|
||||
init([Opts]) ->
|
||||
%% PubSub Helper
|
||||
Helper = {helper, {?HELPER, start_link, [Opts]},
|
||||
Helper = {helper, {?HELPER, start_link, [fun stats/1, Opts]},
|
||||
permanent, infinity, worker, [?HELPER]},
|
||||
|
||||
%% PubSub Pool Sup
|
||||
MFA = {emqttd_pubsub, start_link, [Opts]},
|
||||
PoolSup = emqttd_pool_sup:spec(pool_sup, [
|
||||
pubsub, hash, pool_size(Opts), MFA]),
|
||||
MFA = {emqttd_pubsub, start_link, [fun stats/1, Opts]},
|
||||
PoolSup = emqttd_pool_sup:spec([pubsub, hash, pool_size(Opts), MFA]),
|
||||
{ok, {{one_for_all, 10, 60}, [Helper, PoolSup]}}.
|
||||
|
||||
pool_size(Opts) ->
|
||||
Schedulers = erlang:system_info(schedulers),
|
||||
proplists:get_value(pool_size, Opts, Schedulers).
|
||||
|
||||
stats(topic) ->
|
||||
emqttd_stats:setstats('topics/count', 'topics/max',
|
||||
mnesia:table_info(topic, size));
|
||||
stats(subscription) ->
|
||||
emqttd_stats:setstats('subscriptions/count', 'subscriptions/max',
|
||||
mnesia:table_info(subscription, size)).
|
||||
|
||||
|
|
|
@ -56,8 +56,7 @@ init([]) ->
|
|||
|
||||
%% SM Pool Sup
|
||||
MFA = {?SM, start_link, []},
|
||||
PoolSup = emqttd_pool_sup:spec(pool_sup, [
|
||||
?SM, hash, erlang:system_info(schedulers), MFA]),
|
||||
PoolSup = emqttd_pool_sup:spec([?SM, hash, erlang:system_info(schedulers), MFA]),
|
||||
|
||||
{ok, {{one_for_all, 10, 3600}, [Helper, PoolSup]}}.
|
||||
|
||||
|
|
Loading…
Reference in New Issue