local subscription, shared subscription
This commit is contained in:
parent
811f8a10eb
commit
1bb1799d34
|
@ -35,9 +35,6 @@
|
||||||
%% Hooks API
|
%% Hooks API
|
||||||
-export([hook/4, hook/3, unhook/2, run_hooks/3]).
|
-export([hook/4, hook/3, unhook/2, run_hooks/3]).
|
||||||
|
|
||||||
%% Adapter
|
|
||||||
-export([adapter/1]).
|
|
||||||
|
|
||||||
%% Debug API
|
%% Debug API
|
||||||
-export([dump/0]).
|
-export([dump/0]).
|
||||||
|
|
||||||
|
@ -99,12 +96,12 @@ subscribe(Topic, Subscriber) ->
|
||||||
|
|
||||||
-spec(subscribe(iodata(), subscriber(), [suboption()]) -> ok | pubsub_error()).
|
-spec(subscribe(iodata(), subscriber(), [suboption()]) -> ok | pubsub_error()).
|
||||||
subscribe(Topic, Subscriber, Options) ->
|
subscribe(Topic, Subscriber, Options) ->
|
||||||
with_pubsub(fun(PS) -> PS:subscribe(iolist_to_binary(Topic), Subscriber, Options) end).
|
emqttd_server:subscribe(iolist_to_binary(Topic), Subscriber, Options).
|
||||||
|
|
||||||
%% @doc Publish MQTT Message
|
%% @doc Publish MQTT Message
|
||||||
-spec(publish(mqtt_message()) -> {ok, mqtt_delivery()} | ignore).
|
-spec(publish(mqtt_message()) -> {ok, mqtt_delivery()} | ignore).
|
||||||
publish(Msg) ->
|
publish(Msg) ->
|
||||||
with_pubsub(fun(PS) -> PS:publish(Msg) end).
|
emqttd_server:publish(Msg).
|
||||||
|
|
||||||
%% @doc Unsubscribe
|
%% @doc Unsubscribe
|
||||||
-spec(unsubscribe(iodata()) -> ok | pubsub_error()).
|
-spec(unsubscribe(iodata()) -> ok | pubsub_error()).
|
||||||
|
@ -113,32 +110,30 @@ unsubscribe(Topic) ->
|
||||||
|
|
||||||
-spec(unsubscribe(iodata(), subscriber()) -> ok | pubsub_error()).
|
-spec(unsubscribe(iodata(), subscriber()) -> ok | pubsub_error()).
|
||||||
unsubscribe(Topic, Subscriber) ->
|
unsubscribe(Topic, Subscriber) ->
|
||||||
with_pubsub(fun(PS) -> PS:unsubscribe(iolist_to_binary(Topic), Subscriber) end).
|
emqttd_server:unsubscribe(iolist_to_binary(Topic), Subscriber).
|
||||||
|
|
||||||
-spec(setqos(binary(), subscriber(), mqtt_qos()) -> ok).
|
-spec(setqos(binary(), subscriber(), mqtt_qos()) -> ok).
|
||||||
setqos(Topic, Subscriber, Qos) ->
|
setqos(Topic, Subscriber, Qos) ->
|
||||||
with_pubsub(fun(PS) -> PS:setqos(iolist_to_binary(Topic), Subscriber, Qos) end).
|
emqttd_server:setqos(iolist_to_binary(Topic), Subscriber, Qos).
|
||||||
|
|
||||||
-spec(topics() -> [binary()]).
|
-spec(topics() -> [binary()]).
|
||||||
topics() -> emqttd_router:topics().
|
topics() -> emqttd_router:topics().
|
||||||
|
|
||||||
-spec(subscribers(iodata()) -> list(subscriber())).
|
-spec(subscribers(iodata()) -> list(subscriber())).
|
||||||
subscribers(Topic) ->
|
subscribers(Topic) ->
|
||||||
with_pubsub(fun(PS) -> PS:subscribers(iolist_to_binary(Topic)) end).
|
emqttd_server:subscribers(iolist_to_binary(Topic)).
|
||||||
|
|
||||||
-spec(subscriptions(subscriber()) -> [{binary(), suboption()}]).
|
-spec(subscriptions(subscriber()) -> [{binary(), suboption()}]).
|
||||||
subscriptions(Subscriber) ->
|
subscriptions(Subscriber) ->
|
||||||
with_pubsub(fun(PS) -> PS:subscriptions(Subscriber) end).
|
emqttd_server:subscriptions(Subscriber).
|
||||||
|
|
||||||
-spec(is_subscribed(iodata(), subscriber()) -> boolean()).
|
-spec(is_subscribed(iodata(), subscriber()) -> boolean()).
|
||||||
is_subscribed(Topic, Subscriber) ->
|
is_subscribed(Topic, Subscriber) ->
|
||||||
with_pubsub(fun(PS) -> PS:is_subscribed(iolist_to_binary(Topic), Subscriber) end).
|
emqttd_server:is_subscribed(iolist_to_binary(Topic), Subscriber).
|
||||||
|
|
||||||
-spec(subscriber_down(subscriber()) -> ok).
|
-spec(subscriber_down(subscriber()) -> ok).
|
||||||
subscriber_down(Subscriber) ->
|
subscriber_down(Subscriber) ->
|
||||||
with_pubsub(fun(PS) -> PS:subscriber_down(Subscriber) end).
|
emqttd_server:subscriber_down(Subscriber).
|
||||||
|
|
||||||
with_pubsub(Fun) -> Fun(env(pubsub_server, emqttd_server)).
|
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% Hooks API
|
%% Hooks API
|
||||||
|
@ -160,17 +155,9 @@ unhook(Hook, Function) ->
|
||||||
run_hooks(Hook, Args, Acc) ->
|
run_hooks(Hook, Args, Acc) ->
|
||||||
emqttd_hook:run(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
|
%% Debug
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
dump() -> with_pubsub(fun(PS) -> lists:append([PS:dump(), emqttd_router:dump()]) end).
|
dump() -> lists:append([emqttd_server:dump(), emqttd_router:dump()]).
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
-include("emqttd_internal.hrl").
|
-include("emqttd_internal.hrl").
|
||||||
|
|
||||||
%% API Function Exports
|
%% API Function Exports
|
||||||
-export([start_link/3]).
|
-export([start_link/5]).
|
||||||
|
|
||||||
%% 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,
|
||||||
|
@ -33,7 +33,8 @@
|
||||||
|
|
||||||
-define(PING_DOWN_INTERVAL, 1000).
|
-define(PING_DOWN_INTERVAL, 1000).
|
||||||
|
|
||||||
-record(state, {node, subtopic,
|
-record(state, {pool, id,
|
||||||
|
node, subtopic,
|
||||||
qos = ?QOS_2,
|
qos = ?QOS_2,
|
||||||
topic_suffix = <<>>,
|
topic_suffix = <<>>,
|
||||||
topic_prefix = <<>>,
|
topic_prefix = <<>>,
|
||||||
|
@ -55,25 +56,28 @@
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
%% @doc Start a bridge
|
%% @doc Start a bridge
|
||||||
-spec(start_link(atom(), binary(), [option()]) -> {ok, pid()} | ignore | {error, term()}).
|
-spec(start_link(any(), pos_integer(), atom(), binary(), [option()]) ->
|
||||||
start_link(Node, Topic, Options) ->
|
{ok, pid()} | ignore | {error, term()}).
|
||||||
gen_server2:start_link(?MODULE, [Node, Topic, Options], []).
|
start_link(Pool, Id, Node, Topic, Options) ->
|
||||||
|
gen_server2:start_link(?MODULE, [Pool, Id, Node, Topic, Options], []).
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% gen_server callbacks
|
%% gen_server callbacks
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
init([Node, Topic, Options]) ->
|
init([Pool, Id, Node, Topic, Options]) ->
|
||||||
|
?GPROC_POOL(join, Pool, Id),
|
||||||
process_flag(trap_exit, true),
|
process_flag(trap_exit, true),
|
||||||
case net_kernel:connect_node(Node) of
|
case net_kernel:connect_node(Node) of
|
||||||
true ->
|
true ->
|
||||||
true = erlang:monitor_node(Node, true),
|
true = erlang:monitor_node(Node, true),
|
||||||
|
Share = iolist_to_binary(["$bridge:", atom_to_list(Node), ":", Topic]),
|
||||||
|
emqttd:subscribe(Topic, self(), [local, {share, Share}]),
|
||||||
State = parse_opts(Options, #state{node = Node, subtopic = Topic}),
|
State = parse_opts(Options, #state{node = Node, subtopic = Topic}),
|
||||||
MQueue = emqttd_mqueue:new(qname(Node, Topic),
|
MQueue = emqttd_mqueue:new(qname(Node, Topic),
|
||||||
[{max_len, State#state.max_queue_len}],
|
[{max_len, State#state.max_queue_len}],
|
||||||
emqttd_alarm:alarm_fun()),
|
emqttd_alarm:alarm_fun()),
|
||||||
emqttd:subscribe(Topic),
|
{ok, State#state{pool = Pool, id = Id, mqueue = MQueue}};
|
||||||
{ok, State#state{mqueue = MQueue}};
|
|
||||||
false ->
|
false ->
|
||||||
{stop, {cannot_connect, Node}}
|
{stop, {cannot_connect, Node}}
|
||||||
end.
|
end.
|
||||||
|
@ -119,7 +123,7 @@ handle_info({nodedown, Node}, State = #state{node = Node, ping_down_interval = I
|
||||||
handle_info({nodeup, Node}, State = #state{node = Node}) ->
|
handle_info({nodeup, Node}, State = #state{node = Node}) ->
|
||||||
%% TODO: Really fast??
|
%% TODO: Really fast??
|
||||||
case emqttd:is_running(Node) of
|
case emqttd:is_running(Node) of
|
||||||
true ->
|
true ->
|
||||||
lager:warning("Bridge Node Up: ~p", [Node]),
|
lager:warning("Bridge Node Up: ~p", [Node]),
|
||||||
{noreply, dequeue(State#state{status = up})};
|
{noreply, dequeue(State#state{status = up})};
|
||||||
false ->
|
false ->
|
||||||
|
@ -145,7 +149,8 @@ handle_info({'EXIT', _Pid, normal}, State) ->
|
||||||
handle_info(Info, State) ->
|
handle_info(Info, State) ->
|
||||||
?UNEXPECTED_INFO(Info, State).
|
?UNEXPECTED_INFO(Info, State).
|
||||||
|
|
||||||
terminate(_Reason, _State) ->
|
terminate(_Reason, #state{pool = Pool, id = Id}) ->
|
||||||
|
?GPROC_POOL(leave, Pool, Id),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
code_change(_OldVsn, State, _Extra) ->
|
code_change(_OldVsn, State, _Extra) ->
|
||||||
|
|
|
@ -16,27 +16,15 @@
|
||||||
|
|
||||||
-module(emqttd_bridge_sup).
|
-module(emqttd_bridge_sup).
|
||||||
|
|
||||||
-behavior(supervisor).
|
|
||||||
|
|
||||||
-export([start_link/3]).
|
-export([start_link/3]).
|
||||||
|
|
||||||
-export([init/1]).
|
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% API
|
%% API
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
%% @doc Start bridge supervisor
|
%% @doc Start bridge pool supervisor
|
||||||
-spec(start_link(atom(), binary(), [emqttd_bridge:option()]) -> {ok, pid()} | {error, any()}).
|
-spec(start_link(atom(), binary(), [emqttd_bridge:option()]) -> {ok, pid()} | {error, any()}).
|
||||||
start_link(Node, Topic, Options) ->
|
start_link(Node, Topic, Options) ->
|
||||||
supervisor:start_link(?MODULE, [Node, Topic, Options]).
|
MFA = {emqttd_bridge, start_link, [Node, Topic, Options]},
|
||||||
|
emqttd_pool_sup:start_link({bridge, Node, Topic}, random, MFA).
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% Supervisor callbacks
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
|
|
||||||
init([Node, Topic, Options]) ->
|
|
||||||
{ok, {{one_for_all, 10, 100},
|
|
||||||
[{bridge, {emqttd_bridge, start_link, [Node, Topic, Options]},
|
|
||||||
transient, 10000, worker, [emqttd_bridge]}]}}.
|
|
||||||
|
|
||||||
|
|
|
@ -66,11 +66,7 @@ init([]) ->
|
||||||
{ok, {{one_for_one, 10, 100}, []}}.
|
{ok, {{one_for_one, 10, 100}, []}}.
|
||||||
|
|
||||||
bridge_spec(Node, Topic, Options) ->
|
bridge_spec(Node, Topic, Options) ->
|
||||||
SupMod = sup_mod(emqttd:adapter(bridge)),
|
|
||||||
{?CHILD_ID(Node, Topic),
|
{?CHILD_ID(Node, Topic),
|
||||||
{SupMod, start_link, [Node, Topic, Options]},
|
{emqttd_bridge_sup, start_link, [Node, Topic, Options]},
|
||||||
permanent, infinity, supervisor, [SupMod]}.
|
permanent, infinity, supervisor, [emqttd_bridge_sup]}.
|
||||||
|
|
||||||
sup_mod(Adaper) ->
|
|
||||||
list_to_atom(atom_to_list(Adaper) ++ "_sup").
|
|
||||||
|
|
||||||
|
|
|
@ -22,11 +22,14 @@
|
||||||
|
|
||||||
-include("emqttd_internal.hrl").
|
-include("emqttd_internal.hrl").
|
||||||
|
|
||||||
%% API Exports
|
%% Start API.
|
||||||
-export([start_link/3, subscribe/2, unsubscribe/2, publish/2,
|
-export([start_link/3]).
|
||||||
async_subscribe/2, async_unsubscribe/2]).
|
|
||||||
|
|
||||||
-export([subscribers/1, dispatch/2]).
|
%% PubSub API.
|
||||||
|
-export([subscribe/3, async_subscribe/3, publish/2, unsubscribe/3,
|
||||||
|
async_unsubscribe/3, subscribers/1]).
|
||||||
|
|
||||||
|
-export([dispatch/2]).
|
||||||
|
|
||||||
%% gen_server.
|
%% gen_server.
|
||||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||||
|
@ -36,24 +39,38 @@
|
||||||
|
|
||||||
-define(PUBSUB, ?MODULE).
|
-define(PUBSUB, ?MODULE).
|
||||||
|
|
||||||
-spec(start_link(atom(), pos_integer(), [tuple()]) -> {ok, pid()} | ignore | {error, any()}).
|
-define(is_local(Options), lists:member(local, Options)).
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Start PubSub
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
%% @doc Start one Pubsub
|
||||||
|
-spec(start_link(atom(), pos_integer(), list()) -> {ok, pid()} | ignore | {error, any()}).
|
||||||
start_link(Pool, Id, Env) ->
|
start_link(Pool, Id, Env) ->
|
||||||
gen_server2:start_link({local, ?PROC_NAME(?MODULE, Id)}, ?MODULE, [Pool, Id, Env], []).
|
gen_server2:start_link({local, ?PROC_NAME(?MODULE, Id)}, ?MODULE, [Pool, Id, Env], []).
|
||||||
|
|
||||||
-spec(subscribe(binary(), emqttd:subscriber()) -> ok).
|
%%--------------------------------------------------------------------
|
||||||
subscribe(Topic, Subscriber) ->
|
%% PubSub API
|
||||||
call(pick(Topic), {subscribe, Topic, Subscriber}).
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
-spec(async_subscribe(binary(), emqttd:subscriber()) -> ok).
|
%% @doc Subscribe a Topic
|
||||||
async_subscribe(Topic, Subscriber) ->
|
-spec(subscribe(binary(), emqttd:subscriber(), [emqttd:suboption()]) -> ok).
|
||||||
cast(pick(Topic), {subscribe, Topic, Subscriber}).
|
subscribe(Topic, Subscriber, Options) ->
|
||||||
|
call(pick(Topic), {subscribe, Topic, Subscriber, Options}).
|
||||||
|
|
||||||
|
-spec(async_subscribe(binary(), emqttd:subscriber(), [emqttd:suboption()]) -> ok).
|
||||||
|
async_subscribe(Topic, Subscriber, Options) ->
|
||||||
|
cast(pick(Topic), {subscribe, Topic, Subscriber, Options}).
|
||||||
|
|
||||||
|
%% @doc Publish MQTT Message to Topic
|
||||||
-spec(publish(binary(), any()) -> {ok, mqtt_delivery()} | ignore).
|
-spec(publish(binary(), any()) -> {ok, mqtt_delivery()} | ignore).
|
||||||
publish(Topic, Msg) ->
|
publish(Topic, Msg) ->
|
||||||
route(emqttd_router:match(Topic), delivery(Msg)).
|
route(lists:append(emqttd_router:match(Topic),
|
||||||
|
emqttd_router:match_local(Topic)), delivery(Msg)).
|
||||||
|
|
||||||
route([], _Delivery) ->
|
route([], #mqtt_delivery{message = #mqtt_message{topic = Topic}}) ->
|
||||||
ignore;
|
dropped(Topic), ignore;
|
||||||
|
|
||||||
%% Dispatch on the local node
|
%% Dispatch on the local node
|
||||||
route([#mqtt_route{topic = To, node = Node}],
|
route([#mqtt_route{topic = To, node = Node}],
|
||||||
|
@ -83,7 +100,7 @@ dispatch(Topic, Delivery = #mqtt_delivery{message = Msg, flows = Flows}) ->
|
||||||
dropped(Topic), {ok, Delivery};
|
dropped(Topic), {ok, Delivery};
|
||||||
[Sub] -> %% optimize?
|
[Sub] -> %% optimize?
|
||||||
dispatch(Sub, Topic, Msg),
|
dispatch(Sub, Topic, Msg),
|
||||||
{ok, Delivery#mqtt_delivery{flows = [{dispatch, Topic, 1} | Flows]}};
|
{ok, Delivery#mqtt_delivery{flows = [{dispatch, Topic, 1}|Flows]}};
|
||||||
Subscribers ->
|
Subscribers ->
|
||||||
Flows1 = [{dispatch, Topic, length(Subscribers)} | Flows],
|
Flows1 = [{dispatch, Topic, length(Subscribers)} | Flows],
|
||||||
lists:foreach(fun(Sub) -> dispatch(Sub, Topic, Msg) end, Subscribers),
|
lists:foreach(fun(Sub) -> dispatch(Sub, Topic, Msg) end, Subscribers),
|
||||||
|
@ -93,10 +110,27 @@ dispatch(Topic, Delivery = #mqtt_delivery{message = Msg, flows = Flows}) ->
|
||||||
dispatch(Pid, Topic, Msg) when is_pid(Pid) ->
|
dispatch(Pid, Topic, Msg) when is_pid(Pid) ->
|
||||||
Pid ! {dispatch, Topic, Msg};
|
Pid ! {dispatch, Topic, Msg};
|
||||||
dispatch(SubId, Topic, Msg) when is_binary(SubId) ->
|
dispatch(SubId, Topic, Msg) when is_binary(SubId) ->
|
||||||
emqttd_sm:dispatch(SubId, Topic, Msg).
|
emqttd_sm:dispatch(SubId, Topic, Msg);
|
||||||
|
dispatch({_Share, [Sub]}, Topic, Msg) ->
|
||||||
|
dispatch(Sub, Topic, Msg);
|
||||||
|
dispatch({_Share, []}, _Topic, _Msg) ->
|
||||||
|
ok;
|
||||||
|
dispatch({_Share, Subs}, Topic, Msg) ->
|
||||||
|
dispatch(lists:nth(rand:uniform(length(Subs)), Subs), Topic, Msg).
|
||||||
|
|
||||||
subscribers(Topic) ->
|
subscribers(Topic) ->
|
||||||
try ets:lookup_element(mqtt_subscriber, Topic, 2) catch error:badarg -> [] end.
|
group_by_share(try ets:lookup_element(mqtt_subscriber, Topic, 2) catch error:badarg -> [] end).
|
||||||
|
|
||||||
|
group_by_share([]) -> [];
|
||||||
|
|
||||||
|
group_by_share(Subscribers) ->
|
||||||
|
{Subs1, Shares1} =
|
||||||
|
lists:foldl(fun({Share, Sub}, {Subs, Shares}) ->
|
||||||
|
{Subs, dict:append(Share, Sub, Shares)};
|
||||||
|
(Sub, {Subs, Shares}) ->
|
||||||
|
{[Sub|Subs], Shares}
|
||||||
|
end, {[], dict:new()}, Subscribers),
|
||||||
|
lists:append(Subs1, dict:to_list(Shares1)).
|
||||||
|
|
||||||
%% @private
|
%% @private
|
||||||
%% @doc Ingore $SYS Messages.
|
%% @doc Ingore $SYS Messages.
|
||||||
|
@ -105,45 +139,50 @@ dropped(<<"$SYS/", _/binary>>) ->
|
||||||
dropped(_Topic) ->
|
dropped(_Topic) ->
|
||||||
emqttd_metrics:inc('messages/dropped').
|
emqttd_metrics:inc('messages/dropped').
|
||||||
|
|
||||||
-spec(unsubscribe(binary(), emqttd:subscriber()) -> ok).
|
%% @doc Unsubscribe
|
||||||
unsubscribe(Topic, Subscriber) ->
|
-spec(unsubscribe(binary(), emqttd:subscriber(), [emqttd:suboption()]) -> ok).
|
||||||
call(pick(Topic), {unsubscribe, Topic, Subscriber}).
|
unsubscribe(Topic, Subscriber, Options) ->
|
||||||
|
call(pick(Topic), {unsubscribe, Topic, Subscriber, Options}).
|
||||||
|
|
||||||
-spec(async_unsubscribe(binary(), emqttd:subscriber()) -> ok).
|
-spec(async_unsubscribe(binary(), emqttd:subscriber(), [emqttd:suboption()]) -> ok).
|
||||||
async_unsubscribe(Topic, Subscriber) ->
|
async_unsubscribe(Topic, Subscriber, Options) ->
|
||||||
cast(pick(Topic), {unsubscribe, Topic, Subscriber}).
|
cast(pick(Topic), {unsubscribe, Topic, Subscriber, Options}).
|
||||||
|
|
||||||
call(Server, Req) ->
|
call(PubSub, Req) when is_pid(PubSub) ->
|
||||||
gen_server2:call(Server, Req, infinity).
|
gen_server2:call(PubSub, Req, infinity).
|
||||||
|
|
||||||
cast(Server, Msg) ->
|
cast(PubSub, Msg) when is_pid(PubSub) ->
|
||||||
gen_server2:cast(Server, Msg).
|
gen_server2:cast(PubSub, Msg).
|
||||||
|
|
||||||
pick(Topic) ->
|
pick(Subscriber) ->
|
||||||
gproc_pool:pick_worker(pubsub, Topic).
|
gproc_pool:pick_worker(pubsub, Subscriber).
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% gen_server Callbacks
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
init([Pool, Id, Env]) ->
|
init([Pool, Id, Env]) ->
|
||||||
?GPROC_POOL(join, Pool, Id),
|
?GPROC_POOL(join, Pool, Id),
|
||||||
{ok, #state{pool = Pool, id = Id, env = Env}}.
|
{ok, #state{pool = Pool, id = Id, env = Env}}.
|
||||||
|
|
||||||
handle_call({subscribe, Topic, Subscriber}, _From, State) ->
|
handle_call({subscribe, Topic, Subscriber, Options}, _From, State) ->
|
||||||
add_subscriber_(Topic, Subscriber),
|
add_subscriber(Topic, Subscriber, Options),
|
||||||
{reply, ok, setstats(State)};
|
{reply, ok, setstats(State)};
|
||||||
|
|
||||||
handle_call({unsubscribe, Topic, Subscriber}, _From, State) ->
|
handle_call({unsubscribe, Topic, Subscriber, Options}, _From, State) ->
|
||||||
del_subscriber_(Topic, Subscriber),
|
del_subscriber(Topic, Subscriber, Options),
|
||||||
{reply, ok, setstats(State)};
|
{reply, ok, setstats(State), hibernate};
|
||||||
|
|
||||||
handle_call(Req, _From, State) ->
|
handle_call(Req, _From, State) ->
|
||||||
?UNEXPECTED_REQ(Req, State).
|
?UNEXPECTED_REQ(Req, State).
|
||||||
|
|
||||||
handle_cast({subscribe, Topic, Subscriber}, State) ->
|
handle_cast({subscribe, Topic, Subscriber, Options}, State) ->
|
||||||
add_subscriber_(Topic, Subscriber),
|
add_subscriber(Topic, Subscriber, Options),
|
||||||
{noreply, setstats(State)};
|
{noreply, setstats(State)};
|
||||||
|
|
||||||
handle_cast({unsubscribe, Topic, Subscriber}, State) ->
|
handle_cast({unsubscribe, Topic, Subscriber, Options}, State) ->
|
||||||
del_subscriber_(Topic, Subscriber),
|
del_subscriber(Topic, Subscriber, Options),
|
||||||
{noreply, setstats(State)};
|
{noreply, setstats(State), hibernate};
|
||||||
|
|
||||||
handle_cast(Msg, State) ->
|
handle_cast(Msg, State) ->
|
||||||
?UNEXPECTED_MSG(Msg, State).
|
?UNEXPECTED_MSG(Msg, State).
|
||||||
|
@ -161,17 +200,42 @@ code_change(_OldVsn, State, _Extra) ->
|
||||||
%% Internel Functions
|
%% Internel Functions
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
add_subscriber_(Topic, Subscriber) ->
|
add_subscriber(Topic, Subscriber, Options) ->
|
||||||
(not ets:member(mqtt_subscriber, Topic))
|
Share = proplists:get_value(share, Options),
|
||||||
andalso emqttd_router:add_route(Topic),
|
case ?is_local(Options) of
|
||||||
ets:insert(mqtt_subscriber, {Topic, Subscriber}).
|
false -> add_subscriber_(Share, Topic, Subscriber);
|
||||||
|
true -> add_local_subscriber_(Share, Topic, Subscriber)
|
||||||
|
end.
|
||||||
|
|
||||||
del_subscriber_(Topic, Subscriber) ->
|
add_subscriber_(Share, Topic, Subscriber) ->
|
||||||
ets:delete_object(mqtt_subscriber, {Topic, Subscriber}),
|
(not ets:member(mqtt_subscriber, Topic)) andalso emqttd_router:add_route(Topic),
|
||||||
(not ets:member(mqtt_subscriber, Topic))
|
ets:insert(mqtt_subscriber, {Topic, shared(Share, Subscriber)}).
|
||||||
andalso emqttd_router:del_route(Topic).
|
|
||||||
|
add_local_subscriber_(Share, Topic, Subscriber) ->
|
||||||
|
(not ets:member(mqtt_subscriber, {local, Topic})) andalso emqttd_router:add_local_route(Topic),
|
||||||
|
ets:insert(mqtt_subscriber, {{local, Topic}, shared(Share, Subscriber)}).
|
||||||
|
|
||||||
|
del_subscriber(Topic, Subscriber, Options) ->
|
||||||
|
Share = proplists:get_value(share, Options),
|
||||||
|
case ?is_local(Options) of
|
||||||
|
false -> del_subscriber_(Share, Topic, Subscriber);
|
||||||
|
true -> del_local_subscriber_(Share, Topic, Subscriber)
|
||||||
|
end.
|
||||||
|
|
||||||
|
del_subscriber_(Share, Topic, Subscriber) ->
|
||||||
|
ets:delete_object(mqtt_subscriber, {Topic, shared(Share, Subscriber)}),
|
||||||
|
(not ets:member(mqtt_subscriber, Topic)) andalso emqttd_router:del_route(Topic).
|
||||||
|
|
||||||
|
del_local_subscriber_(Share, Topic, Subscriber) ->
|
||||||
|
ets:delete_object(mqtt_subscriber, {{local, Topic}, shared(Share, Subscriber)}),
|
||||||
|
(not ets:member(subscriber, {local, Topic})) andalso emqttd_router:del_local_route(Topic).
|
||||||
|
|
||||||
|
shared(undefined, Subscriber) ->
|
||||||
|
Subscriber;
|
||||||
|
shared(Share, Subscriber) ->
|
||||||
|
{Share, Subscriber}.
|
||||||
|
|
||||||
setstats(State) ->
|
setstats(State) ->
|
||||||
emqttd_stats:setstats('subscribers/count', 'subscribers/max',
|
emqttd_stats:setstats('subscribers/count', 'subscribers/max', ets:info(mqtt_subscriber, size)),
|
||||||
ets:info(mqtt_subscriber, size)), State.
|
State.
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ pubsub_pool() ->
|
||||||
hd([Pid || {pubsub_pool, Pid, _, _} <- supervisor:which_children(?MODULE)]).
|
hd([Pid || {pubsub_pool, Pid, _, _} <- supervisor:which_children(?MODULE)]).
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% Supervisor callbacks
|
%% Supervisor Callbacks
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
init([Env]) ->
|
init([Env]) ->
|
||||||
|
@ -57,10 +57,10 @@ pool_size(Env) ->
|
||||||
|
|
||||||
pool_sup(Name, Env) ->
|
pool_sup(Name, Env) ->
|
||||||
Pool = list_to_atom(atom_to_list(Name) ++ "_pool"),
|
Pool = list_to_atom(atom_to_list(Name) ++ "_pool"),
|
||||||
MFA = {emqttd:adapter(Name), start_link, [Env]},
|
Mod = list_to_atom("emqttd_" ++ atom_to_list(Name)),
|
||||||
|
MFA = {Mod, start_link, [Env]},
|
||||||
emqttd_pool_sup:spec(Pool, [Name, hash, pool_size(Env), MFA]).
|
emqttd_pool_sup:spec(Pool, [Name, hash, pool_size(Env), MFA]).
|
||||||
|
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% Create PubSub Tables
|
%% Create PubSub Tables
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
|
@ -133,7 +133,9 @@ setqos(Topic, Subscriber, Qos) when is_binary(Topic) ->
|
||||||
|
|
||||||
-spec(subscriptions(emqttd:subscriber()) -> [{binary(), list(emqttd:suboption())}]).
|
-spec(subscriptions(emqttd:subscriber()) -> [{binary(), list(emqttd:suboption())}]).
|
||||||
subscriptions(Subscriber) ->
|
subscriptions(Subscriber) ->
|
||||||
lists:map(fun({_, Topic}) ->
|
lists:map(fun({_, {_Share, Topic}}) ->
|
||||||
|
subscription(Topic, Subscriber);
|
||||||
|
({_, Topic}) ->
|
||||||
subscription(Topic, Subscriber)
|
subscription(Topic, Subscriber)
|
||||||
end, ets:lookup(mqtt_subscription, Subscriber)).
|
end, ets:lookup(mqtt_subscription, Subscriber)).
|
||||||
|
|
||||||
|
@ -235,14 +237,20 @@ code_change(_OldVsn, State, _Extra) ->
|
||||||
do_subscribe_(Topic, Subscriber, Options, State) ->
|
do_subscribe_(Topic, Subscriber, Options, State) ->
|
||||||
case ets:lookup(mqtt_subproperty, {Topic, Subscriber}) of
|
case ets:lookup(mqtt_subproperty, {Topic, Subscriber}) of
|
||||||
[] ->
|
[] ->
|
||||||
emqttd_pubsub:async_subscribe(Topic, Subscriber),
|
emqttd_pubsub:async_subscribe(Topic, Subscriber, Options),
|
||||||
ets:insert(mqtt_subscription, {Subscriber, Topic}),
|
Share = proplists:get_value(share, Options),
|
||||||
|
add_subscription_(Share, Subscriber, Topic),
|
||||||
ets:insert(mqtt_subproperty, {{Topic, Subscriber}, Options}),
|
ets:insert(mqtt_subproperty, {{Topic, Subscriber}, Options}),
|
||||||
{ok, monitor_subpid(Subscriber, State)};
|
{ok, monitor_subpid(Subscriber, State)};
|
||||||
[_] ->
|
[_] ->
|
||||||
{error, {already_subscribed, Topic}}
|
{error, {already_subscribed, Topic}}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
add_subscription_(undefined, Subscriber, Topic) ->
|
||||||
|
ets:insert(mqtt_subscription, {Subscriber, Topic});
|
||||||
|
add_subscription_(Share, Subscriber, Topic) ->
|
||||||
|
ets:insert(mqtt_subscription, {Subscriber, {Share, Topic}}).
|
||||||
|
|
||||||
monitor_subpid(SubPid, State = #state{submon = PMon}) when is_pid(SubPid) ->
|
monitor_subpid(SubPid, State = #state{submon = PMon}) when is_pid(SubPid) ->
|
||||||
State#state{submon = PMon:monitor(SubPid)};
|
State#state{submon = PMon:monitor(SubPid)};
|
||||||
monitor_subpid(_SubPid, State) ->
|
monitor_subpid(_SubPid, State) ->
|
||||||
|
@ -250,9 +258,10 @@ monitor_subpid(_SubPid, State) ->
|
||||||
|
|
||||||
do_unsubscribe_(Topic, Subscriber, State) ->
|
do_unsubscribe_(Topic, Subscriber, State) ->
|
||||||
case ets:lookup(mqtt_subproperty, {Topic, Subscriber}) of
|
case ets:lookup(mqtt_subproperty, {Topic, Subscriber}) of
|
||||||
[_] ->
|
[{_, Options}] ->
|
||||||
emqttd_pubsub:async_unsubscribe(Topic, Subscriber),
|
emqttd_pubsub:async_unsubscribe(Topic, Subscriber, Options),
|
||||||
ets:delete_object(mqtt_subscription, {Subscriber, Topic}),
|
Share = proplists:get_value(share, Options),
|
||||||
|
del_subscription_(Share, Subscriber, Topic),
|
||||||
ets:delete(mqtt_subproperty, {Topic, Subscriber}),
|
ets:delete(mqtt_subproperty, {Topic, Subscriber}),
|
||||||
{ok, case ets:member(mqtt_subscription, Subscriber) of
|
{ok, case ets:member(mqtt_subscription, Subscriber) of
|
||||||
true -> State;
|
true -> State;
|
||||||
|
@ -262,24 +271,32 @@ do_unsubscribe_(Topic, Subscriber, State) ->
|
||||||
{error, {subscription_not_found, Topic}}
|
{error, {subscription_not_found, Topic}}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
del_subscription_(undefined, Subscriber, Topic) ->
|
||||||
|
ets:delete_object(mqtt_subscription, {Subscriber, Topic});
|
||||||
|
del_subscription_(Share, Subscriber, Topic) ->
|
||||||
|
ets:delete_object(mqtt_subscription, {Subscriber, {Share, Topic}}).
|
||||||
|
|
||||||
demonitor_subpid(SubPid, State = #state{submon = PMon}) when is_pid(SubPid) ->
|
demonitor_subpid(SubPid, State = #state{submon = PMon}) when is_pid(SubPid) ->
|
||||||
State#state{submon = PMon:demonitor(SubPid)};
|
State#state{submon = PMon:demonitor(SubPid)};
|
||||||
demonitor_subpid(_SubPid, State) ->
|
demonitor_subpid(_SubPid, State) ->
|
||||||
State.
|
State.
|
||||||
|
|
||||||
subscriber_down_(Subscriber) ->
|
subscriber_down_(Subscriber) ->
|
||||||
lists:foreach(fun({_, Topic}) ->
|
lists:foreach(fun({_, {Share, Topic}}) ->
|
||||||
subscriber_down_(Subscriber, Topic)
|
subscriber_down_(Share, Subscriber, Topic);
|
||||||
|
({_, Topic}) ->
|
||||||
|
subscriber_down_(undefined, Subscriber, Topic)
|
||||||
end, ets:lookup(mqtt_subscription, Subscriber)),
|
end, ets:lookup(mqtt_subscription, Subscriber)),
|
||||||
ets:delete(mqtt_subscription, Subscriber).
|
ets:delete(mqtt_subscription, Subscriber).
|
||||||
|
|
||||||
subscriber_down_(Subscriber, Topic) ->
|
subscriber_down_(Share, Subscriber, Topic) ->
|
||||||
case ets:lookup(mqtt_subproperty, {Topic, Subscriber}) of
|
case ets:lookup(mqtt_subproperty, {Topic, Subscriber}) of
|
||||||
[] ->
|
[] ->
|
||||||
%% here?
|
%% TODO:....???
|
||||||
emqttd_pubsub:async_unsubscribe(Topic, Subscriber);
|
Options = if Share == undefined -> []; true -> [{share, Share}] end,
|
||||||
[_] ->
|
emqttd_pubsub:async_unsubscribe(Topic, Subscriber, Options);
|
||||||
emqttd_pubsub:async_unsubscribe(Topic, Subscriber),
|
[{_, Options}] ->
|
||||||
|
emqttd_pubsub:async_unsubscribe(Topic, Subscriber, Options),
|
||||||
ets:delete(mqtt_subproperty, {Topic, Subscriber})
|
ets:delete(mqtt_subproperty, {Topic, Subscriber})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue