This commit is contained in:
Feng 2016-08-14 20:30:41 +08:00
commit 2d81464fc0
10 changed files with 172 additions and 113 deletions

View File

@ -35,6 +35,9 @@
%% Hooks API
-export([hook/4, hook/3, unhook/2, run_hooks/3]).
%% Adapter
-export([adapter/1]).
%% Debug API
-export([dump/0]).
@ -157,6 +160,14 @@ unhook(Hook, Function) ->
run_hooks(Hook, Args, Acc) ->
emqttd_hook:run(Hook, Args, Acc).
%%--------------------------------------------------------------------
%% Adapter
%%--------------------------------------------------------------------
adapter(server) -> env(pubsub_server, emqttd_server);
adapter(pubsub) -> env(pubsub_adapter, emqttd_pubsub);
adapter(bridge) -> env(bridge_adapter, emqttd_bridge).
%%--------------------------------------------------------------------
%% Debug
%%--------------------------------------------------------------------

View File

@ -91,7 +91,7 @@ start_servers(Sup) ->
{"emqttd broker", emqttd_broker},
{"emqttd alarm", emqttd_alarm},
{"emqttd mod supervisor", emqttd_mod_sup},
{"emqttd bridge supervisor", {supervisor, emqttd_bridge_sup}},
{"emqttd bridge supervisor", {supervisor, emqttd_bridge_sup_sup}},
{"emqttd access control", emqttd_access_control},
{"emqttd system monitor", {supervisor, emqttd_sysmon_sup}}],
[start_server(Sup, Server) || Server <- Servers].

View File

@ -18,56 +18,25 @@
-behavior(supervisor).
-export([start_link/0, bridges/0, start_bridge/2, start_bridge/3, stop_bridge/2]).
-export([start_link/3]).
-export([init/1]).
-define(BRIDGE_ID(Node, Topic), {bridge, Node, Topic}).
%%--------------------------------------------------------------------
%% API
%%--------------------------------------------------------------------
%% @doc Start bridge supervisor
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
%% @doc List all bridges
-spec(bridges() -> [{tuple(), pid()}]).
bridges() ->
[{{Node, Topic}, Pid} || {?BRIDGE_ID(Node, Topic), Pid, worker, _}
<- supervisor:which_children(?MODULE)].
%% @doc Start a bridge
-spec(start_bridge(atom(), binary()) -> {ok, pid()} | {error, any()}).
start_bridge(Node, Topic) when is_atom(Node) andalso is_binary(Topic) ->
start_bridge(Node, Topic, []).
-spec(start_bridge(atom(), binary(), [emqttd_bridge:option()]) -> {ok, pid()} | {error, any()}).
start_bridge(Node, _Topic, _Options) when Node =:= node() ->
{error, bridge_to_self};
start_bridge(Node, Topic, Options) when is_atom(Node) andalso is_binary(Topic) ->
Options1 = emqttd_opts:merge(emqttd_conf:bridge(), Options),
supervisor:start_child(?MODULE, bridge_spec(Node, Topic, Options1)).
%% @doc Stop a bridge
-spec(stop_bridge(atom(), binary()) -> {ok, pid()} | ok).
stop_bridge(Node, Topic) when is_atom(Node) andalso is_binary(Topic) ->
ChildId = ?BRIDGE_ID(Node, Topic),
case supervisor:terminate_child(?MODULE, ChildId) of
ok -> supervisor:delete_child(?MODULE, ChildId);
Error -> Error
end.
-spec(start_link(atom(), binary(), [emqttd_bridge:option()]) -> {ok, pid()} | {error, any()}).
start_link(Node, Topic, Options) ->
supervisor:start_link(?MODULE, [Node, Topic, Options]).
%%--------------------------------------------------------------------
%% Supervisor callbacks
%%--------------------------------------------------------------------
init([]) ->
{ok, {{one_for_one, 10, 100}, []}}.
bridge_spec(Node, Topic, Options) ->
ChildId = ?BRIDGE_ID(Node, Topic),
{ChildId, {emqttd_bridge, start_link, [Node, Topic, Options]},
transient, 10000, worker, [emqttd_bridge]}.
init([Node, Topic, Options]) ->
{ok, {{one_for_all, 10, 100},
[{bridge, {emqttd_bridge, start_link, [Node, Topic, Options]},
transient, 10000, worker, [emqttd_bridge]}]}}.

View File

@ -0,0 +1,76 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2012-2016 Feng Lee <feng@emqtt.io>.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%--------------------------------------------------------------------
-module(emqttd_bridge_sup_sup).
-behavior(supervisor).
-export([start_link/0, bridges/0, start_bridge/2, start_bridge/3, stop_bridge/2]).
-export([init/1]).
-define(CHILD_ID(Node, Topic), {bridge_sup, Node, Topic}).
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
%%--------------------------------------------------------------------
%% API
%%--------------------------------------------------------------------
%% @doc List all bridges
-spec(bridges() -> [{node(), binary(), pid()}]).
bridges() ->
[{Node, Topic, Pid} || {?CHILD_ID(Node, Topic), Pid, supervisor, _}
<- supervisor:which_children(?MODULE)].
%% @doc Start a bridge
-spec(start_bridge(atom(), binary()) -> {ok, pid()} | {error, any()}).
start_bridge(Node, Topic) when is_atom(Node) andalso is_binary(Topic) ->
start_bridge(Node, Topic, []).
-spec(start_bridge(atom(), binary(), [emqttd_bridge:option()]) -> {ok, pid()} | {error, any()}).
start_bridge(Node, _Topic, _Options) when Node =:= node() ->
{error, bridge_to_self};
start_bridge(Node, Topic, Options) when is_atom(Node) andalso is_binary(Topic) ->
Options1 = emqttd_opts:merge(emqttd_conf:bridge(), Options),
supervisor:start_child(?MODULE, bridge_spec(Node, Topic, Options1)).
%% @doc Stop a bridge
-spec(stop_bridge(atom(), binary()) -> {ok, pid()} | ok).
stop_bridge(Node, Topic) when is_atom(Node) andalso is_binary(Topic) ->
ChildId = ?CHILD_ID(Node, Topic),
case supervisor:terminate_child(?MODULE, ChildId) of
ok -> supervisor:delete_child(?MODULE, ChildId);
Error -> Error
end.
%%--------------------------------------------------------------------
%% Supervisor callbacks
%%--------------------------------------------------------------------
init([]) ->
{ok, {{one_for_one, 10, 100}, []}}.
bridge_spec(Node, Topic, Options) ->
SupMod = sup_mod(emqttd:adapter(bridge)),
{?CHILD_ID(Node, Topic),
{SupMod, start_link, [Node, Topic, Options]},
permanent, infinity, supervisor, [SupMod]}.
sup_mod(Adaper) ->
list_to_atom(atom_to_list(Adaper) ++ "_sup").

View File

@ -217,41 +217,45 @@ topics(_) ->
{"topics show <Topic>", "Show a topic"}]).
subscriptions(["list"]) ->
lists:foreach(fun({Sub, Topic, Opts}) when is_pid(Sub) ->
?PRINT("~p -> ~s: ~p~n", [Sub, Topic, Opts]);
({Sub, Topic, Opts}) ->
?PRINT("~s -> ~s: ~p~n", [Sub, Topic, Opts])
end, emqttd:subscriptions());
lists:foreach(fun(Subscription) ->
print(subscription, Subscription)
end, []); %%emqttd:subscriptions());
subscriptions(["show", ClientId]) ->
case ets:dirty_read(mqtt_subscription, bin(ClientId)) of
case ets:lookup(mqtt_subscription, bin(ClientId)) of
[] -> ?PRINT_MSG("Not Found.~n");
Records -> print(Records)
Records -> [print(subscription, Subscription) || Subscription <- Records]
end;
subscriptions(["add", ClientId, Topic, QoS]) ->
Add = fun(IntQos) ->
Subscription = #mqtt_subscription{subid = bin(ClientId),
topic = bin(Topic),
qos = IntQos},
case emqttd_backend:add_subscription(Subscription) of
ok ->
?PRINT_MSG("ok~n");
{error, already_existed} ->
?PRINT_MSG("Error: already existed~n");
{error, Reason} ->
?PRINT("Error: ~p~n", [Reason])
end
end,
if_valid_qos(QoS, Add);
%%
%% subscriptions(["add", ClientId, Topic, QoS]) ->
%% Add = fun(IntQos) ->
%% Subscription = #mqtt_subscription{subid = bin(ClientId),
%% topic = bin(Topic),
%% qos = IntQos},
%% case emqttd_backend:add_subscription(Subscription) of
%% ok ->
%% ?PRINT_MSG("ok~n");
%% {error, already_existed} ->
%% ?PRINT_MSG("Error: already existed~n");
%% {error, Reason} ->
%% ?PRINT("Error: ~p~n", [Reason])
%% end
%% end,
%% if_valid_qos(QoS, Add);
%%
subscriptions(["del", ClientId]) ->
Ok = emqttd_backend:del_subscriptions(bin(ClientId)),
?PRINT("~p~n", [Ok]);
%%
%% subscriptions(["del", ClientId]) ->
%% Ok = emqttd_backend:del_subscriptions(bin(ClientId)),
%% ?PRINT("~p~n", [Ok]);
%%
subscriptions(["del", ClientId, Topic]) ->
Ok = emqttd_backend:del_subscription(bin(ClientId), bin(Topic)),
?PRINT("~p~n", [Ok]);
%%
%% subscriptions(["del", ClientId, Topic]) ->
%% Ok = emqttd_backend:del_subscription(bin(ClientId), bin(Topic)),
%% ?PRINT("~p~n", [Ok]);
%%
subscriptions(_) ->
?USAGE([{"subscriptions list", "List all subscriptions"},
@ -306,7 +310,7 @@ plugins(_) ->
bridges(["list"]) ->
foreach(fun({{Node, Topic}, _Pid}) ->
?PRINT("bridge: ~s--~s-->~s~n", [node(), Topic, Node])
end, emqttd_bridge_sup:bridges());
end, emqttd_bridge_sup_sup:bridges());
bridges(["options"]) ->
?PRINT_MSG("Options:~n"),
@ -318,20 +322,20 @@ bridges(["options"]) ->
?PRINT_MSG(" qos=2,prefix=abc/,suffix=/yxz,queue=1000~n");
bridges(["start", SNode, Topic]) ->
case emqttd_bridge_sup:start_bridge(list_to_atom(SNode), list_to_binary(Topic)) of
case emqttd_bridge_sup_sup:start_bridge(list_to_atom(SNode), list_to_binary(Topic)) of
{ok, _} -> ?PRINT_MSG("bridge is started.~n");
{error, Error} -> ?PRINT("error: ~p~n", [Error])
end;
bridges(["start", SNode, Topic, OptStr]) ->
Opts = parse_opts(bridge, OptStr),
case emqttd_bridge_sup:start_bridge(list_to_atom(SNode), list_to_binary(Topic), Opts) of
case emqttd_bridge_sup_sup:start_bridge(list_to_atom(SNode), list_to_binary(Topic), Opts) of
{ok, _} -> ?PRINT_MSG("bridge is started.~n");
{error, Error} -> ?PRINT("error: ~p~n", [Error])
end;
bridges(["stop", SNode, Topic]) ->
case emqttd_bridge_sup:stop_bridge(list_to_atom(SNode), list_to_binary(Topic)) of
case emqttd_bridge_sup_sup:stop_bridge(list_to_atom(SNode), list_to_binary(Topic)) of
ok -> ?PRINT_MSG("bridge is stopped.~n");
{error, Error} -> ?PRINT("error: ~p~n", [Error])
end;
@ -526,6 +530,11 @@ print({ClientId, _ClientPid, CleanSess, SessInfo}) ->
"created_at=~w)~n",
[ClientId, CleanSess | [format(Key, get_value(Key, SessInfo)) || Key <- InfoKeys]]).
print(subscription, {Sub, Topic, Opts}) when is_pid(Sub) ->
?PRINT("~p -> ~s: ~p~n", [Sub, Topic, Opts]);
print(subscription, {Sub, Topic, Opts}) ->
?PRINT("~s -> ~s: ~p~n", [Sub, Topic, Opts]).
format(created_at, Val) ->
emqttd_time:now_to_secs(Val);

View File

@ -41,10 +41,10 @@ start_link(Pool, Type, MFA) ->
-spec(start_link(atom(), atom(), pos_integer(), mfa()) -> {ok, pid()} | {error, any()}).
start_link(Pool, Type, Size, MFA) ->
supervisor:start_link({local, sup_name(Pool)}, ?MODULE, [Pool, Type, Size, MFA]).
supervisor:start_link(?MODULE, [Pool, Type, Size, MFA]).
sup_name(Pool) when is_atom(Pool) ->
list_to_atom(atom_to_list(Pool) ++ "_pool_sup").
%% sup_name(Pool) when is_atom(Pool) ->
%% list_to_atom(atom_to_list(Pool) ++ "_pool_sup").
init([Pool, Type, Size, {M, F, Args}]) ->
ensure_pool(Pool, Type, [{size, Size}]),

View File

@ -162,18 +162,14 @@ code_change(_OldVsn, State, _Extra) ->
%%--------------------------------------------------------------------
add_subscriber_(Topic, Subscriber) ->
case ets:member(mqtt_subscriber, Topic) of
false -> emqttd_router:add_route(Topic, node());
true -> ok
end,
(not ets:member(mqtt_subscriber, Topic))
andalso emqttd_router:add_route(Topic),
ets:insert(mqtt_subscriber, {Topic, Subscriber}).
del_subscriber_(Topic, Subscriber) ->
ets:delete_object(mqtt_subscriber, {Topic, Subscriber}),
case ets:member(mqtt_subscriber, Topic) of
false -> emqttd_router:del_route(Topic, node());
true -> ok
end.
(not ets:member(mqtt_subscriber, Topic))
andalso emqttd_router:del_route(Topic).
setstats(State) ->
emqttd_stats:setstats('subscribers/count', 'subscribers/max',

View File

@ -57,17 +57,9 @@ pool_size(Env) ->
pool_sup(Name, Env) ->
Pool = list_to_atom(atom_to_list(Name) ++ "_pool"),
MFA = {adapter(Name), start_link, [Env]},
MFA = {emqttd:adapter(Name), start_link, [Env]},
emqttd_pool_sup:spec(Pool, [Name, hash, pool_size(Env), MFA]).
%%--------------------------------------------------------------------
%% Adapter
%%--------------------------------------------------------------------
adapter(server) ->
emqttd:env(pubsub_server, emqttd_server);
adapter(pubsub) ->
emqttd:env(pubsub_adapter, emqttd_pubsub).
%%--------------------------------------------------------------------
%% Create PubSub Tables

View File

@ -172,13 +172,13 @@ init([Pool, Id, Env]) ->
{ok, #state{pool = Pool, id = Id, env = Env, submon = emqttd_pmon:new()}}.
handle_call({subscribe, Topic, Subscriber, Options}, _From, State) ->
case subscribe_(Topic, Subscriber, Options, State) of
case do_subscribe_(Topic, Subscriber, Options, State) of
{ok, NewState} -> {reply, ok, setstats(NewState)};
{error, Error} -> {reply, {error, Error}, State}
end;
handle_call({unsubscribe, Topic, Subscriber}, _From, State) ->
case unsubscribe_(Topic, Subscriber, State) of
case do_unsubscribe_(Topic, Subscriber, State) of
{ok, NewState} -> {reply, ok, setstats(NewState), hibernate};
{error, Error} -> {reply, {error, Error}, State}
end;
@ -198,13 +198,13 @@ handle_call(Req, _From, State) ->
?UNEXPECTED_REQ(Req, State).
handle_cast({subscribe, Topic, Subscriber, Options}, State) ->
case subscribe_(Topic, Subscriber, Options, State) of
case do_subscribe_(Topic, Subscriber, Options, State) of
{ok, NewState} -> {noreply, setstats(NewState)};
{error, _Error} -> {noreply, State}
end;
handle_cast({unsubscribe, Topic, Subscriber}, State) ->
case unsubscribe_(Topic, Subscriber, State) of
case do_unsubscribe_(Topic, Subscriber, State) of
{ok, NewState} -> {noreply, setstats(NewState), hibernate};
{error, _Error} -> {noreply, State}
end;
@ -233,7 +233,7 @@ code_change(_OldVsn, State, _Extra) ->
%% Internal Functions
%%--------------------------------------------------------------------
subscribe_(Topic, Subscriber, Options, State) ->
do_subscribe_(Topic, Subscriber, Options, State) ->
case ets:lookup(mqtt_subproperty, {Topic, Subscriber}) of
[] ->
emqttd_pubsub:async_subscribe(Topic, Subscriber),
@ -244,7 +244,12 @@ subscribe_(Topic, Subscriber, Options, State) ->
{error, {already_subscribed, Topic}}
end.
unsubscribe_(Topic, Subscriber, State) ->
monitor_subpid(SubPid, State = #state{submon = PMon}) when is_pid(SubPid) ->
State#state{submon = PMon:monitor(SubPid)};
monitor_subpid(_SubPid, State) ->
State.
do_unsubscribe_(Topic, Subscriber, State) ->
case ets:lookup(mqtt_subproperty, {Topic, Subscriber}) of
[_] ->
emqttd_pubsub:async_unsubscribe(Topic, Subscriber),
@ -258,11 +263,6 @@ unsubscribe_(Topic, Subscriber, State) ->
{error, {subscription_not_found, Topic}}
end.
monitor_subpid(SubPid, State = #state{submon = PMon}) when is_pid(SubPid) ->
State#state{submon = PMon:monitor(SubPid)};
monitor_subpid(_SubPid, State) ->
State.
demonitor_subpid(SubPid, State = #state{submon = PMon}) when is_pid(SubPid) ->
State#state{submon = PMon:demonitor(SubPid)};
demonitor_subpid(_SubPid, State) ->

View File

@ -284,14 +284,18 @@ handle_call({publish, Msg = #mqtt_message{qos = ?QOS_2, pktid = PktId}},
handle_call(Req, _From, State) ->
?UNEXPECTED_REQ(Req, State).
handle_cast({subscribe, TopicTable0, AckFun}, Session = #session{client_id = ClientId,
subscriptions = Subscriptions}) ->
handle_cast({subscribe, RawTopicTable, AckFun}, Session = #session{client_id = ClientId,
subscriptions = Subscriptions}) ->
%% TODO: Ugly...
TopicTable0 = lists:map(fun({T, Q}) ->
{T1, Opts} = emqttd_topic:strip(T),
{T1, [{qos, Q} | Opts]}
end, RawTopicTable),
case emqttd:run_hooks('client.subscribe', [ClientId], TopicTable0) of
{ok, TopicTable} ->
?LOG(info, "Subscribe ~p", [TopicTable], Session),
Subscriptions1 = lists:foldl(
fun({Topic, Qos}, SubDict) ->
fun({Topic, Opts = [{qos, Qos}|_]}, SubDict) ->
case dict:find(Topic, SubDict) of
{ok, Qos} ->
?LOG(warning, "duplicated subscribe: ~s, qos = ~w", [Topic, Qos], Session),
@ -301,7 +305,7 @@ handle_cast({subscribe, TopicTable0, AckFun}, Session = #session{client_id =
?LOG(warning, "duplicated subscribe ~s, old_qos=~w, new_qos=~w", [Topic, OldQos, Qos], Session),
dict:store(Topic, Qos, SubDict);
error ->
emqttd:subscribe(Topic, ClientId, [{qos, Qos}]),
emqttd:subscribe(Topic, ClientId, Opts),
%%TODO: the design is ugly...
%% <MQTT V3.1.1>: 3.8.4
%% Where the Topic Filter is not identical to any existing Subscriptions filter,
@ -311,7 +315,7 @@ handle_cast({subscribe, TopicTable0, AckFun}, Session = #session{client_id =
dict:store(Topic, Qos, SubDict)
end
end, Subscriptions, TopicTable),
AckFun([Qos || {_, Qos} <- TopicTable]),
AckFun([Qos || {_, Qos} <- RawTopicTable]),
emqttd:run_hooks('client.subscribe.after', [ClientId], TopicTable),
hibernate(Session#session{subscriptions = Subscriptions1});
{stop, TopicTable} ->
@ -319,9 +323,11 @@ handle_cast({subscribe, TopicTable0, AckFun}, Session = #session{client_id =
hibernate(Session)
end;
handle_cast({unsubscribe, Topics0}, Session = #session{client_id = ClientId,
subscriptions = Subscriptions}) ->
handle_cast({unsubscribe, RawTopics}, Session = #session{client_id = ClientId,
subscriptions = Subscriptions}) ->
Topics0 = lists:map(fun(Topic) ->
{T, _Opts} = emqttd_topic:strip(Topic), T
end, RawTopics),
case emqttd:run_hooks('client.unsubscribe', [ClientId], Topics0) of
{ok, Topics} ->
?LOG(info, "unsubscribe ~p", [Topics], Session),
@ -329,7 +335,7 @@ handle_cast({unsubscribe, Topics0}, Session = #session{client_id = ClientId,
fun(Topic, SubDict) ->
case dict:find(Topic, SubDict) of
{ok, _Qos} ->
emqttd:unsubscribe(ClientId, Topic),
emqttd:unsubscribe(Topic, ClientId),
dict:erase(Topic, SubDict);
error ->
SubDict