Upgrade for MQTT Version 5.0 and Erlang/OTP R21

This commit is contained in:
Feng Lee 2018-07-18 13:43:17 +08:00
parent e57411c3e0
commit 62aa072f2f
76 changed files with 1476 additions and 2043 deletions

View File

@ -6,6 +6,6 @@
{applications,[kernel,stdlib,gproc,lager,esockd,mochiweb,lager_syslog,pbkdf2,bcrypt,clique,jsx]},
{env,[]},
{mod,{emqx_app,[]}},
{maintainers,["Feng Lee <feng@emqtt.io>"]},
{maintainers,["Feng Lee <feng@emqx.io>"]},
{licenses,["Apache-2.0"]},
{links,[{"Github","https://github.com/emqx/emqx"}]}]}.

View File

@ -1,42 +1,36 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx).
-include("emqx.hrl").
%% Start/Stop Application
%% Start/Stop the application
-export([start/0, is_running/1, stop/0]).
%% PubSub API
-export([subscribe/1, subscribe/2, subscribe/3, publish/1,
unsubscribe/1, unsubscribe/2]).
-export([subscribe/1, subscribe/2, subscribe/3]).
-export([publish/1]).
-export([unsubscribe/1, unsubscribe/2]).
%% PubSub management API
-export([topics/0, subscriptions/1, subscribers/1, subscribed/2]).
%% Get/Set suboptions
-export([get_subopts/2, set_subopts/3]).
%% Hooks API
-export([hook/4, hook/3, unhook/2, run_hooks/2, run_hooks/3]).
%% Debug API
-export([dump/0]).
%% Shutdown and reboot
-export([shutdown/0, shutdown/1, reboot/0]).
@ -76,16 +70,16 @@ is_running(Node) ->
subscribe(Topic) ->
emqx_broker:subscribe(iolist_to_binary(Topic)).
-spec(subscribe(topic() | iodata(), subscriber() | string()) -> ok | {error, term()}).
-spec(subscribe(topic() | string(), subscriber() | string()) -> ok | {error, term()}).
subscribe(Topic, Subscriber) ->
emqx_broker:subscribe(iolist_to_binary(Topic), list_to_subid(Subscriber)).
-spec(subscribe(topic() | iodata(), subscriber() | string(), [suboption()]) -> ok | {error, term()}).
-spec(subscribe(topic() | string(), subscriber() | string(), [suboption()]) -> ok | {error, term()}).
subscribe(Topic, Subscriber, Options) ->
emqx_broker:subscribe(iolist_to_binary(Topic), list_to_subid(Subscriber), Options).
%% @doc Publish Message
-spec(publish(message()) -> {ok, delivery()} | ignore).
-spec(publish(message()) -> {ok, delivery()} | {error, term()}).
publish(Msg) ->
emqx_broker:publish(Msg).
@ -118,7 +112,7 @@ subscribers(Topic) ->
-spec(subscriptions(subscriber() | string()) -> [{topic(), list(suboption())}]).
subscriptions(Subscriber) ->
emqx_broker:subscriptions(Subscriber).
emqx_broker:subscriptions(list_to_subid(Subscriber)).
-spec(subscribed(topic() | string(), subscriber()) -> boolean()).
subscribed(Topic, Subscriber) ->
@ -170,16 +164,10 @@ shutdown() ->
shutdown(normal).
shutdown(Reason) ->
emqx_logger:error("EMQ shutdown for ~s", [Reason]),
emqx_logger:error("emqx shutdown for ~s", [Reason]),
emqx_plugins:unload(),
lists:foreach(fun application:stop/1, [emqx, ekka, mochiweb, esockd, gproc]).
reboot() ->
lists:foreach(fun application:start/1, [gproc, esockd, mochiweb, ekka, emqx]).
%%--------------------------------------------------------------------
%% Debug
%%--------------------------------------------------------------------
dump() -> lists:append([emqx_broker:dump(), emqx_router:dump()]).

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_access_control).

View File

@ -1,5 +1,4 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@ -12,7 +11,6 @@
%% 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(emqx_access_rule).
@ -73,8 +71,7 @@ compile(topic, Topic) ->
end.
'pattern?'(Words) ->
lists:member(<<"%u">>, Words)
orelse lists:member(<<"%c">>, Words).
lists:member(<<"%u">>, Words) orelse lists:member(<<"%c">>, Words).
bin(L) when is_list(L) ->
list_to_binary(L);
@ -99,7 +96,7 @@ match_who(_Client, {user, all}) ->
true;
match_who(_Client, {client, all}) ->
true;
match_who(#client{client_id = ClientId}, {client, ClientId}) ->
match_who(#client{id = ClientId}, {client, ClientId}) ->
true;
match_who(#client{username = Username}, {user, Username}) ->
true;
@ -137,9 +134,9 @@ feed_var(Client, Pattern) ->
feed_var(Client, Pattern, []).
feed_var(_Client, [], Acc) ->
lists:reverse(Acc);
feed_var(Client = #client{client_id = undefined}, [<<"%c">>|Words], Acc) ->
feed_var(Client = #client{id = undefined}, [<<"%c">>|Words], Acc) ->
feed_var(Client, Words, [<<"%c">>|Acc]);
feed_var(Client = #client{client_id = ClientId}, [<<"%c">>|Words], Acc) ->
feed_var(Client = #client{id = ClientId}, [<<"%c">>|Words], Acc) ->
feed_var(Client, Words, [ClientId |Acc]);
feed_var(Client = #client{username = undefined}, [<<"%u">>|Words], Acc) ->
feed_var(Client, Words, [<<"%u">>|Acc]);

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_acl_internal).

View File

@ -1,5 +1,4 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@ -12,7 +11,6 @@
%% 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(emqx_acl_mod).

View File

@ -1,5 +1,4 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@ -12,29 +11,24 @@
%% 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(emqx_alarm).
-module(emqx_alarm_mgr).
-behaviour(gen_event).
-include("emqx.hrl").
-define(ALARM_MGR, ?MODULE).
%% API Function Exports
-export([start_link/0, alarm_fun/0, get_alarms/0,
set_alarm/1, clear_alarm/1,
add_alarm_handler/1, add_alarm_handler/2,
delete_alarm_handler/1]).
-export([start_link/0]).
-export([alarm_fun/0, get_alarms/0, set_alarm/1, clear_alarm/1]).
-export([add_alarm_handler/1, add_alarm_handler/2, delete_alarm_handler/1]).
%% gen_event callbacks
-export([init/1, handle_event/2, handle_call/2, handle_info/2,
terminate/2, code_change/3]).
-export([init/1, handle_event/2, handle_call/2, handle_info/2, terminate/2,
code_change/3]).
%%--------------------------------------------------------------------
%% API
%%--------------------------------------------------------------------
-define(ALARM_MGR, ?MODULE).
-record(state, {alarms}).
start_link() ->
start_with(fun(Pid) -> gen_event:add_handler(Pid, ?MODULE, []) end).
@ -75,70 +69,72 @@ add_alarm_handler(Module, Args) when is_atom(Module) ->
delete_alarm_handler(Module) when is_atom(Module) ->
gen_event:delete_handler(?ALARM_MGR, Module, []).
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% Default Alarm handler
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
init(_) -> {ok, []}.
init(_) -> {ok, #state{alarms = []}}.
handle_event({set_alarm, Alarm = #alarm{id = AlarmId,
severity = Severity,
title = Title,
summary = Summary}}, Alarms)->
TS = os:timestamp(),
case catch emqx_json:encode([{id, AlarmId},
{severity, Severity},
{title, iolist_to_binary(Title)},
{summary, iolist_to_binary(Summary)},
{ts, emqx_time:now_secs(TS)}]) of
{'EXIT', Reason} ->
emqx_logger:error("[Alarm] Failed to encode set_alarm: ~p", [Reason]);
JSON ->
emqx_broker:publish(alarm_msg(alert, AlarmId, JSON))
handle_event({set_alarm, Alarm = #alarm{timestamp = undefined}}, State)->
handle_event({set_alarm, Alarm#alarm{timestamp = os:timestamp()}}, State);
handle_event({set_alarm, Alarm = #alarm{id = AlarmId}}, State = #state{alarms = Alarms}) ->
case encode_alarm(Alarm) of
{ok, Json} ->
emqx_broker:safe_publish(alarm_msg(alert, AlarmId, Json));
{error, Reason} ->
emqx_logger:error("[AlarmMgr] Failed to encode alarm: ~p", [Reason])
end,
{ok, [Alarm#alarm{timestamp = TS} | Alarms]};
{ok, State#state{alarms = [Alarm|Alarms]}};
handle_event({clear_alarm, AlarmId}, Alarms) ->
case catch emqx_json:encode([{id, AlarmId}, {ts, emqx_time:now_secs()}]) of
{'EXIT', Reason} ->
emqx_logger:error("[Alarm] Failed to encode clear_alarm: ~p", [Reason]);
JSON ->
emqx_broker:publish(alarm_msg(clear, AlarmId, JSON))
handle_event({clear_alarm, AlarmId}, State = #state{alarms = Alarms}) ->
case emqx_json:safe_encode([{id, AlarmId}, {ts, emqx_time:now_secs()}]) of
{ok, Json} ->
emqx_broker:safe_publish(alarm_msg(clear, AlarmId, Json));
{error, Reason} ->
emqx_logger:error("[AlarmMgr] Failed to encode clear: ~p", [Reason])
end,
{ok, lists:keydelete(AlarmId, 2, Alarms), hibernate};
{ok, State#state{alarms = lists:keydelete(AlarmId, 2, Alarms)}, hibernate};
handle_event(_, Alarms)->
{ok, Alarms}.
handle_event(Event, State)->
error_logger:error("[AlarmMgr] unexpected event: ~p", [Event]),
{ok, State}.
handle_info(_, Alarms) ->
{ok, Alarms}.
handle_info(Info, State) ->
error_logger:error("[AlarmMgr] unexpected info: ~p", [Info]),
{ok, State}.
handle_call(get_alarms, Alarms) ->
{ok, Alarms, Alarms};
handle_call(get_alarms, State = #state{alarms = Alarms}) ->
{ok, Alarms, State};
handle_call(_Query, Alarms) ->
{ok, {error, bad_query}, Alarms}.
terminate(swap, Alarms) ->
{?MODULE, Alarms};
handle_call(Req, State) ->
error_logger:error("[AlarmMgr] unexpected call: ~p", [Req]),
{ok, ignored, State}.
terminate(swap, State) ->
{?MODULE, State};
terminate(_, _) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
encode_alarm(#alarm{id = AlarmId, severity = Severity, title = Title,
summary = Summary, timestamp = Ts}) ->
emqx_json:safe_encode([{id, AlarmId}, {severity, Severity},
{title, iolist_to_binary(Title)},
{summary, iolist_to_binary(Summary)},
{ts, emqx_time:now_secs(Ts)}]).
alarm_msg(Type, AlarmId, Json) ->
Msg = emqx_message:make(alarm, topic(Type, AlarmId), iolist_to_binary(Json)),
emqx_message:set_flag(sys, Msg).
emqx_message:make(?ALARM_MGR, #{sys => true, qos => 0}, topic(Type, AlarmId), Json).
topic(alert, AlarmId) ->
emqx_topic:systop(<<"alarms/", AlarmId/binary, "/alert">>);
topic(clear, AlarmId) ->
emqx_topic:systop(<<"alarms/", AlarmId/binary, "/clear">>).

View File

@ -1,24 +1,21 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_app).
-behaviour(application).
%% Application callbacks
-export([start/2, stop/1]).
-define(APP, emqx).
@ -34,7 +31,7 @@ start(_Type, _Args) ->
emqx_modules:load(),
emqx_plugins:init(),
emqx_plugins:load(),
emqx_listeners:start(),
emqx_listeners:start_all(),
start_autocluster(),
register(emqx, self()),
print_vsn(),

View File

@ -1,5 +1,4 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@ -12,7 +11,6 @@
%% 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(emqx_auth_mod).

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_banned).
@ -70,9 +68,7 @@ start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
-spec(check(client()) -> boolean()).
check(#client{client_id = ClientId,
username = Username,
peername = {IPAddr, _}}) ->
check(#client{id = ClientId, username = Username, peername = {IPAddr, _}}) ->
ets:member(?TAB, {client_id, ClientId})
orelse ets:member(?TAB, {username, Username})
orelse ets:member(?TAB, {ipaddr, IPAddr}).

View File

@ -1,5 +1,4 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2013-2018 EMQ Inc. All Rights Reserved.
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@ -12,7 +11,6 @@
%% 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(emqx_base62).

View File

@ -1,63 +0,0 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2013-2018 EMQ Inc. All Rights Reserved.
%%
%% 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(emqx_boot).
-export([apply_module_attributes/1, all_module_attributes/1]).
%% only {F, Args}...
apply_module_attributes(Name) ->
[{Module, [apply(Module, F, Args) || {F, Args} <- Attrs]} ||
{_App, Module, Attrs} <- all_module_attributes(Name)].
%% Copy from rabbit_misc.erl
all_module_attributes(Name) ->
Targets =
lists:usort(
lists:append(
[[{App, Module} || Module <- Modules] ||
{App, _, _} <- ignore_lib_apps(application:loaded_applications()),
{ok, Modules} <- [application:get_key(App, modules)]])),
lists:foldl(
fun ({App, Module}, Acc) ->
case lists:append([Atts || {N, Atts} <- module_attributes(Module),
N =:= Name]) of
[] -> Acc;
Atts -> [{App, Module, Atts} | Acc]
end
end, [], Targets).
%% Copy from rabbit_misc.erl
module_attributes(Module) ->
case catch Module:module_info(attributes) of
{'EXIT', {undef, [{Module, module_info, [attributes], []} | _]}} ->
[];
{'EXIT', Reason} ->
exit(Reason);
V ->
V
end.
ignore_lib_apps(Apps) ->
LibApps = [kernel, stdlib, sasl, appmon, eldap, erts,
syntax_tools, ssl, crypto, mnesia, os_mon,
inets, goldrush, lager, gproc, runtime_tools,
snmp, otp_mibs, public_key, asn1, ssh, hipe,
common_test, observer, webtool, xmerl, tools,
test_server, compiler, debugger, eunit, et,
wx],
[App || App = {Name, _, _} <- Apps, not lists:member(Name, LibApps)].

View File

@ -1,5 +1,4 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@ -12,22 +11,18 @@
%% 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(emqx_bridge).
-behaviour(gen_server).
-include("emqx.hrl").
-include("emqx_mqtt.hrl").
%% API Function Exports
-export([start_link/5]).
%% gen_server Function Exports
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
code_change/3]).
-define(PING_DOWN_INTERVAL, 1000).
@ -49,35 +44,30 @@
-export_type([option/0]).
%%--------------------------------------------------------------------
%% API
%%--------------------------------------------------------------------
%% @doc Start a bridge
-spec(start_link(any(), pos_integer(), atom(), binary(), [option()]) ->
{ok, pid()} | ignore | {error, term()}).
-spec(start_link(term(), pos_integer(), atom(), binary(), [option()])
-> {ok, pid()} | ignore | {error, term()}).
start_link(Pool, Id, Node, Topic, Options) ->
gen_server:start_link(?MODULE, [Pool, Id, Node, Topic, Options], []).
gen_server:start_link(?MODULE, [Pool, Id, Node, Topic, Options], [{hibernate_after, 5000}]).
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% gen_server callbacks
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
init([Pool, Id, Node, Topic, Options]) ->
process_flag(trap_exit, true),
gproc_pool:connect_worker(Pool, {Pool, Id}),
true = gproc_pool:connect_worker(Pool, {Pool, Id}),
case net_kernel:connect_node(Node) of
true ->
true = erlang:monitor_node(Node, true),
Share = iolist_to_binary(["$bridge:", atom_to_list(Node), ":", Topic]),
%% TODO:: local???
emqx_broker:subscribe(Topic, self(), [local, {share, Share}, {qos, ?QOS_0}]),
emqx_broker:subscribe(Topic, self(), [{share, Share}, {qos, ?QOS_0}]),
State = parse_opts(Options, #state{node = Node, subtopic = Topic}),
%%TODO: queue....
MQueue = emqx_mqueue:new(qname(Node, Topic),
[{max_len, State#state.max_queue_len}],
emqx_alarm:alarm_fun()),
{ok, State#state{pool = Pool, id = Id, mqueue = MQueue},
hibernate, {backoff, 1000, 1000, 10000}};
{ok, State#state{pool = Pool, id = Id, mqueue = MQueue}};
false ->
{stop, {cannot_connect_node, Node}}
end.
@ -103,33 +93,32 @@ qname(Node, Topic) ->
iolist_to_binary(["Bridge:", Node, ":", Topic]).
handle_call(Req, _From, State) ->
emqx_logger:error("[Bridge] Unexpected request: ~p", [Req]),
{reply, ignore, State}.
emqx_logger:error("[Bridge] unexpected call: ~p", [Req]),
{reply, ignored, State}.
handle_cast(Msg, State) ->
emqx_logger:error("[Bridge] Unexpected msg: ~p", [Msg]),
emqx_logger:error("[Bridge] unexpected cast: ~p", [Msg]),
{noreply, State}.
handle_info({dispatch, _Topic, Msg}, State = #state{mqueue = MQ, status = down}) ->
{noreply, State#state{mqueue = emqx_mqueue:in(Msg, MQ)}};
handle_info({dispatch, _Topic, Msg}, State = #state{mqueue = Q, status = down}) ->
%% TODO: how to drop???
{noreply, State#state{mqueue = emqx_mqueue:in(Msg, Q)}};
handle_info({dispatch, _Topic, Msg}, State = #state{node = Node, status = up}) ->
emqx_rpc:cast(Node, emqx, publish, [transform(Msg, State)]),
{noreply, State, hibernate};
ok = emqx_rpc:cast(Node, emqx_broker, publish, [transform(Msg, State)]),
{noreply, State};
handle_info({nodedown, Node}, State = #state{node = Node, ping_down_interval = Interval}) ->
emqx_logger:warning("[Bridge] Node Down: ~s", [Node]),
emqx_logger:warning("[Bridge] node down: ~s", [Node]),
erlang:send_after(Interval, self(), ping_down_node),
{noreply, State#state{status = down}, hibernate};
handle_info({nodeup, Node}, State = #state{node = Node}) ->
%% TODO: Really fast??
case emqx:is_running(Node) of
true ->
emqx_logger:warning("[Bridge] Node up: ~s", [Node]),
true -> emqx_logger:warning("[Bridge] Node up: ~s", [Node]),
{noreply, dequeue(State#state{status = up})};
false ->
self() ! {nodedown, Node},
false -> self() ! {nodedown, Node},
{noreply, State#state{status = down}}
end;
@ -137,10 +126,8 @@ handle_info(ping_down_node, State = #state{node = Node, ping_down_interval = Int
Self = self(),
spawn_link(fun() ->
case net_kernel:connect_node(Node) of
true -> %%TODO: this is not right... fixme later
Self ! {nodeup, Node};
false ->
erlang:send_after(Interval, Self, ping_down_node)
true -> Self ! {nodeup, Node};
false -> erlang:send_after(Interval, Self, ping_down_node)
end
end),
{noreply, State};
@ -149,7 +136,7 @@ handle_info({'EXIT', _Pid, normal}, State) ->
{noreply, State};
handle_info(Info, State) ->
emqx_logger:error("[Bridge] Unexpected info: ~p", [Info]),
emqx_logger:error("[Bridge] unexpected info: ~p", [Info]),
{noreply, State}.
terminate(_Reason, #state{pool = Pool, id = Id}) ->

View File

@ -1,5 +1,4 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@ -12,16 +11,14 @@
%% 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(emqx_bridge_sup).
-include("emqx.hrl").
-export([start_link/3]).
%% @doc Start bridge pool supervisor
-spec(start_link(atom(), binary(), [emqx_bridge:option()])
-> {ok, pid()} | {error, term()}).
-spec(start_link(node(), topic(), [emqx_bridge:option()]) -> {ok, pid()} | {error, term()}).
start_link(Node, Topic, Options) ->
MFA = {emqx_bridge, start_link, [Node, Topic, Options]},
emqx_pool_sup:start_link({bridge, Node, Topic}, random, MFA).

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_bridge_sup_sup).
@ -23,6 +21,7 @@
-export([start_link/0, bridges/0]).
-export([start_bridge/2, start_bridge/3, stop_bridge/2]).
%% Supervisor callbacks
-export([init/1]).
-define(CHILD_ID(Node, Topic), {bridge_sup, Node, Topic}).
@ -30,10 +29,6 @@
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
%%--------------------------------------------------------------------
%% API
%%--------------------------------------------------------------------
%% @doc List all bridges
-spec(bridges() -> [{node(), topic(), pid()}]).
bridges() ->
@ -50,8 +45,7 @@ start_bridge(Node, Topic) when is_atom(Node), is_binary(Topic) ->
start_bridge(Node, _Topic, _Options) when Node =:= node() ->
{error, bridge_to_self};
start_bridge(Node, Topic, Options) when is_atom(Node), is_binary(Topic) ->
{ok, BridgeEnv} = emqx_config:get_env(bridge),
Options1 = emqx_misc:merge_opts(BridgeEnv, Options),
Options1 = emqx_misc:merge_opts(emqx_config:get_env(bridge, []), Options),
supervisor:start_child(?MODULE, bridge_spec(Node, Topic, Options1)).
%% @doc Stop a bridge
@ -63,15 +57,18 @@ stop_bridge(Node, Topic) when is_atom(Node), is_binary(Topic) ->
Error -> Error
end.
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% Supervisor callbacks
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
init([]) ->
{ok, {{one_for_one, 10, 3600}, []}}.
bridge_spec(Node, Topic, Options) ->
{?CHILD_ID(Node, Topic),
{emqx_bridge_sup, start_link, [Node, Topic, Options]},
permanent, infinity, supervisor, [emqx_bridge_sup]}.
#{id => ?CHILD_ID(Node, Topic),
start => {emqx_bridge_sup, start_link, [Node, Topic, Options]},
restart => permanent,
shutdown => infinity,
type => supervisor,
modules => [emqx_bridge_sup]}.

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_broker).
@ -21,18 +19,17 @@
-include("emqx.hrl").
-export([start_link/2]).
-export([subscribe/1, subscribe/2, subscribe/3, subscribe/4]).
-export([publish/1, publish/2]).
-export([publish/1, publish/2, safe_publish/1]).
-export([unsubscribe/1, unsubscribe/2]).
-export([dispatch/2, dispatch/3]).
-export([subscriptions/1, subscribers/1, subscribed/2]).
-export([get_subopts/2, set_subopts/3]).
-export([topics/0]).
%% gen_server Function Exports
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
code_change/3]).
-record(state, {pool, id, submon}).
@ -44,19 +41,14 @@
-define(SUBSCRIBER, emqx_subscriber).
-define(SUBSCRIPTION, emqx_subscription).
%%--------------------------------------------------------------------
%% Start a broker
%%--------------------------------------------------------------------
-spec(start_link(atom(), pos_integer())
-> {ok, pid()} | ignore | {error, term()}).
-spec(start_link(atom(), pos_integer()) -> {ok, pid()} | ignore | {error, term()}).
start_link(Pool, Id) ->
gen_server:start_link({local, emqx_misc:proc_name(?MODULE, Id)},
?MODULE, [Pool, Id], [{hibernate_after, 2000}]).
%%--------------------------------------------------------------------
%% Subscriber/Unsubscribe
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% Subscribe/Unsubscribe
%%------------------------------------------------------------------------------
-spec(subscribe(topic()) -> ok | {error, term()}).
subscribe(Topic) when is_binary(Topic) ->
@ -85,52 +77,60 @@ unsubscribe(Topic) when is_binary(Topic) ->
unsubscribe(Topic, Subscriber) when is_binary(Topic) ->
unsubscribe(Topic, Subscriber, ?TIMEOUT).
-spec(unsubscribe(topic(), subscriber(), timeout())
-> ok | {error, term()}).
-spec(unsubscribe(topic(), subscriber(), timeout()) -> ok | {error, term()}).
unsubscribe(Topic, Subscriber, Timeout) ->
{Topic1, _} = emqx_topic:parse(Topic),
UnsubReq = {unsubscribe, Topic1, with_subpid(Subscriber)},
async_call(pick(Subscriber), UnsubReq, Timeout).
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% Publish
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
-spec(publish(topic(), payload()) -> delivery() | stopped).
publish(Topic, Payload) when is_binary(Topic), is_binary(Payload) ->
publish(emqx_message:make(Topic, Payload)).
-spec(publish(message()) -> delivery() | stopped).
-spec(publish(message()) -> {ok, delivery()} | {error, stopped}).
publish(Msg = #message{from = From}) ->
%% Hook to trace?
trace(publish, From, Msg),
_ = trace(publish, From, Msg),
case emqx_hooks:run('message.publish', [], Msg) of
{ok, Msg1 = #message{topic = Topic}} ->
route(aggre(emqx_router:match_routes(Topic)), delivery(Msg1));
{ok, route(aggre(emqx_router:match_routes(Topic)), delivery(Msg1))};
{stop, Msg1} ->
emqx_logger:warning("Stop publishing: ~s", [emqx_message:format(Msg1)]),
stopped
{error, stopped}
end.
%%--------------------------------------------------------------------
%% called internally
safe_publish(Msg) ->
try
publish(Msg)
catch
_:Error:Stacktrace ->
emqx_logger:error("[Broker] publish error: ~p~n~p~n~p", [Error, Msg, Stacktrace])
end.
%%------------------------------------------------------------------------------
%% Trace
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
trace(publish, From, _Msg) when is_atom(From) ->
%% Dont' trace '$SYS' publish
ignore;
trace(public, #client{client_id = ClientId, username = Username},
trace(public, #client{id = ClientId, username = Username},
#message{topic = Topic, payload = Payload}) ->
emqx_logger:info([{client, ClientId}, {topic, Topic}],
"~s/~s PUBLISH to ~s: ~p", [ClientId, Username, Topic, Payload]);
"~s/~s PUBLISH to ~s: ~p", [Username, ClientId, Topic, Payload]);
trace(public, From, #message{topic = Topic, payload = Payload})
when is_binary(From); is_list(From) ->
emqx_logger:info([{client, From}, {topic, Topic}],
"~s PUBLISH to ~s: ~p", [From, Topic, Payload]).
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% Route
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
route([], Delivery = #delivery{message = Msg}) ->
emqx_hooks:run('message.dropped', [undefined, Msg]),
@ -193,7 +193,7 @@ dispatch({SubId, SubPid}, Topic, Msg) when is_binary(SubId), is_pid(SubPid) ->
dispatch(SubId, Topic, Msg) when is_binary(SubId) ->
emqx_sm:dispatch(SubId, Topic, Msg);
dispatch({share, _Group, _Sub}, _Topic, _Msg) ->
ignore.
ignored.
dropped(<<"$SYS/", _/binary>>) ->
ok;
@ -201,7 +201,7 @@ dropped(_Topic) ->
emqx_metrics:inc('messages/dropped').
delivery(Msg) ->
#delivery{message = Msg, flows = []}.
#delivery{node = node(), message = Msg, flows = []}.
subscribers(Topic) ->
try ets:lookup_element(?SUBSCRIBER, Topic, 2) catch error:badarg -> [] end.
@ -221,9 +221,7 @@ subscribed(Topic, SubPid) when is_binary(Topic), is_pid(SubPid) ->
ets:member(?SUBOPTION, {Topic, SubPid});
subscribed(Topic, SubId) when is_binary(Topic), is_binary(SubId) ->
length(ets:match_object(?SUBOPTION, {{Topic, {SubId, '_'}}, '_'}, 1)) == 1;
subscribed(Topic, {SubId, SubPid}) when is_binary(Topic),
is_binary(SubId),
is_pid(SubPid) ->
subscribed(Topic, {SubId, SubPid}) when is_binary(Topic), is_binary(SubId), is_pid(SubPid) ->
ets:member(?SUBOPTION, {Topic, {SubId, SubPid}}).
-spec(get_subopts(topic(), subscriber()) -> [suboption()]).
@ -262,22 +260,22 @@ pick(SubPid) when is_pid(SubPid) ->
pick(SubId) when is_binary(SubId) ->
gproc_pool:pick_worker(broker, SubId);
pick({SubId, SubPid}) when is_binary(SubId), is_pid(SubPid) ->
pick(SubId).
pick(SubPid).
-spec(topics() -> [topic()]).
topics() -> emqx_router:topics().
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% gen_server callbacks
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
init([Pool, Id]) ->
gproc_pool:connect_worker(Pool, {Pool, Id}),
true = gproc_pool:connect_worker(Pool, {Pool, Id}),
{ok, #state{pool = Pool, id = Id, submon = emqx_pmon:new()}}.
handle_call(Req, _From, State) ->
emqx_logger:error("[Broker] Unexpected request: ~p", [Req]),
{reply, ignore, State}.
emqx_logger:error("[Broker] unexpected call: ~p", [Req]),
{reply, ignored, State}.
handle_cast({From, {subscribe, Topic, Subscriber, Options}}, State) ->
case ets:lookup(?SUBOPTION, {Topic, Subscriber}) of
@ -307,11 +305,10 @@ handle_cast({From, {unsubscribe, Topic, Subscriber}}, State) ->
{noreply, State};
handle_cast(Msg, State) ->
emqx_logger:error("[Broker] Unexpected msg: ~p", [Msg]),
emqx_logger:error("[Broker] unexpected cast: ~p", [Msg]),
{noreply, State}.
handle_info({'DOWN', _MRef, process, SubPid, _Reason},
State = #state{submon = SubMon}) ->
handle_info({'DOWN', _MRef, process, SubPid, _Reason}, State = #state{submon = SubMon}) ->
Subscriber = case SubMon:find(SubPid) of
undefined -> SubPid;
SubId -> {SubId, SubPid}
@ -321,7 +318,8 @@ handle_info({'DOWN', _MRef, process, SubPid, _Reason},
({_, Topic}) ->
Topic
end, ets:lookup(?SUBSCRIPTION, Subscriber)),
lists:foreach(fun(Topic) ->
lists:foreach(
fun(Topic) ->
case ets:lookup(?SUBOPTION, {Topic, Subscriber}) of
[{_, Options}] ->
Group = proplists:get_value(share, Options),
@ -336,18 +334,18 @@ handle_info({'DOWN', _MRef, process, SubPid, _Reason},
{noreply, demonitor_subscriber(SubPid, State)};
handle_info(Info, State) ->
emqx_logger:error("[Broker] Unexpected info: ~p", [Info]),
emqx_logger:error("[Broker] unexpected info: ~p", [Info]),
{noreply, State}.
terminate(_Reason, #state{pool = Pool, id = Id}) ->
gproc_pool:disconnect_worker(Pool, {Pool, Id}).
true = gproc_pool:disconnect_worker(Pool, {Pool, Id}).
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%% Internal Functions
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% Internal functions
%%------------------------------------------------------------------------------
do_subscribe(Group, Topic, Subscriber, Options) ->
ets:insert(?SUBSCRIPTION, {Subscriber, shared(Group, Topic)}),

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_broker_helper).
@ -20,8 +18,8 @@
-export([start_link/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
code_change/3]).
-define(HELPER, ?MODULE).
@ -31,24 +29,24 @@
start_link() ->
gen_server:start_link({local, ?HELPER}, ?MODULE, [], []).
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% gen_server callbacks
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
init([]) ->
emqx_stats:update_interval(broker_stats, stats_fun()),
{ok, #state{}, hibernate}.
handle_call(Req, _From, State) ->
emqx_logger:error("[BrokerHelper] Unexpected request: ~p", [Req]),
emqx_logger:error("[BrokerHelper] unexpected call: ~p", [Req]),
{reply, ignore, State}.
handle_cast(Msg, State) ->
emqx_logger:error("[BrokerHelper] Unexpected msg: ~p", [Msg]),
emqx_logger:error("[BrokerHelper] unexpected cast: ~p", [Msg]),
{noreply, State}.
handle_info(Info, State) ->
emqx_logger:error("[BrokerHelper] Unexpected info: ~p", [Info]),
emqx_logger:error("[BrokerHelper] unexpected info: ~p", [Info]),
{noreply, State}.
terminate(_Reason, #state{}) ->
@ -57,9 +55,9 @@ terminate(_Reason, #state{}) ->
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
stats_fun() ->
fun() ->

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_broker_sup).
@ -27,15 +25,15 @@
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% Supervisor callbacks
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
init([]) ->
%% Create the pubsub tables
lists:foreach(fun create_tab/1, [subscription, subscriber, suboption]),
ok = lists:foreach(fun create_tab/1, [subscription, subscriber, suboption]),
%% Shared Subscription
%% Shared subscription
SharedSub = {shared_sub, {emqx_shared_sub, start_link, []},
permanent, 5000, worker, [emqx_shared_sub]},
@ -43,16 +41,16 @@ init([]) ->
Helper = {broker_helper, {emqx_broker_helper, start_link, []},
permanent, 5000, worker, [emqx_broker_helper]},
%% Broker Pool
%% Broker pool
BrokerPool = emqx_pool_sup:spec(emqx_broker_pool,
[broker, hash, emqx_vm:schedulers() * 2,
{emqx_broker, start_link, []}]),
{ok, {{one_for_all, 0, 1}, [SharedSub, Helper, BrokerPool]}}.
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% Create tables
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
create_tab(suboption) ->
%% Suboption: {Topic, Sub} -> [{qos, 1}]

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_cli).

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_client).
@ -142,9 +140,9 @@
-define(PROPERTY(Name, Val), #state{properties = #{Name := Val}}).
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% API
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
-spec(start_link() -> gen_statem:start_ret()).
start_link() -> start_link([]).
@ -302,9 +300,9 @@ disconnect(Client, ReasonCode) ->
disconnect(Client, ReasonCode, Properties) ->
gen_statem:call(Client, {disconnect, ReasonCode, Properties}).
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% For test cases
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
puback(Client, PacketId) when is_integer(PacketId) ->
puback(Client, PacketId, ?RC_SUCCESS).
@ -357,9 +355,9 @@ pause(Client) ->
resume(Client) ->
gen_statem:call(Client, resume).
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% gen_statem callbacks
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
init([Options]) ->
process_flag(trap_exit, true),
@ -892,9 +890,9 @@ terminate(_Reason, _State, #state{socket = Socket}) ->
code_change(_Vsn, State, Data, _Extra) ->
{ok, State, Data}.
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
ensure_keepalive_timer(State = ?PROPERTY('Server-Keep-Alive', Secs)) ->
ensure_keepalive_timer(timer:seconds(Secs), State);
@ -1017,7 +1015,7 @@ msg_to_packet(#mqtt_message{qos = Qos,
payload = Payload}.
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% Socket Connect/Send
sock_connect(Hosts, SockOpts, Timeout) ->
@ -1057,7 +1055,7 @@ send(Packet, State = #state{socket = Sock, proto_ver = Ver})
run_sock(State = #state{socket = Sock}) ->
emqx_client_sock:setopts(Sock, [{active, once}]), State.
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% Receive Loop
receive_loop(<<>>, State) ->
@ -1076,7 +1074,7 @@ receive_loop(Bytes, State = #state{parse_state = ParseState}) ->
{stop, Error}
end.
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% Next packet id
next_packet_id(State = #state{last_packet_id = 16#ffff}) ->

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_client_sock).
@ -26,8 +24,7 @@
-type(sockname() :: {inet:ip_address(), inet:port_number()}).
-type(option() :: gen_tcp:connect_option()
| {ssl_opts, [ssl:ssl_option()]}).
-type(option() :: gen_tcp:connect_option() | {ssl_opts, [ssl:ssl_option()]}).
-export_type([socket/0, option/0]).

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_cm).
@ -22,20 +20,18 @@
-export([start_link/0]).
-export([lookup_client/1, register_client/1, register_client/2,
unregister_client/1]).
-export([lookup_client/1]).
-export([register_client/1, register_client/2, unregister_client/1]).
-export([get_client_attrs/1, lookup_client_pid/1]).
-export([get_client_stats/1, set_client_stats/2]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
code_change/3]).
-record(state, {client_pmon}).
-define(CM, ?MODULE).
%% ETS Tables.
-define(CLIENT, emqx_client).
-define(CLIENT_ATTRS, emqx_client_attrs).
@ -56,22 +52,20 @@ lookup_client(ClientId) when is_binary(ClientId) ->
register_client(ClientId) when is_binary(ClientId) ->
register_client({ClientId, self()});
register_client({ClientId, ClientPid}) when is_binary(ClientId),
is_pid(ClientPid) ->
register_client({ClientId, ClientPid}) when is_binary(ClientId), is_pid(ClientPid) ->
register_client({ClientId, ClientPid}, []).
-spec(register_client({client_id(), pid()}, list()) -> ok).
register_client({ClientId, ClientPid}, Attrs) when is_binary(ClientId),
is_pid(ClientPid) ->
ets:insert(?CLIENT, {ClientId, ClientPid}),
ets:insert(?CLIENT_ATTRS, {{ClientId, ClientPid}, Attrs}),
register_client(CObj = {ClientId, ClientPid}, Attrs) when is_binary(ClientId), is_pid(ClientPid) ->
_ = ets:insert(?CLIENT, CObj),
_ = ets:insert(?CLIENT_ATTRS, {CObj, Attrs}),
notify({registered, ClientId, ClientPid}).
%% @doc Get client attrs
-spec(get_client_attrs({client_id(), pid()}) -> list()).
get_client_attrs({ClientId, ClientPid}) when is_binary(ClientId),
is_pid(ClientPid) ->
try ets:lookup_element(?CLIENT_ATTRS, {ClientId, ClientPid}, 2)
get_client_attrs(CObj = {ClientId, ClientPid}) when is_binary(ClientId), is_pid(ClientPid) ->
try
ets:lookup_element(?CLIENT_ATTRS, CObj, 2)
catch
error:badarg -> []
end.
@ -81,11 +75,10 @@ get_client_attrs({ClientId, ClientPid}) when is_binary(ClientId),
unregister_client(ClientId) when is_binary(ClientId) ->
unregister_client({ClientId, self()});
unregister_client({ClientId, ClientPid}) when is_binary(ClientId),
is_pid(ClientPid) ->
ets:delete(?CLIENT_STATS, {ClientId, ClientPid}),
ets:delete(?CLIENT_ATTRS, {ClientId, ClientPid}),
ets:delete_object(?CLIENT, {ClientId, ClientPid}),
unregister_client(CObj = {ClientId, ClientPid}) when is_binary(ClientId), is_pid(ClientPid) ->
_ = ets:delete(?CLIENT_STATS, CObj),
_ = ets:delete(?CLIENT_ATTRS, CObj),
_ = ets:delete_object(?CLIENT, CObj),
notify({unregistered, ClientId, ClientPid}).
%% @doc Lookup client pid
@ -98,9 +91,8 @@ lookup_client_pid(ClientId) when is_binary(ClientId) ->
%% @doc Get client stats
-spec(get_client_stats({client_id(), pid()}) -> list(emqx_stats:stats())).
get_client_stats({ClientId, ClientPid}) when is_binary(ClientId),
is_pid(ClientPid) ->
try ets:lookup_element(?CLIENT_STATS, {ClientId, ClientPid}, 2)
get_client_stats(CObj = {ClientId, ClientPid}) when is_binary(ClientId), is_pid(ClientPid) ->
try ets:lookup_element(?CLIENT_STATS, CObj, 2)
catch
error:badarg -> []
end.
@ -110,16 +102,15 @@ get_client_stats({ClientId, ClientPid}) when is_binary(ClientId),
set_client_stats(ClientId, Stats) when is_binary(ClientId) ->
set_client_stats({ClientId, self()}, Stats);
set_client_stats({ClientId, ClientPid}, Stats) when is_binary(ClientId),
is_pid(ClientPid) ->
ets:insert(?CLIENT_STATS, {{ClientId, ClientPid}, Stats}).
set_client_stats(CObj = {ClientId, ClientPid}, Stats) when is_binary(ClientId), is_pid(ClientPid) ->
ets:insert(?CLIENT_STATS, {CObj, Stats}).
notify(Msg) ->
gen_server:cast(?CM, {notify, Msg}).
%%--------------------------------------------------------------------
%%-----------------------------------------------------------------------------
%% gen_server callbacks
%%--------------------------------------------------------------------
%%-----------------------------------------------------------------------------
init([]) ->
TabOpts = [public, set, {write_concurrency, true}],
@ -130,8 +121,8 @@ init([]) ->
{ok, #state{client_pmon = emqx_pmon:new()}}.
handle_call(Req, _From, State) ->
emqx_logger:error("[CM] Unexpected request: ~p", [Req]),
{reply, ignore, State}.
emqx_logger:error("[CM] unexpected call: ~p", [Req]),
{reply, ignored, State}.
handle_cast({notify, {registered, ClientId, Pid}}, State = #state{client_pmon = PMon}) ->
{noreply, State#state{client_pmon = emqx_pmon:monitor(Pid, ClientId, PMon)}};
@ -140,7 +131,7 @@ handle_cast({notify, {unregistered, _ClientId, Pid}}, State = #state{client_pmon
{noreply, State#state{client_pmon = emqx_pmon:demonitor(Pid, PMon)}};
handle_cast(Msg, State) ->
emqx_logger:error("[CM] Unexpected msg: ~p", [Msg]),
emqx_logger:error("[CM] unexpected cast: ~p", [Msg]),
{noreply, State}.
handle_info({'DOWN', _MRef, process, DownPid, _Reason}, State = #state{client_pmon = PMon}) ->
@ -152,7 +143,7 @@ handle_info({'DOWN', _MRef, process, DownPid, _Reason}, State = #state{client_pm
end;
handle_info(Info, State) ->
emqx_logger:error("[CM] Unexpected info: ~p", [Info]),
emqx_logger:error("[CM] unexpected info: ~p", [Info]),
{noreply, State}.
terminate(_Reason, _State = #state{}) ->
@ -161,9 +152,9 @@ terminate(_Reason, _State = #state{}) ->
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%%-----------------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
%%-----------------------------------------------------------------------------
update_client_stats() ->
case ets:info(?CLIENT, size) of

View File

@ -1,18 +1,17 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%%
%% 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(emqx_cm_sup).
@ -26,7 +25,11 @@ start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
CM = {emqx_cm, {emqx_cm, start_link, []},
permanent, 5000, worker, [emqx_cm]},
{ok, {{one_for_all, 10, 3600}, [CM]}}.
{ok, {{one_for_all, 10, 3600},
[#{id => manager,
start => {emqx_cm, start_link, []},
restart => permanent,
shutdown => 5000,
type => worker,
modules => [emqx_cm]}]}}.

View File

@ -1,5 +1,4 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@ -12,7 +11,6 @@
%% 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.
%%--------------------------------------------------------------------
%% @doc Hot Configuration
%%
@ -35,7 +33,7 @@
-define(APP, emqx).
-spec(get_env(Key :: atom(), Default :: any()) -> undefined | any()).
-spec(get_env(Key :: atom(), Default :: term()) -> term()).
get_env(Key, Default) ->
application:get_env(?APP, Key, Default).

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_connection).
@ -41,8 +39,8 @@
-export([session/1]).
%% gen_server Function Exports
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
code_change/3, terminate/2]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, code_change/3,
terminate/2]).
%% Unused fields: connname, peerhost, peerport
-record(state, {transport, socket, peername, conn_state, await_recv,
@ -88,7 +86,7 @@ clean_acl_cache(CPid, Topic) ->
gen_server:call(CPid, {clean_acl_cache, Topic}).
%%--------------------------------------------------------------------
%% gen_server Callbacks
%% gen_server callbacks
%%--------------------------------------------------------------------
init([Transport, Sock, Env]) ->

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_ctl).
@ -22,9 +20,8 @@
-export([register_command/2, register_command/3, unregister_command/1]).
-export([run_command/2, lookup_command/1]).
%% gen_server Function Exports
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
code_change/3]).
-record(state, {seq = 0}).
@ -33,10 +30,6 @@
-define(SERVER, ?MODULE).
-define(TAB, emqx_command).
%%--------------------------------------------------------------------
%% API
%%--------------------------------------------------------------------
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
@ -64,9 +57,8 @@ run_command(Cmd, Args) when is_atom(Cmd) ->
try Mod:Fun(Args) of
_ -> ok
catch
_:Reason ->
emqx_logger:error("[CTL] CMD Error:~p, Stacktrace:~p",
[Reason, erlang:get_stacktrace()]),
_:Reason:Stacktrace ->
emqx_logger:error("[CTL] CMD Error:~p, Stacktrace:~p", [Reason, Stacktrace]),
{error, Reason}
end;
[] ->
@ -85,17 +77,17 @@ usage() ->
[begin io:format("~80..-s~n", [""]), Mod:Cmd(usage) end
|| {_, {Mod, Cmd}, _} <- ets:tab2list(?TAB)].
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% gen_server callbacks
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
init([]) ->
_ = emqx_tables:new(?TAB, [ordered_set, protected]),
{ok, #state{seq = 0}}.
handle_call(Req, _From, State) ->
emqx_logger:error("Unexpected request: ~p", [Req]),
{reply, ignore, State}.
emqx_logger:error("unexpected call: ~p", [Req]),
{reply, ignored, State}.
handle_cast({register_command, Cmd, MF, Opts}, State = #state{seq = Seq}) ->
case ets:match(?TAB, {{'$1', Cmd}, '_', '_'}) of
@ -111,11 +103,11 @@ handle_cast({unregister_command, Cmd}, State) ->
noreply(State);
handle_cast(Msg, State) ->
emqx_logger:error("Unexpected msg: ~p", [Msg]),
emqx_logger:error("Unexpected cast: ~p", [Msg]),
noreply(State).
handle_info(Info, State) ->
emqx_logger:error("Unexpected info: ~p", [Info]),
emqx_logger:error("unexpected info: ~p", [Info]),
noreply(State).
terminate(_Reason, _State) ->
@ -124,9 +116,9 @@ terminate(_Reason, _State) ->
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% Internal Function
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
noreply(State) ->
{noreply, State, hibernate}.

View File

@ -1,5 +1,4 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@ -12,8 +11,8 @@
%% 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.
%%--------------------------------------------------------------------
%% @doc TODO:
%% 1. Flapping Detection
%% 2. Conflict Detection?
-module(emqx_flapping).

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_flow_control).

View File

@ -1,25 +1,26 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_frame).
-include("emqx.hrl").
-include("emqx_mqtt.hrl").
-export([initial_state/0, initial_state/1]).
-export([parse/2]).
-export([serialize/1, serialize/2]).
-type(options() :: #{max_packet_size => 1..?MAX_PACKET_SIZE,
version => mqtt_version()}).
@ -30,16 +31,11 @@
-export_type([options/0, parse_state/0]).
-export([initial_state/0, initial_state/1]).
-export([parse/2]).
-export([serialize/1, serialize/2]).
-define(DEFAULT_OPTIONS, #{max_packet_size => ?MAX_PACKET_SIZE, version => ?MQTT_PROTO_V4}).
-define(DEFAULT_OPTIONS, #{max_packet_size => ?MAX_PACKET_SIZE,
version => ?MQTT_PROTO_V4}).
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% Init parse state
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
-spec(initial_state() -> {none, options()}).
initial_state() ->
@ -52,12 +48,11 @@ initial_state(Options) when is_map(Options) ->
merge_opts(Options) ->
maps:merge(?DEFAULT_OPTIONS, Options).
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% Parse MQTT Frame
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
-spec(parse(binary(), parse_state())
-> {ok, mqtt_packet(), binary()} | {more, cont_fun(binary())}).
-spec(parse(binary(), parse_state()) -> {ok, mqtt_packet(), binary()} | {more, cont_fun(binary())}).
parse(<<>>, {none, Options}) ->
{more, fun(Bin) -> parse(Bin, {none, Options}) end};
parse(<<Type:4, Dup:1, QoS:2, Retain:1, Rest/binary>>, {none, Options}) ->
@ -357,9 +352,9 @@ parse_utf8_string(<<Len:16/big, Str:Len/binary, Rest/binary>>) ->
parse_binary_data(<<Len:16/big, Data:Len/binary, Rest/binary>>) ->
{Data, Rest}.
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% Serialize MQTT Packet
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
-spec(serialize(mqtt_packet()) -> iodata()).
serialize(Packet) ->
@ -369,8 +364,7 @@ serialize(Packet) ->
serialize(#mqtt_packet{header = Header,
variable = Variable,
payload = Payload}, Options) when is_map(Options) ->
serialize(Header, serialize_variable(Variable, merge_opts(Options)),
serialize_payload(Payload)).
serialize(Header, serialize_variable(Variable, merge_opts(Options)), serialize_payload(Payload)).
serialize(#mqtt_packet_header{type = Type,
dup = Dup,
@ -622,10 +616,6 @@ serialize_variable_byte_integer(N) when N =< ?LOWBITS ->
serialize_variable_byte_integer(N) ->
<<1:1, (N rem ?HIGHBIT):7, (serialize_variable_byte_integer(N div ?HIGHBIT))/binary>>.
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
bool(0) -> false;
bool(1) -> true.

View File

@ -1,5 +1,4 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@ -12,12 +11,16 @@
%% 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.
%%--------------------------------------------------------------------
%% GC Utility functions.
-module(emqx_gc).
%% Memory: (10, 100, 1000)
%%
%%-record
-export([conn_max_gc_count/0, reset_conn_gc_count/2, maybe_force_gc/2,
maybe_force_gc/3]).

View File

@ -1,5 +1,4 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@ -12,7 +11,6 @@
%% 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(emqx_gen_mod).

View File

@ -1,5 +1,4 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@ -12,7 +11,6 @@
%% 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.
%%--------------------------------------------------------------------
%% @doc Generate global unique id for mqtt message.
%%

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_hooks).

View File

@ -1,90 +1,89 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_inflight).
-export([new/1, contain/2, lookup/2, insert/3, update/3, delete/2, values/1,
to_list/1, size/1, max_size/1, is_full/1, is_empty/1, window/1]).
-type(inflight() :: {max_size, gb_trees:tree()}).
-type(max_size() :: pos_integer()).
-type(inflight() :: {?MODULE, max_size(), gb_trees:tree()}).
-export_type([inflight/0]).
-spec(new(non_neg_integer()) -> inflight()).
new(MaxSize) when MaxSize >= 0 ->
{MaxSize, gb_trees:empty()}.
{?MODULE, MaxSize, gb_trees:empty()}.
-spec(contain(Key :: term(), inflight()) -> boolean()).
contain(Key, {_MaxSize, Tree}) ->
contain(Key, {?MODULE, _MaxSize, Tree}) ->
gb_trees:is_defined(Key, Tree).
-spec(lookup(Key :: term(), inflight()) -> {value, term()} | none).
lookup(Key, {_MaxSize, Tree}) ->
lookup(Key, {?MODULE, _MaxSize, Tree}) ->
gb_trees:lookup(Key, Tree).
-spec(insert(Key :: term(), Value :: term(), inflight()) -> inflight()).
insert(Key, Value, {MaxSize, Tree}) ->
{MaxSize, gb_trees:insert(Key, Value, Tree)}.
insert(Key, Value, {?MODULE, MaxSize, Tree}) ->
{?MODULE, MaxSize, gb_trees:insert(Key, Value, Tree)}.
-spec(delete(Key :: term(), inflight()) -> inflight()).
delete(Key, {MaxSize, Tree}) ->
{MaxSize, gb_trees:delete(Key, Tree)}.
delete(Key, {?MODULE, MaxSize, Tree}) ->
{?MODULE, MaxSize, gb_trees:delete(Key, Tree)}.
-spec(update(Key :: term(), Val :: term(), inflight()) -> inflight()).
update(Key, Val, {MaxSize, Tree}) ->
{MaxSize, gb_trees:update(Key, Val, Tree)}.
update(Key, Val, {?MODULE, MaxSize, Tree}) ->
{?MODULE, MaxSize, gb_trees:update(Key, Val, Tree)}.
-spec(is_full(inflight()) -> boolean()).
is_full({0, _Tree}) ->
is_full({?MODULE, 0, _Tree}) ->
false;
is_full({MaxSize, Tree}) ->
is_full({?MODULE, MaxSize, Tree}) ->
MaxSize =< gb_trees:size(Tree).
-spec(is_empty(inflight()) -> boolean()).
is_empty({_MaxSize, Tree}) ->
is_empty({?MODULE, _MaxSize, Tree}) ->
gb_trees:is_empty(Tree).
-spec(smallest(inflight()) -> {K :: term(), V :: term()}).
smallest({_MaxSize, Tree}) ->
smallest({?MODULE, _MaxSize, Tree}) ->
gb_trees:smallest(Tree).
-spec(largest(inflight()) -> {K :: term(), V :: term()}).
largest({_MaxSize, Tree}) ->
largest({?MODULE, _MaxSize, Tree}) ->
gb_trees:largest(Tree).
-spec(values(inflight()) -> list()).
values({_MaxSize, Tree}) ->
values({?MODULE, _MaxSize, Tree}) ->
gb_trees:values(Tree).
-spec(to_list(inflight()) -> list({K :: term(), V :: term()})).
to_list({_MaxSize, Tree}) ->
to_list({?MODULE, _MaxSize, Tree}) ->
gb_trees:to_list(Tree).
-spec(window(inflight()) -> list()).
window(Inflight = {_MaxSize, Tree}) ->
window(Inflight = {?MODULE, _MaxSize, Tree}) ->
case gb_trees:is_empty(Tree) of
true -> [];
false -> [Key || {Key, _Val} <- [smallest(Inflight), largest(Inflight)]]
end.
-spec(size(inflight()) -> non_neg_integer()).
size({_MaxSize, Tree}) ->
size({?MODULE, _MaxSize, Tree}) ->
gb_trees:size(Tree).
-spec(max_size(inflight()) -> non_neg_integer()).
max_size({MaxSize, _Tree}) ->
max_size({?MODULE, MaxSize, _Tree}) ->
MaxSize.

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_json).

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_keepalive).

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_kernel_sup).
@ -36,8 +34,18 @@ init([]) ->
child_spec(emqx_tracer, worker)]}}.
child_spec(M, worker) ->
{M, {M, start_link, []}, permanent, 5000, worker, [M]};
#{id => M,
start => {M, start_link, []},
restart => permanent,
shutdown => 5000,
type => worker,
modules => [M]};
child_spec(M, supervisor) ->
{M, {M, start_link, []},
permanent, infinity, supervisor, [M]}.
#{id => M,
start => {M, start_link, []},
restart => permanent,
shutdown => infinity,
type => supervisor,
modules => [M]}.

View File

@ -1,5 +1,4 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@ -12,7 +11,6 @@
%% 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(emqx_lager_backend).

View File

@ -1,97 +1,62 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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.
%% @doc start/stop MQTT listeners.
-module(emqx_listeners).
-include("emqx_mqtt.hrl").
-export([start/0, restart/0, stop/0]).
-export([start_all/0, restart_all/0, stop_all/0]).
-export([start_listener/1, stop_listener/1, restart_listener/1]).
-export([all/0]).
-type(listener() :: {atom(), esockd:listen_on(), [esockd:option()]}).
%%--------------------------------------------------------------------
%% Start/Stop Listeners
%%--------------------------------------------------------------------
%% @doc Start Listeners.
-spec(start() -> ok).
start() ->
%% @doc Start all listeners
-spec(start_all() -> ok).
start_all() ->
lists:foreach(fun start_listener/1, emqx_config:get_env(listeners, [])).
%% Start mqtt listener
-spec(start_listener(listener()) -> {ok, pid()} | {error, any()}).
start_listener({tcp, ListenOn, Opts}) ->
start_listener('mqtt:tcp', ListenOn, Opts);
%% Start MQTT/TCP listener
-spec(start_listener(listener()) -> {ok, pid()} | {error, term()}).
start_listener({tcp, ListenOn, Options}) ->
start_mqtt_listener('mqtt:tcp', ListenOn, Options);
%% Start MQTT/TLS listener
start_listener({Proto, ListenOn, Options}) when Proto == ssl; Proto == tls ->
start_mqtt_listener('mqtt:tls', ListenOn, Options);
%% Start MQTT/WS listener
start_listener({Proto, ListenOn, Options}) when Proto == http; Proto == ws ->
start_http_listener('mqtt:ws', ListenOn, Options);
%% Start MQTT/WSS listener
start_listener({Proto, ListenOn, Options}) when Proto == https; Proto == wss ->
start_http_listener('mqtt:wss', ListenOn, Options).
%% Start mqtt(SSL) listener
start_listener({ssl, ListenOn, Opts}) ->
start_listener('mqtt:ssl', ListenOn, Opts);
start_mqtt_listener(Name, ListenOn, Options) ->
{ok, _} = esockd:open(Name, ListenOn, merge_sockopts(Options), {emqx_connection, start_link, []}).
%% Start http listener
start_listener({Proto, ListenOn, Opts}) when Proto == http; Proto == ws ->
{ok, _} = mochiweb:start_http('mqtt:ws', ListenOn, Opts, {emqx_ws, handle_request, []});
start_http_listener(Name, ListenOn, Options) ->
{ok, _} = mochiweb:start_http(Name, ListenOn, Options, {emqx_ws, handle_request, []}).
%% Start https listener
start_listener({Proto, ListenOn, Opts}) when Proto == https; Proto == wss ->
{ok, _} = mochiweb:start_http('mqtt:wss', ListenOn, Opts, {emqx_ws, handle_request, []}).
start_listener(Proto, ListenOn, Opts) ->
Env = lists:append(emqx_config:get_env(client, []),
emqx_config:get_env(protocol, [])),
MFArgs = {emqx_connection, start_link, [Env]},
{ok, _} = esockd:open(Proto, ListenOn, merge_sockopts(Opts), MFArgs).
all() ->
[Listener || Listener = {{Proto, _}, _Pid} <- esockd:listeners(), is_mqtt(Proto)].
is_mqtt('mqtt:tcp') -> true;
is_mqtt('mqtt:ssl') -> true;
is_mqtt('mqtt:ws') -> true;
is_mqtt('mqtt:wss') -> true;
is_mqtt(_Proto) -> false.
%% @doc Stop Listeners
-spec(stop() -> ok).
stop() ->
lists:foreach(fun stop_listener/1, emqx_config:get_env(listeners, [])).
-spec(stop_listener(listener()) -> ok | {error, any()}).
stop_listener({tcp, ListenOn, _Opts}) ->
esockd:close('mqtt:tcp', ListenOn);
stop_listener({ssl, ListenOn, _Opts}) ->
esockd:close('mqtt:ssl', ListenOn);
stop_listener({Proto, ListenOn, _Opts}) when Proto == http; Proto == ws ->
mochiweb:stop_http('mqtt:ws', ListenOn);
stop_listener({Proto, ListenOn, _Opts}) when Proto == https; Proto == wss ->
mochiweb:stop_http('mqtt:wss', ListenOn);
stop_listener({Proto, ListenOn, _Opts}) ->
esockd:close(Proto, ListenOn).
%% @doc Restart Listeners
-spec(restart() -> ok).
restart() ->
%% @doc Restart all listeners
-spec(restart_all() -> ok).
restart_all() ->
lists:foreach(fun restart_listener/1, emqx_config:get_env(listeners, [])).
-spec(restart_listener(listener()) -> any()).
restart_listener({tcp, ListenOn, _Opts}) ->
esockd:reopen('mqtt:tcp', ListenOn);
restart_listener({ssl, ListenOn, _Opts}) ->
esockd:reopen('mqtt:ssl', ListenOn);
restart_listener({Proto, ListenOn, _Opts}) when Proto == ssl; Proto == tls ->
esockd:reopen('mqtt:tls', ListenOn);
restart_listener({Proto, ListenOn, _Opts}) when Proto == http; Proto == ws ->
mochiweb:restart_http('mqtt:ws', ListenOn);
restart_listener({Proto, ListenOn, _Opts}) when Proto == https; Proto == wss ->
@ -99,10 +64,36 @@ restart_listener({Proto, ListenOn, _Opts}) when Proto == https; Proto == wss ->
restart_listener({Proto, ListenOn, _Opts}) ->
esockd:reopen(Proto, ListenOn).
%% @doc Stop all listeners
-spec(stop_all() -> ok).
stop_all() ->
lists:foreach(fun stop_listener/1, emqx_config:get_env(listeners, [])).
-spec(stop_listener(listener()) -> ok | {error, any()}).
stop_listener({tcp, ListenOn, _Opts}) ->
esockd:close('mqtt:tcp', ListenOn);
stop_listener({Proto, ListenOn, _Opts}) when Proto == ssl; Proto == tls ->
esockd:close('mqtt:tls', ListenOn);
stop_listener({Proto, ListenOn, _Opts}) when Proto == http; Proto == ws ->
mochiweb:stop_http('mqtt:ws', ListenOn);
stop_listener({Proto, ListenOn, _Opts}) when Proto == https; Proto == wss ->
mochiweb:stop_http('mqtt:wss', ListenOn);
stop_listener({Proto, ListenOn, _Opts}) ->
esockd:close(Proto, ListenOn).
merge_sockopts(Options) ->
%%TODO: tcp_options?
SockOpts = emqx_misc:merge_opts(
?MQTT_SOCKOPTS, proplists:get_value(sockopts, Options, [])),
emqx_misc:merge_opts(Options, [{sockopts, SockOpts}]).
case lists:keytake(tcp_options, 1, Options) of
{value, {tcp_options, TcpOpts}, Options1} ->
[{tcp_options, emqx_misc:merge_opts(?MQTT_SOCKOPTS, TcpOpts)} | Options1];
false ->
[{tcp_options, ?MQTT_SOCKOPTS} | Options]
end.
%% all() ->
%% [Listener || Listener = {{Proto, _}, _Pid} <- esockd:listeners(), is_mqtt(Proto)].
%%is_mqtt('mqtt:tcp') -> true;
%%is_mqtt('mqtt:tls') -> true;
%%is_mqtt('mqtt:ws') -> true;
%%is_mqtt('mqtt:wss') -> true;
%%is_mqtt(_Proto) -> false.

View File

@ -1,28 +1,26 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_logger).
-compile({no_auto_import,[error/1]}).
-export([debug/1, debug/2, debug/3,
info/1, info/2, info/3,
warning/1, warning/2, warning/3,
error/1, error/2, error/3,
critical/1, critical/2, critical/3]).
-export([debug/1, debug/2, debug/3]).
-export([info/1, info/2, info/3]).
-export([warning/1, warning/2, warning/3]).
-export([error/1, error/2, error/3]).
-export([critical/1, critical/2, critical/3]).
debug(Msg) ->
lager:debug(Msg).

View File

@ -1,45 +1,46 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_message).
-include("emqx.hrl").
-include("emqx_mqtt.hrl").
-export([make/2, make/3, make/4]).
-export([get_flag/2, get_flag/3, set_flag/2, unset_flag/2]).
-export([new/2, new/3, new/4, new/5]).
-export([get_flag/2, get_flag/3, set_flag/2, set_flag/3, unset_flag/2]).
-export([get_header/2, get_header/3, set_header/3]).
-export([get_user_property/2, get_user_property/3, set_user_property/3]).
-spec(make(topic(), payload()) -> message()).
make(Topic, Payload) ->
make(undefined, Topic, Payload).
-spec(new(topic(), payload()) -> message()).
new(Topic, Payload) ->
new(undefined, Topic, Payload).
%% Create a message
-spec(make(atom() | client(), topic(), payload()) -> message()).
make(From, Topic, Payload) when is_atom(From); is_record(From, client) ->
make(From, ?QOS_0, Topic, Payload).
-spec(new(atom() | client(), topic(), payload()) -> message()).
new(From, Topic, Payload) when is_atom(From); is_record(From, client) ->
new(From, #{qos => ?QOS0}, Topic, Payload).
make(From, QoS, Topic, Payload) when is_atom(From); is_record(From, client) ->
-spec(new(atom() | client(), message_flags(), topic(), payload()) -> message()).
new(From, Flags, Topic, Payload) when is_atom(From); is_record(From, client) ->
new(From, Flags, #{}, Topic, Payload).
-spec(new(atom() | client(), message_flags(), message_headers(), topic(), payload()) -> message()).
new(From, Flags, Headers, Topic, Payload) when is_atom(From); is_record(From, client) ->
#message{id = msgid(),
qos = ?QOS_I(QoS),
from = From,
sender = self(),
flags = #{},
headers = #{},
flags = Flags,
headers = Headers,
topic = Topic,
properties = #{},
payload = Payload,
@ -58,6 +59,10 @@ get_flag(Flag, #message{flags = Flags}, Default) ->
set_flag(Flag, Msg = #message{flags = Flags}) when is_atom(Flag) ->
Msg#message{flags = maps:put(Flag, true, Flags)}.
-spec(set_flag(message_flag(), boolean() | integer(), message()) -> message()).
set_flag(Flag, Val, Msg = #message{flags = Flags}) when is_atom(Flag) ->
Msg#message{flags = maps:put(Flag, Val, Flags)}.
%% @doc Unset flag
-spec(unset_flag(message_flag(), message()) -> message()).
unset_flag(Flag, Msg = #message{flags = Flags}) ->

View File

@ -1,35 +1,30 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_metrics).
-include("emqx_mqtt.hrl").
-export([start_link/0]).
-export([new/1, all/0]).
-export([val/1, inc/1, inc/2, inc/3, dec/2, dec/3, set/2]).
%% Received/sent metrics
-export([received/1, sent/1]).
%% gen_server Function Exports
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
code_change/3]).
-record(state, {}).
@ -86,7 +81,6 @@
]).
-define(TAB, ?MODULE).
-define(SERVER, ?MODULE).
%% @doc Start the metrics server
@ -94,9 +88,9 @@
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% Metrics API
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
new({gauge, Name}) ->
ets:insert(?TAB, {{Name, 0}, 0});
@ -168,9 +162,9 @@ key(counter, Metric) ->
update_counter(Key, UpOp) ->
ets:update_counter(?TAB, Key, UpOp).
%%--------------------------------------------------------------------
%% Receive/Sent metrics
%%--------------------------------------------------------------------
%%-----------------------------------------------------------------------------
%% Received/Sent metrics
%%-----------------------------------------------------------------------------
%% @doc Count packets received.
-spec(received(mqtt_packet()) -> ok).
@ -248,9 +242,9 @@ qos_sent(?QOS_1) ->
qos_sent(?QOS_2) ->
inc('messages/qos2/sent').
%%--------------------------------------------------------------------
%%-----------------------------------------------------------------------------
%% gen_server callbacks
%%--------------------------------------------------------------------
%%-----------------------------------------------------------------------------
init([]) ->
% Create metrics table
@ -259,15 +253,15 @@ init([]) ->
{ok, #state{}, hibernate}.
handle_call(Req, _From, State) ->
emqx_logger:error("[METRICS] Unexpected request: ~p", [Req]),
{reply, ignore, State}.
emqx_logger:error("[Metrics] unexpected call: ~p", [Req]),
{reply, ignored, State}.
handle_cast(Msg, State) ->
emqx_logger:error("[METRICS] Unexpected msg: ~p", [Msg]),
emqx_logger:error("[Metrics] unexpected cast: ~p", [Msg]),
{noreply, State}.
handle_info(Info, State) ->
emqx_logger:error("[METRICS] Unexpected info: ~p", [Info]),
emqx_logger:error("[Metrics] unexpected info: ~p", [Info]),
{noreply, State}.
terminate(_Reason, #state{}) ->

View File

@ -1,25 +1,23 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_misc).
-export([merge_opts/2, start_timer/2, start_timer/3, cancel_timer/1,
proc_name/2, proc_stats/0, proc_stats/1]).
%% @doc Merge Options
%% @doc Merge options
-spec(merge_opts(list(), list()) -> list()).
merge_opts(Defaults, Options) ->
lists:foldl(
@ -42,7 +40,8 @@ cancel_timer(undefined) ->
ok;
cancel_timer(Timer) ->
case catch erlang:cancel_timer(Timer) of
false -> receive {timeout, Timer, _} -> ok after 0 -> ok end;
false ->
receive {timeout, Timer, _} -> ok after 0 -> ok end;
_ -> ok
end.

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_mod_presence).
@ -21,14 +19,13 @@
-include("emqx.hrl").
-export([load/1, unload/1]).
-export([on_client_connected/3, on_client_disconnected/3]).
load(Env) ->
emqx:hook('client.connected', fun ?MODULE:on_client_connected/3, [Env]),
emqx:hook('client.disconnected', fun ?MODULE:on_client_disconnected/3, [Env]).
on_client_connected(ConnAck, Client = #client{client_id = ClientId,
on_client_connected(ConnAck, Client = #client{id = ClientId,
username = Username,
peername = {IpAddr, _}
%%clean_sess = CleanSess,
@ -36,7 +33,7 @@ on_client_connected(ConnAck, Client = #client{client_id = ClientId,
}, Env) ->
case emqx_json:safe_encode([{clientid, ClientId},
{username, Username},
{ipaddress, iolist_to_binary(emqx_net:ntoa(IpAddr))},
{ipaddress, iolist_to_binary(esockd_net:ntoa(IpAddr))},
%%{clean_sess, CleanSess}, %%TODO:: fixme later
%%{protocol, ProtoVer},
{connack, ConnAck},
@ -49,8 +46,7 @@ on_client_connected(ConnAck, Client = #client{client_id = ClientId,
end,
{ok, Client}.
on_client_disconnected(Reason, #client{client_id = ClientId,
username = Username}, Env) ->
on_client_disconnected(Reason, #client{id = ClientId, username = Username}, Env) ->
case emqx_json:safe_encode([{clientid, ClientId},
{username, Username},
{reason, reason(Reason)},

View File

@ -1,5 +1,4 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@ -12,7 +11,6 @@
%% 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(emqx_mod_rewrite).

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_mod_subscription).
@ -33,9 +31,7 @@
load(Topics) ->
emqx:hook('client.connected', fun ?MODULE:on_client_connected/3, [Topics]).
on_client_connected(RC, Client = #client{client_id = ClientId,
client_pid = ClientPid,
username = Username}, Topics)
on_client_connected(RC, Client = #client{id = ClientId, pid = ClientPid, username = Username}, Topics)
when RC < 16#80 ->
Replace = fun(Topic) -> rep(<<"%u">>, Username, rep(<<"%c">>, ClientId, Topic)) end,
TopicTable = [{Replace(Topic), Qos} || {Topic, Qos} <- Topics],

View File

@ -1,5 +1,4 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@ -12,27 +11,17 @@
%% 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(emqx_mod_sup).
-behaviour(supervisor).
-include("emqx.hrl").
%% API
-export([start_link/0, start_child/1, start_child/2, stop_child/1]).
%% Supervisor callbacks
-export([init/1]).
%% Helper macro for declaring children of supervisor
-define(CHILD(Mod, Type), {Mod, {Mod, start_link, []}, permanent, 5000, Type, [Mod]}).
%%--------------------------------------------------------------------
%% API
%%--------------------------------------------------------------------
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
@ -49,10 +38,9 @@ stop_child(ChildId) ->
Error -> Error
end.
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% Supervisor callbacks
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
init([]) ->
{ok, {{one_for_one, 10, 100}, []}}.

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_modules).

View File

@ -1,19 +1,18 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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.
%% @doc MQTT5 Properties
-module(emqx_mqtt_properties).
-include("emqx_mqtt.hrl").

View File

@ -1,5 +1,4 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@ -12,8 +11,8 @@
%% 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.
%%--------------------------------------------------------------------
%% TODO: should be a bound queue.
%% @doc A Simple in-memory message queue.
%%
%% Notice that MQTT is not an enterprise messaging queue. MQTT assume that client
@ -155,7 +154,7 @@ stats(#mqueue{type = Type, q = Q, max_len = MaxLen, len = Len, dropped = Dropped
%% @doc Enqueue a message.
-spec(in(message(), mqueue()) -> mqueue()).
in(#message{qos = ?QOS_0}, MQ = #mqueue{qos0 = false}) ->
in(#message{flags = #{qos := ?QOS_0}}, MQ = #mqueue{qos0 = false}) ->
MQ;
in(Msg, MQ = #mqueue{type = simple, q = Q, len = Len, max_len = 0}) ->
MQ#mqueue{q = queue:in(Msg, Q), len = Len + 1};

View File

@ -1,232 +0,0 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%
%% 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(emqx_net).
-include_lib("kernel/include/inet.hrl").
-export([tcp_name/3, tcp_host/1, getopts/2, setopts/2, getaddr/2,
port_to_listeners/1]).
-export([peername/1, sockname/1, format/2, format/1, ntoa/1,
connection_string/2]).
-define(FIRST_TEST_BIND_PORT, 10000).
%%--------------------------------------------------------------------
%% inet_parse:address takes care of ip string, like "0.0.0.0"
%% inet:getaddr returns immediately for ip tuple {0,0,0,0},
%% and runs 'inet_gethost' port process for dns lookups.
%% On Windows inet:getaddr runs dns resolver for ip string, which may fail.
getaddr(Host, Family) ->
case inet_parse:address(Host) of
{ok, IPAddress} -> [{IPAddress, resolve_family(IPAddress, Family)}];
{error, _} -> gethostaddr(Host, Family)
end.
gethostaddr(Host, auto) ->
Lookups = [{Family, inet:getaddr(Host, Family)} || Family <- [inet, inet6]],
case [{IP, Family} || {Family, {ok, IP}} <- Lookups] of
[] -> host_lookup_error(Host, Lookups);
IPs -> IPs
end;
gethostaddr(Host, Family) ->
case inet:getaddr(Host, Family) of
{ok, IPAddress} -> [{IPAddress, Family}];
{error, Reason} -> host_lookup_error(Host, Reason)
end.
host_lookup_error(Host, Reason) ->
error_logger:error_msg("invalid host ~p - ~p~n", [Host, Reason]),
throw({error, {invalid_host, Host, Reason}}).
resolve_family({_,_,_,_}, auto) -> inet;
resolve_family({_,_,_,_,_,_,_,_}, auto) -> inet6;
resolve_family(IP, auto) -> throw({error, {strange_family, IP}});
resolve_family(_, F) -> F.
%%--------------------------------------------------------------------
%% There are three kinds of machine (for our purposes).
%%
%% * Those which treat IPv4 addresses as a special kind of IPv6 address
%% ("Single stack")
%% - Linux by default, Windows Vista and later
%% - We also treat any (hypothetical?) IPv6-only machine the same way
%% * Those which consider IPv6 and IPv4 to be completely separate things
%% ("Dual stack")
%% - OpenBSD, Windows XP / 2003, Linux if so configured
%% * Those which do not support IPv6.
%% - Ancient/weird OSes, Linux if so configured
%%
%% How to reconfigure Linux to test this:
%% Single stack (default):
%% echo 0 > /proc/sys/net/ipv6/bindv6only
%% Dual stack:
%% echo 1 > /proc/sys/net/ipv6/bindv6only
%% IPv4 only:
%% add ipv6.disable=1 to GRUB_CMDLINE_LINUX_DEFAULT in /etc/default/grub then
%% sudo update-grub && sudo reboot
%%
%% This matters in (and only in) the case where the sysadmin (or the
%% app descriptor) has only supplied a port and we wish to bind to
%% "all addresses". This means different things depending on whether
%% we're single or dual stack. On single stack binding to "::"
%% implicitly includes all IPv4 addresses, and subsequently attempting
%% to bind to "0.0.0.0" will fail. On dual stack, binding to "::" will
%% only bind to IPv6 addresses, and we need another listener bound to
%% "0.0.0.0" for IPv4. Finally, on IPv4-only systems we of course only
%% want to bind to "0.0.0.0".
%%
%% Unfortunately it seems there is no way to detect single vs dual stack
%% apart from attempting to bind to the port.
port_to_listeners(Port) ->
IPv4 = {"0.0.0.0", Port, inet},
IPv6 = {"::", Port, inet6},
case ipv6_status(?FIRST_TEST_BIND_PORT) of
single_stack -> [IPv6];
ipv6_only -> [IPv6];
dual_stack -> [IPv6, IPv4];
ipv4_only -> [IPv4]
end.
ipv6_status(TestPort) ->
IPv4 = [inet, {ip, {0,0,0,0}}],
IPv6 = [inet6, {ip, {0,0,0,0,0,0,0,0}}],
case gen_tcp:listen(TestPort, IPv6) of
{ok, LSock6} ->
case gen_tcp:listen(TestPort, IPv4) of
{ok, LSock4} ->
%% Dual stack
gen_tcp:close(LSock6),
gen_tcp:close(LSock4),
dual_stack;
%% Checking the error here would only let us
%% distinguish single stack IPv6 / IPv4 vs IPv6 only,
%% which we figure out below anyway.
{error, _} ->
gen_tcp:close(LSock6),
case gen_tcp:listen(TestPort, IPv4) of
%% Single stack
{ok, LSock4} -> gen_tcp:close(LSock4),
single_stack;
%% IPv6-only machine. Welcome to the future.
{error, eafnosupport} -> ipv6_only; %% Linux
{error, eprotonosupport}-> ipv6_only; %% FreeBSD
%% Dual stack machine with something already
%% on IPv4.
{error, _} -> ipv6_status(TestPort + 1)
end
end;
%% IPv4-only machine. Welcome to the 90s.
{error, eafnosupport} -> %% Linux
ipv4_only;
{error, eprotonosupport} -> %% FreeBSD
ipv4_only;
%% Port in use
{error, _} ->
ipv6_status(TestPort + 1)
end.
tcp_name(Prefix, IPAddress, Port)
when is_atom(Prefix) andalso is_number(Port) ->
list_to_atom(
lists:flatten(
io_lib:format(
"~w_~s:~w", [Prefix, inet_parse:ntoa(IPAddress), Port]))).
connection_string(Sock, Direction) ->
case socket_ends(Sock, Direction) of
{ok, {FromAddress, FromPort, ToAddress, ToPort}} ->
{ok, lists:flatten(
io_lib:format(
"~s:~p -> ~s:~p",
[maybe_ntoab(FromAddress), FromPort,
maybe_ntoab(ToAddress), ToPort]))};
Error ->
Error
end.
socket_ends(Sock, Direction) ->
{From, To} = sock_funs(Direction),
case {From(Sock), To(Sock)} of
{{ok, {FromAddress, FromPort}}, {ok, {ToAddress, ToPort}}} ->
{ok, {rdns(FromAddress), FromPort,
rdns(ToAddress), ToPort}};
{{error, _Reason} = Error, _} ->
Error;
{_, {error, _Reason} = Error} ->
Error
end.
maybe_ntoab(Addr) when is_tuple(Addr) -> ntoab(Addr);
maybe_ntoab(Host) -> Host.
rdns(Addr) -> Addr.
sock_funs(inbound) -> {fun peername/1, fun sockname/1};
sock_funs(outbound) -> {fun sockname/1, fun peername/1}.
getopts(Sock, Options) when is_port(Sock) ->
inet:getopts(Sock, Options).
setopts(Sock, Options) when is_port(Sock) ->
inet:setopts(Sock, Options).
sockname(Sock) when is_port(Sock) -> inet:sockname(Sock).
peername(Sock) when is_port(Sock) -> inet:peername(Sock).
format(sockname, SockName) ->
format(SockName);
format(peername, PeerName) ->
format(PeerName).
format({Addr, Port}) ->
lists:flatten(io_lib:format("~s:~p", [maybe_ntoab(Addr), Port])).
ntoa({0,0,0,0,0,16#ffff,AB,CD}) ->
inet_parse:ntoa({AB bsr 8, AB rem 256, CD bsr 8, CD rem 256});
ntoa(IP) ->
inet_parse:ntoa(IP).
ntoab(IP) ->
Str = ntoa(IP),
case string:str(Str, ":") of
0 -> Str;
_ -> "[" ++ Str ++ "]"
end.
tcp_host({0,0,0,0}) ->
hostname();
tcp_host({0,0,0,0,0,0,0,0}) ->
hostname();
tcp_host(IPAddress) ->
case inet:gethostbyaddr(IPAddress) of
{ok, #hostent{h_name = Name}} -> Name;
{error, _Reason} -> ntoa(IPAddress)
end.
hostname() ->
{ok, Hostname} = inet:gethostname(),
case inet:gethostbyname(Hostname) of
{ok, #hostent{h_name = Name}} -> Name;
{error, _Reason} -> Hostname
end.

View File

@ -1,5 +1,4 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@ -12,7 +11,6 @@
%% 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(emqx_packet).
@ -21,9 +19,7 @@
-include("emqx_mqtt.hrl").
-export([protocol_name/1, type_name/1]).
-export([format/1]).
-export([to_message/1, from_message/1]).
%% @doc Protocol name of version
@ -39,9 +35,8 @@ type_name(Type) when Type > ?RESERVED andalso Type =< ?AUTH ->
%% @doc From Message to Packet
-spec(from_message(message()) -> mqtt_packet()).
from_message(Msg = #message{qos = Qos,
topic = Topic,
payload = Payload}) ->
from_message(Msg = #message{topic = Topic, payload = Payload}) ->
Qos = emqx_message:get_flag(qos, Msg, 0),
Dup = emqx_message:get_flag(dup, Msg, false),
Retain = emqx_message:get_flag(retain, Msg, false),
PacketId = emqx_message:get_header(packet_id, Msg),
@ -61,13 +56,12 @@ to_message(#mqtt_packet{header = #mqtt_packet_header{type = ?PUBLISH,
dup = Dup},
variable = #mqtt_packet_publish{topic_name = Topic,
packet_id = PacketId,
properties = Props},
properties = Properties},
payload = Payload}) ->
Msg = emqx_message:make(undefined, Topic, Payload),
Msg#message{qos = Qos,
flags = #{dup => Dup, retain => Retain},
headers = #{packet_id => PacketId},
properties = Props};
Flags = #{dup => Dup, retain => Retain, qos => Qos},
Msg = emqx_message:new(undefined, Flags, #{packet_id => PacketId}, Topic, Payload),
Msg#message{properties = Properties};
to_message(#mqtt_packet_connect{will_flag = false}) ->
undefined;
to_message(#mqtt_packet_connect{will_retain = Retain,
@ -75,10 +69,8 @@ to_message(#mqtt_packet_connect{will_retain = Retain,
will_topic = Topic,
will_props = Props,
will_payload = Payload}) ->
Msg = emqx_message:make(undefined, Topic, Payload),
Msg#message{flags = #{retain => Retain},
headers = #{qos => Qos},
properties = Props}.
Msg = emqx_message:new(undefined, #{qos => Qos, retain => Retain}, Topic, Payload),
Msg#message{properties = Props}.
%% @doc Format packet
-spec(format(mqtt_packet()) -> iolist()).

View File

@ -1,57 +1,52 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_pmon).
-export([new/0]).
-export([monitor/2, monitor/3, demonitor/2, find/2, erase/2]).
-compile({no_auto_import,[monitor/3]}).
-type(pmon() :: map()).
-type(pmon() :: {?MODULE, map()}).
-export_type([pmon/0]).
-spec(new() -> pmon()).
new() ->
maps:new().
new() -> {?MODULE, maps:new()}.
-spec(monitor(pid(), pmon()) -> pmon()).
monitor(Pid, PM) ->
monitor(Pid, undefined, PM).
monitor(Pid, Val, PM) ->
case maps:is_key(Pid, PM) of
monitor(Pid, Val, {?MODULE, PM}) ->
{?MODULE, case maps:is_key(Pid, PM) of
true -> PM;
false -> Ref = erlang:monitor(process, Pid),
maps:put(Pid, {Ref, Val}, PM)
end.
end}.
-spec(demonitor(pid(), pmon()) -> pmon()).
demonitor(Pid, PM) ->
case maps:find(Pid, PM) of
demonitor(Pid, {?MODULE, PM}) ->
{?MODULE, case maps:find(Pid, PM) of
{ok, {Ref, _Val}} ->
%% Don't flush
_ = erlang:demonitor(Ref),
maps:remove(Pid, PM);
error -> PM
end.
end}.
-spec(find(pid(), pmon()) -> undefined | term()).
find(Pid, PM) ->
find(Pid, {?MODULE, PM}) ->
case maps:find(Pid, PM) of
{ok, {_Ref, Val}} ->
Val;
@ -59,6 +54,6 @@ find(Pid, PM) ->
end.
-spec(erase(pid(), pmon()) -> pmon()).
erase(Pid, PM) ->
maps:remove(Pid, PM).
erase(Pid, {?MODULE, PM}) ->
{?MODULE, maps:remove(Pid, PM)}.

View File

@ -1,50 +1,40 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_pool).
-behaviour(gen_server).
%% Start the pool supervisor
-export([start_link/0]).
-export([start_link/0, start_link/2]).
-export([submit/1, async_submit/1]).
%% API Exports
-export([start_link/2, submit/1, async_submit/1]).
%% gen_server Function Exports
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
code_change/3]).
-record(state, {pool, id}).
-define(POOL, ?MODULE).
%% @doc Start Pooler Supervisor.
%% @doc Start pooler supervisor.
start_link() ->
emqx_pool_sup:start_link(?POOL, random, {?MODULE, start_link, []}).
%%--------------------------------------------------------------------
%% API
%%--------------------------------------------------------------------
-spec(start_link(atom(), pos_integer())
-> {ok, pid()} | ignore | {error, term()}).
%% @doc Start pool
-spec(start_link(atom(), pos_integer()) -> {ok, pid()} | ignore | {error, term()}).
start_link(Pool, Id) ->
gen_server:start_link({local, emqx_misc:proc_name(?MODULE, Id)},
?MODULE, [Pool, Id], []).
gen_server:start_link({local, emqx_misc:proc_name(?MODULE, Id)}, ?MODULE, [Pool, Id], []).
%% @doc Submit work to the pool
-spec(submit(fun()) -> any()).
@ -59,45 +49,45 @@ async_submit(Fun) ->
worker() ->
gproc_pool:pick_worker(pool).
%%--------------------------------------------------------------------
%%-----------------------------------------------------------------------------
%% gen_server callbacks
%%--------------------------------------------------------------------
%%-----------------------------------------------------------------------------
init([Pool, Id]) ->
gproc_pool:connect_worker(Pool, {Pool, Id}),
true = gproc_pool:connect_worker(Pool, {Pool, Id}),
{ok, #state{pool = Pool, id = Id}}.
handle_call({submit, Fun}, _From, State) ->
{reply, catch run(Fun), State};
handle_call(Req, _From, State) ->
emqx_logger:error("[POOL] Unexpected request: ~p", [Req]),
{reply, ignore, State}.
emqx_logger:error("[Pool] unexpected call: ~p", [Req]),
{reply, ignored, State}.
handle_cast({async_submit, Fun}, State) ->
try run(Fun)
catch _:Error ->
emqx_logger:error("[POOL] Error: ~p, ~p", [Error, erlang:get_stacktrace()])
catch _:Error:Stacktrace ->
emqx_logger:error("[Pool] error: ~p, ~p", [Error, Stacktrace])
end,
{noreply, State};
handle_cast(Msg, State) ->
emqx_logger:error("[POOL] Unexpected msg: ~p", [Msg]),
emqx_logger:error("[Pool] unexpected cast: ~p", [Msg]),
{noreply, State}.
handle_info(Info, State) ->
emqx_logger:error("[POOL] Unexpected info: ~p", [Info]),
emqx_logger:error("[Pool] unexpected info: ~p", [Info]),
{noreply, State}.
terminate(_Reason, #state{pool = Pool, id = Id}) ->
gproc_pool:disconnect_worker(Pool, {Pool, Id}).
true = gproc_pool:disconnect_worker(Pool, {Pool, Id}).
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%%-----------------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
%%-----------------------------------------------------------------------------
run({M, F, A}) ->
erlang:apply(M, F, A);

View File

@ -1,27 +1,23 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_pool_sup).
-behaviour(supervisor).
%% API
-export([spec/1, spec/2, start_link/3, start_link/4]).
%% Supervisor callbacks
-export([init/1]).
-spec(spec(list()) -> supervisor:child_spec()).
@ -35,8 +31,7 @@ spec(ChildId, Args) ->
-spec(start_link(atom() | tuple(), atom(), mfa()) -> {ok, pid()} | {error, term()}).
start_link(Pool, Type, MFA) ->
Schedulers = erlang:system_info(schedulers),
start_link(Pool, Type, Schedulers, MFA).
start_link(Pool, Type, emqx_vm:schedulers(schedulers), MFA).
-spec(start_link(atom() | tuple(), atom(), pos_integer(), mfa()) -> {ok, pid()} | {error, term()}).
start_link(Pool, Type, Size, MFA) when is_atom(Pool) ->
@ -49,8 +44,7 @@ init([Pool, Type, Size, {M, F, Args}]) ->
{ok, {{one_for_one, 10, 3600}, [
begin
ensure_pool_worker(Pool, {Pool, I}, I),
{{M, I}, {M, F, [Pool, I | Args]},
transient, 5000, worker, [M]}
{{M, I}, {M, F, [Pool, I | Args]}, transient, 5000, worker, [M]}
end || I <- lists:seq(1, Size)]}}.
ensure_pool(Pool, Type, Opts) ->

View File

@ -129,11 +129,10 @@ client(#proto_state{client_id = ClientId,
WillMsg =:= undefined -> undefined;
true -> WillMsg#message.topic
end,
#client{client_id = ClientId,
client_pid = ClientPid,
#client{id = ClientId,
pid = ClientPid,
username = Username,
peername = Peername,
mountpoint = MountPoint}.
peername = Peername}.
session(#proto_state{session = Session}) ->
Session.
@ -334,7 +333,7 @@ publish(Packet = ?PUBLISH_PACKET(?QOS_0, _PacketId),
mountpoint = MountPoint,
session = Session}) ->
Msg = emqx_packet:to_message(Packet),
Msg1 = Msg#message{from = #client{client_id = ClientId, username = Username}},
Msg1 = Msg#message{from = #client{id = ClientId, username = Username}},
emqx_session:publish(Session, mount(replvar(MountPoint, State), Msg1));
publish(Packet = ?PUBLISH_PACKET(?QOS_1, _PacketId), State) ->
@ -350,7 +349,7 @@ with_puback(Type, Packet = ?PUBLISH_PACKET(_Qos, PacketId),
session = Session}) ->
%% TODO: ...
Msg = emqx_packet:to_message(Packet),
Msg1 = Msg#message{from = #client{client_id = ClientId, username = Username}},
Msg1 = Msg#message{from = #client{id = ClientId, username = Username}},
case emqx_session:publish(Session, mount(replvar(MountPoint, State), Msg1)) of
ok ->
case Type of

View File

@ -1,19 +1,18 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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.
%% @doc MQTT5 reason codes
-module(emqx_reason_codes).
-export([name/1, text/1]).
@ -61,7 +60,7 @@ name(16#9F) -> connection_rate_exceeded;
name(16#A0) -> maximum_connect_time;
name(16#A1) -> subscription_identifiers_not_supported;
name(16#A2) -> wildcard_subscriptions_not_supported;
name(Code) -> list_to_atom("unkown_" ++ integer_to_list(Code)).
name(Code) -> list_to_atom("unkown_reason_code" ++ integer_to_list(Code)).
text(16#00) -> <<"Success">>;
text(16#01) -> <<"Granted QoS 1">>;
@ -106,5 +105,5 @@ text(16#9F) -> <<"Connection rate exceeded">>;
text(16#A0) -> <<"Maximum connect time">>;
text(16#A1) -> <<"Subscription Identifiers not supported">>;
text(16#A2) -> <<"Wildcard Subscriptions not supported">>;
text(Code) -> iolist_to_binary(["Unkown", integer_to_list(Code)]).
text(Code) -> iolist_to_binary(["Unkown Reason Code:", integer_to_list(Code)]).

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_router).
@ -34,13 +32,11 @@
-export([get_routes/1]).
-export([del_route/1, del_route/2, del_route/3]).
-export([has_routes/1, match_routes/1, print_routes/1]).
%% Topics
-export([topics/0]).
%% gen_server Function Exports
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
code_change/3]).
-type(destination() :: node() | {binary(), node()}).
@ -49,15 +45,12 @@
-record(state, {pool, id, batch :: #batch{}}).
-define(ROUTE, emqx_route).
-define(BATCH(Enabled), #batch{enabled = Enabled}).
-define(BATCH(Enabled, Pending), #batch{enabled = Enabled, pending = Pending}).
-define(BATCH(Enabled, Pending),
#batch{enabled = Enabled, pending = Pending}).
%%--------------------------------------------------------------------
%%-----------------------------------------------------------------------------
%% Mnesia bootstrap
%%--------------------------------------------------------------------
%%-----------------------------------------------------------------------------
mnesia(boot) ->
ok = ekka_mnesia:create_table(?ROUTE, [
@ -65,23 +58,21 @@ mnesia(boot) ->
{ram_copies, [node()]},
{record_name, route},
{attributes, record_info(fields, route)}]);
mnesia(copy) ->
ok = ekka_mnesia:copy_table(?ROUTE).
%%--------------------------------------------------------------------
%% Strat a Router
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% Strat a router
%%------------------------------------------------------------------------------
-spec(start_link(atom(), pos_integer())
-> {ok, pid()} | ignore | {error, term()}).
-spec(start_link(atom(), pos_integer()) -> {ok, pid()} | ignore | {error, term()}).
start_link(Pool, Id) ->
gen_server:start_link({local, emqx_misc:proc_name(?MODULE, Id)},
?MODULE, [Pool, Id], [{hibernate_after, 2000}]).
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% Route APIs
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
-spec(add_route(topic() | route()) -> ok).
add_route(Topic) when is_binary(Topic) ->
@ -95,8 +86,7 @@ add_route(Topic, Dest) when is_binary(Topic) ->
-spec(add_route({pid(), reference()}, topic(), destination()) -> ok).
add_route(From, Topic, Dest) when is_binary(Topic) ->
Route = #route{topic = Topic, dest = Dest},
cast(pick(Topic), {add_route, From, Route}).
cast(pick(Topic), {add_route, From, #route{topic = Topic, dest = Dest}}).
-spec(get_routes(topic()) -> [route()]).
get_routes(Topic) ->
@ -114,8 +104,7 @@ del_route(Topic, Dest) when is_binary(Topic) ->
-spec(del_route({pid(), reference()}, topic(), destination()) -> ok).
del_route(From, Topic, Dest) when is_binary(Topic) ->
Route = #route{topic = Topic, dest = Dest},
cast(pick(Topic), {del_route, From, Route}).
cast(pick(Topic), {del_route, From, #route{topic = Topic, dest = Dest}}).
-spec(has_routes(topic()) -> boolean()).
has_routes(Topic) when is_binary(Topic) ->
@ -144,9 +133,9 @@ cast(Router, Msg) ->
pick(Topic) ->
gproc_pool:pick_worker(router, Topic).
%%--------------------------------------------------------------------
%%-----------------------------------------------------------------------------
%% gen_server callbacks
%%--------------------------------------------------------------------
%%-----------------------------------------------------------------------------
init([Pool, Id]) ->
rand:seed(exsplus, erlang:timestamp()),
@ -156,12 +145,12 @@ init([Pool, Id]) ->
{ok, ensure_batch_timer(#state{pool = Pool, id = Id, batch = Batch})}.
handle_call(Req, _From, State) ->
emqx_logger:error("[Router] Unexpected request: ~p", [Req]),
{reply, ignore, State}.
emqx_logger:error("[Router] unexpected call: ~p", [Req]),
{reply, ignored, State}.
handle_cast({add_route, From, Route}, State) ->
{noreply, NewState} = handle_cast({add_route, Route}, State),
gen_server:reply(From, ok),
_ = gen_server:reply(From, ok),
{noreply, NewState};
handle_cast({add_route, Route = #route{topic = Topic, dest = Dest}}, State) ->
@ -178,7 +167,7 @@ handle_cast({add_route, Route = #route{topic = Topic, dest = Dest}}, State) ->
handle_cast({del_route, From, Route}, State) ->
{noreply, NewState} = handle_cast({del_route, Route}, State),
gen_server:reply(From, ok),
_ = gen_server:reply(From, ok),
{noreply, NewState};
handle_cast({del_route, Route = #route{topic = Topic}}, State) ->
@ -194,7 +183,7 @@ handle_cast({del_route, Route = #route{topic = Topic}}, State) ->
end};
handle_cast(Msg, State) ->
emqx_logger:error("[Router] Unexpected msg: ~p", [Msg]),
emqx_logger:error("[Router] unexpected cast: ~p", [Msg]),
{noreply, State}.
handle_info({timeout, _TRef, batch_delete}, State = #state{batch = Batch}) ->
@ -202,7 +191,7 @@ handle_info({timeout, _TRef, batch_delete}, State = #state{batch = Batch}) ->
{noreply, ensure_batch_timer(State#state{batch = ?BATCH(true, sets:new())}), hibernate};
handle_info(Info, State) ->
emqx_logger:error("[Router] Unexpected info: ~p", [Info]),
emqx_logger:error("[Router] unexpected info: ~p", [Info]),
{noreply, State}.
terminate(_Reason, #state{pool = Pool, id = Id, batch = Batch}) ->
@ -212,9 +201,9 @@ terminate(_Reason, #state{pool = Pool, id = Id, batch = Batch}) ->
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%%-----------------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
%%-----------------------------------------------------------------------------
ensure_batch_timer(State = #state{batch = #batch{enabled = false}}) ->
State;
@ -225,7 +214,7 @@ ensure_batch_timer(State = #state{batch = Batch}) ->
cacel_batch_timer(#batch{enabled = false}) ->
ok;
cacel_batch_timer(#batch{enabled = true, timer = TRef}) ->
erlang:cancel_timer(TRef).
catch erlang:cancel_timer(TRef).
add_direct_route(Route) ->
mnesia:async_dirty(fun mnesia:write/3, [?ROUTE, Route, sticky_write]).
@ -275,6 +264,6 @@ trans(Fun, Args) ->
end.
log(ok) -> ok;
log({error, Error}) ->
emqx_logger:error("[Router] Mnesia aborted: ~p", [Error]).
log({error, Reason}) ->
emqx_logger:error("[Router] mnesia aborted: ~p", [Reason]).

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_router_helper).
@ -30,11 +28,10 @@
-export([start_link/0, monitor/1]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
code_change/3]).
-record(routing_node, {name, const = unused}).
-record(state, {nodes = []}).
-compile({no_auto_import, [monitor/1]}).
@ -42,12 +39,11 @@
-define(SERVER, ?MODULE).
-define(ROUTE, emqx_route).
-define(ROUTING_NODE, emqx_routing_node).
-define(LOCK, {?MODULE, cleanup_routes}).
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% Mnesia bootstrap
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
mnesia(boot) ->
ok = ekka_mnesia:create_table(?ROUTING_NODE, [
@ -59,9 +55,9 @@ mnesia(boot) ->
mnesia(copy) ->
ok = ekka_mnesia:copy_table(?ROUTING_NODE).
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% API
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% @doc Starts the router helper
-spec(start_link() -> {ok, pid()} | ignore | {error, any()}).
@ -79,9 +75,9 @@ monitor(Node) when is_atom(Node) ->
false -> mnesia:dirty_write(?ROUTING_NODE, #routing_node{name = Node})
end.
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% gen_server callbacks
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
init([]) ->
ekka:monitor(membership),
@ -98,16 +94,15 @@ init([]) ->
{ok, #state{nodes = Nodes}, hibernate}.
handle_call(Req, _From, State) ->
emqx_logger:error("[RouterHelper] Unexpected request: ~p", [Req]),
{reply, ignore, State}.
emqx_logger:error("[RouterHelper] unexpected call: ~p", [Req]),
{reply, ignored, State}.
handle_cast(Msg, State) ->
emqx_logger:error("[RouterHelper] Unexpected msg: ~p", [Msg]),
emqx_logger:error("[RouterHelper] unexpected cast: ~p", [Msg]),
{noreply, State}.
handle_info({mnesia_table_event, {write, #routing_node{name = Node}, _}},
State = #state{nodes = Nodes}) ->
emqx_logger:info("[RouterHelper] New routing node: ~s", [Node]),
handle_info({mnesia_table_event, {write, #routing_node{name = Node}, _}}, State = #state{nodes = Nodes}) ->
emqx_logger:info("[RouterHelper] write routing node: ~s", [Node]),
case ekka:is_member(Node) orelse lists:member(Node, Nodes) of
true -> {noreply, State};
false -> _ = erlang:monitor_node(Node, true),
@ -132,7 +127,7 @@ handle_info({membership, _Event}, State) ->
{noreply, State};
handle_info(Info, State) ->
emqx_logger:error("[RouteHelper] Unexpected info: ~p", [Info]),
emqx_logger:error("[RouteHelper] unexpected info: ~p", [Info]),
{noreply, State}.
terminate(_Reason, #state{}) ->
@ -143,9 +138,9 @@ terminate(_Reason, #state{}) ->
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
stats_fun() ->
fun() ->

View File

@ -1,25 +1,22 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_router_sup).
-behaviour(supervisor).
-export([start_link/0]).
-export([init/1]).
start_link() ->
@ -34,6 +31,5 @@ init([]) ->
RouterPool = emqx_pool_sup:spec(emqx_router_pool,
[router, hash, emqx_vm:schedulers(),
{emqx_router, start_link, []}]),
{ok, {{one_for_all, 0, 1}, [Helper, RouterPool]}}.

View File

@ -1,26 +1,24 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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.
%% @doc wrap gen_rpc?
-module(emqx_rpc).
-export([call/4, cast/4]).
-export([multicall/4]).
-define(RPC, rpc).
-define(RPC, gen_rpc).
call(Node, Mod, Fun, Args) ->
?RPC:call(Node, Mod, Fun, Args).

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_session_sup).
@ -37,6 +35,10 @@ start_session(Attrs) ->
init([]) ->
{ok, {{simple_one_for_one, 0, 1},
[{session, {emqx_session, start_link, []},
temporary, 5000, worker, [emqx_session]}]}}.
[#{id => session,
start => {emqx_session, start_link, []},
restart => temporary,
shutdown => 5000,
type => worker,
modules => [emqx_session]}]}}.

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_shared_sub).
@ -33,20 +31,18 @@
-export([dispatch/3]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
code_change/3]).
-define(SERVER, ?MODULE).
-define(TAB, emqx_shared_subscription).
-record(state, {pmon}).
-record(shared_subscription, {group, topic, subpid}).
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% Mnesia bootstrap
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
mnesia(boot) ->
ok = ekka_mnesia:create_table(?TAB, [
@ -58,15 +54,15 @@ mnesia(boot) ->
mnesia(copy) ->
ok = ekka_mnesia:copy_table(?TAB).
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% API
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
-spec(start_link() -> {ok, pid()} | ignore | {error, any()}).
-spec(start_link() -> {ok, pid()} | ignore | {error, term()}).
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
-spec(strategy() -> random | hash).
-spec(strategy() -> round_robin | random | hash).
strategy() ->
emqx_config:get_env(shared_subscription_strategy, random).
@ -84,7 +80,7 @@ unsubscribe(Group, Topic, SubPid) when is_pid(SubPid) ->
record(Group, Topic, SubPid) ->
#shared_subscription{group = Group, topic = Topic, subpid = SubPid}.
%% TODO: ensure the delivery...
%% TODO: dispatch strategy, ensure the delivery...
dispatch(Group, Topic, Delivery = #delivery{message = Msg, flows = Flows}) ->
case pick(subscribers(Group, Topic)) of
false -> Delivery;
@ -97,16 +93,15 @@ pick([]) ->
pick([SubPid]) ->
SubPid;
pick(SubPids) ->
X = abs(erlang:monotonic_time()
bxor erlang:unique_integer()),
X = abs(erlang:monotonic_time() bxor erlang:unique_integer()),
lists:nth((X rem length(SubPids)) + 1, SubPids).
subscribers(Group, Topic) ->
ets:select(?TAB, [{{shared_subscription, Group, Topic, '$1'}, [], ['$1']}]).
%%--------------------------------------------------------------------
%%-----------------------------------------------------------------------------
%% gen_server callbacks
%%--------------------------------------------------------------------
%%-----------------------------------------------------------------------------
init([]) ->
{atomic, PMon} = mnesia:transaction(fun init_monitors/0),
@ -120,14 +115,14 @@ init_monitors() ->
end, emqx_pmon:new(), ?TAB).
handle_call(Req, _From, State) ->
emqx_logger:error("[Shared] Unexpected request: ~p", [Req]),
{reply, ignore, State}.
emqx_logger:error("[SharedSub] unexpected call: ~p", [Req]),
{reply, ignored, State}.
handle_cast({monitor, SubPid}, State= #state{pmon = PMon}) ->
{noreply, update_stats(State#state{pmon = emqx_pmon:monitor(SubPid, PMon)})};
handle_cast(Msg, State) ->
emqx_logger:error("[Shared] Unexpected msg: ~p", [Msg]),
emqx_logger:error("[SharedSub] unexpected cast: ~p", [Msg]),
{noreply, State}.
handle_info({mnesia_table_event, {write, NewRecord, _}}, State = #state{pmon = PMon}) ->
@ -142,12 +137,12 @@ handle_info({mnesia_table_event, _Event}, State) ->
{noreply, State};
handle_info({'DOWN', _MRef, process, SubPid, _Reason}, State = #state{pmon = PMon}) ->
emqx_logger:info("Shared subscription down: ~p", [SubPid]),
emqx_logger:info("[SharedSub] shared subscriber down: ~p", [SubPid]),
mnesia:async_dirty(fun cleanup_down/1, [SubPid]),
{noreply, update_stats(State#state{pmon = emqx_pmon:erase(SubPid, PMon)})};
handle_info(Info, State) ->
emqx_logger:error("[Shared] Unexpected info: ~p", [Info]),
emqx_logger:error("[SharedSub] unexpected info: ~p", [Info]),
{noreply, State}.
terminate(_Reason, _State) ->
@ -161,8 +156,8 @@ code_change(_OldVsn, State, _Extra) ->
%%--------------------------------------------------------------------
cleanup_down(SubPid) ->
Pat = #shared_subscription{_ = '_', subpid = SubPid},
lists:foreach(fun(Record) -> mnesia:delete_object(?TAB, Record) end, mnesia:match_object(Pat)).
lists:foreach(fun(Record) -> mnesia:delete_object(?TAB, Record) end,
mnesia:match_object(#shared_subscription{_ = '_', subpid = SubPid})).
update_stats(State) ->
emqx_stats:setstat('subscriptions/shared/count', 'subscriptions/shared/max', ets:info(?TAB, size)), State.

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_sm).
@ -30,8 +28,8 @@
%% Internal functions for rpc
-export([dispatch/3]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-record(state, {session_pmon}).
@ -50,7 +48,8 @@ start_link() ->
%% @doc Open a session.
-spec(open_session(map()) -> {ok, pid()} | {error, term()}).
open_session(Attrs = #{clean_start := true,
client_id := ClientId, client_pid := ClientPid}) ->
client_id := ClientId,
client_pid := ClientPid}) ->
CleanStart = fun(_) ->
ok = discard_session(ClientId, ClientPid),
emqx_session_sup:start_session(Attrs)
@ -58,7 +57,8 @@ open_session(Attrs = #{clean_start := true,
emqx_sm_locker:trans(ClientId, CleanStart);
open_session(Attrs = #{clean_start := false,
client_id := ClientId, client_pid := ClientPid}) ->
client_id := ClientId,
client_pid := ClientPid}) ->
ResumeStart = fun(_) ->
case resume_session(ClientId, ClientPid) of
{ok, SessionPid} ->
@ -72,7 +72,7 @@ open_session(Attrs = #{clean_start := false,
emqx_sm_locker:trans(ClientId, ResumeStart).
%% @doc Discard all the sessions identified by the ClientId.
-spec(discard_session(map()) -> ok).
-spec(discard_session(client_id()) -> ok).
discard_session(ClientId) when is_binary(ClientId) ->
discard_session(ClientId, self()).
@ -80,8 +80,8 @@ discard_session(ClientId, ClientPid) when is_binary(ClientId) ->
lists:foreach(
fun({_ClientId, SessionPid}) ->
case catch emqx_session:discard(SessionPid, ClientPid) of
{'EXIT', Error} ->
emqx_logger:error("[SM] Failed to discard ~p: ~p", [SessionPid, Error]);
{Err, Reason} when Err =:= 'EXIT'; Err =:= error ->
emqx_logger:error("[SM] Failed to discard ~p: ~p", [SessionPid, Reason]);
ok -> ok
end
end, lookup_session(ClientId)).
@ -197,11 +197,12 @@ safe_lookup_element(Tab, Key, Default) ->
error:badarg -> Default
end.
notify(Event) -> gen_server:cast(?SM, {notify, Event}).
notify(Event) ->
gen_server:cast(?SM, {notify, Event}).
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% gen_server callbacks
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
init([]) ->
TabOpts = [public, set, {write_concurrency, true}],
@ -213,8 +214,8 @@ init([]) ->
{ok, #state{session_pmon = emqx_pmon:new()}}.
handle_call(Req, _From, State) ->
emqx_logger:error("[SM] Unexpected request: ~p", [Req]),
{reply, ignore, State}.
emqx_logger:error("[SM] unexpected call: ~p", [Req]),
{reply, ignored, State}.
handle_cast({notify, {registered, ClientId, SessionPid}}, State = #state{session_pmon = PMon}) ->
{noreply, State#state{session_pmon = emqx_pmon:monitor(SessionPid, ClientId, PMon)}};
@ -223,7 +224,7 @@ handle_cast({notify, {unregistered, _ClientId, SessionPid}}, State = #state{sess
{noreply, State#state{session_pmon = emqx_pmon:demonitor(SessionPid, PMon)}};
handle_cast(Msg, State) ->
emqx_logger:error("[SM] Unexpected msg: ~p", [Msg]),
emqx_logger:error("[SM] unexpected cast: ~p", [Msg]),
{noreply, State}.
handle_info({'DOWN', _MRef, process, DownPid, _Reason}, State = #state{session_pmon = PMon}) ->
@ -235,24 +236,23 @@ handle_info({'DOWN', _MRef, process, DownPid, _Reason}, State = #state{session_p
end;
handle_info(Info, State) ->
emqx_logger:error("[SM] Unexpected info: ~p", [Info]),
emqx_logger:error("[SM] unexpected info: ~p", [Info]),
{noreply, State}.
terminate(_Reason, _State) ->
emqx_stats:cancel_update(cm_stats).
emqx_stats:cancel_update(sm_stats).
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
stats_fun() ->
fun() ->
safe_update_stats(?SESSION, 'sessions/count', 'sessions/max'),
safe_update_stats(?SESSION_P, 'sessions/persistent/count',
'sessions/persistent/max')
safe_update_stats(?SESSION_P, 'sessions/persistent/count', 'sessions/persistent/max')
end.
safe_update_stats(Tab, Stat, MaxStat) ->

View File

@ -1,27 +1,23 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_sm_locker).
-include("emqx.hrl").
-export([start_link/0]).
-export([trans/2, trans/3]).
-export([lock/1, lock/2, unlock/1]).
-spec(start_link() -> {ok, pid()} | ignore | {error, term()}).
@ -32,9 +28,7 @@ start_link() ->
trans(ClientId, Fun) ->
trans(ClientId, Fun, undefined).
-spec(trans(client_id() | undefined,
fun(([node()]) -> any()),
ekka_locker:piggyback()) -> any()).
-spec(trans(client_id() | undefined, fun(([node()]) -> any()), ekka_locker:piggyback()) -> any()).
trans(undefined, Fun, _Piggyback) ->
Fun([]);
trans(ClientId, Fun, Piggyback) ->
@ -49,8 +43,7 @@ trans(ClientId, Fun, Piggyback) ->
lock(ClientId) ->
ekka_locker:aquire(?MODULE, ClientId, strategy()).
-spec(lock(client_id(), ekka_locker:piggyback())
-> ekka_locker:lock_result()).
-spec(lock(client_id(), ekka_locker:piggyback()) -> ekka_locker:lock_result()).
lock(ClientId, Piggyback) ->
ekka_locker:aquire(?MODULE, ClientId, strategy(), Piggyback).
@ -60,5 +53,5 @@ unlock(ClientId) ->
-spec(strategy() -> local | one | quorum | all).
strategy() ->
application:get_env(emqx, session_locking_strategy, quorum).
emqx_config:get_env(session_locking_strategy, quorum).

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_sm_registry).
@ -20,25 +18,17 @@
-include("emqx.hrl").
%% API
-export([start_link/0]).
-export([is_enabled/0]).
-export([register_session/1, lookup_session/1, unregister_session/1]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-define(REGISTRY, ?MODULE).
-define(TAB, emqx_session_registry).
-define(LOCK, {?MODULE, cleanup_sessions}).
-record(global_session, {sid, pid}).
-record(state, {}).
-type(session_pid() :: pid()).
@ -58,21 +48,19 @@ lookup_session(ClientId) ->
<- mnesia:dirty_read(?TAB, ClientId)].
-spec(register_session({client_id(), session_pid()}) -> ok).
register_session({ClientId, SessionPid}) when is_binary(ClientId),
is_pid(SessionPid) ->
register_session({ClientId, SessionPid}) when is_binary(ClientId), is_pid(SessionPid) ->
mnesia:dirty_write(?TAB, record(ClientId, SessionPid)).
-spec(unregister_session({client_id(), session_pid()}) -> ok).
unregister_session({ClientId, SessionPid}) when is_binary(ClientId),
is_pid(SessionPid) ->
unregister_session({ClientId, SessionPid}) when is_binary(ClientId), is_pid(SessionPid) ->
mnesia:dirty_delete_object(?TAB, record(ClientId, SessionPid)).
record(ClientId, SessionPid) ->
#global_session{sid = ClientId, pid = SessionPid}.
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% gen_server callbacks
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
init([]) ->
ok = ekka_mnesia:create_table(?TAB, [
@ -85,11 +73,11 @@ init([]) ->
{ok, #state{}}.
handle_call(Req, _From, State) ->
emqx_logger:error("[Registry] Unexpected request: ~p", [Req]),
{reply, ignore, State}.
emqx_logger:error("[Registry] unexpected call: ~p", [Req]),
{reply, ignored, State}.
handle_cast(Msg, State) ->
emqx_logger:error("[Registry] Unexpected msg: ~p", [Msg]),
emqx_logger:error("[Registry] unexpected cast: ~p", [Msg]),
{noreply, State}.
handle_info({membership, {mnesia, down, Node}}, State) ->
@ -103,7 +91,7 @@ handle_info({membership, _Event}, State) ->
{noreply, State};
handle_info(Info, State) ->
emqx_logger:error("[Registry] Unexpected info: ~p", [Info]),
emqx_logger:error("[Registry] unexpected info: ~p", [Info]),
{noreply, State}.
terminate(_Reason, _State) ->
@ -112,9 +100,9 @@ terminate(_Reason, _State) ->
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
cleanup_sessions(Node) ->
Pat = [{#global_session{pid = '$1', _ = '_'},

View File

@ -1,18 +1,17 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%%
%% 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(emqx_sm_sup).
@ -26,17 +25,26 @@ start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
%% Session Locker
Locker = {locker, {emqx_sm_locker, start_link, []},
permanent, 5000, worker, [emqx_sm_locker]},
%% Session Registry
Registry = {registry, {emqx_sm_registry, start_link, []},
permanent, 5000, worker, [emqx_sm_registry]},
%% Session locker
Locker = #{id => locker,
start => {emqx_sm_locker, start_link, []},
restart => permanent,
shutdown => 5000,
type => worker,
modules => [emqx_sm_locker]},
%% Session registry
Registry = #{id => registry,
start => {emqx_sm_registry, start_link, []},
restart => permanent,
shutdown => 5000,
type => worker,
modules => [emqx_sm_registry]},
%% Session Manager
Manager = {manager, {emqx_sm, start_link, []},
permanent, 5000, worker, [emqx_sm]},
Manager = #{id => manager,
start => {emqx_sm, start_link, []},
restart => permanent,
shutdown => 5000,
type => worker,
modules => [emqx_sm]},
{ok, {{rest_for_one, 10, 3600}, [Locker, Registry, Manager]}}.

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_stats).
@ -27,15 +25,13 @@
%% Stats API.
-export([statsfun/1, statsfun/2, getstats/0, getstat/1, setstat/2, setstat/3]).
-export([update_interval/2, update_interval/3, cancel_update/1]).
%% gen_server Function Exports
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
code_change/3]).
-record(update, {name, countdown, interval, func}).
-record(state, {timer, updates :: #update{}}).
-type(stats() :: list({atom(), non_neg_integer()})).
@ -85,10 +81,6 @@
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
%%--------------------------------------------------------------------
%% API
%%--------------------------------------------------------------------
%% Get all stats.
-spec(all() -> stats()).
all() -> getstats().
@ -147,23 +139,23 @@ rec(Name, Secs, UpFun) ->
cast(Msg) ->
gen_server:cast(?SERVER, Msg).
%%--------------------------------------------------------------------
%%-----------------------------------------------------------------------------
%% gen_server callbacks
%%--------------------------------------------------------------------
%%-----------------------------------------------------------------------------
init([]) ->
_ = emqx_tables:new(?TAB, [set, public, {write_concurrency, true}]),
Stats = lists:append([?CLIENT_STATS, ?SESSION_STATS, ?PUBSUB_STATS,
?ROUTE_STATS, ?RETAINED_STATS]),
ets:insert(?TAB, [{Name, 0} || Name <- Stats]),
true = ets:insert(?TAB, [{Name, 0} || Name <- Stats]),
{ok, start_timer(#state{updates = []}), hibernate}.
start_timer(State) ->
State#state{timer = emqx_misc:start_timer(timer:seconds(1), tick)}.
handle_call(Req, _From, State) ->
emqx_logger:error("[STATS] Unexpected request: ~p", [Req]),
{reply, ignore, State}.
emqx_logger:error("[Stats] unexpected call: ~p", [Req]),
{reply, ignored, State}.
handle_cast({setstat, Stat, MaxStat, Val}, State) ->
try ets:lookup_element(?TAB, MaxStat, 2) of
@ -177,11 +169,10 @@ handle_cast({setstat, Stat, MaxStat, Val}, State) ->
safe_update_element(Stat, Val),
{noreply, State};
handle_cast({update_interval, Update = #update{name = Name}},
State = #state{updates = Updates}) ->
handle_cast({update_interval, Update = #update{name = Name}}, State = #state{updates = Updates}) ->
case lists:keyfind(Name, #update.name, Updates) of
#update{} ->
emqx_logger:error("[STATS]: Duplicated update: ~s", [Name]),
emqx_logger:error("[Stats]: duplicated update: ~s", [Name]),
{noreply, State};
false ->
{noreply, State#state{updates = [Update | Updates]}}
@ -191,17 +182,16 @@ handle_cast({cancel_update, Name}, State = #state{updates = Updates}) ->
{noreply, State#state{updates = lists:keydelete(Name, #update.name, Updates)}};
handle_cast(Msg, State) ->
emqx_logger:error("[STATS] Unexpected msg: ~p", [Msg]),
emqx_logger:error("[Stats] unexpected cast: ~p", [Msg]),
{noreply, State}.
handle_info({timeout, TRef, tick}, State = #state{timer = TRef,
updates = Updates}) ->
handle_info({timeout, TRef, tick}, State = #state{timer= TRef, updates = Updates}) ->
lists:foldl(
fun(Update = #update{name = Name, countdown = C, interval = I,
func = UpFun}, Acc) when C =< 0 ->
try UpFun()
catch _:Error ->
emqx_logger:error("[STATS] Update ~s error: ~p", [Name, Error])
emqx_logger:error("[Stats] update ~s error: ~p", [Name, Error])
end,
[Update#update{countdown = I} | Acc];
(Update = #update{countdown = C}, Acc) ->
@ -210,7 +200,7 @@ handle_info({timeout, TRef, tick}, State = #state{timer = TRef,
{noreply, start_timer(State), hibernate};
handle_info(Info, State) ->
emqx_logger:error("[STATS] Unexpected info: ~p", [Info]),
emqx_logger:error("[Stats] unexpected info: ~p", [Info]),
{noreply, State}.
terminate(_Reason, #state{timer = TRef}) ->
@ -219,9 +209,9 @@ terminate(_Reason, #state{timer = TRef}) ->
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%%-----------------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
%%-----------------------------------------------------------------------------
safe_update_element(Key, Val) ->
try ets:update_element(?TAB, Key, {2, Val})

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_sys).
@ -21,13 +19,10 @@
-include("emqx.hrl").
-export([start_link/0]).
-export([version/0, uptime/0, datetime/0, sysdescr/0, sys_interval/0]).
-export([info/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
code_change/3]).
-import(emqx_topic, [systop/1]).
-import(emqx_misc, [start_timer/2]).
@ -48,10 +43,6 @@
start_link() ->
gen_server:start_link({local, ?SYS}, ?MODULE, [], []).
%%--------------------------------------------------------------------
%% API
%%--------------------------------------------------------------------
%% @doc Get sys version
-spec(version() -> string()).
version() ->
@ -64,7 +55,8 @@ sysdescr() ->
%% @doc Get sys uptime
-spec(uptime() -> string()).
uptime() -> gen_server:call(?SYS, uptime).
uptime() ->
gen_server:call(?SYS, uptime).
%% @doc Get sys datetime
-spec(datetime() -> string()).
@ -87,9 +79,9 @@ info() ->
{uptime, uptime()},
{datetime, datetime()}].
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% gen_server callbacks
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
init([]) ->
State = #state{start_time = erlang:timestamp(),
@ -106,11 +98,11 @@ handle_call(uptime, _From, State) ->
{reply, uptime(State), State};
handle_call(Req, _From, State) ->
emqx_logger:error("[SYS] Unexpected request: ~p", [Req]),
{reply, ignore, State}.
emqx_logger:error("[SYS] unexpected call: ~p", [Req]),
{reply, ignored, State}.
handle_cast(Msg, State) ->
emqx_logger:error("[SYS] Unexpected msg: ~p", [Msg]),
emqx_logger:error("[SYS] unexpected cast: ~p", [Msg]),
{noreply, State}.
handle_info({timeout, TRef, heartbeat}, State = #state{heartbeat = TRef}) ->
@ -118,9 +110,7 @@ handle_info({timeout, TRef, heartbeat}, State = #state{heartbeat = TRef}) ->
publish(datetime, iolist_to_binary(datetime())),
{noreply, heartbeat(State)};
handle_info({timeout, TRef, tick}, State = #state{ticker = TRef,
version = Version,
sysdescr = Descr}) ->
handle_info({timeout, TRef, tick}, State = #state{ticker = TRef, version = Version, sysdescr = Descr}) ->
publish(version, Version),
publish(sysdescr, Descr),
publish(brokers, ekka_mnesia:running_nodes()),
@ -129,19 +119,18 @@ handle_info({timeout, TRef, tick}, State = #state{ticker = TRef,
{noreply, tick(State), hibernate};
handle_info(Info, State) ->
emqx_logger:error("[SYS] Unexpected info: ~p", [Info]),
emqx_logger:error("[SYS] unexpected info: ~p", [Info]),
{noreply, State}.
terminate(_Reason, #state{heartbeat = HBRef, ticker = TRef}) ->
emqx_misc:cancel_timer(HBRef),
emqx_misc:cancel_timer(TRef).
terminate(_Reason, #state{heartbeat = TRef1, ticker = TRef2}) ->
lists:foreach(fun emqx_misc:cancel_timer/1, [TRef1, TRef2]).
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%%-----------------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
%%-----------------------------------------------------------------------------
uptime(#state{start_time = Ts}) ->
Secs = timer:now_diff(erlang:timestamp(), Ts) div 1000000,
@ -162,37 +151,26 @@ uptime(days, D) ->
[integer_to_list(D), " days,"].
publish(uptime, Uptime) ->
safe_publish(systop(uptime), [sys], Uptime);
safe_publish(systop(uptime), Uptime);
publish(datetime, Datetime) ->
safe_publish(systop(datatype), [sys], Datetime);
safe_publish(systop(datatype), Datetime);
publish(version, Version) ->
safe_publish(systop(version), [sys, retain], Version);
safe_publish(systop(version), #{retain => true}, Version);
publish(sysdescr, Descr) ->
safe_publish(systop(sysdescr), [sys, retain], Descr);
safe_publish(systop(sysdescr), #{retain => true}, Descr);
publish(brokers, Nodes) ->
Payload = string:join([atom_to_list(N) || N <- Nodes], ","),
safe_publish(<<"$SYS/brokers">>, [sys, retain], Payload);
safe_publish(<<"$SYS/brokers">>, #{retain => true}, Payload);
publish(stats, Stats) ->
[begin
Topic = systop(lists:concat(['stats/', Stat])),
safe_publish(Topic, [sys], integer_to_binary(Val))
end || {Stat, Val} <- Stats, is_atom(Stat), is_integer(Val)];
[safe_publish(systop(lists:concat(['stats/', Stat])), integer_to_binary(Val))
|| {Stat, Val} <- Stats, is_atom(Stat), is_integer(Val)];
publish(metrics, Metrics) ->
[begin
Topic = systop(lists:concat(['metrics/', Metric])),
safe_publish(Topic, [sys], integer_to_binary(Val))
end || {Metric, Val} <- Metrics, is_atom(Metric), is_integer(Val)].
[safe_publish(systop(lists:concat(['metrics/', Metric])), integer_to_binary(Val))
|| {Metric, Val} <- Metrics, is_atom(Metric), is_integer(Val)].
safe_publish(Topic, Payload) ->
safe_publish(Topic, #{}, Payload).
safe_publish(Topic, Flags, Payload) ->
try do_publish(Topic, Flags, Payload)
catch
_:Error ->
emqx_logger:error("[SYS] Publish error: ~p", [Error])
end.
do_publish(Topic, Flags, Payload) ->
Msg0 = emqx_message:make(?SYS, Topic, iolist_to_binary(Payload)),
emqx_broker:publish(lists:foldl(fun(Flag, Msg) ->
emqx_message:set_flag(Flag, Msg)
end, Msg0, Flags)).
Flags1 = maps:merge(#{sys => true, qos => 0}, Flags),
emqx_broker:safe_publish(emqx_message:new(?SYS, Flags1, Topic, iolist_to_binary(Payload))).

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_sys_mon).
@ -20,30 +18,25 @@
-export([start_link/1]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
code_change/3]).
-record(state, {timer, events}).
-define(LOG(Msg, ProcInfo),
emqx_logger:warning([{sysmon, true}],
"[SYSMON] ~s~n~p", [WarnMsg, ProcInfo])).
-define(LOG(Msg, ProcInfo, PortInfo),
emqx_logger:warning([{sysmon, true}],
"[SYSMON] ~s~n~p~n~p", [WarnMsg, ProcInfo, PortInfo])).
-define(SYSMON, ?MODULE).
-define(LOG(Msg, ProcInfo),
emqx_logger:warning([{sysmon, true}], "[SYSMON] ~s~n~p", [WarnMsg, ProcInfo])).
-define(LOG(Msg, ProcInfo, PortInfo),
emqx_logger:warning([{sysmon, true}], "[SYSMON] ~s~n~p~n~p", [WarnMsg, ProcInfo, PortInfo])).
%% @doc Start system monitor
-spec(start_link(Opts :: list(tuple()))
-> {ok, pid()} | ignore | {error, term()}).
-spec(start_link(Opts :: list(tuple())) -> {ok, pid()} | ignore | {error, term()}).
start_link(Opts) ->
gen_server:start_link({local, ?SYSMON}, ?MODULE, [Opts], []).
%%--------------------------------------------------------------------
%%-----------------------------------------------------------------------------
%% gen_server callbacks
%%--------------------------------------------------------------------
%%-----------------------------------------------------------------------------
init([Opts]) ->
erlang:system_monitor(self(), parse_opt(Opts)),
@ -78,50 +71,56 @@ parse_opt([_Opt|Opts], Acc) ->
parse_opt(Opts, Acc).
handle_call(Req, _From, State) ->
emqx_logger:error("[SYSMON] Unexpected request: ~p", [Req]),
{reply, ignore, State}.
emqx_logger:error("[SYSMON] unexpected call: ~p", [Req]),
{reply, ignored, State}.
handle_cast(Msg, State) ->
emqx_logger:error("[SYSMON] Unexpected msg: ~p", [Msg]),
emqx_logger:error("[SYSMON] unexpected cast: ~p", [Msg]),
{noreply, State}.
handle_info({monitor, Pid, long_gc, Info}, State) ->
suppress({long_gc, Pid}, fun() ->
suppress({long_gc, Pid},
fun() ->
WarnMsg = io_lib:format("long_gc warning: pid = ~p, info: ~p", [Pid, Info]),
?LOG(WarnMsg, procinfo(Pid)),
safe_publish(long_gc, WarnMsg)
end, State);
handle_info({monitor, Pid, long_schedule, Info}, State) when is_pid(Pid) ->
suppress({long_schedule, Pid}, fun() ->
suppress({long_schedule, Pid},
fun() ->
WarnMsg = io_lib:format("long_schedule warning: pid = ~p, info: ~p", [Pid, Info]),
?LOG(WarnMsg, procinfo(Pid)),
safe_publish(long_schedule, WarnMsg)
end, State);
handle_info({monitor, Port, long_schedule, Info}, State) when is_port(Port) ->
suppress({long_schedule, Port}, fun() ->
suppress({long_schedule, Port},
fun() ->
WarnMsg = io_lib:format("long_schedule warning: port = ~p, info: ~p", [Port, Info]),
?LOG(WarnMsg, erlang:port_info(Port)),
safe_publish(long_schedule, WarnMsg)
end, State);
handle_info({monitor, Pid, large_heap, Info}, State) ->
suppress({large_heap, Pid}, fun() ->
suppress({large_heap, Pid},
fun() ->
WarnMsg = io_lib:format("large_heap warning: pid = ~p, info: ~p", [Pid, Info]),
?LOG(WarnMsg, procinfo(Pid)),
safe_publish(large_heap, WarnMsg)
end, State);
handle_info({monitor, SusPid, busy_port, Port}, State) ->
suppress({busy_port, Port}, fun() ->
suppress({busy_port, Port},
fun() ->
WarnMsg = io_lib:format("busy_port warning: suspid = ~p, port = ~p", [SusPid, Port]),
?LOG(WarnMsg, procinfo(SusPid), erlang:port_info(Port)),
safe_publish(busy_port, WarnMsg)
end, State);
handle_info({monitor, SusPid, busy_dist_port, Port}, State) ->
suppress({busy_dist_port, Port}, fun() ->
suppress({busy_dist_port, Port},
fun() ->
WarnMsg = io_lib:format("busy_dist_port warning: suspid = ~p, port = ~p", [SusPid, Port]),
?LOG(WarnMsg, procinfo(SusPid), erlang:port_info(Port)),
safe_publish(busy_dist_port, WarnMsg)
@ -131,7 +130,7 @@ handle_info(reset, State) ->
{noreply, State#state{events = []}, hibernate};
handle_info(Info, State) ->
lager:error("[SYSMON] Unexpected Info: ~p", [Info]),
lager:error("[SYSMON] unexpected Info: ~p", [Info]),
{noreply, State}.
terminate(_Reason, #state{timer = TRef}) ->
@ -155,12 +154,9 @@ procinfo(Pid) ->
end.
safe_publish(Event, WarnMsg) ->
try
Topic = emqx_topic:systop(lists:concat(['sysmon/', Event])),
Msg = emqx_message:make(?SYSMON, Topic, iolist_to_binary(WarnMsg)),
emqx_broker:publish(emqx_message:set_flag(sys, Msg))
catch
_:Error ->
emqx_logger:error("[SYSMON] Publish error: ~p", [Error])
end.
emqx_broker:safe_publish(sysmon_msg(Topic, iolist_to_binary(WarnMsg))).
sysmon_msg(Topic, Payload) ->
emqx_message:new(?SYSMON, #{sys => true, qos => 0}, Topic, Payload).

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_sys_sup).
@ -26,11 +24,18 @@ start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
Sys = {sys, {emqx_sys, start_link, []},
permanent, 5000, worker, [emqx_sys]},
{ok, Env} = emqx_config:get_env(sysmon),
Sysmon = {sys_mon, {emqx_sys_mon, start_link, [Env]},
permanent, 5000, worker, [emqx_sys_mon]},
Sys = #{id => sys,
start => {emqx_sys, start_link, []},
restart => permanent,
shutdown => 5000,
type => worker,
modules => [emqx_sys]},
Sysmon = #{id => sys_mon,
start => {emqx_sys_mon, start_link,
[emqx_config:get_env(sysmon, [])]},
restart => permanent,
shutdown => 5000,
type => worker,
modules => [emqx_sys_mon]},
{ok, {{one_for_one, 10, 100}, [Sys, Sysmon]}}.

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_tables).

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_time).

View File

@ -1,39 +1,31 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_topic).
-include("emqx.hrl").
-include("emqx_mqtt.hrl").
-import(lists, [reverse/1]).
-export([match/2, validate/1, triples/1, words/1, wildcard/1]).
-export([join/1, feed_var/3, systop/1]).
-export([parse/1, parse/2]).
-type(option() :: {qos, mqtt_qos()} | {share, '$queue' | binary()}).
-type(word() :: '' | '+' | '#' | binary()).
-type(words() :: list(word())).
-type(option() :: {qos, mqtt_qos()} | {share, '$queue' | binary()}).
-type(triple() :: {root | binary(), word(), binary()}).
-export_type([option/0, word/0, triple/0]).
@ -110,14 +102,13 @@ validate3(<<C/utf8, _Rest/binary>>) when C == $#; C == $+; C == 0 ->
validate3(<<_/utf8, Rest/binary>>) ->
validate3(Rest).
%% @doc Topic to Triples
%% @doc Topic to triples
-spec(triples(topic()) -> list(triple())).
triples(Topic) when is_binary(Topic) ->
triples(words(Topic), root, []).
triples([], _Parent, Acc) ->
reverse(Acc);
triples([W|Words], Parent, Acc) ->
Node = join(Parent, W),
triples(Words, Node, [{Parent, W, Node}|Acc]).
@ -176,24 +167,16 @@ join(Words) ->
parse(Topic) when is_binary(Topic) ->
parse(Topic, []).
parse(Topic = <<"$fastlane/", Topic1/binary>>, Options) ->
case lists:member(fastlane, Options) of
true -> error({invalid_topic, Topic});
false -> parse(Topic1, [fastlane | Options])
end;
parse(Topic = <<"$queue/", Topic1/binary>>, Options) ->
case lists:keyfind(share, 1, Options) of
{share, _} -> error({invalid_topic, Topic});
false -> parse(Topic1, [{share, '$queue'} | Options])
end;
parse(Topic = <<"$share/", Topic1/binary>>, Options) ->
case lists:keyfind(share, 1, Options) of
{share, _} -> error({invalid_topic, Topic});
false -> [Group, Topic2] = binary:split(Topic1, <<"/">>),
{Topic2, [{share, Group} | Options]}
end;
parse(Topic, Options) -> {Topic, Options}.

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_tracer).
@ -21,32 +19,29 @@
-include("emqx.hrl").
-export([start_link/0]).
-export([start_trace/2, lookup_traces/0, stop_trace/1]).
%% gen_server Function Exports
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
code_change/3]).
-record(state, {level, traces}).
-type(trace_who() :: {client | topic, binary()}).
-define(TRACER, ?MODULE).
-define(OPTIONS, [{formatter_config, [time, " [",severity,"] ", message, "\n"]}]).
-define(TRACER, ?MODULE).
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% Start the tracer
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
-spec(start_link() -> {ok, pid()} | ignore | {error, term()}).
start_link() ->
gen_server:start_link({local, ?TRACER}, ?MODULE, [], []).
%%--------------------------------------------------------------------
%% Start/Stop Trace
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% Start/Stop trace
%%------------------------------------------------------------------------------
%% @doc Start to trace client or topic.
-spec(start_trace(trace_who(), string()) -> ok | {error, term()}).
@ -55,8 +50,7 @@ start_trace({client, ClientId}, LogFile) ->
start_trace({topic, Topic}, LogFile) ->
start_trace({start_trace, {topic, Topic}, LogFile}).
start_trace(Req) ->
gen_server:call(?MODULE, Req, infinity).
start_trace(Req) -> gen_server:call(?MODULE, Req, infinity).
%% @doc Stop tracing client or topic.
-spec(stop_trace(trace_who()) -> ok | {error, term()}).
@ -70,26 +64,24 @@ stop_trace({topic, Topic}) ->
lookup_traces() ->
gen_server:call(?TRACER, lookup_traces).
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% gen_server callbacks
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
init([]) ->
Level = emqx_config:get_env(trace_level, debug),
{ok, #state{level = Level, traces = #{}}}.
{ok, #state{level = emqx_config:get_env(trace_level, debug), traces = #{}}}.
handle_call({start_trace, Who, LogFile}, _From,
State = #state{level = Level, traces = Traces}) ->
handle_call({start_trace, Who, LogFile}, _From, State = #state{level = Level, traces = Traces}) ->
case catch lager:trace_file(LogFile, [Who], Level, ?OPTIONS) of
{ok, exists} ->
{reply, {error, alread_existed}, State};
{reply, {error, already_existed}, State};
{ok, Trace} ->
{reply, ok, State#state{traces = maps:put(Who, {Trace, LogFile}, Traces)}};
{error, Reason} ->
emqx_logger:error("[TRACER] trace error: ~p", [Reason]),
emqx_logger:error("[Tracer] trace error: ~p", [Reason]),
{reply, {error, Reason}, State};
{'EXIT', Error} ->
emqx_logger:error("[TRACER] trace exit: ~p", [Error]),
emqx_logger:error("[Tracer] trace exit: ~p", [Error]),
{reply, {error, Error}, State}
end;
@ -98,27 +90,27 @@ handle_call({stop_trace, Who}, _From, State = #state{traces = Traces}) ->
{ok, {Trace, _LogFile}} ->
case lager:stop_trace(Trace) of
ok -> ok;
{error, Error} -> lager:error("Stop trace ~p error: ~p", [Who, Error])
{error, Error} ->
emqx_logger:error("[Tracer] stop trace ~p error: ~p", [Who, Error])
end,
{reply, ok, State#state{traces = maps:remove(Who, Traces)}};
error ->
{reply, {error, trance_not_found}, State}
{reply, {error, not_found}, State}
end;
handle_call(lookup_traces, _From, State = #state{traces = Traces}) ->
{reply, [{Who, LogFile} || {Who, {_Trace, LogFile}}
<- maps:to_list(Traces)], State};
{reply, [{Who, LogFile} || {Who, {_Trace, LogFile}} <- maps:to_list(Traces)], State};
handle_call(Req, _From, State) ->
emqx_logger:error("[TRACER] Unexpected request: ~p", [Req]),
{reply, ignore, State}.
emqx_logger:error("[Tracer] unexpected call: ~p", [Req]),
{reply, ignored, State}.
handle_cast(Msg, State) ->
emqx_logger:error("[TRACER] Unexpected msg: ~p", [Msg]),
emqx_logger:error("[Tracer] unexpected cast: ~p", [Msg]),
{noreply, State}.
handle_info(Info, State) ->
emqx_logger:error("[TRACER] Unexpected info: ~p", [Info]),
emqx_logger:error("[Tracer] unexpected info: ~p", [Info]),
{noreply, State}.
terminate(_Reason, _State) ->

View File

@ -1,18 +1,16 @@
%%%===================================================================
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
%%%
%%% 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.
%%%===================================================================
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% 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(emqx_trie).
@ -31,9 +29,9 @@
-define(TRIE, emqx_trie).
-define(TRIE_NODE, emqx_trie_node).
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% Mnesia bootstrap
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% @doc Create or replicate trie tables.
-spec(mnesia(boot | copy) -> ok).
@ -55,9 +53,9 @@ mnesia(copy) ->
%% Copy trie_node table
ok = ekka_mnesia:copy_table(?TRIE_NODE).
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% Trie APIs
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% @doc Insert a topic into the trie
-spec(insert(Topic :: topic()) -> ok).
@ -94,13 +92,12 @@ delete(Topic) when is_binary(Topic) ->
delete_path(lists:reverse(emqx_topic:triples(Topic)));
[TrieNode] ->
write_trie_node(TrieNode#trie_node{topic = undefined});
[] ->
ok
[] -> ok
end.
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% @private
%% @doc Add a path to the trie.
@ -112,8 +109,7 @@ add_path({Node, Word, Child}) ->
[] ->
write_trie_node(TrieNode#trie_node{edge_count = Count + 1}),
write_trie(#trie{edge = Edge, node_id = Child});
[_] ->
ok
[_] -> ok
end;
[] ->
write_trie_node(#trie_node{node_id = Node, edge_count = 1}),
@ -145,8 +141,7 @@ match_node(NodeId, [W|Words], ResAcc) ->
case mnesia:read(?TRIE, #trie_edge{node_id = NodeId, word = '#'}) of
[#trie{node_id = ChildId}] ->
mnesia:read(?TRIE_NODE, ChildId) ++ ResAcc;
[] ->
ResAcc
[] -> ResAcc
end.
%% @private