This commit is contained in:
Feng 2015-12-05 02:18:06 +08:00
parent 0c13490092
commit 855152f653
11 changed files with 103 additions and 70 deletions

View File

@ -36,7 +36,7 @@
%% API Function Exports %% API Function Exports
-export([start_link/2, session/1, info/1, kick/1]). -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]). -export([subscribe/2, unsubscribe/2]).
%% gen_server Function Exports %% gen_server Function Exports
@ -243,7 +243,7 @@ with_session(Fun, State = #client_state{proto_state = ProtoState}) ->
Fun(emqttd_protocol:session(ProtoState)), Fun(emqttd_protocol:session(ProtoState)),
hibernate(State). hibernate(State).
%% receive and parse tcp data %% Receive and parse tcp data
received(<<>>, State) -> received(<<>>, State) ->
hibernate(State); hibernate(State);

View File

@ -48,7 +48,7 @@ init([]) ->
%% CM Pool Sup %% CM Pool Sup
MFA = {?CM, start_link, [emqttd_stats:statsfun('clients/count', 'clients/max')]}, 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]}}. {ok, {{one_for_all, 10, 3600}, [PoolSup]}}.

View File

@ -41,7 +41,7 @@ handle_request('GET', "/status", Req) ->
AppStatus = AppStatus =
case lists:keysearch(emqttd, 1, application:which_applications()) of case lists:keysearch(emqttd, 1, application:which_applications()) of
false -> not_running; false -> not_running;
{value, _Ver} -> running {value, _Val} -> running
end, end,
Status = io_lib:format("Node ~s is ~s~nemqttd is ~s", Status = io_lib:format("Node ~s is ~s~nemqttd is ~s",
[node(), InternalStatus, AppStatus]), [node(), InternalStatus, AppStatus]),
@ -78,7 +78,7 @@ handle_request('POST', "/mqtt/publish", Req) ->
%% MQTT Over WebSocket %% MQTT Over WebSocket
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
handle_request('GET', "/mqtt", Req) -> 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"), Upgrade = Req:get_header_value("Upgrade"),
Proto = Req:get_header_value("Sec-WebSocket-Protocol"), Proto = Req:get_header_value("Sec-WebSocket-Protocol"),
case {is_websocket(Upgrade), Proto} of case {is_websocket(Upgrade), Proto} of

View File

@ -35,14 +35,12 @@ merge(Defaults, Options) ->
lists:foldl( lists:foldl(
fun({Opt, Val}, Acc) -> fun({Opt, Val}, Acc) ->
case lists:keymember(Opt, 1, Acc) of case lists:keymember(Opt, 1, Acc) of
true -> true -> lists:keyreplace(Opt, 1, Acc, {Opt, Val});
lists:keyreplace(Opt, 1, Acc, {Opt, Val}); false -> [{Opt, Val}|Acc]
false ->
[{Opt, Val}|Acc]
end; end;
(Opt, Acc) -> (Opt, Acc) ->
case lists:member(Opt, Acc) of case lists:member(Opt, Acc) of
true -> Acc; true -> Acc;
false -> [Opt | Acc] false -> [Opt | Acc]
end end
end, Defaults, Options). end, Defaults, Options).

View File

@ -28,14 +28,18 @@
-behaviour(supervisor). -behaviour(supervisor).
%% API %% API
-export([spec/2, start_link/3, start_link/4]). -export([spec/1, spec/2, start_link/3, start_link/4]).
%% Supervisor callbacks %% Supervisor callbacks
-export([init/1]). -export([init/1]).
-spec spec(list()) -> supervisor:child_spec().
spec(Args) ->
spec(pool_sup, Args).
-spec spec(any(), list()) -> supervisor:child_spec(). -spec spec(any(), list()) -> supervisor:child_spec().
spec(Id, Args) -> spec(ChildId, Args) ->
{Id, {?MODULE, start_link, Args}, {ChildId, {?MODULE, start_link, Args},
transient, infinity, supervisor, [?MODULE]}. transient, infinity, supervisor, [?MODULE]}.
-spec start_link(atom(), atom(), mfa()) -> {ok, pid()} | {error, any()}. -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) -> start_link(Pool, Type, Size, MFA) ->
supervisor:start_link({local, sup_name(Pool)}, ?MODULE, [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"). list_to_atom(atom_to_list(Pool) ++ "_pool_sup").
init([Pool, Type, Size, {M, F, Args}]) -> init([Pool, Type, Size, {M, F, Args}]) ->

View File

@ -63,16 +63,17 @@ name(Id) ->
%% @end %% @end
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
submit(Fun) -> 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 %% @doc Submit work to pooler asynchronously
%% @end %% @end
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
async_submit(Fun) -> 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 %%% gen_server callbacks

View File

@ -40,9 +40,9 @@
-copy_mnesia({mnesia, [copy]}). -copy_mnesia({mnesia, [copy]}).
%% API Exports %% 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]). unsubscribe/1, unsubscribe/2, publish/1]).
%% Local node %% Local node
@ -56,7 +56,7 @@
-compile(export_all). -compile(export_all).
-endif. -endif.
-record(state, {pool, id}). -record(state, {pool, id, statsfun}).
-define(ROUTER, emqttd_router). -define(ROUTER, emqttd_router).
@ -123,26 +123,33 @@ cache_env(Key) ->
%% @doc Start one pubsub server %% @doc Start one pubsub server
%% @end %% @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(), Pool :: atom(),
Id :: pos_integer(), Id :: pos_integer(),
Opts :: list(tuple()). StatsFun :: fun(),
start_link(Pool, Id, Opts) -> Opts :: list(tuple()).
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) -> name(Id) ->
list_to_atom("emqttd_pubsub_" ++ integer_to_list(Id)). list_to_atom("emqttd_pubsub_" ++ integer_to_list(Id)).
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
%% @doc Create Topic. %% @doc Create Topic or Subscription.
%% @end %% @end
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
-spec create(Topic :: binary()) -> ok | {error, Error :: any()}. -spec create(topic | subscription, binary()) -> ok | {error, any()}.
create(Topic) when is_binary(Topic) -> create(topic, Topic) when is_binary(Topic) ->
Record = #mqtt_topic{topic = Topic, node = node()}, Record = #mqtt_topic{topic = Topic, node = node()},
case mnesia:transaction(fun add_topic/1, [Record]) of case mnesia:transaction(fun add_topic/1, [Record]) of
{atomic, ok} -> ok; {atomic, ok} -> ok;
{aborted, Error} -> {error, Error} {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. end.
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
@ -233,12 +240,13 @@ match(Topic) when is_binary(Topic) ->
%%% gen_server callbacks %%% gen_server callbacks
%%%============================================================================= %%%=============================================================================
init([Pool, Id, Opts]) -> init([Pool, Id, StatsFun, Opts]) ->
?ROUTER:init(Opts), ?ROUTER:init(Opts),
?GPROC_POOL(join, Pool, Id), ?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 %% Add routes first
?ROUTER:add_routes(TopicTable, SubPid), ?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 case mnesia:transaction(fun add_topics/1, [Topics]) of
{atomic, _} -> {atomic, _} ->
StatsFun(topic),
if_subscription( if_subscription(
fun(_) -> fun(_) ->
%% Add subscriptions %% Add subscriptions
Args = [fun add_subscriptions/2, [SubId, TopicTable]], 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), end),
{reply, {ok, [Qos || {_Topic, Qos} <- TopicTable]}, State}; {reply, {ok, [Qos || {_Topic, Qos} <- TopicTable]}, State};
{aborted, Error} -> {aborted, Error} ->
@ -262,14 +272,16 @@ handle_call(Req, _From, State) ->
lager:error("Bad Request: ~p", [Req]), lager:error("Bad Request: ~p", [Req]),
{reply, {error, badreq}, State}. {reply, {error, badreq}, State}.
handle_cast({unsubscribe, {SubId, SubPid}, Topics}, State) -> handle_cast({unsubscribe, {SubId, SubPid}, Topics}, State = #state{statsfun = StatsFun}) ->
%% Delete routes first %% Delete routes first
?ROUTER:delete_routes(Topics, SubPid), ?ROUTER:delete_routes(Topics, SubPid),
%% Remove subscriptions %% Remove subscriptions
if_subscription( if_subscription(
fun(_) -> fun(_) ->
Args = [fun remove_subscriptions/2, [SubId, Topics]], 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), end),
{noreply, State}; {noreply, State};
@ -311,7 +323,7 @@ add_topic(TopicR = #mqtt_topic{topic = Topic}) ->
mnesia:write(topic, TopicR, write); mnesia:write(topic, TopicR, write);
Records -> Records ->
case lists:member(TopicR, Records) of case lists:member(TopicR, Records) of
true -> ok; true -> ok;
false -> mnesia:write(topic, TopicR, write) false -> mnesia:write(topic, TopicR, write)
end end
end. end.
@ -320,20 +332,36 @@ add_subscriptions(undefined, _TopicTable) ->
ok; ok;
add_subscriptions(SubId, TopicTable) -> add_subscriptions(SubId, TopicTable) ->
lists:foreach(fun({Topic, Qos}) -> lists:foreach(fun({Topic, Qos}) ->
%%TODO: this is not right... add_subscription(SubId, {Topic, Qos})
Subscription = #mqtt_subscription{subid = SubId, topic = Topic, qos = Qos}, end,TopicTable).
mnesia:write(subscription, Subscription, write)
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) -> remove_subscriptions(undefined, _Topics) ->
ok; ok;
remove_subscriptions(SubId, Topics) -> remove_subscriptions(SubId, Topics) ->
lists:foreach(fun(Topic) -> lists:foreach(fun(Topic) ->
Pattern = #mqtt_subscription{subid = SubId, topic = Topic, qos = '_'}, Pattern = #mqtt_subscription{subid = SubId, topic = Topic, qos = '_'},
[mnesia:delete_object(subscription, Subscription, write) Records = mnesia:match_object(subscription, Pattern, write),
|| Subscription <- mnesia:match_object(subscription, Pattern, write)] [delete_subscription(Record) || Record <- Records]
end, Topics). end, Topics).
delete_subscription(Record) ->
mnesia:delete_object(subscription, Record, write).
%%%============================================================================= %%%=============================================================================
%%% Trace Functions %%% Trace Functions
%%%============================================================================= %%%=============================================================================

View File

@ -30,7 +30,7 @@
-include("emqttd.hrl"). -include("emqttd.hrl").
%% API Function Exports %% API Function Exports
-export([start_link/1, aging/1, setstats/1]). -export([start_link/2, aging/1]).
%% gen_server Function Exports %% gen_server Function Exports
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
@ -42,7 +42,7 @@
-record(aging, {topics, time, tref}). -record(aging, {topics, time, tref}).
-record(state, {aging :: #aging{}}). -record(state, {aging :: #aging{}, statsfun}).
-define(SERVER, ?MODULE). -define(SERVER, ?MODULE).
@ -56,9 +56,9 @@
%% @doc Start pubsub helper. %% @doc Start pubsub helper.
%% @end %% @end
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
-spec start_link(list(tuple())) -> {ok, pid()} | ignore | {error, any()}. -spec start_link(fun(), list(tuple())) -> {ok, pid()} | ignore | {error, any()}.
start_link(Opts) -> start_link(StatsFun, Opts) ->
gen_server2:start_link({local, ?SERVER}, ?MODULE, [Opts], []). gen_server2:start_link({local, ?SERVER}, ?MODULE, [StatsFun, Opts], []).
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
%% @doc Aging topics %% @doc Aging topics
@ -68,19 +68,11 @@ start_link(Opts) ->
aging(Topics) -> aging(Topics) ->
gen_server2:cast(?SERVER, {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 %%% gen_server callbacks
%%%============================================================================= %%%=============================================================================
init([Opts]) -> init([StatsFun, Opts]) ->
mnesia:subscribe(system), mnesia:subscribe(system),
AgingSecs = proplists:get_value(route_aging, Opts, 5), AgingSecs = proplists:get_value(route_aging, Opts, 5),
@ -90,13 +82,15 @@ init([Opts]) ->
{ok, #state{aging = #aging{topics = dict:new(), {ok, #state{aging = #aging{topics = dict:new(),
time = AgingSecs, time = AgingSecs,
tref = AgingTref}}}. tref = AgingTref},
statsfun = StatsFun}}.
start_tick(Secs) -> start_tick(Secs) ->
timer:send_interval(timer:seconds(Secs), {clean, aged}). timer:send_interval(timer:seconds(Secs), {clean, aged}).
handle_call(_Request, _From, State) -> handle_call(Req, _From, State) ->
{reply, ok, State}. lager:error("Unexpected Request: ~p", [Req]),
{reply, {error, unsupported_request}, State}.
handle_cast({aging, Topics}, State = #state{aging = Aging}) -> handle_cast({aging, Topics}, State = #state{aging = Aging}) ->
#aging{topics = Dict} = 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)}, 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) -> handle_info({mnesia_system_event, {mnesia_down, Node}}, State) ->
Pattern = #mqtt_topic{_ = '_', node = Node}, 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)] R <- mnesia:match_object(topic, Pattern, write)]
end, end,
mnesia:async_dirty(F), mnesia:async_dirty(F),
{noreply, State}; noreply(State);
handle_info(_Info, State) -> handle_info(_Info, State) ->
{noreply, State}. {noreply, State}.
@ -147,6 +141,9 @@ code_change(_OldVsn, State, _Extra) ->
%%% Internal Functions %%% Internal Functions
%%%============================================================================= %%%=============================================================================
noreply(State = #state{statsfun = StatsFun}) ->
StatsFun(topic), {noreply, State, hibernate}.
try_clean(ByTime, List) -> try_clean(ByTime, List) ->
try_clean(ByTime, List, []). try_clean(ByTime, List, []).

View File

@ -42,16 +42,22 @@ start_link() ->
init([Opts]) -> init([Opts]) ->
%% PubSub Helper %% PubSub Helper
Helper = {helper, {?HELPER, start_link, [Opts]}, Helper = {helper, {?HELPER, start_link, [fun stats/1, Opts]},
permanent, infinity, worker, [?HELPER]}, permanent, infinity, worker, [?HELPER]},
%% PubSub Pool Sup %% PubSub Pool Sup
MFA = {emqttd_pubsub, start_link, [Opts]}, MFA = {emqttd_pubsub, start_link, [fun stats/1, Opts]},
PoolSup = emqttd_pool_sup:spec(pool_sup, [ PoolSup = emqttd_pool_sup:spec([pubsub, hash, pool_size(Opts), MFA]),
pubsub, hash, pool_size(Opts), MFA]),
{ok, {{one_for_all, 10, 60}, [Helper, PoolSup]}}. {ok, {{one_for_all, 10, 60}, [Helper, PoolSup]}}.
pool_size(Opts) -> pool_size(Opts) ->
Schedulers = erlang:system_info(schedulers), Schedulers = erlang:system_info(schedulers),
proplists:get_value(pool_size, Opts, 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)).

View File

@ -56,8 +56,7 @@ init([]) ->
%% SM Pool Sup %% SM Pool Sup
MFA = {?SM, start_link, []}, MFA = {?SM, start_link, []},
PoolSup = emqttd_pool_sup:spec(pool_sup, [ PoolSup = emqttd_pool_sup:spec([?SM, hash, erlang:system_info(schedulers), MFA]),
?SM, hash, erlang:system_info(schedulers), MFA]),
{ok, {{one_for_all, 10, 3600}, [Helper, PoolSup]}}. {ok, {{one_for_all, 10, 3600}, [Helper, PoolSup]}}.