Merge branch 'master' of github.com:emqtt/emqtt

This commit is contained in:
Ery Lee 2015-03-23 15:17:36 +08:00
commit 42c220cfa5
12 changed files with 225 additions and 145 deletions

View File

@ -2,6 +2,30 @@
eMQTTD ChangeLog eMQTTD ChangeLog
================== ==================
0.5.4-alpha (2015-03-22)
-------------------------
Benchmark this release on a ubuntu/14.04 server with 8 cores, 32G memory from QingCloud.com:
```
200K Connections,
30K Messages/Sec,
20Mbps In/Out Traffic,
200K Topics,
200K Subscribers,
Consumed 7G memory, 40% CPU/core
```
Benchmark code: https://github.com/emqtt/emqttd_benchmark
Change: rewrite emqttd_pubsub to handle more concurrent subscribe requests.
Change: ./bin/emqttd_ctl add 'stats', 'metrics' commands.
Bugfix: issue #71, #72
0.5.3-alpha (2015-03-19) 0.5.3-alpha (2015-03-19)
------------------------- -------------------------

View File

@ -1,15 +1,24 @@
# eMQTT [![Build Status](https://travis-ci.org/emqtt/emqttd.svg?branch=master)](https://travis-ci.org/emqtt/emqttd) # eMQTTD [![Build Status](https://travis-ci.org/emqtt/emqttd.svg?branch=master)](https://travis-ci.org/emqtt/emqttd)
eMQTT is a clusterable, massively scalable, fault-tolerant and extensible MQTT V3.1/V3.1.1 broker written in Erlang/OTP. eMQTTD is a clusterable, massively scalable, fault-tolerant and extensible MQTT V3.1/V3.1.1 broker written in Erlang/OTP.
eMQTT support MQTT V3.1/V3.1.1 Protocol Specification. eMQTTD support MQTT V3.1/V3.1.1 Protocol Specification.
eMQTTD requires Erlang R17+.
## Benchmark
Benchmark 0.5.4-alpha on a ubuntu/14.04 server with 8 cores, 32G memory from QingCloud:
200K Connections, 200K Topics, 20K Messages/sec, 20Mbps In/Out with 7G Memory, 40%CPU/core
eMQTT requires Erlang R17+.
## NOTICE ## NOTICE
eMQTTD still cannot handle massive retained messages. eMQTTD still cannot handle massive retained messages.
## Featues ## Featues
Full MQTT V3.1.1 Support Full MQTT V3.1.1 Support
@ -41,15 +50,15 @@ Bridge brokers locally or remotelly
## Startup in Five Minutes ## Startup in Five Minutes
``` ```
$ git clone git://github.com/emqtt/emqtt.git $ git clone git://github.com/emqtt/emqttd.git
$ cd emqtt $ cd emqttd
$ make && make dist $ make && make dist
$ cd rel/emqtt $ cd rel/emqttd
$ ./bin/emqtt console $ ./bin/emqttd console
``` ```
## Deploy and Start ## Deploy and Start
@ -57,18 +66,18 @@ $ ./bin/emqtt console
### start ### start
``` ```
cp -R rel/emqtt $INSTALL_DIR cp -R rel/emqttd $INSTALL_DIR
cd $INSTALL_DIR/emqtt cd $INSTALL_DIR/emqttd
./bin/emqtt start ./bin/emqttd start
``` ```
### stop ### stop
``` ```
./bin/emqtt stop ./bin/emqttd stop
``` ```
@ -77,7 +86,7 @@ cd $INSTALL_DIR/emqtt
### etc/app.config ### etc/app.config
``` ```
{emqtt, [ {emqttd, [
{auth, {anonymous, []}}, %internal, anonymous {auth, {anonymous, []}}, %internal, anonymous
{listen, [ {listen, [
{mqtt, 1883, [ {mqtt, 1883, [
@ -104,7 +113,7 @@ cd $INSTALL_DIR/emqtt
``` ```
-name emqtt@127.0.0.1 -name emqttd@127.0.0.1
-setcookie emqtt -setcookie emqtt
@ -113,7 +122,7 @@ cd $INSTALL_DIR/emqtt
When nodes clustered, vm.args should be configured as below: When nodes clustered, vm.args should be configured as below:
``` ```
-name emqtt@host1 -name emqttd@host1
``` ```
## Cluster ## Cluster
@ -123,22 +132,22 @@ Suppose we cluster two nodes on 'host1', 'host2', Steps:
on 'host1': on 'host1':
``` ```
./bin/emqtt start ./bin/emqttd start
``` ```
on 'host2': on 'host2':
``` ```
./bin/emqtt start ./bin/emqttd start
./bin/emqtt_ctl cluster emqtt@host1 ./bin/emqttd_ctl cluster emqttd@host1
``` ```
Run './bin/emqtt_ctl cluster' on 'host1' or 'host2' to check cluster nodes. Run './bin/emqttd_ctl cluster' on 'host1' or 'host2' to check cluster nodes.
## HTTP API ## HTTP API
eMQTT support http to publish message. eMQTTD support http to publish message.
Example: Example:
@ -163,7 +172,7 @@ message | Message
## Design ## Design
[Design Wiki](https://github.com/emqtt/emqtt/wiki) [Design Wiki](https://github.com/emqtt/emqttd/wiki)
## License ## License

5
TODO
View File

@ -1,4 +1,9 @@
v0.9.0-alpha (2015-03-20)
-------------------------
emqtt_sm, emqtt_cm, emqtt_pubsub performance issue...
v0.8.0-alpha (2015-03-20) v0.8.0-alpha (2015-03-20)
------------------------- -------------------------

View File

@ -1,7 +1,7 @@
{application, emqttd, {application, emqttd,
[ [
{description, "Erlang MQTT Broker"}, {description, "Erlang MQTT Broker"},
{vsn, "0.5.0"}, {vsn, "0.5.4"},
{modules, []}, {modules, []},
{registered, []}, {registered, []},
{applications, [kernel, {applications, [kernel,

View File

@ -81,7 +81,7 @@ init([Node, SubTopic, Options]) ->
true -> true ->
true = erlang:monitor_node(Node, true), true = erlang:monitor_node(Node, true),
State = parse_opts(Options, #state{node = Node, subtopic = SubTopic}), State = parse_opts(Options, #state{node = Node, subtopic = SubTopic}),
emqttd_pubsub:subscribe({SubTopic, ?QOS_0}, self()), emqttd_pubsub:subscribe({SubTopic, ?QOS_0}),
{ok, State}; {ok, State};
false -> false ->
{stop, {cannot_connect, Node}} {stop, {cannot_connect, Node}}

View File

@ -150,8 +150,8 @@ init([Options]) ->
Topics = ?SYSTOP_CLIENTS ++ ?SYSTOP_SESSIONS ++ ?SYSTOP_PUBSUB, Topics = ?SYSTOP_CLIENTS ++ ?SYSTOP_SESSIONS ++ ?SYSTOP_PUBSUB,
[ets:insert(?BROKER_TAB, {Topic, 0}) || Topic <- Topics], [ets:insert(?BROKER_TAB, {Topic, 0}) || Topic <- Topics],
% Create $SYS Topics % Create $SYS Topics
[{atomic, _} = create(systop(Topic)) || Topic <- ?SYSTOP_BROKERS], [ok = create(systop(Topic)) || Topic <- ?SYSTOP_BROKERS],
[{atomic, _} = create(systop(Topic)) || Topic <- Topics], [ok = create(systop(Topic)) || Topic <- Topics],
SysInterval = proplists:get_value(sys_interval, Options, 60), SysInterval = proplists:get_value(sys_interval, Options, 60),
State = #state{started_at = os:timestamp(), sys_interval = SysInterval}, State = #state{started_at = os:timestamp(), sys_interval = SysInterval},
Delay = if Delay = if

View File

@ -86,7 +86,8 @@ lookup(ClientId) when is_binary(ClientId) ->
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
-spec register(ClientId :: binary(), Pid :: pid()) -> ok. -spec register(ClientId :: binary(), Pid :: pid()) -> ok.
register(ClientId, Pid) when is_binary(ClientId), is_pid(Pid) -> register(ClientId, Pid) when is_binary(ClientId), is_pid(Pid) ->
gen_server:call(?SERVER, {register, ClientId, Pid}). %%TODO: infinify to block requests when too many clients, this will be redesinged in 0.9.x...
gen_server:call(?SERVER, {register, ClientId, Pid}, infinity).
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
%% @doc %% @doc

View File

@ -38,6 +38,8 @@
-export([status/1, -export([status/1,
broker/1, broker/1,
stats/1,
metrics/1,
cluster/1, cluster/1,
listeners/1, listeners/1,
bridges/1, bridges/1,
@ -84,12 +86,12 @@ userdel([Username]) ->
broker([]) -> broker([]) ->
Funs = [sysdescr, version, uptime, datetime], Funs = [sysdescr, version, uptime, datetime],
[?PRINT("~s: ~s~n", [Fun, emqttd_broker:Fun()]) || Fun <- Funs]; [?PRINT("~s: ~s~n", [Fun, emqttd_broker:Fun()]) || Fun <- Funs].
broker(["stats"]) -> stats([]) ->
[?PRINT("~s: ~p~n", [Stat, Val]) || {Stat, Val} <- emqttd_broker:getstats()]; [?PRINT("~s: ~p~n", [Stat, Val]) || {Stat, Val} <- emqttd_broker:getstats()].
broker(["metrics"]) -> metrics([]) ->
[?PRINT("~s: ~p~n", [Metric, Val]) || {Metric, Val} <- emqttd_metrics:all()]. [?PRINT("~s: ~p~n", [Metric, Val]) || {Metric, Val} <- emqttd_metrics:all()].
listeners([]) -> listeners([]) ->

View File

@ -182,7 +182,7 @@ init([Options]) ->
% Init metrics % Init metrics
[new_metric(Metric) || Metric <- Metrics], [new_metric(Metric) || Metric <- Metrics],
% $SYS Topics for metrics % $SYS Topics for metrics
[{atomic, _} = emqttd_pubsub:create(systop(Topic)) || {_, Topic} <- Metrics], [ok = emqttd_pubsub:create(systop(Topic)) || {_, Topic} <- Metrics],
PubInterval = proplists:get_value(pub_interval, Options, 60), PubInterval = proplists:get_value(pub_interval, Options, 60),
Delay = if Delay = if
PubInterval == 0 -> 0; PubInterval == 0 -> 0;

View File

@ -22,6 +22,8 @@
%%% @doc %%% @doc
%%% emqttd core pubsub. %%% emqttd core pubsub.
%%% %%%
%%% TODO: should not use gen_server:call to create, subscribe topics...
%%%
%%% @end %%% @end
%%%----------------------------------------------------------------------------- %%%-----------------------------------------------------------------------------
-module(emqttd_pubsub). -module(emqttd_pubsub).
@ -46,8 +48,8 @@
-export([topics/0, -export([topics/0,
create/1, create/1,
subscribe/2, subscribe/1,
unsubscribe/2, unsubscribe/1,
publish/1, publish/1,
publish/2, publish/2,
%local node %local node
@ -106,8 +108,8 @@ topics() ->
%% @end %% @end
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
-spec create(binary()) -> {atomic, Reason :: any()} | {aborted, Reason :: any()}. -spec create(binary()) -> {atomic, Reason :: any()} | {aborted, Reason :: any()}.
create(Topic) -> create(Topic) when is_binary(Topic) ->
gen_server:call(?SERVER, {create, Topic}). {atomic, ok} = mnesia:transaction(fun trie_add/1, [Topic]), ok.
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
%% @doc %% @doc
@ -115,12 +117,25 @@ create(Topic) ->
%% %%
%% @end %% @end
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
-spec subscribe({binary(), mqtt_qos()} | list(), pid()) -> {ok, list(mqtt_qos())}. -spec subscribe({binary(), mqtt_qos()} | list()) -> {ok, list(mqtt_qos())}.
subscribe({Topic, Qos}, SubPid) when is_binary(Topic) and is_pid(SubPid) -> subscribe({Topic, Qos}) when is_binary(Topic) ->
subscribe([{Topic, Qos}], SubPid); case subscribe([{Topic, Qos}]) of
{ok, [GrantedQos]} -> {ok, GrantedQos};
{error, Error} -> {error, Error}
end;
subscribe(Topics = [{_Topic, _Qos}|_]) ->
subscribe(Topics, self(), []).
subscribe(Topics, SubPid) when is_list(Topics) and is_pid(SubPid) -> subscribe([], _SubPid, Acc) ->
gen_server:call(?SERVER, {subscribe, Topics, SubPid}). {ok, lists:reverse(Acc)};
%%TODO: check this function later.
subscribe([{Topic, Qos}|Topics], SubPid, Acc) ->
Subscriber = #topic_subscriber{topic=Topic, qos = Qos, subpid=SubPid},
F = fun() -> trie_add(Topic), mnesia:write(Subscriber) end,
case mnesia:transaction(F) of
{atomic, ok} -> subscribe(Topics, SubPid, [Qos|Acc]);
Error -> {error, Error}
end.
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
%% @doc %% @doc
@ -128,12 +143,27 @@ subscribe(Topics, SubPid) when is_list(Topics) and is_pid(SubPid) ->
%% %%
%% @end %% @end
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
-spec unsubscribe(binary() | list(binary()), pid()) -> ok. -spec unsubscribe(binary() | list(binary())) -> ok.
unsubscribe(Topic, SubPid) when is_binary(Topic) and is_pid(SubPid) -> unsubscribe(Topic) when is_binary(Topic) ->
unsubscribe([Topic], SubPid); unsubscribe([Topic]);
unsubscribe(Topics, SubPid) when is_list(Topics) and is_pid(SubPid) -> unsubscribe(Topics = [Topic|_]) when is_list(Topics) and is_binary(Topic) ->
gen_server:cast(?SERVER, {unsubscribe, Topics, SubPid}). unsubscribe(Topics, self()).
%%TODO: check this function later.
unsubscribe(Topics, SubPid) ->
F = fun() ->
Subscribers = mnesia:index_read(topic_subscriber, SubPid, #topic_subscriber.subpid),
lists:foreach(fun(Sub = #topic_subscriber{topic = Topic}) ->
case lists:member(Topic, Topics) of
true -> mneisa:delete_object(Sub);
false -> ok
end
end, Subscribers)
%TODO: try to remove topic??? if topic is dynamic...
%%try_remove_topic(Topic)
end,
{atomic, _} = mneisa:transaction(F), ok.
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
%% @doc %% @doc
@ -164,7 +194,7 @@ publish(Topic, Msg) when is_binary(Topic) ->
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
-spec dispatch(Topic :: binary(), Msg :: mqtt_message()) -> non_neg_integer(). -spec dispatch(Topic :: binary(), Msg :: mqtt_message()) -> non_neg_integer().
dispatch(Topic, Msg = #mqtt_message{qos = Qos}) when is_binary(Topic) -> dispatch(Topic, Msg = #mqtt_message{qos = Qos}) when is_binary(Topic) ->
Subscribers = ets:lookup(topic_subscriber, Topic), Subscribers = mnesia:dirty_read(topic_subscriber, Topic),
lists:foreach( lists:foreach(
fun(#topic_subscriber{qos = SubQos, subpid=SubPid}) -> fun(#topic_subscriber{qos = SubQos, subpid=SubPid}) ->
Msg1 = if Msg1 = if
@ -193,72 +223,79 @@ match(Topic) when is_binary(Topic) ->
%% ------------------------------------------------------------------ %% ------------------------------------------------------------------
init([]) -> init([]) ->
mnesia:create_table(topic_trie, [ %% trie and topic tables, will be copied by all nodes.
{ram_copies, [node()]},
{attributes, record_info(fields, topic_trie)}]),
mnesia:create_table(topic_trie_node, [ mnesia:create_table(topic_trie_node, [
{ram_copies, [node()]}, {ram_copies, [node()]},
{attributes, record_info(fields, topic_trie_node)}]), {attributes, record_info(fields, topic_trie_node)}]),
mnesia:add_table_copy(topic_trie_node, node(), ram_copies),
mnesia:create_table(topic_trie, [
{ram_copies, [node()]},
{attributes, record_info(fields, topic_trie)}]),
mnesia:add_table_copy(topic_trie, node(), ram_copies),
mnesia:create_table(topic, [ mnesia:create_table(topic, [
{type, bag}, {type, bag},
{record_name, topic}, {record_name, topic},
{ram_copies, [node()]}, {ram_copies, [node()]},
{attributes, record_info(fields, topic)}]), {attributes, record_info(fields, topic)}]),
mnesia:add_table_copy(topic_trie, node(), ram_copies),
mnesia:add_table_copy(topic_trie_node, node(), ram_copies),
mnesia:add_table_copy(topic, node(), ram_copies), mnesia:add_table_copy(topic, node(), ram_copies),
ets:new(topic_subscriber, [bag, named_table, {keypos, 2}]), mnesia:subscribe({table, topic, simple}),
%% local table, not shared with other table
mnesia:create_table(topic_subscriber, [
{type, bag},
{record_name, topic_subscriber},
{ram_copies, [node()]},
{attributes, record_info(fields, topic_subscriber)},
{index, [subpid]},
{local_content, true}]),
mnesia:subscribe({table, topic_subscriber, simple}),
{ok, #state{}}. {ok, #state{}}.
handle_call(getstats, _From, State = #state{max_subs = Max}) -> handle_call(getstats, _From, State = #state{max_subs = Max}) ->
Stats = [{'topics/count', mnesia:table_info(topic, size)}, Stats = [{'topics/count', mnesia:table_info(topic, size)},
{'subscribers/count', ets:info(topic_subscriber, size)}, {'subscribers/count', mnesia:info(topic_subscriber, size)},
{'subscribers/max', Max}], {'subscribers/max', Max}],
{reply, Stats, State}; {reply, Stats, State};
handle_call({create, Topic}, _From, State) ->
Result = mnesia:transaction(fun trie_add/1, [Topic]),
{reply, Result, setstats(State)};
handle_call({subscribe, Topics, SubPid}, _From, State) ->
Result = [subscribe_topic({Topic, Qos}, SubPid) || {Topic, Qos} <- Topics],
Reply =
case [Err || Err = {error, _} <- Result] of
[] -> {ok, [Qos || {ok, Qos} <- Result]};
Errors -> hd(Errors)
end,
{reply, Reply, setstats(State)};
handle_call(Req, _From, State) -> handle_call(Req, _From, State) ->
{stop, {badreq, Req}, State}. lager:error("Bad Req: ~p", [Req]),
{reply, error, State}.
handle_cast({unsubscribe, Topics, SubPid}, State) ->
lists:foreach(fun(Topic) ->
ets:match_delete(topic_subscriber, #topic_subscriber{topic=Topic, qos ='_', subpid=SubPid}),
try_remove_topic(Topic)
end, Topics),
{noreply, setstats(State)};
handle_cast(Msg, State) -> handle_cast(Msg, State) ->
{stop, {badmsg, Msg}, State}. lager:error("Bad Msg: ~p", [Msg]),
{noreply, State}.
handle_info({'DOWN', Mon, _Type, _Object, _Info}, State) -> %% a new record has been written.
case get({submon, Mon}) of handle_info({mnesia_table_event, {write, #topic_subscriber{subpid = Pid}, _ActivityId}}, State) ->
undefined -> erlang:monitor(process, Pid),
lager:error("unexpected 'DOWN': ~p", [Mon]); {noreply, setstats(State)};
SubPid ->
erase({submon, Mon}), %% {write, #topic{}, _ActivityId}
erase({subscriber, SubPid}), %% {delete_object, _OldRecord, _ActivityId}
Subs = ets:match_object(topic_subscriber, #topic_subscriber{subpid=SubPid, _='_'}), %% {delete, {Tab, Key}, ActivityId}
[ets:delete_object(topic_subscriber, Sub) || Sub <- Subs], handle_info({mnesia_table_event, _Event}, State) ->
[try_remove_topic(Topic) || #topic_subscriber{topic=Topic} <- Subs] {noreply, setstats(State)};
handle_info({'DOWN', _Mon, _Type, DownPid, _Info}, State) ->
F = fun() ->
%%TODO: how to read with write lock?
[mnesia:delete_object(Sub) || Sub <- mnesia:index_read(topic_subscriber, DownPid, #topic_subscriber.subpid)]
%%TODO: try to remove dynamic topics without subscribers
%% [try_remove_topic(Topic) || #topic_subscriber{topic=Topic} <- Subs]
end,
case catch mnesia:transaction(F) of
{atomic, _} -> ok;
{aborted, Reason} -> lager:error("Failed to delete 'DOWN' subscriber ~p: ~p", [DownPid, Reason])
end, end,
{noreply, setstats(State)}; {noreply, setstats(State)};
handle_info(Info, State) -> handle_info(Info, State) ->
{stop, {badinfo, Info}, State}. lager:error("Bad Info: ~p", [Info]),
{noreply, State}.
terminate(_Reason, _State) -> terminate(_Reason, _State) ->
mnesia:unsubscribe({table, topic, simple}),
mnesia:unsubscribe({table, topic_subscriber, simple}),
%%TODO: clear topics belongs to this node???
ok. ok.
code_change(_OldVsn, State, _Extra) -> code_change(_OldVsn, State, _Extra) ->
@ -267,57 +304,22 @@ code_change(_OldVsn, State, _Extra) ->
%%%============================================================================= %%%=============================================================================
%%% Internal functions %%% Internal functions
%%%============================================================================= %%%=============================================================================
subscribe_topic({Topic, Qos}, SubPid) ->
case mnesia:transaction(fun trie_add/1, [Topic]) of
{atomic, _} ->
case get({subscriber, SubPid}) of
undefined ->
%%TODO: refactor later...
MonRef = erlang:monitor(process, SubPid),
put({subcriber, SubPid}, MonRef),
put({submon, MonRef}, SubPid);
_ ->
already_monitored
end,
%% remove duplicated subscribers
try_remove_subscriber({Topic, Qos}, SubPid),
ets:insert(topic_subscriber, #topic_subscriber{topic=Topic, qos = Qos, subpid=SubPid}),
%TODO: GrantedQos??
{ok, Qos};
{aborted, Reason} ->
{error, Reason}
end.
try_remove_subscriber({Topic, Qos}, SubPid) -> %%try_remove_topic(Name) when is_binary(Name) ->
case ets:lookup(topic_subscriber, Topic) of %% case ets:member(topic_subscriber, Name) of
[] -> %% false ->
not_found; %% Topic = emqttd_topic:new(Name),
Subs -> %% Fun = fun() ->
DupSubs = [Sub || Sub = #topic_subscriber{qos = OldQos, subpid = OldPid} %% mnesia:delete_object(Topic),
<- Subs, Qos =/= OldQos, OldPid =:= SubPid], %% case mnesia:read(topic, Name) of
case DupSubs of %% [] -> trie_delete(Name);
[] -> ok; %% _ -> ignore
[DupSub] -> %% end
lager:warning("PubSub: remove duplicated subscriber ~p", [DupSub]), %% end,
ets:delete(topic_subscriber, DupSub) %% mnesia:transaction(Fun);
end %% true ->
end. %% ok
%% end.
try_remove_topic(Name) when is_binary(Name) ->
case ets:member(topic_subscriber, Name) of
false ->
Topic = emqttd_topic:new(Name),
Fun = fun() ->
mnesia:delete_object(Topic),
case mnesia:read(topic, Name) of
[] -> trie_delete(Name);
_ -> ignore
end
end,
mnesia:transaction(Fun);
true ->
ok
end.
trie_add(Topic) when is_binary(Topic) -> trie_add(Topic) when is_binary(Topic) ->
mnesia:write(emqttd_topic:new(Topic)), mnesia:write(emqttd_topic:new(Topic)),
@ -325,7 +327,7 @@ trie_add(Topic) when is_binary(Topic) ->
[TrieNode=#topic_trie_node{topic=undefined}] -> [TrieNode=#topic_trie_node{topic=undefined}] ->
mnesia:write(TrieNode#topic_trie_node{topic=Topic}); mnesia:write(TrieNode#topic_trie_node{topic=Topic});
[#topic_trie_node{topic=Topic}] -> [#topic_trie_node{topic=Topic}] ->
{atomic, already_exist}; ok;
[] -> [] ->
%add trie path %add trie path
[trie_add_path(Triple) || Triple <- emqttd_topic:triples(Topic)], [trie_add_path(Triple) || Triple <- emqttd_topic:triples(Topic)],
@ -401,7 +403,7 @@ trie_delete_path([{NodeId, Word, _} | RestPath]) ->
setstats(State = #state{max_subs = Max}) -> setstats(State = #state{max_subs = Max}) ->
emqttd_broker:setstat('topics/count', mnesia:table_info(topic, size)), emqttd_broker:setstat('topics/count', mnesia:table_info(topic, size)),
SubCount = ets:info(topic_subscriber, size), SubCount = mnesia:table_info(topic_subscriber, size),
emqttd_broker:setstat('subscribers/count', SubCount), emqttd_broker:setstat('subscribers/count', SubCount),
if if
SubCount > Max -> SubCount > Max ->

View File

@ -185,7 +185,7 @@ subscribe(SessState = #session_state{client_id = ClientId, submap = SubMap}, Top
_ -> lager:warning("~s resubscribe ~p", [ClientId, Resubs]) _ -> lager:warning("~s resubscribe ~p", [ClientId, Resubs])
end, end,
SubMap1 = lists:foldl(fun({Name, Qos}, Acc) -> maps:put(Name, Qos, Acc) end, SubMap, Topics), SubMap1 = lists:foldl(fun({Name, Qos}, Acc) -> maps:put(Name, Qos, Acc) end, SubMap, Topics),
{ok, GrantedQos} = emqttd_pubsub:subscribe(Topics, self()), {ok, GrantedQos} = emqttd_pubsub:subscribe(Topics),
%%TODO: should be gen_event and notification... %%TODO: should be gen_event and notification...
emqttd_server:subscribe([ Name || {Name, _} <- Topics ], self()), emqttd_server:subscribe([ Name || {Name, _} <- Topics ], self()),
{ok, SessState#session_state{submap = SubMap1}, GrantedQos}; {ok, SessState#session_state{submap = SubMap1}, GrantedQos};
@ -208,7 +208,7 @@ unsubscribe(SessState = #session_state{client_id = ClientId, submap = SubMap}, T
BadUnsubs -> lager:warning("~s should not unsubscribe ~p", [ClientId, BadUnsubs]) BadUnsubs -> lager:warning("~s should not unsubscribe ~p", [ClientId, BadUnsubs])
end, end,
%%unsubscribe from topic tree %%unsubscribe from topic tree
ok = emqttd_pubsub:unsubscribe(Topics, self()), ok = emqttd_pubsub:unsubscribe(Topics),
SubMap1 = lists:foldl(fun(Topic, Acc) -> maps:remove(Topic, Acc) end, SubMap, Topics), SubMap1 = lists:foldl(fun(Topic, Acc) -> maps:remove(Topic, Acc) end, SubMap, Topics),
{ok, SessState#session_state{submap = SubMap1}}; {ok, SessState#session_state{submap = SubMap1}};

View File

@ -149,8 +149,8 @@ case "$1" in
;; ;;
broker) broker)
if [ $# -gt 2 ]; then if [ $# -ne 1 ]; then
echo "Usage: $SCRIPT broker [status | stats | metrics]" echo "Usage: $SCRIPT broker"
exit 1 exit 1
fi fi
@ -165,6 +165,41 @@ case "$1" in
$NODETOOL rpc emqttd_ctl broker $@ $NODETOOL rpc emqttd_ctl broker $@
;; ;;
stats)
if [ $# -ne 1 ]; then
echo "Usage: $SCRIPT stats"
exit 1
fi
# Make sure the local node IS running
RES=`$NODETOOL ping`
if [ "$RES" != "pong" ]; then
echo "emqttd is not running!"
exit 1
fi
shift
$NODETOOL rpc emqttd_ctl stats $@
;;
metrics)
if [ $# -ne 1 ]; then
echo "Usage: $SCRIPT metrics"
exit 1
fi
# Make sure the local node IS running
RES=`$NODETOOL ping`
if [ "$RES" != "pong" ]; then
echo "emqttd is not running!"
exit 1
fi
shift
$NODETOOL rpc emqttd_ctl metrics $@
;;
bridges) bridges)
# Make sure the local node IS running # Make sure the local node IS running
RES=`$NODETOOL ping` RES=`$NODETOOL ping`
@ -223,8 +258,10 @@ case "$1" in
*) *)
echo "Usage: $SCRIPT" echo "Usage: $SCRIPT"
echo " status #query status" echo " status #query broker status"
echo " broker [stats | metrics] #query broker stats or metrics" echo " broker #query broker version, uptime and description"
echo " stats #query broker statistics of clients, topics, subscribers"
echo " metrics #query broker metrics"
echo " cluster [<Node>] #query or cluster nodes" echo " cluster [<Node>] #query or cluster nodes"
echo " plugins list #query loaded plugins" echo " plugins list #query loaded plugins"
echo " plugins load <Plugin> #load plugin" echo " plugins load <Plugin> #load plugin"