Fix conflict

This commit is contained in:
Feng Lee 2019-07-02 14:02:42 +08:00
commit 8c37ea3f38
92 changed files with 2953 additions and 3900 deletions

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-ifndef(EMQ_X_HRL). -ifndef(EMQ_X_HRL).
-define(EMQ_X_HRL, true). -define(EMQ_X_HRL, true).
@ -19,10 +21,6 @@
%% Banner %% Banner
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-define(COPYRIGHT, "Copyright (c) 2013-2019 EMQ Technologies Co., Ltd").
-define(LICENSE_MESSAGE, "Licensed under the Apache License, Version 2.0").
-define(PROTOCOL_VERSION, "MQTT/5.0"). -define(PROTOCOL_VERSION, "MQTT/5.0").
-define(ERTS_MINIMUM_REQUIRED, "10.0"). -define(ERTS_MINIMUM_REQUIRED, "10.0").
@ -47,8 +45,6 @@
%% Message and Delivery %% Message and Delivery
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-record(session, {sid, pid}).
-record(subscription, {topic, subid, subopts}). -record(subscription, {topic, subid, subopts}).
%% See 'Application Message' in MQTT Version 5.0 %% See 'Application Message' in MQTT Version 5.0
@ -72,9 +68,12 @@
}). }).
-record(delivery, { -record(delivery, {
sender :: pid(), %% Sender of the delivery %% Sender of the delivery
message :: #message{}, %% The message delivered sender :: pid(),
results :: list() %% Dispatches of the message %% The message delivered
message :: #message{},
%% Dispatches of the message
results :: list()
}). }).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
@ -152,6 +151,7 @@
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Banned %% Banned
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-type(banned_who() :: {client_id, binary()} -type(banned_who() :: {client_id, binary()}
| {username, binary()} | {username, binary()}
| {ip_address, inet:ip_address()}). | {ip_address, inet:ip_address()}).

View File

@ -12,7 +12,6 @@
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
-ifndef(EMQX_CLIENT_HRL). -ifndef(EMQX_CLIENT_HRL).
-define(EMQX_CLIENT_HRL, true). -define(EMQX_CLIENT_HRL, true).
-include("emqx_mqtt.hrl"). -include("emqx_mqtt.hrl").

View File

@ -351,6 +351,13 @@
variable = #mqtt_packet_publish{packet_id = PacketId} variable = #mqtt_packet_publish{packet_id = PacketId}
}). }).
-define(PUBLISH_PACKET(QoS, Topic, PacketId),
#mqtt_packet{header = #mqtt_packet_header{type = ?PUBLISH,
qos = QoS},
variable = #mqtt_packet_publish{topic_name = Topic,
packet_id = PacketId}
}).
-define(PUBLISH_PACKET(QoS, Topic, PacketId, Payload), -define(PUBLISH_PACKET(QoS, Topic, PacketId, Payload),
#mqtt_packet{header = #mqtt_packet_header{type = ?PUBLISH, #mqtt_packet{header = #mqtt_packet_header{type = ?PUBLISH,
qos = QoS}, qos = QoS},

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,9 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-define(Otherwise, true).
-type(maybe(T) :: undefined | T). -type(maybe(T) :: undefined | T).

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx). -module(emqx).
@ -61,9 +63,13 @@
-define(APP, ?MODULE). -define(APP, ?MODULE).
%%------------------------------------------------------------------------------ -define(COPYRIGHT, "Copyright (c) 2019 EMQ Technologies Co., Ltd").
-define(LICENSE_MESSAGE, "Licensed under the Apache License, Version 2.0").
%%--------------------------------------------------------------------
%% Bootstrap, is_running... %% Bootstrap, is_running...
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% @doc Start emqx application %% @doc Start emqx application
-spec(start() -> {ok, list(atom())} | {error, term()}). -spec(start() -> {ok, list(atom())} | {error, term()}).
@ -95,9 +101,9 @@ is_running(Node) ->
Pid when is_pid(Pid) -> true Pid when is_pid(Pid) -> true
end. end.
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% PubSub API %% PubSub API
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
-spec(subscribe(emqx_topic:topic() | string()) -> ok). -spec(subscribe(emqx_topic:topic() | string()) -> ok).
subscribe(Topic) -> subscribe(Topic) ->
@ -122,9 +128,9 @@ publish(Msg) ->
unsubscribe(Topic) -> unsubscribe(Topic) ->
emqx_broker:unsubscribe(iolist_to_binary(Topic)). emqx_broker:unsubscribe(iolist_to_binary(Topic)).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% PubSub management API %% PubSub management API
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
-spec(topics() -> list(emqx_topic:topic())). -spec(topics() -> list(emqx_topic:topic())).
topics() -> emqx_router:topics(). topics() -> emqx_router:topics().
@ -143,9 +149,9 @@ subscribed(SubPid, Topic) when is_pid(SubPid) ->
subscribed(SubId, Topic) when is_atom(SubId); is_binary(SubId) -> subscribed(SubId, Topic) when is_atom(SubId); is_binary(SubId) ->
emqx_broker:subscribed(SubId, iolist_to_binary(Topic)). emqx_broker:subscribed(SubId, iolist_to_binary(Topic)).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Hooks API %% Hooks API
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
-spec(hook(emqx_hooks:hookpoint(), emqx_hooks:action()) -> ok | {error, already_exists}). -spec(hook(emqx_hooks:hookpoint(), emqx_hooks:action()) -> ok | {error, already_exists}).
hook(HookPoint, Action) -> hook(HookPoint, Action) ->
@ -177,9 +183,9 @@ run_hook(HookPoint, Args) ->
run_fold_hook(HookPoint, Args, Acc) -> run_fold_hook(HookPoint, Args, Acc) ->
emqx_hooks:run_fold(HookPoint, Args, Acc). emqx_hooks:run_fold(HookPoint, Args, Acc).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Shutdown and reboot %% Shutdown and reboot
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
shutdown() -> shutdown() ->
shutdown(normal). shutdown(normal).
@ -193,12 +199,13 @@ shutdown(Reason) ->
reboot() -> reboot() ->
lists:foreach(fun application:start/1, [gproc, esockd, ranch, cowboy, ekka, emqx]). lists:foreach(fun application:start/1, [gproc, esockd, ranch, cowboy, ekka, emqx]).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Internal functions %% Internal functions
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
reload_config(ConfFile) -> reload_config(ConfFile) ->
{ok, [Conf]} = file:consult(ConfFile), {ok, [Conf]} = file:consult(ConfFile),
lists:foreach(fun({App, Vals}) -> lists:foreach(fun({App, Vals}) ->
[application:set_env(App, Par, Val) || {Par, Val} <- Vals] [application:set_env(App, Par, Val) || {Par, Val} <- Vals]
end, Conf). end, Conf).

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_access_control). -module(emqx_access_control).
@ -22,9 +24,10 @@
, reload_acl/0 , reload_acl/0
]). ]).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% APIs %% APIs
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
-spec(authenticate(emqx_types:credentials()) -spec(authenticate(emqx_types:credentials())
-> {ok, emqx_types:credentials()} | {error, term()}). -> {ok, emqx_types:credentials()} | {error, term()}).
authenticate(Credentials) -> authenticate(Credentials) ->
@ -39,7 +42,8 @@ authenticate(Credentials) ->
end. end.
%% @doc Check ACL %% @doc Check ACL
-spec(check_acl(emqx_types:credentials(), emqx_types:pubsub(), emqx_types:topic()) -> allow | deny). -spec(check_acl(emqx_types:credentials(), emqx_types:pubsub(), emqx_types:topic())
-> allow | deny).
check_acl(Credentials, PubSub, Topic) -> check_acl(Credentials, PubSub, Topic) ->
case emqx_acl_cache:is_enabled() of case emqx_acl_cache:is_enabled() of
false -> false ->
@ -50,8 +54,7 @@ check_acl(Credentials, PubSub, Topic) ->
AclResult = do_check_acl(Credentials, PubSub, Topic), AclResult = do_check_acl(Credentials, PubSub, Topic),
emqx_acl_cache:put_acl_cache(PubSub, Topic, AclResult), emqx_acl_cache:put_acl_cache(PubSub, Topic, AclResult),
AclResult; AclResult;
AclResult -> AclResult -> AclResult
AclResult
end end
end. end.

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_access_rule). -module(emqx_access_rule).
@ -21,6 +23,8 @@
, compile/1 , compile/1
]). ]).
-export_type([rule/0]).
-type(acl_result() :: allow | deny). -type(acl_result() :: allow | deny).
-type(who() :: all | binary() | -type(who() :: all | binary() |
@ -33,14 +37,12 @@
-type(rule() :: {acl_result(), all} | -type(rule() :: {acl_result(), all} |
{acl_result(), who(), access(), list(emqx_topic:topic())}). {acl_result(), who(), access(), list(emqx_topic:topic())}).
-export_type([rule/0]).
-define(ALLOW_DENY(A), ((A =:= allow) orelse (A =:= deny))). -define(ALLOW_DENY(A), ((A =:= allow) orelse (A =:= deny))).
-define(PUBSUB(A), ((A =:= subscribe) orelse (A =:= publish) orelse (A =:= pubsub))). -define(PUBSUB(A), ((A =:= subscribe) orelse (A =:= publish) orelse (A =:= pubsub))).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% APIs %% APIs
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% @doc Compile Access Rule. %% @doc Compile Access Rule.
compile({A, all}) when ?ALLOW_DENY(A) -> compile({A, all}) when ?ALLOW_DENY(A) ->

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_acl_cache). -module(emqx_acl_cache).
@ -124,6 +126,7 @@ map_acl_cache(Fun) ->
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Internal functions %% Internal functions
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
add_acl(PubSub, Topic, AclResult) -> add_acl(PubSub, Topic, AclResult) ->
K = cache_k(PubSub, Topic), K = cache_k(PubSub, Topic),
V = cache_v(AclResult), V = cache_v(AclResult),

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_alarm_handler). -module(emqx_alarm_handler).
@ -47,9 +49,9 @@
-define(ALARM_TAB, emqx_alarm). -define(ALARM_TAB, emqx_alarm).
-define(ALARM_HISTORY_TAB, emqx_alarm_history). -define(ALARM_HISTORY_TAB, emqx_alarm_history).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Mnesia bootstrap %% Mnesia bootstrap
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
mnesia(boot) -> mnesia(boot) ->
ok = ekka_mnesia:create_table(?ALARM_TAB, [ ok = ekka_mnesia:create_table(?ALARM_TAB, [
@ -64,13 +66,14 @@ mnesia(boot) ->
{local_content, true}, {local_content, true},
{record_name, alarm_history}, {record_name, alarm_history},
{attributes, record_info(fields, alarm_history)}]); {attributes, record_info(fields, alarm_history)}]);
mnesia(copy) -> mnesia(copy) ->
ok = ekka_mnesia:copy_table(?ALARM_TAB), ok = ekka_mnesia:copy_table(?ALARM_TAB),
ok = ekka_mnesia:copy_table(?ALARM_HISTORY_TAB). ok = ekka_mnesia:copy_table(?ALARM_HISTORY_TAB).
%%---------------------------------------------------------------------- %%--------------------------------------------------------------------
%% API %% API
%%---------------------------------------------------------------------- %%--------------------------------------------------------------------
load() -> load() ->
gen_event:swap_handler(alarm_handler, {alarm_handler, swap}, {?MODULE, []}). gen_event:swap_handler(alarm_handler, {alarm_handler, swap}, {?MODULE, []}).
@ -89,13 +92,14 @@ get_alarms(history) ->
Alarms = ets:tab2list(?ALARM_HISTORY_TAB), Alarms = ets:tab2list(?ALARM_HISTORY_TAB),
[{Id, Desc, ClearAt} || #alarm_history{id = Id, desc = Desc, clear_at = ClearAt} <- Alarms]. [{Id, Desc, ClearAt} || #alarm_history{id = Id, desc = Desc, clear_at = ClearAt} <- Alarms].
%%---------------------------------------------------------------------- %%--------------------------------------------------------------------
%% gen_event callbacks %% gen_event callbacks
%%---------------------------------------------------------------------- %%--------------------------------------------------------------------
init({_Args, {alarm_handler, ExistingAlarms}}) -> init({_Args, {alarm_handler, ExistingAlarms}}) ->
init_tables(ExistingAlarms), init_tables(ExistingAlarms),
{ok, []}; {ok, []};
init(_) -> init(_) ->
init_tables([]), init_tables([]),
{ok, []}. {ok, []}.
@ -188,7 +192,7 @@ clear_alarm_(Id) ->
end. end.
set_alarm_history(Id, Desc) -> set_alarm_history(Id, Desc) ->
mnesia:dirty_write(?ALARM_HISTORY_TAB, #alarm_history{id = Id, His = #alarm_history{id = Id,
desc = Desc, desc = Desc,
clear_at = os:timestamp()}). clear_at = os:timestamp()},
mnesia:dirty_write(?ALARM_HISTORY_TAB, His}).

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_app). -module(emqx_app).
@ -30,10 +32,10 @@ start(_Type, _Args) ->
print_banner(), print_banner(),
ekka:start(), ekka:start(),
{ok, Sup} = emqx_sup:start_link(), {ok, Sup} = emqx_sup:start_link(),
emqx_modules:load(), ok = emqx_modules:load(),
emqx_plugins:init(), ok = emqx_plugins:init(),
emqx_plugins:load(), emqx_plugins:load(),
emqx_listeners:start(), ok = emqx_listeners:start(),
start_autocluster(), start_autocluster(),
register(emqx, self()), register(emqx, self()),

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_banned). -module(emqx_banned).
@ -44,14 +46,14 @@
, code_change/3 , code_change/3
]). ]).
-define(TAB, ?MODULE). -define(BANNED_TAB, ?MODULE).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Mnesia bootstrap %% Mnesia bootstrap
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
mnesia(boot) -> mnesia(boot) ->
ok = ekka_mnesia:create_table(?TAB, [ ok = ekka_mnesia:create_table(?BANNED_TAB, [
{type, set}, {type, set},
{disc_copies, [node()]}, {disc_copies, [node()]},
{record_name, banned}, {record_name, banned},
@ -59,7 +61,7 @@ mnesia(boot) ->
{storage_properties, [{ets, [{read_concurrency, true}]}]}]); {storage_properties, [{ets, [{read_concurrency, true}]}]}]);
mnesia(copy) -> mnesia(copy) ->
ok = ekka_mnesia:copy_table(?TAB). ok = ekka_mnesia:copy_table(?BANNED_TAB).
%% @doc Start the banned server. %% @doc Start the banned server.
-spec(start_link() -> startlink_ret()). -spec(start_link() -> startlink_ret()).
@ -68,23 +70,23 @@ start_link() ->
-spec(check(emqx_types:credentials()) -> boolean()). -spec(check(emqx_types:credentials()) -> boolean()).
check(#{client_id := ClientId, username := Username, peername := {IPAddr, _}}) -> check(#{client_id := ClientId, username := Username, peername := {IPAddr, _}}) ->
ets:member(?TAB, {client_id, ClientId}) ets:member(?BANNED_TAB, {client_id, ClientId})
orelse ets:member(?TAB, {username, Username}) orelse ets:member(?BANNED_TAB, {username, Username})
orelse ets:member(?TAB, {ipaddr, IPAddr}). orelse ets:member(?BANNED_TAB, {ipaddr, IPAddr}).
-spec(add(emqx_types:banned()) -> ok). -spec(add(emqx_types:banned()) -> ok).
add(Banned) when is_record(Banned, banned) -> add(Banned) when is_record(Banned, banned) ->
mnesia:dirty_write(?TAB, Banned). mnesia:dirty_write(?BANNED_TAB, Banned).
-spec(delete({client_id, emqx_types:client_id()} -spec(delete({client_id, emqx_types:client_id()}
| {username, emqx_types:username()} | {username, emqx_types:username()}
| {peername, emqx_types:peername()}) -> ok). | {peername, emqx_types:peername()}) -> ok).
delete(Key) -> delete(Key) ->
mnesia:dirty_delete(?TAB, Key). mnesia:dirty_delete(?BANNED_TAB, Key).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% gen_server callbacks %% gen_server callbacks
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
init([]) -> init([]) ->
{ok, ensure_expiry_timer(#{expiry_timer => undefined})}. {ok, ensure_expiry_timer(#{expiry_timer => undefined})}.
@ -98,7 +100,8 @@ handle_cast(Msg, State) ->
{noreply, State}. {noreply, State}.
handle_info({timeout, TRef, expire}, State = #{expiry_timer := TRef}) -> handle_info({timeout, TRef, expire}, State = #{expiry_timer := TRef}) ->
mnesia:async_dirty(fun expire_banned_items/1, [erlang:system_time(second)]), mnesia:async_dirty(fun expire_banned_items/1,
[erlang:system_time(second)]),
{noreply, ensure_expiry_timer(State), hibernate}; {noreply, ensure_expiry_timer(State), hibernate};
handle_info(Info, State) -> handle_info(Info, State) ->
@ -111,9 +114,9 @@ terminate(_Reason, #{expiry_timer := TRef}) ->
code_change(_OldVsn, State, _Extra) -> code_change(_OldVsn, State, _Extra) ->
{ok, State}. {ok, State}.
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Internal functions %% Internal functions
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
-ifdef(TEST). -ifdef(TEST).
ensure_expiry_timer(State) -> ensure_expiry_timer(State) ->
@ -126,6 +129,7 @@ ensure_expiry_timer(State) ->
expire_banned_items(Now) -> expire_banned_items(Now) ->
mnesia:foldl( mnesia:foldl(
fun(B = #banned{until = Until}, _Acc) when Until < Now -> fun(B = #banned{until = Until}, _Acc) when Until < Now ->
mnesia:delete_object(?TAB, B, sticky_write); mnesia:delete_object(?BANNED_TAB, B, sticky_write);
(_, _Acc) -> ok (_, _Acc) -> ok
end, ok, ?TAB). end, ok, ?BANNED_TAB).

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_base62). -module(emqx_base62).
@ -21,9 +23,9 @@
, decode/2 , decode/2
]). ]).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% APIs %% APIs
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% @doc Encode any data to base62 binary %% @doc Encode any data to base62 binary
-spec encode(string() | integer() | binary()) -> binary(). -spec encode(string() | integer() | binary()) -> binary().
@ -43,9 +45,9 @@ decode(L) when is_list(L) ->
decode(B) when is_binary(B) -> decode(B) when is_binary(B) ->
decode(B, <<>>). decode(B, <<>>).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Interval Functions %% Interval Functions
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
encode(D, string) -> encode(D, string) ->
binary_to_list(encode(D)); binary_to_list(encode(D));

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_batch). -module(emqx_batch).
@ -22,46 +24,49 @@
, items/1 , items/1
]). ]).
-record(batch, -export_type([options/0, batch/0]).
{ batch_size :: non_neg_integer()
, batch_q :: list(any())
, linger_ms :: pos_integer()
, linger_timer :: reference() | undefined
, commit_fun :: function()
}).
-type(options() :: -record(batch, {
#{ batch_size => non_neg_integer() batch_size :: non_neg_integer(),
, linger_ms => pos_integer() batch_q :: list(any()),
, commit_fun := function() linger_ms :: pos_integer(),
linger_timer :: reference() | undefined,
commit_fun :: function()
}).
-type(options() :: #{
batch_size => non_neg_integer(),
linger_ms => pos_integer(),
commit_fun := function()
}). }).
-opaque(batch() :: #batch{}). -opaque(batch() :: #batch{}).
-export_type([options/0]). %%--------------------------------------------------------------------
-export_type([batch/0]).
%%------------------------------------------------------------------------------
%% APIs %% APIs
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
-spec(init(options()) -> batch()). -spec(init(options()) -> batch()).
init(Opts) when is_map(Opts) -> init(Opts) when is_map(Opts) ->
#batch{batch_size = maps:get(batch_size, Opts, 1000), #batch{batch_size = maps:get(batch_size, Opts, 1000),
batch_q = [], batch_q = [],
linger_ms = maps:get(linger_ms, Opts, 1000), linger_ms = maps:get(linger_ms, Opts, 1000),
commit_fun = maps:get(commit_fun, Opts)}. commit_fun = maps:get(commit_fun, Opts)}.
-spec(push(any(), batch()) -> batch()). -spec(push(any(), batch()) -> batch()).
push(El, Batch = #batch{batch_q = Q, linger_ms = Ms, linger_timer = undefined}) when length(Q) == 0 -> push(El, Batch = #batch{batch_q = Q,
Batch#batch{batch_q = [El], linger_timer = erlang:send_after(Ms, self(), batch_linger_expired)}; linger_ms = Ms,
linger_timer = undefined})
when length(Q) == 0 ->
TRef = erlang:send_after(Ms, self(), batch_linger_expired),
Batch#batch{batch_q = [El], linger_timer = TRef};
%% no limit. %% no limit.
push(El, Batch = #batch{batch_size = 0, batch_q = Q}) -> push(El, Batch = #batch{batch_size = 0, batch_q = Q}) ->
Batch#batch{batch_q = [El|Q]}; Batch#batch{batch_q = [El|Q]};
push(El, Batch = #batch{batch_size = MaxSize, batch_q = Q}) when length(Q) >= MaxSize -> push(El, Batch = #batch{batch_size = MaxSize, batch_q = Q})
when length(Q) >= MaxSize ->
commit(Batch#batch{batch_q = [El|Q]}); commit(Batch#batch{batch_q = [El|Q]});
push(El, Batch = #batch{batch_q = Q}) -> push(El, Batch = #batch{batch_q = Q}) ->

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_broker). -module(emqx_broker).
@ -284,7 +286,7 @@ dispatch(Topic, Delivery = #delivery{message = Msg, results = Results}) ->
dispatch(SubPid, Topic, Msg) when is_pid(SubPid) -> dispatch(SubPid, Topic, Msg) when is_pid(SubPid) ->
case erlang:is_process_alive(SubPid) of case erlang:is_process_alive(SubPid) of
true -> true ->
SubPid ! {dispatch, Topic, Msg}, SubPid ! {deliver, Topic, Msg},
1; 1;
false -> 0 false -> 0
end; end;

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_broker_helper). -module(emqx_broker_helper).
@ -92,9 +94,9 @@ create_seq(Topic) ->
reclaim_seq(Topic) -> reclaim_seq(Topic) ->
emqx_sequence:reclaim(?SUBSEQ, Topic). emqx_sequence:reclaim(?SUBSEQ, Topic).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% gen_server callbacks %% gen_server callbacks
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
init([]) -> init([]) ->
%% Helper table %% Helper table
@ -142,9 +144,9 @@ terminate(_Reason, _State) ->
code_change(_OldVsn, State, _Extra) -> code_change(_OldVsn, State, _Extra) ->
{ok, State}. {ok, State}.
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Internal functions %% Internal functions
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
clean_down(SubPid) -> clean_down(SubPid) ->
case ets:lookup(?SUBMON, SubPid) of case ets:lookup(?SUBMON, SubPid) of

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_broker_sup). -module(emqx_broker_sup).
@ -23,9 +25,9 @@
start_link() -> start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []). supervisor:start_link({local, ?MODULE}, ?MODULE, []).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Supervisor callbacks %% Supervisor callbacks
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
init([]) -> init([]) ->
%% Broker pool %% Broker pool
@ -34,20 +36,20 @@ init([]) ->
{emqx_broker, start_link, []}]), {emqx_broker, start_link, []}]),
%% Shared subscription %% Shared subscription
SharedSub = #{id => shared_sub, SharedSub = #{id => shared_sub,
start => {emqx_shared_sub, start_link, []}, start => {emqx_shared_sub, start_link, []},
restart => permanent, restart => permanent,
shutdown => 2000, shutdown => 2000,
type => worker, type => worker,
modules => [emqx_shared_sub]}, modules => [emqx_shared_sub]},
%% Broker helper %% Broker helper
Helper = #{id => helper, Helper = #{id => helper,
start => {emqx_broker_helper, start_link, []}, start => {emqx_broker_helper, start_link, []},
restart => permanent, restart => permanent,
shutdown => 2000, shutdown => 2000,
type => worker, type => worker,
modules => [emqx_broker_helper]}, modules => [emqx_broker_helper]},
{ok, {{one_for_all, 0, 1}, [BrokerPool, SharedSub, Helper]}}. {ok, {{one_for_all, 0, 1}, [BrokerPool, SharedSub, Helper]}}.

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_cli). -module(emqx_cli).
@ -35,3 +37,4 @@ usage(CmdList) ->
usage(Format, Args) -> usage(Format, Args) ->
usage([{Format, Args}]). usage([{Format, Args}]).

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_client_sock). -module(emqx_client_sock).
@ -24,6 +26,8 @@
, getstat/2 , getstat/2
]). ]).
-export_type([socket/0, option/0]).
-record(ssl_socket, {tcp, ssl}). -record(ssl_socket, {tcp, ssl}).
-type(socket() :: inet:socket() | #ssl_socket{}). -type(socket() :: inet:socket() | #ssl_socket{}).
@ -32,8 +36,6 @@
-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]).
-define(DEFAULT_TCP_OPTIONS, [binary, {packet, raw}, {active, false}, -define(DEFAULT_TCP_OPTIONS, [binary, {packet, raw}, {active, false},
{nodelay, true}, {reuseaddr, true}]). {nodelay, true}, {reuseaddr, true}]).
@ -105,3 +107,4 @@ default_ciphers(TlsVersions) ->
fun(TlsVer, Ciphers) -> fun(TlsVer, Ciphers) ->
Ciphers ++ ssl:cipher_suites(all, TlsVer) Ciphers ++ ssl:cipher_suites(all, TlsVer)
end, [], TlsVersions). end, [], TlsVersions).

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,7 +12,9 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
%% Channel Manager
-module(emqx_cm). -module(emqx_cm).
-behaviour(gen_server). -behaviour(gen_server).
@ -24,25 +27,39 @@
-export([start_link/0]). -export([start_link/0]).
-export([ register_connection/1 -export([ register_channel/1
, register_connection/2 , unregister_channel/1
, unregister_connection/1 , unregister_channel/2
, unregister_connection/2
]). ]).
-export([ get_conn_attrs/1 -export([ get_conn_attrs/1
, get_conn_attrs/2 , get_conn_attrs/2
, set_conn_attrs/2 , set_conn_attrs/2
, set_conn_attrs/3
]). ]).
-export([ get_conn_stats/1 -export([ get_conn_stats/1
, get_conn_stats/2 , get_conn_stats/2
, set_conn_stats/2 , set_conn_stats/2
, set_conn_stats/3
]). ]).
-export([lookup_conn_pid/1]). -export([ open_session/1
, discard_session/1
, resume_session/1
]).
-export([ get_session_attrs/1
, get_session_attrs/2
, set_session_attrs/2
]).
-export([ get_session_stats/1
, get_session_stats/2
, set_session_stats/2
]).
-export([ lookup_channels/1
, lookup_channels/2
]).
%% gen_server callbacks %% gen_server callbacks
-export([ init/1 -export([ init/1
@ -53,159 +70,350 @@
, code_change/3 , code_change/3
]). ]).
%% internal export %% Internal export
-export([stats_fun/0]). -export([stats_fun/0]).
-define(CM, ?MODULE). -export_type([attrs/0, stats/0]).
%% ETS tables for connection management. -type(chan_pid() :: pid()).
-define(CONN_TAB, emqx_conn).
-define(CONN_ATTRS_TAB, emqx_conn_attrs).
-define(CONN_STATS_TAB, emqx_conn_stats).
-opaque(attrs() :: #{atom() => term()}).
-opaque(stats() :: #{atom() => integer()}).
%% Tables for channel management.
-define(CHAN_TAB, emqx_channel).
-define(CONN_TAB, emqx_connection).
-define(SESSION_TAB, emqx_session).
-define(SESSION_P_TAB, emqx_session_p).
%% Chan stats
-define(CHAN_STATS,
[{?CHAN_TAB, 'channels.count', 'channels.max'},
{?CONN_TAB, 'connections.count', 'connections.max'},
{?SESSION_TAB, 'sessions.count', 'sessions.max'},
{?SESSION_P_TAB, 'sessions.persistent.count', 'sessions.persistent.max'}
]).
%% Batch drain
-define(BATCH_SIZE, 100000). -define(BATCH_SIZE, 100000).
%% @doc Start the connection manager. %% Server name
-define(CM, ?MODULE).
%% @doc Start the channel manager.
-spec(start_link() -> startlink_ret()). -spec(start_link() -> startlink_ret()).
start_link() -> start_link() ->
gen_server:start_link({local, ?CM}, ?MODULE, [], []). gen_server:start_link({local, ?CM}, ?MODULE, [], []).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% API %% API
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% @doc Register a connection. %% @doc Register a channel.
-spec(register_connection(emqx_types:client_id()) -> ok). -spec(register_channel(emqx_types:client_id()) -> ok).
register_connection(ClientId) when is_binary(ClientId) -> register_channel(ClientId) when is_binary(ClientId) ->
register_connection(ClientId, self()). register_channel(ClientId, self()).
-spec(register_connection(emqx_types:client_id(), pid()) -> ok). -spec(register_channel(emqx_types:client_id(), chan_pid()) -> ok).
register_connection(ClientId, ConnPid) when is_binary(ClientId), is_pid(ConnPid) -> register_channel(ClientId, ChanPid) ->
true = ets:insert(?CONN_TAB, {ClientId, ConnPid}), Chan = {ClientId, ChanPid},
notify({registered, ClientId, ConnPid}). true = ets:insert(?CHAN_TAB, Chan),
ok = emqx_cm_registry:register_channel(Chan),
cast({registered, Chan}).
%% @doc Unregister a connection. %% @doc Unregister a channel.
-spec(unregister_connection(emqx_types:client_id()) -> ok). -spec(unregister_channel(emqx_types:client_id()) -> ok).
unregister_connection(ClientId) when is_binary(ClientId) -> unregister_channel(ClientId) when is_binary(ClientId) ->
unregister_connection(ClientId, self()). unregister_channel(ClientId, self()).
-spec(unregister_connection(emqx_types:client_id(), pid()) -> ok). -spec(unregister_channel(emqx_types:client_id(), chan_pid()) -> ok).
unregister_connection(ClientId, ConnPid) when is_binary(ClientId), is_pid(ConnPid) -> unregister_channel(ClientId, ChanPid) ->
true = do_unregister_connection({ClientId, ConnPid}), Chan = {ClientId, ChanPid},
notify({unregistered, ConnPid}). true = do_unregister_channel(Chan),
cast({unregistered, Chan}).
do_unregister_connection(Conn) -> %% @private
true = ets:delete(?CONN_STATS_TAB, Conn), do_unregister_channel(Chan) ->
true = ets:delete(?CONN_ATTRS_TAB, Conn), ok = emqx_cm_registry:unregister_channel(Chan),
true = ets:delete_object(?CONN_TAB, Conn). true = ets:delete_object(?SESSION_P_TAB, Chan),
true = ets:delete(?SESSION_TAB, Chan),
true = ets:delete(?CONN_TAB, Chan),
ets:delete_object(?CHAN_TAB, Chan).
%% @doc Get conn attrs %% @doc Get conn attrs.
-spec(get_conn_attrs(emqx_types:client_id()) -> list()). -spec(get_conn_attrs(emqx_types:client_id()) -> maybe(attrs())).
get_conn_attrs(ClientId) when is_binary(ClientId) -> get_conn_attrs(ClientId) ->
ConnPid = lookup_conn_pid(ClientId), with_channel(ClientId, fun(ChanPid) ->
get_conn_attrs(ClientId, ConnPid). get_conn_attrs(ClientId, ChanPid)
end).
-spec(get_conn_attrs(emqx_types:client_id(), pid()) -> list()). -spec(get_conn_attrs(emqx_types:client_id(), chan_pid()) -> maybe(attrs())).
get_conn_attrs(ClientId, ConnPid) when is_binary(ClientId) -> get_conn_attrs(ClientId, ChanPid) when node(ChanPid) == node() ->
emqx_tables:lookup_value(?CONN_ATTRS_TAB, {ClientId, ConnPid}, []). Chan = {ClientId, ChanPid},
try ets:lookup_element(?CONN_TAB, Chan, 2) of
Attrs -> Attrs
catch
error:badarg -> undefined
end;
get_conn_attrs(ClientId, ChanPid) ->
rpc_call(node(ChanPid), get_conn_attrs, [ClientId, ChanPid]).
%% @doc Set conn attrs %% @doc Set conn attrs.
-spec(set_conn_attrs(emqx_types:client_id(), list()) -> true). -spec(set_conn_attrs(emqx_types:client_id(), attrs()) -> ok).
set_conn_attrs(ClientId, Attrs) when is_binary(ClientId) -> set_conn_attrs(ClientId, Attrs) when is_binary(ClientId), is_map(Attrs) ->
set_conn_attrs(ClientId, self(), Attrs). Chan = {ClientId, self()},
case ets:update_element(?CONN_TAB, Chan, {2, Attrs}) of
true -> ok;
false -> true = ets:insert(?CONN_TAB, {Chan, Attrs, #{}}),
ok
end.
-spec(set_conn_attrs(emqx_types:client_id(), pid(), list()) -> true). %% @doc Get conn stats.
set_conn_attrs(ClientId, ConnPid, Attrs) when is_binary(ClientId), is_pid(ConnPid) -> -spec(get_conn_stats(emqx_types:client_id()) -> maybe(stats())).
Conn = {ClientId, ConnPid}, get_conn_stats(ClientId) ->
ets:insert(?CONN_ATTRS_TAB, {Conn, Attrs}). with_channel(ClientId, fun(ChanPid) ->
get_conn_stats(ClientId, ChanPid)
end).
%% @doc Get conn stats -spec(get_conn_stats(emqx_types:client_id(), chan_pid()) -> maybe(stats())).
-spec(get_conn_stats(emqx_types:client_id()) -> list(emqx_stats:stats())). get_conn_stats(ClientId, ChanPid) when node(ChanPid) == node() ->
get_conn_stats(ClientId) when is_binary(ClientId) -> Chan = {ClientId, ChanPid},
ConnPid = lookup_conn_pid(ClientId), try ets:lookup_element(?CONN_TAB, Chan, 3) of
get_conn_stats(ClientId, ConnPid). Stats -> Stats
catch
-spec(get_conn_stats(emqx_types:client_id(), pid()) -> list(emqx_stats:stats())). error:badarg -> undefined
get_conn_stats(ClientId, ConnPid) when is_binary(ClientId) -> end;
Conn = {ClientId, ConnPid}, get_conn_stats(ClientId, ChanPid) ->
emqx_tables:lookup_value(?CONN_STATS_TAB, Conn, []). rpc_call(node(ChanPid), get_conn_stats, [ClientId, ChanPid]).
%% @doc Set conn stats. %% @doc Set conn stats.
-spec(set_conn_stats(emqx_types:client_id(), list(emqx_stats:stats())) -> true). -spec(set_conn_stats(emqx_types:client_id(), stats()) -> ok).
set_conn_stats(ClientId, Stats) when is_binary(ClientId) -> set_conn_stats(ClientId, Stats) when is_binary(ClientId) ->
set_conn_stats(ClientId, self(), Stats). set_conn_stats(ClientId, self(), Stats).
-spec(set_conn_stats(emqx_types:client_id(), pid(), list(emqx_stats:stats())) -> true). -spec(set_conn_stats(emqx_types:client_id(), chan_pid(), stats()) -> ok).
set_conn_stats(ClientId, ConnPid, Stats) when is_binary(ClientId), is_pid(ConnPid) -> set_conn_stats(ClientId, ChanPid, Stats) ->
Conn = {ClientId, ConnPid}, Chan = {ClientId, ChanPid},
ets:insert(?CONN_STATS_TAB, {Conn, Stats}). _ = ets:update_element(?CONN_TAB, Chan, {3, Stats}),
ok.
%% @doc Lookup connection pid. %% @doc Open a session.
-spec(lookup_conn_pid(emqx_types:client_id()) -> maybe(pid())). -spec(open_session(map()) -> {ok, emqx_session:session()}
lookup_conn_pid(ClientId) when is_binary(ClientId) -> | {error, Reason :: term()}).
emqx_tables:lookup_value(?CONN_TAB, ClientId). open_session(Attrs = #{clean_start := true,
client_id := ClientId}) ->
CleanStart = fun(_) ->
ok = discard_session(ClientId),
{ok, emqx_session:new(Attrs), false}
end,
emqx_cm_locker:trans(ClientId, CleanStart);
notify(Msg) -> open_session(Attrs = #{clean_start := false,
gen_server:cast(?CM, {notify, Msg}). client_id := ClientId}) ->
ResumeStart = fun(_) ->
case resume_session(ClientId) of
{ok, Session} ->
{ok, Session, true};
{error, not_found} ->
{ok, emqx_session:new(Attrs), false}
end
end,
emqx_cm_locker:trans(ClientId, ResumeStart).
%%----------------------------------------------------------------------------- %% @doc Try to resume a session.
-spec(resume_session(emqx_types:client_id())
-> {ok, emqx_session:session()} | {error, Reason :: term()}).
resume_session(ClientId) ->
case lookup_channels(ClientId) of
[] -> {error, not_found};
[ChanPid] ->
emqx_channel:resume(ChanPid);
ChanPids ->
[ChanPid|StalePids] = lists:reverse(ChanPids),
?LOG(error, "[SM] More than one channel found: ~p", [ChanPids]),
lists:foreach(fun(StalePid) ->
catch emqx_channel:discard(StalePid)
end, StalePids),
emqx_channel:resume(ChanPid)
end.
%% @doc Discard all the sessions identified by the ClientId.
-spec(discard_session(emqx_types:client_id()) -> ok).
discard_session(ClientId) when is_binary(ClientId) ->
case lookup_channels(ClientId) of
[] -> ok;
ChanPids ->
lists:foreach(
fun(ChanPid) ->
try emqx_channel:discard(ChanPid)
catch
_:Error:_Stk ->
?LOG(warning, "[SM] Failed to discard ~p: ~p", [ChanPid, Error])
end
end, ChanPids)
end.
%% @doc Get session attrs.
-spec(get_session_attrs(emqx_types:client_id()) -> attrs()).
get_session_attrs(ClientId) ->
with_channel(ClientId, fun(ChanPid) ->
get_session_attrs(ClientId, ChanPid)
end).
-spec(get_session_attrs(emqx_types:client_id(), chan_pid()) -> maybe(attrs())).
get_session_attrs(ClientId, ChanPid) when node(ChanPid) == node() ->
Chan = {ClientId, ChanPid},
try ets:lookup_element(?SESSION_TAB, Chan, 2) of
Attrs -> Attrs
catch
error:badarg -> undefined
end;
get_session_attrs(ClientId, ChanPid) ->
rpc_call(node(ChanPid), get_session_attrs, [ClientId, ChanPid]).
%% @doc Set session attrs.
-spec(set_session_attrs(emqx_types:client_id(), attrs()) -> ok).
set_session_attrs(ClientId, Attrs) when is_binary(ClientId) ->
Chan = {ClientId, self()},
case ets:update_element(?SESSION_TAB, Chan, {2, Attrs}) of
true -> ok;
false ->
true = ets:insert(?SESSION_TAB, {Chan, Attrs, #{}}),
is_clean_start(Attrs) orelse ets:insert(?SESSION_P_TAB, Chan),
ok
end.
%% @doc Is clean start?
is_clean_start(#{clean_start := false}) -> false;
is_clean_start(_Attrs) -> true.
%% @doc Get session stats.
-spec(get_session_stats(emqx_types:client_id()) -> stats()).
get_session_stats(ClientId) ->
with_channel(ClientId, fun(ChanPid) ->
get_session_stats(ClientId, ChanPid)
end).
-spec(get_session_stats(emqx_types:client_id(), chan_pid()) -> maybe(stats())).
get_session_stats(ClientId, ChanPid) when node(ChanPid) == node() ->
Chan = {ClientId, ChanPid},
try ets:lookup_element(?SESSION_TAB, Chan, 3) of
Stats -> Stats
catch
error:badarg -> undefined
end;
get_session_stats(ClientId, ChanPid) ->
rpc_call(node(ChanPid), get_session_stats, [ClientId, ChanPid]).
%% @doc Set session stats.
-spec(set_session_stats(emqx_types:client_id(), stats()) -> ok).
set_session_stats(ClientId, Stats) when is_binary(ClientId) ->
set_session_stats(ClientId, self(), Stats).
-spec(set_session_stats(emqx_types:client_id(), chan_pid(), stats()) -> ok).
set_session_stats(ClientId, ChanPid, Stats) ->
Chan = {ClientId, ChanPid},
_ = ets:update_element(?SESSION_TAB, Chan, {3, Stats}),
ok.
with_channel(ClientId, Fun) ->
case lookup_channels(ClientId) of
[] -> undefined;
[Pid] -> Fun(Pid);
Pids -> Fun(lists:last(Pids))
end.
%% @doc Lookup channels.
-spec(lookup_channels(emqx_types:client_id()) -> list(chan_pid())).
lookup_channels(ClientId) ->
lookup_channels(global, ClientId).
%% @doc Lookup local or global channels.
-spec(lookup_channels(local | global, emqx_types:client_id()) -> list(chan_pid())).
lookup_channels(global, ClientId) ->
case emqx_cm_registry:is_enabled() of
true ->
emqx_cm_registry:lookup_channels(ClientId);
false ->
lookup_channels(local, ClientId)
end;
lookup_channels(local, ClientId) ->
[ChanPid || {_, ChanPid} <- ets:lookup(?CHAN_TAB, ClientId)].
%% @private
rpc_call(Node, Fun, Args) ->
case rpc:call(Node, ?MODULE, Fun, Args) of
{badrpc, Reason} -> error(Reason);
Res -> Res
end.
%% @private
cast(Msg) -> gen_server:cast(?CM, Msg).
%%--------------------------------------------------------------------
%% gen_server callbacks %% gen_server callbacks
%%----------------------------------------------------------------------------- %%--------------------------------------------------------------------
init([]) -> init([]) ->
TabOpts = [public, set, {write_concurrency, true}], TabOpts = [public, {write_concurrency, true}],
ok = emqx_tables:new(?CONN_TAB, [{read_concurrency, true} | TabOpts]), ok = emqx_tables:new(?CHAN_TAB, [bag, {read_concurrency, true} | TabOpts]),
ok = emqx_tables:new(?CONN_ATTRS_TAB, TabOpts), ok = emqx_tables:new(?CONN_TAB, [set, compressed | TabOpts]),
ok = emqx_tables:new(?CONN_STATS_TAB, TabOpts), ok = emqx_tables:new(?SESSION_TAB, [set, compressed | TabOpts]),
ok = emqx_stats:update_interval(conn_stats, fun ?MODULE:stats_fun/0), ok = emqx_tables:new(?SESSION_P_TAB, [bag | TabOpts]),
{ok, #{conn_pmon => emqx_pmon:new()}}. ok = emqx_stats:update_interval(chan_stats, fun ?MODULE:stats_fun/0),
{ok, #{chan_pmon => emqx_pmon:new()}}.
handle_call(Req, _From, State) -> handle_call(Req, _From, State) ->
?LOG(error, "Unexpected call: ~p", [Req]), ?LOG(error, "Unexpected call: ~p", [Req]),
{reply, ignored, State}. {reply, ignored, State}.
handle_cast({notify, {registered, ClientId, ConnPid}}, State = #{conn_pmon := PMon}) -> handle_cast({registered, {ClientId, ChanPid}}, State = #{chan_pmon := PMon}) ->
{noreply, State#{conn_pmon := emqx_pmon:monitor(ConnPid, ClientId, PMon)}}; PMon1 = emqx_pmon:monitor(ChanPid, ClientId, PMon),
{noreply, State#{chan_pmon := PMon1}};
handle_cast({notify, {unregistered, ConnPid}}, State = #{conn_pmon := PMon}) -> handle_cast({unregistered, {_ClientId, ChanPid}}, State = #{chan_pmon := PMon}) ->
{noreply, State#{conn_pmon := emqx_pmon:demonitor(ConnPid, PMon)}}; PMon1 = emqx_pmon:demonitor(ChanPid, PMon),
{noreply, State#{chan_pmon := PMon1}};
handle_cast(Msg, State) -> handle_cast(Msg, State) ->
?LOG(error, "Unexpected cast: ~p", [Msg]), ?LOG(error, "Unexpected cast: ~p", [Msg]),
{noreply, State}. {noreply, State}.
handle_info({'DOWN', _MRef, process, Pid, _Reason}, State = #{conn_pmon := PMon}) -> handle_info({'DOWN', _MRef, process, Pid, _Reason}, State = #{chan_pmon := PMon}) ->
ConnPids = [Pid | emqx_misc:drain_down(?BATCH_SIZE)], ChanPids = [Pid | emqx_misc:drain_down(?BATCH_SIZE)],
{Items, PMon1} = emqx_pmon:erase_all(ConnPids, PMon), {Items, PMon1} = emqx_pmon:erase_all(ChanPids, PMon),
ok = emqx_pool:async_submit( ok = emqx_pool:async_submit(fun lists:foreach/2, [fun clean_down/1, Items]),
fun lists:foreach/2, [fun clean_down/1, Items]), {noreply, State#{chan_pmon := PMon1}};
{noreply, State#{conn_pmon := PMon1}};
handle_info(Info, State) -> handle_info(Info, State) ->
?LOG(error, "Unexpected info: ~p", [Info]), ?LOG(error, "Unexpected info: ~p", [Info]),
{noreply, State}. {noreply, State}.
terminate(_Reason, _State) -> terminate(_Reason, _State) ->
emqx_stats:cancel_update(conn_stats). emqx_stats:cancel_update(chan_stats).
code_change(_OldVsn, State, _Extra) -> code_change(_OldVsn, State, _Extra) ->
{ok, State}. {ok, State}.
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Internal functions %% Internal functions
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
clean_down({Pid, ClientId}) -> clean_down({ChanPid, ClientId}) ->
Conn = {ClientId, Pid}, Chan = {ClientId, ChanPid},
case ets:member(?CONN_TAB, ClientId) do_unregister_channel(Chan).
orelse ets:member(?CONN_ATTRS_TAB, Conn) of
true ->
do_unregister_connection(Conn);
false -> false
end.
stats_fun() -> stats_fun() ->
case ets:info(?CONN_TAB, size) of lists:foreach(fun update_stats/1, ?CHAN_STATS).
update_stats({Tab, Stat, MaxStat}) ->
case ets:info(Tab, size) of
undefined -> ok; undefined -> ok;
Size -> emqx_stats:setstat('connections.count', 'connections.max', Size) Size -> emqx_stats:setstat(Stat, MaxStat, Size)
end. end.

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,8 +12,9 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_sm_locker). -module(emqx_cm_locker).
-include("emqx.hrl"). -include("emqx.hrl").
-include("types.hrl"). -include("types.hrl").
@ -26,10 +28,6 @@
, unlock/1 , unlock/1
]). ]).
%%------------------------------------------------------------------------------
%% APIs
%%------------------------------------------------------------------------------
-spec(start_link() -> startlink_ret()). -spec(start_link() -> startlink_ret()).
start_link() -> start_link() ->
ekka_locker:start_link(?MODULE). ekka_locker:start_link(?MODULE).

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,8 +12,10 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_sm_registry). %% Global Channel Registry
-module(emqx_cm_registry).
-behaviour(gen_server). -behaviour(gen_server).
@ -24,12 +27,14 @@
-export([start_link/0]). -export([start_link/0]).
-export([ is_enabled/0 -export([is_enabled/0]).
, register_session/1
, lookup_session/1 -export([ register_channel/1
, unregister_session/1 , unregister_channel/1
]). ]).
-export([lookup_channels/1]).
%% gen_server callbacks %% gen_server callbacks
-export([ init/1 -export([ init/1
, handle_call/3 , handle_call/3
@ -40,57 +45,67 @@
]). ]).
-define(REGISTRY, ?MODULE). -define(REGISTRY, ?MODULE).
-define(TAB, emqx_session_registry). -define(TAB, emqx_channel_registry).
-define(LOCK, {?MODULE, cleanup_sessions}). -define(LOCK, {?MODULE, cleanup_down}).
-record(global_session, {sid, pid}). -record(channel, {chid, pid}).
-type(session_pid() :: pid()). %% @doc Start the global channel registry.
%%------------------------------------------------------------------------------
%% APIs
%%------------------------------------------------------------------------------
%% @doc Start the global session manager.
-spec(start_link() -> startlink_ret()). -spec(start_link() -> startlink_ret()).
start_link() -> start_link() ->
gen_server:start_link({local, ?REGISTRY}, ?MODULE, [], []). gen_server:start_link({local, ?REGISTRY}, ?MODULE, [], []).
%%--------------------------------------------------------------------
%% APIs
%%--------------------------------------------------------------------
%% @doc Is the global registry enabled?
-spec(is_enabled() -> boolean()). -spec(is_enabled() -> boolean()).
is_enabled() -> is_enabled() ->
emqx_config:get_env(enable_session_registry, true). emqx_config:get_env(enable_channel_registry, true).
-spec(lookup_session(emqx_types:client_id()) -> list(session_pid())). %% @doc Register a global channel.
lookup_session(ClientId) -> -spec(register_channel(emqx_types:client_id()
[SessPid || #global_session{pid = SessPid} <- mnesia:dirty_read(?TAB, ClientId)]. | {emqx_types:client_id(), pid()}) -> ok).
register_channel(ClientId) when is_binary(ClientId) ->
register_channel({ClientId, self()});
-spec(register_session({emqx_types:client_id(), session_pid()}) -> ok). register_channel({ClientId, ChanPid}) when is_binary(ClientId), is_pid(ChanPid) ->
register_session({ClientId, SessPid}) when is_binary(ClientId), is_pid(SessPid) ->
case is_enabled() of case is_enabled() of
true -> mnesia:dirty_write(?TAB, record(ClientId, SessPid)); true -> mnesia:dirty_write(?TAB, record(ClientId, ChanPid));
false -> ok false -> ok
end. end.
-spec(unregister_session({emqx_types:client_id(), session_pid()}) -> ok). %% @doc Unregister a global channel.
unregister_session({ClientId, SessPid}) when is_binary(ClientId), is_pid(SessPid) -> -spec(unregister_channel(emqx_types:client_id()
| {emqx_types:client_id(), pid()}) -> ok).
unregister_channel(ClientId) when is_binary(ClientId) ->
unregister_channel({ClientId, self()});
unregister_channel({ClientId, ChanPid}) when is_binary(ClientId), is_pid(ChanPid) ->
case is_enabled() of case is_enabled() of
true -> mnesia:dirty_delete_object(?TAB, record(ClientId, SessPid)); true -> mnesia:dirty_delete_object(?TAB, record(ClientId, ChanPid));
false -> ok false -> ok
end. end.
record(ClientId, SessPid) -> %% @doc Lookup the global channels.
#global_session{sid = ClientId, pid = SessPid}. -spec(lookup_channels(emqx_types:client_id()) -> list(pid())).
lookup_channels(ClientId) ->
[ChanPid || #channel{pid = ChanPid} <- mnesia:dirty_read(?TAB, ClientId)].
%%------------------------------------------------------------------------------ record(ClientId, ChanPid) ->
#channel{chid = ClientId, pid = ChanPid}.
%%--------------------------------------------------------------------
%% gen_server callbacks %% gen_server callbacks
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
init([]) -> init([]) ->
ok = ekka_mnesia:create_table(?TAB, [ ok = ekka_mnesia:create_table(?TAB, [
{type, bag}, {type, bag},
{ram_copies, [node()]}, {ram_copies, [node()]},
{record_name, global_session}, {record_name, channel},
{attributes, record_info(fields, global_session)}, {attributes, record_info(fields, channel)},
{storage_properties, [{ets, [{read_concurrency, true}, {storage_properties, [{ets, [{read_concurrency, true},
{write_concurrency, true}]}]}]), {write_concurrency, true}]}]}]),
ok = ekka_mnesia:copy_table(?TAB), ok = ekka_mnesia:copy_table(?TAB),
@ -108,7 +123,7 @@ handle_cast(Msg, State) ->
handle_info({membership, {mnesia, down, Node}}, State) -> handle_info({membership, {mnesia, down, Node}}, State) ->
global:trans({?LOCK, self()}, global:trans({?LOCK, self()},
fun() -> fun() ->
mnesia:transaction(fun cleanup_sessions/1, [Node]) mnesia:transaction(fun cleanup_channels/1, [Node])
end), end),
{noreply, State}; {noreply, State};
@ -125,14 +140,14 @@ terminate(_Reason, _State) ->
code_change(_OldVsn, State, _Extra) -> code_change(_OldVsn, State, _Extra) ->
{ok, State}. {ok, State}.
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Internal functions %% Internal functions
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
cleanup_sessions(Node) -> cleanup_channels(Node) ->
Pat = [{#global_session{pid = '$1', _ = '_'}, [{'==', {node, '$1'}, Node}], ['$_']}], Pat = [{#channel{pid = '$1', _ = '_'}, [{'==', {node, '$1'}, Node}], ['$_']}],
lists:foreach(fun delete_session/1, mnesia:select(?TAB, Pat, write)). lists:foreach(fun delete_channel/1, mnesia:select(?TAB, Pat, write)).
delete_session(Session) -> delete_channel(Chan) ->
mnesia:delete_object(?TAB, Session, write). mnesia:delete_object(?TAB, Chan, write).

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_cm_sup). -module(emqx_cm_sup).
@ -36,13 +38,33 @@ init([]) ->
shutdown => 1000, shutdown => 1000,
type => worker, type => worker,
modules => [emqx_flapping]}, modules => [emqx_flapping]},
%% Channel locker
Locker = #{id => locker,
start => {emqx_cm_locker, start_link, []},
restart => permanent,
shutdown => 5000,
type => worker,
modules => [emqx_cm_locker]
},
%% Channel registry
Registry = #{id => registry,
start => {emqx_cm_registry, start_link, []},
restart => permanent,
shutdown => 5000,
type => worker,
modules => [emqx_cm_registry]
},
%% Channel Manager
Manager = #{id => manager, Manager = #{id => manager,
start => {emqx_cm, start_link, []}, start => {emqx_cm, start_link, []},
restart => permanent, restart => permanent,
shutdown => 2000, shutdown => 5000,
type => worker, type => worker,
modules => [emqx_cm]}, modules => [emqx_cm]
},
SupFlags = #{strategy => one_for_one, SupFlags = #{strategy => one_for_one,
intensity => 100, intensity => 100,
period => 10}, period => 10
{ok, {SupFlags, [Banned, Manager, Flapping]}}. },
{ok, {SupFlags, [Banned, Flapping, Locker, Registry, Manager]}}.

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
%% @doc Hot Configuration %% @doc Hot Configuration
%% %%

591
src/emqx_connection.erl Normal file
View File

@ -0,0 +1,591 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2019 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.
%%--------------------------------------------------------------------
%% MQTT TCP/SSL Connection
-module(emqx_connection).
-behaviour(gen_statem).
-include("emqx.hrl").
-include("emqx_mqtt.hrl").
-include("logger.hrl").
-include("types.hrl").
-logger_header("[Conn]").
-export([start_link/3]).
%% APIs
-export([ info/1
, attrs/1
, stats/1
]).
%% gen_statem callbacks
-export([ idle/3
, connected/3
, disconnected/3
]).
-export([ init/1
, callback_mode/0
, code_change/4
, terminate/3
]).
-record(state, {
transport :: esockd:transport(),
socket :: esockd:socket(),
peername :: emqx_types:peername(),
sockname :: emqx_types:peername(),
conn_state :: running | blocked,
active_n :: pos_integer(),
rate_limit :: maybe(esockd_rate_limit:bucket()),
pub_limit :: maybe(esockd_rate_limit:bucket()),
limit_timer :: maybe(reference()),
parse_state :: emqx_frame:parse_state(),
chan_state :: emqx_channel:channel(),
gc_state :: emqx_gc:gc_state(),
keepalive :: maybe(emqx_keepalive:keepalive()),
stats_timer :: disabled | maybe(reference()),
idle_timeout :: timeout()
}).
-define(ACTIVE_N, 100).
-define(HANDLE(T, C, D), handle((T), (C), (D))).
-define(CHAN_STATS, [recv_pkt, recv_msg, send_pkt, send_msg]).
-define(SOCK_STATS, [recv_oct, recv_cnt, send_oct, send_cnt, send_pend]).
-spec(start_link(esockd:transport(), esockd:socket(), proplists:proplist())
-> {ok, pid()}).
start_link(Transport, Socket, Options) ->
{ok, proc_lib:spawn_link(?MODULE, init, [{Transport, Socket, Options}])}.
%%--------------------------------------------------------------------
%% API
%%--------------------------------------------------------------------
%% For debug
-spec(info(pid() | #state{}) -> map()).
info(CPid) when is_pid(CPid) ->
call(CPid, info);
info(#state{transport = Transport,
socket = Socket,
peername = Peername,
sockname = Sockname,
conn_state = ConnState,
active_n = ActiveN,
rate_limit = RateLimit,
pub_limit = PubLimit,
chan_state = ChanState}) ->
ConnInfo = #{socktype => Transport:type(Socket),
peername => Peername,
sockname => Sockname,
conn_state => ConnState,
active_n => ActiveN,
rate_limit => rate_limit_info(RateLimit),
pub_limit => rate_limit_info(PubLimit)
},
ChanInfo = emqx_channel:info(ChanState),
maps:merge(ConnInfo, ChanInfo).
rate_limit_info(undefined) ->
undefined;
rate_limit_info(Limit) ->
esockd_rate_limit:info(Limit).
%% For dashboard
attrs(CPid) when is_pid(CPid) ->
call(CPid, attrs);
attrs(#state{peername = Peername,
sockname = Sockname,
conn_state = ConnState,
chan_state = ChanState}) ->
SockAttrs = #{peername => Peername,
sockname => Sockname,
conn_state => ConnState
},
ChanAttrs = emqx_channel:attrs(ChanState),
maps:merge(SockAttrs, ChanAttrs).
%% @doc Get connection stats
stats(CPid) when is_pid(CPid) ->
call(CPid, stats);
stats(#state{transport = Transport, socket = Socket}) ->
SockStats = case Transport:getstat(Socket, ?SOCK_STATS) of
{ok, Ss} -> Ss;
{error, _} -> []
end,
ChanStats = [{Name, emqx_pd:get_counter(Name)} || Name <- ?CHAN_STATS],
lists:append([SockStats, ChanStats, emqx_misc:proc_stats()]).
call(CPid, Req) ->
gen_statem:call(CPid, Req, infinity).
%%--------------------------------------------------------------------
%% gen_statem callbacks
%%--------------------------------------------------------------------
init({Transport, RawSocket, Options}) ->
{ok, Socket} = Transport:wait(RawSocket),
{ok, Peername} = Transport:ensure_ok_or_exit(peername, [Socket]),
{ok, Sockname} = Transport:ensure_ok_or_exit(sockname, [Socket]),
Peercert = Transport:ensure_ok_or_exit(peercert, [Socket]),
emqx_logger:set_metadata_peername(esockd_net:format(Peername)),
Zone = proplists:get_value(zone, Options),
RateLimit = init_limiter(proplists:get_value(rate_limit, Options)),
PubLimit = init_limiter(emqx_zone:get_env(Zone, publish_limit)),
ActiveN = proplists:get_value(active_n, Options, ?ACTIVE_N),
MaxSize = emqx_zone:get_env(Zone, max_packet_size, ?MAX_PACKET_SIZE),
ParseState = emqx_frame:initial_parse_state(#{max_size => MaxSize}),
ChanState = emqx_channel:init(#{peername => Peername,
sockname => Sockname,
peercert => Peercert,
conn_mod => ?MODULE}, Options),
GcPolicy = emqx_zone:get_env(Zone, force_gc_policy, false),
GcState = emqx_gc:init(GcPolicy),
EnableStats = emqx_zone:get_env(Zone, enable_stats, true),
StatsTimer = if EnableStats -> undefined; ?Otherwise-> disabled end,
IdleTimout = emqx_zone:get_env(Zone, idle_timeout, 30000),
ok = emqx_misc:init_proc_mng_policy(Zone),
State = #state{transport = Transport,
socket = Socket,
peername = Peername,
conn_state = running,
active_n = ActiveN,
rate_limit = RateLimit,
pub_limit = PubLimit,
parse_state = ParseState,
chan_state = ChanState,
gc_state = GcState,
stats_timer = StatsTimer,
idle_timeout = IdleTimout
},
gen_statem:enter_loop(?MODULE, [{hibernate_after, 2 * IdleTimout}],
idle, State, self(), [IdleTimout]).
init_limiter(undefined) ->
undefined;
init_limiter({Rate, Burst}) ->
esockd_rate_limit:new(Rate, Burst).
callback_mode() ->
[state_functions, state_enter].
%%--------------------------------------------------------------------
%% Idle State
idle(enter, _, State) ->
case activate_socket(State) of
ok -> keep_state_and_data;
{error, Reason} ->
shutdown(Reason, State)
end;
idle(timeout, _Timeout, State) ->
stop(idle_timeout, State);
idle(cast, {incoming, Packet = ?CONNECT_PACKET(ConnVar)}, State) ->
handle_incoming(Packet,
fun(St = #state{chan_state = ChanState}) ->
%% Ensure keepalive after connected successfully.
Interval = emqx_channel:keepalive(ChanState),
NextEvent = {next_event, info, {keepalive, start, Interval}},
{next_state, connected, St, NextEvent}
end, State);
idle(cast, {incoming, Packet}, State) ->
?LOG(warning, "Unexpected incoming: ~p", [Packet]),
shutdown(unexpected_incoming_packet, State);
idle(EventType, Content, State) ->
?HANDLE(EventType, Content, State).
%%--------------------------------------------------------------------
%% Connected State
connected(enter, _, _State) ->
%% What to do?
keep_state_and_data;
connected(cast, {incoming, Packet = ?PACKET(?CONNECT)}, State) ->
?LOG(warning, "Unexpected connect: ~p", [Packet]),
shutdown(unexpected_incoming_connect, State);
connected(cast, {incoming, Packet = ?PACKET(Type)}, State) ->
handle_incoming(Packet, fun keep_state/1, State);
connected(info, Deliver = {deliver, _Topic, _Msg},
State = #state{chan_state = ChanState}) ->
Delivers = emqx_misc:drain_deliver([Deliver]),
%% TODO: ...
case BatchLen = length(Delivers) of
1 -> ok;
N -> io:format("Batch Deliver: ~w~n", [N])
end,
case emqx_channel:handle_out(Delivers, ChanState) of
{ok, NChanState} ->
keep_state(State#state{chan_state = NChanState});
{ok, Packets, NChanState} ->
NState = State#state{chan_state = NChanState},
handle_outgoing(Packets, fun keep_state/1, NState);
{error, Reason} ->
shutdown(Reason, State)
end;
%% Start Keepalive
connected(info, {keepalive, start, Interval}, State) ->
case ensure_keepalive(Interval, State) of
ignore -> keep_state(State);
{ok, KeepAlive} ->
keep_state(State#state{keepalive = KeepAlive});
{error, Reason} ->
shutdown(Reason, State)
end;
%% Keepalive timer
connected(info, {keepalive, check}, State = #state{keepalive = KeepAlive}) ->
case emqx_keepalive:check(KeepAlive) of
{ok, KeepAlive1} ->
keep_state(State#state{keepalive = KeepAlive1});
{error, timeout} ->
shutdown(keepalive_timeout, State);
{error, Reason} ->
shutdown(Reason, State)
end;
connected(EventType, Content, State) ->
?HANDLE(EventType, Content, State).
%%--------------------------------------------------------------------
%% Disconnected State
disconnected(enter, _, _State) ->
%% TODO: What to do?
keep_state_and_data;
disconnected(EventType, Content, State) ->
?HANDLE(EventType, Content, State).
%% Handle call
handle({call, From}, info, State) ->
reply(From, info(State), State);
handle({call, From}, attrs, State) ->
reply(From, attrs(State), State);
handle({call, From}, stats, State) ->
reply(From, stats(State), State);
%%handle({call, From}, kick, State) ->
%% ok = gen_statem:reply(From, ok),
%% shutdown(kicked, State);
%%handle({call, From}, discard, State) ->
%% ok = gen_statem:reply(From, ok),
%% shutdown(discard, State);
handle({call, From}, Req, State) ->
?LOG(error, "Unexpected call: ~p", [Req]),
reply(From, ignored, State);
%% Handle cast
handle(cast, Msg, State) ->
?LOG(error, "Unexpected cast: ~p", [Msg]),
keep_state(State);
%% Handle Incoming
handle(info, {Inet, _Sock, Data}, State) when Inet == tcp;
Inet == ssl ->
?LOG(debug, "RECV ~p", [Data]),
Oct = iolist_size(Data),
emqx_pd:update_counter(incoming_bytes, Oct),
ok = emqx_metrics:inc('bytes.received', Oct),
NState = ensure_stats_timer(maybe_gc(1, Oct, State)),
process_incoming(Data, [], NState);
handle(info, {Error, _Sock, Reason}, State)
when Error == tcp_error; Error == ssl_error ->
shutdown(Reason, State);
handle(info, {Closed, _Sock}, State)
when Closed == tcp_closed; Closed == ssl_closed ->
shutdown(closed, State);
handle(info, {Passive, _Sock}, State) when Passive == tcp_passive;
Passive == ssl_passive ->
%% Rate limit here:)
NState = ensure_rate_limit(State),
case activate_socket(NState) of
ok -> keep_state(NState);
{error, Reason} ->
shutdown(Reason, NState)
end;
handle(info, activate_socket, State) ->
%% Rate limit timer expired.
NState = State#state{conn_state = running},
case activate_socket(NState) of
ok ->
keep_state(NState#state{limit_timer = undefined});
{error, Reason} ->
shutdown(Reason, NState)
end;
handle(info, {inet_reply, _Sock, ok}, State) ->
%% something sent
keep_state(ensure_stats_timer(State));
handle(info, {inet_reply, _Sock, {error, Reason}}, State) ->
shutdown(Reason, State);
handle(info, {timeout, Timer, emit_stats},
State = #state{stats_timer = Timer,
chan_state = ChanState,
gc_state = GcState}) ->
ClientId = emqx_channel:client_id(ChanState),
ok = emqx_cm:set_conn_stats(ClientId, stats(State)),
NState = State#state{stats_timer = undefined},
Limits = erlang:get(force_shutdown_policy),
case emqx_misc:conn_proc_mng_policy(Limits) of
continue ->
keep_state(NState);
hibernate ->
%% going to hibernate, reset gc stats
GcState1 = emqx_gc:reset(GcState),
{keep_state, NState#state{gc_state = GcState1}, hibernate};
{shutdown, Reason} ->
?LOG(error, "Shutdown exceptionally due to ~p", [Reason]),
shutdown(Reason, NState)
end;
handle(info, {shutdown, discard, {ClientId, ByPid}}, State) ->
?LOG(error, "Discarded by ~s:~p", [ClientId, ByPid]),
shutdown(discard, State);
handle(info, {shutdown, conflict, {ClientId, NewPid}}, State) ->
?LOG(warning, "Clientid '~s' conflict with ~p", [ClientId, NewPid]),
shutdown(conflict, State);
handle(info, {shutdown, Reason}, State) ->
shutdown(Reason, State);
handle(info, Info, State) ->
?LOG(error, "Unexpected info: ~p", [Info]),
keep_state(State).
code_change(_Vsn, State, Data, _Extra) ->
{ok, State, Data}.
terminate(Reason, _StateName, #state{transport = Transport,
socket = Socket,
keepalive = KeepAlive,
chan_state = ChanState}) ->
?LOG(debug, "Terminated for ~p", [Reason]),
ok = Transport:fast_close(Socket),
ok = emqx_keepalive:cancel(KeepAlive),
emqx_channel:terminate(Reason, ChanState).
%%--------------------------------------------------------------------
%% Process incoming data
process_incoming(<<>>, Packets, State) ->
{keep_state, State, next_events(Packets)};
process_incoming(Data, Packets, State = #state{parse_state = ParseState}) ->
try emqx_frame:parse(Data, ParseState) of
{ok, NParseState} ->
NState = State#state{parse_state = NParseState},
{keep_state, NState, next_events(Packets)};
{ok, Packet, Rest, NParseState} ->
NState = State#state{parse_state = NParseState},
process_incoming(Rest, [Packet|Packets], NState);
{error, Reason} ->
shutdown(Reason, State)
catch
error:Reason:Stk ->
?LOG(error, "Parse failed for ~p~n\
Stacktrace:~p~nError data:~p", [Reason, Stk, Data]),
shutdown(parse_error, State)
end.
next_events(Packets) when is_list(Packets) ->
[next_events(Packet) || Packet <- lists:reverse(Packets)];
next_events(Packet) ->
{next_event, cast, {incoming, Packet}}.
%%--------------------------------------------------------------------
%% Handle incoming packet
handle_incoming(Packet = ?PACKET(Type), SuccFun,
State = #state{chan_state = ChanState}) ->
_ = inc_incoming_stats(Type),
ok = emqx_metrics:inc_recv(Packet),
?LOG(debug, "RECV ~s", [emqx_packet:format(Packet)]),
case emqx_channel:handle_in(Packet, ChanState) of
{ok, NChanState} ->
SuccFun(State#state{chan_state = NChanState});
{ok, OutPacket, NChanState} ->
handle_outgoing(OutPacket, SuccFun,
State#state{chan_state = NChanState});
{error, Reason, NChanState} ->
shutdown(Reason, State#state{chan_state = NChanState});
{stop, Error, NChanState} ->
stop(Error, State#state{chan_state = NChanState})
end.
%%--------------------------------------------------------------------
%% Handle outgoing packets
handle_outgoing(Packets, SuccFun, State = #state{chan_state = ChanState})
when is_list(Packets) ->
ProtoVer = emqx_channel:proto_ver(ChanState),
IoData = lists:foldl(
fun(Packet = ?PACKET(Type), Acc) ->
?LOG(debug, "SEND ~s", [emqx_packet:format(Packet)]),
_ = inc_outgoing_stats(Type),
[emqx_frame:serialize(Packet, ProtoVer)|Acc]
end, [], Packets),
send(lists:reverse(IoData), SuccFun, State);
handle_outgoing(Packet = ?PACKET(Type), SuccFun, State = #state{chan_state = ChanState}) ->
?LOG(debug, "SEND ~s", [emqx_packet:format(Packet)]),
_ = inc_outgoing_stats(Type),
ProtoVer = emqx_channel:proto_ver(ChanState),
IoData = emqx_frame:serialize(Packet, ProtoVer),
send(IoData, SuccFun, State).
%%--------------------------------------------------------------------
%% Send data
send(IoData, SuccFun, State = #state{transport = Transport, socket = Socket}) ->
Oct = iolist_size(IoData),
ok = emqx_metrics:inc('bytes.sent', Oct),
case Transport:async_send(Socket, IoData) of
ok -> SuccFun(maybe_gc(1, Oct, State));
{error, Reason} ->
shutdown(Reason, State)
end.
%%--------------------------------------------------------------------
%% Ensure keepalive
ensure_keepalive(0, State) ->
ignore;
ensure_keepalive(Interval, State = #state{transport = Transport,
socket = Socket,
chan_state = ChanState}) ->
StatFun = fun() ->
case Transport:getstat(Socket, [recv_oct]) of
{ok, [{recv_oct, RecvOct}]} ->
{ok, RecvOct};
Error -> Error
end
end,
Backoff = emqx_zone:get_env(emqx_channel:zone(ChanState),
keepalive_backoff, 0.75),
emqx_keepalive:start(StatFun, round(Interval * Backoff), {keepalive, check}).
%%--------------------------------------------------------------------
%% Ensure rate limit
ensure_rate_limit(State = #state{rate_limit = Rl, pub_limit = Pl}) ->
Limiters = [{Pl, #state.pub_limit, emqx_pd:reset_counter(incoming_pubs)},
{Rl, #state.rate_limit, emqx_pd:reset_counter(incoming_bytes)}],
ensure_rate_limit(Limiters, State).
ensure_rate_limit([], State) ->
State;
ensure_rate_limit([{undefined, _Pos, _Cnt}|Limiters], State) ->
ensure_rate_limit(Limiters, State);
ensure_rate_limit([{Rl, Pos, Cnt}|Limiters], State) ->
case esockd_rate_limit:check(Cnt, Rl) of
{0, Rl1} ->
ensure_rate_limit(Limiters, setelement(Pos, State, Rl1));
{Pause, Rl1} ->
?LOG(debug, "Rate limit pause connection ~pms", [Pause]),
TRef = erlang:send_after(Pause, self(), activate_socket),
setelement(Pos, State#state{conn_state = blocked, limit_timer = TRef}, Rl1)
end.
%%--------------------------------------------------------------------
%% Activate Socket
activate_socket(#state{conn_state = blocked}) ->
ok;
activate_socket(#state{transport = Transport,
socket = Socket,
active_n = N}) ->
Transport:setopts(Socket, [{active, N}]).
%%--------------------------------------------------------------------
%% Inc incoming/outgoing stats
inc_incoming_stats(Type) ->
emqx_pd:update_counter(recv_pkt, 1),
case Type == ?PUBLISH of
true ->
emqx_pd:update_counter(recv_msg, 1),
emqx_pd:update_counter(incoming_pubs, 1);
false -> ok
end.
inc_outgoing_stats(Type) ->
emqx_pd:update_counter(send_pkt, 1),
(Type == ?PUBLISH)
andalso emqx_pd:update_counter(send_msg, 1).
%%--------------------------------------------------------------------
%% Ensure stats timer
ensure_stats_timer(State = #state{stats_timer = undefined,
idle_timeout = IdleTimeout}) ->
State#state{stats_timer = emqx_misc:start_timer(IdleTimeout, emit_stats)};
%% disabled or timer existed
ensure_stats_timer(State) -> State.
%%--------------------------------------------------------------------
%% Maybe GC
maybe_gc(_Cnt, _Oct, State = #state{gc_state = undefined}) ->
State;
maybe_gc(Cnt, Oct, State = #state{gc_state = GCSt}) ->
{_, GCSt1} = emqx_gc:run(Cnt, Oct, GCSt),
%% TODO: gc metric?
State#state{gc_state = GCSt1}.
%%--------------------------------------------------------------------
%% Helper functions
-compile({inline, [reply/3]}).
reply(From, Reply, State) ->
{keep_state, State, [{reply, From, Reply}]}.
-compile({inline, [keep_state/1]}).
keep_state(State) ->
{keep_state, State}.
-compile({inline, [shutdown/2]}).
shutdown(Reason, State) ->
stop({shutdown, Reason}, State).
-compile({inline, [stop/2]}).
stop(Reason, State) ->
{stop, Reason, State}.

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_ctl). -module(emqx_ctl).

95
src/emqx_endpoint.erl Normal file
View File

@ -0,0 +1,95 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2019 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_endpoint).
-include("types.hrl").
%% APIs
-export([ new/0
, new/1
]).
-export([ zone/1
, client_id/1
, mountpoint/1
, is_superuser/1
, credentials/1
]).
-export([update/2]).
-export([to_map/1]).
-export_type([endpoint/0]).
-opaque(endpoint() ::
{endpoint,
#{zone := emqx_types:zone(),
peername := emqx_types:peername(),
sockname => emqx_types:peername(),
client_id := emqx_types:client_id(),
username := emqx_types:username(),
peercert := esockd_peercert:peercert(),
is_superuser := boolean(),
mountpoint := maybe(binary()),
ws_cookie := maybe(list()),
password => binary(),
auth_result => emqx_types:auth_result(),
anonymous => boolean(),
atom() => term()
}
}).
-define(Endpoint(M), {endpoint, M}).
-define(Default, #{is_superuser => false,
anonymous => false
}).
-spec(new() -> endpoint()).
new() ->
?Endpoint(?Default).
-spec(new(map()) -> endpoint()).
new(M) when is_map(M) ->
?Endpoint(maps:merge(?Default, M)).
-spec(zone(endpoint()) -> emqx_zone:zone()).
zone(?Endpoint(#{zone := Zone})) ->
Zone.
client_id(?Endpoint(#{client_id := ClientId})) ->
ClientId.
-spec(mountpoint(endpoint()) -> maybe(binary())).
mountpoint(?Endpoint(#{mountpoint := Mountpoint})) ->
Mountpoint;
mountpoint(_) -> undefined.
is_superuser(?Endpoint(#{is_superuser := B})) ->
B.
update(Attrs, ?Endpoint(M)) ->
?Endpoint(maps:merge(M, Attrs)).
credentials(?Endpoint(M)) ->
M. %% TODO: ...
-spec(to_map(endpoint()) -> map()).
to_map(?Endpoint(M)) ->
M.

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,9 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
%% @doc This module is used to garbage clean the flapping records.
-module(emqx_flapping). -module(emqx_flapping).
@ -21,14 +25,12 @@
-export([start_link/0]). -export([start_link/0]).
%% This module is used to garbage clean the flapping records
%% gen_statem callbacks %% gen_statem callbacks
-export([ terminate/3 -export([ init/1
, code_change/4
, init/1
, initialized/3 , initialized/3
, callback_mode/0 , callback_mode/0
, terminate/3
, code_change/4
]). ]).
-define(FLAPPING_TAB, ?MODULE). -define(FLAPPING_TAB, ?MODULE).
@ -37,15 +39,15 @@
-export([check/3]). -export([check/3]).
-record(flapping, -record(flapping, {
{ client_id :: binary() client_id :: binary(),
, check_count :: integer() check_count :: integer(),
, timestamp :: integer() timestamp :: integer()
}). }).
-type(flapping_record() :: #flapping{}). -type(flapping_record() :: #flapping{}).
-type(flapping_state() :: flapping | ok).
-type(flapping_state() :: flapping | ok).
%% @doc This function is used to initialize flapping records %% @doc This function is used to initialize flapping records
%% the expiry time unit is minutes. %% the expiry time unit is minutes.
@ -103,14 +105,14 @@ start_link() ->
gen_statem:start_link({local, ?MODULE}, ?MODULE, [], []). gen_statem:start_link({local, ?MODULE}, ?MODULE, [], []).
init([]) -> init([]) ->
TimerInterval = emqx_config:get_env(flapping_clean_interval, ?default_flapping_clean_interval), Interval = emqx_config:get_env(flapping_clean_interval, ?default_flapping_clean_interval),
TabOpts = [ public TabOpts = [ public
, set , set
, {keypos, 2} , {keypos, 2}
, {write_concurrency, true} , {write_concurrency, true}
, {read_concurrency, true}], , {read_concurrency, true}],
ok = emqx_tables:new(?FLAPPING_TAB, TabOpts), ok = emqx_tables:new(?FLAPPING_TAB, TabOpts),
{ok, initialized, #{timer_interval => TimerInterval}}. {ok, initialized, #{timer_interval => Interval}}.
callback_mode() -> [state_functions, state_enter]. callback_mode() -> [state_functions, state_enter].
@ -137,3 +139,4 @@ clean_expired_records() ->
NowTime = emqx_time:now_secs(), NowTime = emqx_time:now_secs(),
MatchSpec = [{{'$1', '$2', '$3'},[{'<', '$3', NowTime}], [true]}], MatchSpec = [{{'$1', '$2', '$3'},[{'<', '$3', NowTime}], [true]}],
ets:select_delete(?FLAPPING_TAB, MatchSpec). ets:select_delete(?FLAPPING_TAB, MatchSpec).

View File

@ -29,22 +29,22 @@
, serialize/2 , serialize/2
]). ]).
-export_type([ options/0
, parse_state/0
, parse_result/0
]).
-type(options() :: #{max_size => 1..?MAX_PACKET_SIZE, -type(options() :: #{max_size => 1..?MAX_PACKET_SIZE,
version => emqx_mqtt_types:version() version => emqx_mqtt:version()
}). }).
-opaque(parse_state() :: {none, options()} | {more, cont_fun()}). -opaque(parse_state() :: {none, options()} | {more, cont_fun()}).
-opaque(parse_result() :: {ok, parse_state()} -opaque(parse_result() :: {ok, parse_state()}
| {ok, emqx_mqtt_types:packet(), binary(), parse_state()}). | {ok, emqx_mqtt:packet(), binary(), parse_state()}).
-type(cont_fun() :: fun((binary()) -> parse_result())). -type(cont_fun() :: fun((binary()) -> parse_result())).
-export_type([ options/0
, parse_state/0
, parse_result/0
]).
-define(none(Opts), {none, Opts}). -define(none(Opts), {none, Opts}).
-define(more(Cont), {more, Cont}). -define(more(Cont), {more, Cont}).
-define(DEFAULT_OPTIONS, -define(DEFAULT_OPTIONS,
@ -385,15 +385,15 @@ parse_binary_data(<<Len:16/big, Data:Len/binary, Rest/binary>>) ->
%% Serialize MQTT Packet %% Serialize MQTT Packet
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-spec(serialize(emqx_mqtt_types:packet()) -> iodata()). -spec(serialize(emqx_mqtt:packet()) -> iodata()).
serialize(Packet) -> serialize(Packet) ->
serialize(Packet, ?DEFAULT_OPTIONS). serialize(Packet, ?MQTT_PROTO_V4).
-spec(serialize(emqx_mqtt_types:packet(), options()) -> iodata()). -spec(serialize(emqx_mqtt:packet(), emqx_mqtt:version()) -> iodata()).
serialize(#mqtt_packet{header = Header, serialize(#mqtt_packet{header = Header,
variable = Variable, variable = Variable,
payload = Payload}, Options) when is_map(Options) -> payload = Payload}, Ver) ->
serialize(Header, serialize_variable(Variable, merge_opts(Options)), serialize_payload(Payload)). serialize(Header, serialize_variable(Variable, Ver), serialize_payload(Payload)).
serialize(#mqtt_packet_header{type = Type, serialize(#mqtt_packet_header{type = Type,
dup = Dup, dup = Dup,
@ -420,7 +420,7 @@ serialize_variable(#mqtt_packet_connect{
will_topic = WillTopic, will_topic = WillTopic,
will_payload = WillPayload, will_payload = WillPayload,
username = Username, username = Username,
password = Password}, _Options) -> password = Password}, _Ver) ->
[serialize_binary_data(ProtoName), [serialize_binary_data(ProtoName),
<<(case IsBridge of <<(case IsBridge of
true -> 16#80 + ProtoVer; true -> 16#80 + ProtoVer;
@ -447,14 +447,12 @@ serialize_variable(#mqtt_packet_connect{
serialize_variable(#mqtt_packet_connack{ack_flags = AckFlags, serialize_variable(#mqtt_packet_connack{ack_flags = AckFlags,
reason_code = ReasonCode, reason_code = ReasonCode,
properties = Properties}, properties = Properties}, Ver) ->
#{version := Ver}) ->
[AckFlags, ReasonCode, serialize_properties(Properties, Ver)]; [AckFlags, ReasonCode, serialize_properties(Properties, Ver)];
serialize_variable(#mqtt_packet_publish{topic_name = TopicName, serialize_variable(#mqtt_packet_publish{topic_name = TopicName,
packet_id = PacketId, packet_id = PacketId,
properties = Properties}, properties = Properties}, Ver) ->
#{version := Ver}) ->
[serialize_utf8_string(TopicName), [serialize_utf8_string(TopicName),
if if
PacketId =:= undefined -> <<>>; PacketId =:= undefined -> <<>>;
@ -462,59 +460,54 @@ serialize_variable(#mqtt_packet_publish{topic_name = TopicName,
end, end,
serialize_properties(Properties, Ver)]; serialize_properties(Properties, Ver)];
serialize_variable(#mqtt_packet_puback{packet_id = PacketId}, serialize_variable(#mqtt_packet_puback{packet_id = PacketId}, Ver)
#{version := Ver})
when Ver == ?MQTT_PROTO_V3; Ver == ?MQTT_PROTO_V4 -> when Ver == ?MQTT_PROTO_V3; Ver == ?MQTT_PROTO_V4 ->
<<PacketId:16/big-unsigned-integer>>; <<PacketId:16/big-unsigned-integer>>;
serialize_variable(#mqtt_packet_puback{packet_id = PacketId, serialize_variable(#mqtt_packet_puback{packet_id = PacketId,
reason_code = ReasonCode, reason_code = ReasonCode,
properties = Properties}, properties = Properties},
#{version := ?MQTT_PROTO_V5}) -> ?MQTT_PROTO_V5) ->
[<<PacketId:16/big-unsigned-integer>>, ReasonCode, [<<PacketId:16/big-unsigned-integer>>, ReasonCode,
serialize_properties(Properties, ?MQTT_PROTO_V5)]; serialize_properties(Properties, ?MQTT_PROTO_V5)];
serialize_variable(#mqtt_packet_subscribe{packet_id = PacketId, serialize_variable(#mqtt_packet_subscribe{packet_id = PacketId,
properties = Properties, properties = Properties,
topic_filters = TopicFilters}, topic_filters = TopicFilters}, Ver) ->
#{version := Ver}) ->
[<<PacketId:16/big-unsigned-integer>>, serialize_properties(Properties, Ver), [<<PacketId:16/big-unsigned-integer>>, serialize_properties(Properties, Ver),
serialize_topic_filters(subscribe, TopicFilters, Ver)]; serialize_topic_filters(subscribe, TopicFilters, Ver)];
serialize_variable(#mqtt_packet_suback{packet_id = PacketId, serialize_variable(#mqtt_packet_suback{packet_id = PacketId,
properties = Properties, properties = Properties,
reason_codes = ReasonCodes}, reason_codes = ReasonCodes}, Ver) ->
#{version := Ver}) ->
[<<PacketId:16/big-unsigned-integer>>, serialize_properties(Properties, Ver), [<<PacketId:16/big-unsigned-integer>>, serialize_properties(Properties, Ver),
serialize_reason_codes(ReasonCodes)]; serialize_reason_codes(ReasonCodes)];
serialize_variable(#mqtt_packet_unsubscribe{packet_id = PacketId, serialize_variable(#mqtt_packet_unsubscribe{packet_id = PacketId,
properties = Properties, properties = Properties,
topic_filters = TopicFilters}, topic_filters = TopicFilters}, Ver) ->
#{version := Ver}) ->
[<<PacketId:16/big-unsigned-integer>>, serialize_properties(Properties, Ver), [<<PacketId:16/big-unsigned-integer>>, serialize_properties(Properties, Ver),
serialize_topic_filters(unsubscribe, TopicFilters, Ver)]; serialize_topic_filters(unsubscribe, TopicFilters, Ver)];
serialize_variable(#mqtt_packet_unsuback{packet_id = PacketId, serialize_variable(#mqtt_packet_unsuback{packet_id = PacketId,
properties = Properties, properties = Properties,
reason_codes = ReasonCodes}, reason_codes = ReasonCodes}, Ver) ->
#{version := Ver}) ->
[<<PacketId:16/big-unsigned-integer>>, serialize_properties(Properties, Ver), [<<PacketId:16/big-unsigned-integer>>, serialize_properties(Properties, Ver),
serialize_reason_codes(ReasonCodes)]; serialize_reason_codes(ReasonCodes)];
serialize_variable(#mqtt_packet_disconnect{}, #{version := Ver}) serialize_variable(#mqtt_packet_disconnect{}, Ver)
when Ver == ?MQTT_PROTO_V3; Ver == ?MQTT_PROTO_V4 -> when Ver == ?MQTT_PROTO_V3; Ver == ?MQTT_PROTO_V4 ->
<<>>; <<>>;
serialize_variable(#mqtt_packet_disconnect{reason_code = ReasonCode, serialize_variable(#mqtt_packet_disconnect{reason_code = ReasonCode,
properties = Properties}, properties = Properties},
#{version := Ver = ?MQTT_PROTO_V5}) -> Ver = ?MQTT_PROTO_V5) ->
[ReasonCode, serialize_properties(Properties, Ver)]; [ReasonCode, serialize_properties(Properties, Ver)];
serialize_variable(#mqtt_packet_disconnect{}, _Ver) -> serialize_variable(#mqtt_packet_disconnect{}, _Ver) ->
<<>>; <<>>;
serialize_variable(#mqtt_packet_auth{reason_code = ReasonCode, serialize_variable(#mqtt_packet_auth{reason_code = ReasonCode,
properties = Properties}, properties = Properties},
#{version := Ver = ?MQTT_PROTO_V5}) -> Ver = ?MQTT_PROTO_V5) ->
[ReasonCode, serialize_properties(Properties, Ver)]; [ReasonCode, serialize_properties(Properties, Ver)];
serialize_variable(PacketId, ?MQTT_PROTO_V3) when is_integer(PacketId) -> serialize_variable(PacketId, ?MQTT_PROTO_V3) when is_integer(PacketId) ->

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,13 +12,16 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
%% @doc This module manages an opaque collection of statistics data used to %%--------------------------------------------------------------------
%% force garbage collection on `self()' process when hitting thresholds. %% @doc This module manages an opaque collection of statistics data used
%% to force garbage collection on `self()' process when hitting thresholds.
%% Namely: %% Namely:
%% (1) Total number of messages passed through %% (1) Total number of messages passed through
%% (2) Total data volume passed through %% (2) Total data volume passed through
%% @end %% @end
%%--------------------------------------------------------------------
-module(emqx_gc). -module(emqx_gc).
@ -29,6 +33,8 @@
, reset/1 , reset/1
]). ]).
-export_type([gc_state/0]).
-type(opts() :: #{count => integer(), -type(opts() :: #{count => integer(),
bytes => integer()}). bytes => integer()}).
@ -37,8 +43,6 @@
-opaque(gc_state() :: {?MODULE, st()}). -opaque(gc_state() :: {?MODULE, st()}).
-export_type([gc_state/0]).
-define(GCS(St), {?MODULE, St}). -define(GCS(St), {?MODULE, St}).
-define(disabled, disabled). -define(disabled, disabled).
@ -85,9 +89,9 @@ reset(?GCS(St)) ->
reset(undefined) -> reset(undefined) ->
undefined. undefined.
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Internal functions %% Internal functions
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
-spec(dec(cnt | oct, pos_integer(), st()) -> {boolean(), st()}). -spec(dec(cnt | oct, pos_integer(), st()) -> {boolean(), st()}).
dec(Key, Num, St) -> dec(Key, Num, St) ->

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_gen_mod). -module(emqx_gen_mod).

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
%% @doc Generate global unique id for mqtt message. %% @doc Generate global unique id for mqtt message.
%% %%

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_hooks). -module(emqx_hooks).
@ -21,7 +23,9 @@
-logger_header("[Hooks]"). -logger_header("[Hooks]").
-export([start_link/0, stop/0]). -export([ start_link/0
, stop/0
]).
%% Hooks API %% Hooks API
-export([ add/2 -export([ add/2
@ -42,6 +46,11 @@
, code_change/3 , code_change/3
]). ]).
-export_type([ hookpoint/0
, action/0
, filter/0
]).
%% Multiple callbacks can be registered on a hookpoint. %% Multiple callbacks can be registered on a hookpoint.
%% The execution order depends on the priority value: %% The execution order depends on the priority value:
%% - Callbacks with greater priority values will be run before %% - Callbacks with greater priority values will be run before
@ -54,28 +63,32 @@
-type(action() :: function() | mfa()). -type(action() :: function() | mfa()).
-type(filter() :: function() | mfa()). -type(filter() :: function() | mfa()).
-record(callback, {action :: action(), -record(callback, {
filter :: filter(), action :: action(),
priority :: integer()}). filter :: filter(),
priority :: integer()
}).
-record(hook, {name :: hookpoint(), callbacks :: list(#callback{})}). -record(hook, {
name :: hookpoint(),
-export_type([hookpoint/0, action/0, filter/0]). callbacks :: list(#callback{})
}).
-define(TAB, ?MODULE). -define(TAB, ?MODULE).
-define(SERVER, ?MODULE). -define(SERVER, ?MODULE).
-spec(start_link() -> startlink_ret()). -spec(start_link() -> startlink_ret()).
start_link() -> start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], [{hibernate_after, 1000}]). gen_server:start_link({local, ?SERVER},
?MODULE, [], [{hibernate_after, 1000}]).
-spec(stop() -> ok). -spec(stop() -> ok).
stop() -> stop() ->
gen_server:stop(?SERVER, normal, infinity). gen_server:stop(?SERVER, normal, infinity).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Hooks API %% Hooks API
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% @doc Register a callback %% @doc Register a callback
-spec(add(hookpoint(), action() | #callback{}) -> ok_or_error(already_exists)). -spec(add(hookpoint(), action() | #callback{}) -> ok_or_error(already_exists)).
@ -113,7 +126,6 @@ run(HookPoint, Args) ->
run_fold(HookPoint, Args, Acc) -> run_fold(HookPoint, Args, Acc) ->
do_run_fold(lookup(HookPoint), Args, Acc). do_run_fold(lookup(HookPoint), Args, Acc).
do_run([#callback{action = Action, filter = Filter} | Callbacks], Args) -> do_run([#callback{action = Action, filter = Filter} | Callbacks], Args) ->
case filter_passed(Filter, Args) andalso execute(Action, Args) of case filter_passed(Filter, Args) andalso execute(Action, Args) of
%% stop the hook chain and return %% stop the hook chain and return
@ -165,12 +177,12 @@ lookup(HookPoint) ->
[] -> [] [] -> []
end. end.
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% gen_server callbacks %% gen_server callbacks
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
init([]) -> init([]) ->
ok = emqx_tables:new(?TAB, [{keypos, #hook.name}, {read_concurrency, true}, protected]), ok = emqx_tables:new(?TAB, [{keypos, #hook.name}, {read_concurrency, true}]),
{ok, #{}}. {ok, #{}}.
handle_call({add, HookPoint, Callback = #callback{action = Action}}, _From, State) -> handle_call({add, HookPoint, Callback = #callback{action = Action}}, _From, State) ->

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_inflight). -module(emqx_inflight).
@ -31,6 +33,8 @@
, window/1 , window/1
]). ]).
-export_type([inflight/0]).
-type(key() :: term()). -type(key() :: term()).
-type(max_size() :: pos_integer()). -type(max_size() :: pos_integer()).
@ -38,17 +42,16 @@
-opaque(inflight() :: {?MODULE, max_size(), gb_trees:tree()}). -opaque(inflight() :: {?MODULE, max_size(), gb_trees:tree()}).
-define(Inflight(Tree), {?MODULE, _MaxSize, Tree}). -define(Inflight(Tree), {?MODULE, _MaxSize, Tree}).
-define(Inflight(MaxSize, Tree), {?MODULE, MaxSize, (Tree)}). -define(Inflight(MaxSize, Tree), {?MODULE, MaxSize, (Tree)}).
-export_type([inflight/0]). %%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% APIs %% APIs
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
-spec(new(non_neg_integer()) -> inflight()). -spec(new(non_neg_integer()) -> inflight()).
new(MaxSize) when MaxSize >= 0 -> new(MaxSize) when MaxSize >= 0 ->
{?MODULE, MaxSize, gb_trees:empty()}. ?Inflight(MaxSize, gb_trees:empty()).
-spec(contain(key(), inflight()) -> boolean()). -spec(contain(key(), inflight()) -> boolean()).
contain(Key, ?Inflight(Tree)) -> contain(Key, ?Inflight(Tree)) ->

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_json). -module(emqx_json).
@ -35,12 +37,12 @@ encode(Term, Opts) ->
jsx:encode(Term, Opts). jsx:encode(Term, Opts).
-spec(safe_encode(jsx:json_term()) -spec(safe_encode(jsx:json_term())
-> {ok, jsx:json_text()} | {error, term()}). -> {ok, jsx:json_text()} | {error, Reason :: term()}).
safe_encode(Term) -> safe_encode(Term) ->
safe_encode(Term, []). safe_encode(Term, []).
-spec(safe_encode(jsx:json_term(), jsx_to_json:config()) -spec(safe_encode(jsx:json_term(), jsx_to_json:config())
-> {ok, jsx:json_text()} | {error, term()}). -> {ok, jsx:json_text()} | {error, Reason :: term()}).
safe_encode(Term, Opts) -> safe_encode(Term, Opts) ->
try encode(Term, Opts) of try encode(Term, Opts) of
Json -> {ok, Json} Json -> {ok, Json}
@ -58,12 +60,12 @@ decode(Json, Opts) ->
jsx:decode(Json, Opts). jsx:decode(Json, Opts).
-spec(safe_decode(jsx:json_text()) -spec(safe_decode(jsx:json_text())
-> {ok, jsx:json_term()} | {error, term()}). -> {ok, jsx:json_term()} | {error, Reason :: term()}).
safe_decode(Json) -> safe_decode(Json) ->
safe_decode(Json, []). safe_decode(Json, []).
-spec(safe_decode(jsx:json_text(), jsx_to_json:config()) -spec(safe_decode(jsx:json_text(), jsx_to_json:config())
-> {ok, jsx:json_term()} | {error, term()}). -> {ok, jsx:json_term()} | {error, Reason :: term()}).
safe_decode(Json, Opts) -> safe_decode(Json, Opts) ->
try decode(Json, Opts) of try decode(Json, Opts) of
Term -> {ok, Term} Term -> {ok, Term}

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_keepalive). -module(emqx_keepalive).
@ -20,15 +22,22 @@
, cancel/1 , cancel/1
]). ]).
-record(keepalive, {statfun, statval, tsec, tmsg, tref, repeat = 0}). -export_type([keepalive/0]).
-record(keepalive, {
statfun,
statval,
tsec,
tmsg,
tref,
repeat = 0
}).
-opaque(keepalive() :: #keepalive{}). -opaque(keepalive() :: #keepalive{}).
-export_type([keepalive/0]). %%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% APIs %% APIs
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% @doc Start a keepalive %% @doc Start a keepalive
-spec(start(fun(), integer(), any()) -> {ok, keepalive()} | {error, term()}). -spec(start(fun(), integer(), any()) -> {ok, keepalive()} | {error, term()}).
@ -79,3 +88,4 @@ cancel(_) ->
timer(Secs, Msg) -> timer(Secs, Msg) ->
erlang:send_after(timer:seconds(Secs), self(), Msg). erlang:send_after(timer:seconds(Secs), self(), Msg).

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_kernel_sup). -module(emqx_kernel_sup).

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
%% @doc Start/Stop MQTT listeners. %% @doc Start/Stop MQTT listeners.
-module(emqx_listeners). -module(emqx_listeners).
@ -33,9 +35,9 @@
-type(listener() :: {esockd:proto(), esockd:listen_on(), [esockd:option()]}). -type(listener() :: {esockd:proto(), esockd:listen_on(), [esockd:option()]}).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% APIs %% APIs
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% @doc Start all listeners. %% @doc Start all listeners.
-spec(start() -> ok). -spec(start() -> ok).
@ -73,7 +75,7 @@ start_listener(Proto, ListenOn, Options) when Proto == https; Proto == wss ->
start_mqtt_listener(Name, ListenOn, Options) -> start_mqtt_listener(Name, ListenOn, Options) ->
SockOpts = esockd:parse_opt(Options), SockOpts = esockd:parse_opt(Options),
esockd:open(Name, ListenOn, merge_default(SockOpts), esockd:open(Name, ListenOn, merge_default(SockOpts),
{emqx_channel, start_link, [Options -- SockOpts]}). {emqx_connection, start_link, [Options -- SockOpts]}).
start_http_listener(Start, Name, ListenOn, RanchOpts, ProtoOpts) -> start_http_listener(Start, Name, ListenOn, RanchOpts, ProtoOpts) ->
Start(Name, with_port(ListenOn, RanchOpts), ProtoOpts). Start(Name, with_port(ListenOn, RanchOpts), ProtoOpts).
@ -82,7 +84,7 @@ mqtt_path(Options) ->
proplists:get_value(mqtt_path, Options, "/mqtt"). proplists:get_value(mqtt_path, Options, "/mqtt").
ws_opts(Options) -> ws_opts(Options) ->
Dispatch = cowboy_router:compile([{'_', [{mqtt_path(Options), emqx_ws_channel, Options}]}]), Dispatch = cowboy_router:compile([{'_', [{mqtt_path(Options), emqx_ws_connection, Options}]}]),
#{env => #{dispatch => Dispatch}, proxy_header => proplists:get_value(proxy_protocol, Options, false)}. #{env => #{dispatch => Dispatch}, proxy_header => proplists:get_value(proxy_protocol, Options, false)}.
ranch_opts(Options) -> ranch_opts(Options) ->
@ -167,3 +169,4 @@ format({Addr, Port}) when is_list(Addr) ->
io_lib:format("~s:~w", [Addr, Port]); io_lib:format("~s:~w", [Addr, Port]);
format({Addr, Port}) when is_tuple(Addr) -> format({Addr, Port}) when is_tuple(Addr) ->
io_lib:format("~s:~w", [esockd_net:ntoab(Addr), Port]). io_lib:format("~s:~w", [esockd_net:ntoab(Addr), Port]).

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,10 +12,11 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_logger). -module(emqx_logger).
-compile({no_auto_import,[error/1]}). -compile({no_auto_import, [error/1]}).
%% Logs %% Logs
-export([ debug/1 -export([ debug/1
@ -50,9 +52,9 @@
-export([parse_transform/2]). -export([parse_transform/2]).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% APIs %% APIs
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
debug(Msg) -> debug(Msg) ->
logger:debug(Msg). logger:debug(Msg).
@ -125,9 +127,9 @@ set_log_level(Level) ->
parse_transform(AST, _Opts) -> parse_transform(AST, _Opts) ->
trans(AST, "", []). trans(AST, "", []).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Internal Functions %% Internal Functions
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
log_hanlder_info(#{id := Id, level := Level, module := logger_std_h, log_hanlder_info(#{id := Id, level := Level, module := logger_std_h,
config := #{type := Type}}) when Type =:= standard_io; config := #{type := Type}}) when Type =:= standard_io;

View File

@ -34,18 +34,22 @@
-define(IS_STRING(String), -define(IS_STRING(String),
(is_list(String) orelse is_binary(String))). (is_list(String) orelse is_binary(String))).
%%%----------------------------------------------------------------- %%--------------------------------------------------------------------
%%% Types %% Types
-type config() :: #{chars_limit => pos_integer() | unlimited,
depth => pos_integer() | unlimited, -type(config() :: #{chars_limit => pos_integer() | unlimited,
max_size => pos_integer() | unlimited, depth => pos_integer() | unlimited,
report_cb => logger:report_cb(), max_size => pos_integer() | unlimited,
quit => template()}. report_cb => logger:report_cb(),
-type template() :: [metakey() | {metakey(),template(),template()} | string()]. quit => template()}).
-type metakey() :: atom() | [atom()].
-type(template() :: [metakey() | {metakey(),template(),template()} | string()]).
-type(metakey() :: atom() | [atom()]).
%%--------------------------------------------------------------------
%% API
%%%-----------------------------------------------------------------
%%% API
-spec format(LogEvent,Config) -> unicode:chardata() when -spec format(LogEvent,Config) -> unicode:chardata() when
LogEvent :: logger:log_event(), LogEvent :: logger:log_event(),
Config :: config(). Config :: config().

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_logger_handler). -module(emqx_logger_handler).
@ -19,8 +21,8 @@
-export([init/0]). -export([init/0]).
init() -> init() ->
logger:add_handler(emqx_logger_handler, logger:add_handler(emqx_logger_handler,
emqx_logger_handler, emqx_logger_handler,
#{level => error, #{level => error,
filters => [{easy_filter, {fun filter_by_level/2, []}}], filters => [{easy_filter, {fun filter_by_level/2, []}}],
filters_default => stop}). filters_default => stop}).
@ -41,3 +43,4 @@ filter_by_level(LogEvent = #{level := error}, _Extra) ->
LogEvent; LogEvent;
filter_by_level(_LogEvent, _Extra) -> filter_by_level(_LogEvent, _Extra) ->
stop. stop.

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_message). -module(emqx_message).

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_metrics). -module(emqx_metrics).
@ -58,12 +60,12 @@
, code_change/3 , code_change/3
]). ]).
-export_type([metric_idx/0]).
-opaque(metric_idx() :: 1..1024). -opaque(metric_idx() :: 1..1024).
-type(metric_name() :: atom() | string() | binary()). -type(metric_name() :: atom() | string() | binary()).
-export_type([metric_idx/0]).
-define(MAX_SIZE, 1024). -define(MAX_SIZE, 1024).
-define(RESERVED_IDX, 256). -define(RESERVED_IDX, 256).
-define(TAB, ?MODULE). -define(TAB, ?MODULE).
@ -149,9 +151,9 @@ start_link() ->
stop() -> stop() ->
gen_server:stop(?SERVER). gen_server:stop(?SERVER).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Metrics API %% Metrics API
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
-spec(new(metric_name()) -> ok). -spec(new(metric_name()) -> ok).
new(Name) -> new(Name) ->
@ -255,9 +257,9 @@ update_counter(Name, Value) ->
end, end,
counters:add(CRef, CIdx, Value). counters:add(CRef, CIdx, Value).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Inc Received/Sent metrics %% Inc Received/Sent metrics
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% @doc Inc packets received. %% @doc Inc packets received.
-spec(inc_recv(emqx_mqtt_types:packet()) -> ok). -spec(inc_recv(emqx_mqtt_types:packet()) -> ok).
@ -343,9 +345,9 @@ do_inc_sent(?PACKET(?AUTH)) ->
do_inc_sent(_Packet) -> do_inc_sent(_Packet) ->
ignore. ignore.
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% gen_server callbacks %% gen_server callbacks
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
init([]) -> init([]) ->
% Create counters array % Create counters array
@ -395,9 +397,9 @@ terminate(_Reason, _State) ->
code_change(_OldVsn, State, _Extra) -> code_change(_OldVsn, State, _Extra) ->
{ok, State}. {ok, State}.
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Internal functions %% Internal functions
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
reserved_idx('bytes.received') -> 01; reserved_idx('bytes.received') -> 01;
reserved_idx('bytes.sent') -> 02; reserved_idx('bytes.sent') -> 02;

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_misc). -module(emqx_misc).
@ -27,7 +29,14 @@
, conn_proc_mng_policy/1 , conn_proc_mng_policy/1
]). ]).
-export([drain_down/1]). -export([ drain_deliver/1
, drain_down/1
]).
-compile({inline,
[ start_timer/2
, start_timer/3
]}).
%% @doc Merge options %% @doc Merge options
-spec(merge_opts(list(), list()) -> list()). -spec(merge_opts(list(), list()) -> list()).
@ -119,6 +128,16 @@ proc_info(Key) ->
{Key, Value} = erlang:process_info(self(), Key), {Key, Value} = erlang:process_info(self(), Key),
Value. Value.
%% @doc Drain delivers from channel's mailbox.
drain_deliver(Acc) ->
receive
Deliver = {deliver, _Topic, _Msg} ->
drain_deliver([Deliver|Acc])
after 0 ->
lists:reverse(Acc)
end.
%% @doc Drain process down events.
-spec(drain_down(pos_integer()) -> list(pid())). -spec(drain_down(pos_integer()) -> list(pid())).
drain_down(Cnt) when Cnt > 0 -> drain_down(Cnt) when Cnt > 0 ->
drain_down(Cnt, []). drain_down(Cnt, []).

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_mod_acl_internal). -module(emqx_mod_acl_internal).
@ -37,9 +39,9 @@
-type(acl_rules() :: #{publish => [emqx_access_rule:rule()], -type(acl_rules() :: #{publish => [emqx_access_rule:rule()],
subscribe => [emqx_access_rule:rule()]}). subscribe => [emqx_access_rule:rule()]}).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% API %% API
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
load(_Env) -> load(_Env) ->
Rules = rules_from_file(acl_file()), Rules = rules_from_file(acl_file()),
@ -54,9 +56,9 @@ unload(_Env) ->
all_rules() -> all_rules() ->
rules_from_file(acl_file()). rules_from_file(acl_file()).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% ACL callbacks %% ACL callbacks
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% @doc Check ACL %% @doc Check ACL
-spec(check_acl(emqx_types:credentials(), emqx_types:pubsub(), emqx_topic:topic(), -spec(check_acl(emqx_types:credentials(), emqx_types:pubsub(), emqx_topic:topic(),
@ -73,9 +75,9 @@ check_acl(Credentials, PubSub, Topic, _AclResult, Rules) ->
reload_acl() -> reload_acl() ->
unload([]), load([]). unload([]), load([]).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Internal Functions %% Internal Functions
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
acl_file() -> acl_file() ->
emqx_config:get_env(acl_file). emqx_config:get_env(acl_file).

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_mod_presence). -module(emqx_mod_presence).
@ -33,9 +35,9 @@
-define(ATTR_KEYS, [clean_start, proto_ver, proto_name, keepalive]). -define(ATTR_KEYS, [clean_start, proto_ver, proto_name, keepalive]).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% APIs %% APIs
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
load(Env) -> load(Env) ->
emqx_hooks:add('client.connected', fun ?MODULE:on_client_connected/4, [Env]), emqx_hooks:add('client.connected', fun ?MODULE:on_client_connected/4, [Env]),

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_mod_rewrite). -module(emqx_mod_rewrite).

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_mod_subscription). -module(emqx_mod_subscription).

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,11 +12,14 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_mod_sup). -module(emqx_mod_sup).
-behaviour(supervisor). -behaviour(supervisor).
-include("types.hrl").
-export([ start_link/0 -export([ start_link/0
, start_child/1 , start_child/1
, start_child/2 , start_child/2
@ -25,8 +29,14 @@
-export([init/1]). -export([init/1]).
%% Helper macro for declaring children of supervisor %% Helper macro for declaring children of supervisor
-define(CHILD(Mod, Type), {Mod, {Mod, start_link, []}, permanent, 5000, Type, [Mod]}). -define(CHILD(Mod, Type), #{id => Mod,
start => {Mod, start_link, []},
restart => permanent,
shutdown => 5000,
type => Type,
modules => [Mod]}).
-spec(start_link() -> startlink_ret()).
start_link() -> start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []). supervisor:start_link({local, ?MODULE}, ?MODULE, []).
@ -39,13 +49,14 @@ start_child(Mod, Type) when is_atom(Mod) andalso is_atom(Type) ->
-spec(stop_child(any()) -> ok | {error, term()}). -spec(stop_child(any()) -> ok | {error, term()}).
stop_child(ChildId) -> stop_child(ChildId) ->
case supervisor:terminate_child(?MODULE, ChildId) of case supervisor:terminate_child(?MODULE, ChildId) of
ok -> supervisor:delete_child(?MODULE, ChildId); ok -> supervisor:delete_child(?MODULE, ChildId);
Error -> Error Error -> Error
end. end.
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Supervisor callbacks %% Supervisor callbacks
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
init([]) -> init([]) ->
{ok, {{one_for_one, 10, 100}, []}}. {ok, {{one_for_one, 10, 100}, []}}.

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_modules). -module(emqx_modules).
@ -22,20 +24,25 @@
, unload/0 , unload/0
]). ]).
%% @doc Load all the extended modules.
-spec(load() -> ok). -spec(load() -> ok).
load() -> load() ->
ok = emqx_mod_acl_internal:load([]), ok = emqx_mod_acl_internal:load([]),
lists:foreach( lists:foreach(fun load/1, modules()).
fun({Mod, Env}) ->
ok = Mod:load(Env),
?LOG(info, "Load ~s module successfully.", [Mod])
end, emqx_config:get_env(modules, [])).
load({Mod, Env}) ->
ok = Mod:load(Env),
?LOG(info, "Load ~s module successfully.", [Mod]).
modules() ->
emqx_config:get_env(modules, []).
%% @doc Unload all the extended modules.
-spec(unload() -> ok). -spec(unload() -> ok).
unload() -> unload() ->
ok = emqx_mod_acl_internal:unload([]), ok = emqx_mod_acl_internal:unload([]),
lists:foreach( lists:foreach(fun unload/1, modules()).
fun({Mod, Env}) ->
Mod:unload(Env) end, unload({Mod, Env}) ->
emqx_config:get_env(modules, [])). Mod:unload(Env).

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,27 +12,26 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_mountpoint). -module(emqx_mountpoint).
-include("emqx.hrl"). -include("emqx.hrl").
-include("logger.hrl"). -include("logger.hrl").
-logger_header("[Mountpoint]").
-export([ mount/2 -export([ mount/2
, unmount/2 , unmount/2
]). ]).
-export([replvar/2]). -export([replvar/2]).
-type(mountpoint() :: binary()).
-export_type([mountpoint/0]). -export_type([mountpoint/0]).
%%------------------------------------------------------------------------------ -type(mountpoint() :: binary()).
%%--------------------------------------------------------------------
%% APIs %% APIs
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
mount(undefined, Any) -> mount(undefined, Any) ->
Any; Any;
@ -39,7 +39,8 @@ mount(MountPoint, Msg = #message{topic = Topic}) ->
Msg#message{topic = <<MountPoint/binary, Topic/binary>>}; Msg#message{topic = <<MountPoint/binary, Topic/binary>>};
mount(MountPoint, TopicFilters) when is_list(TopicFilters) -> mount(MountPoint, TopicFilters) when is_list(TopicFilters) ->
[{<<MountPoint/binary, Topic/binary>>, SubOpts} || {Topic, SubOpts} <- TopicFilters]. [{<<MountPoint/binary, Topic/binary>>, SubOpts}
|| {Topic, SubOpts} <- TopicFilters].
unmount(undefined, Msg) -> unmount(undefined, Msg) ->
Msg; Msg;
@ -47,8 +48,7 @@ unmount(MountPoint, Msg = #message{topic = Topic}) ->
try split_binary(Topic, byte_size(MountPoint)) of try split_binary(Topic, byte_size(MountPoint)) of
{MountPoint, Topic1} -> Msg#message{topic = Topic1} {MountPoint, Topic1} -> Msg#message{topic = Topic1}
catch catch
_Error:Reason -> error:badarg->
?LOG(error, "Unmount error : ~p", [Reason]),
Msg Msg
end. end.

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,16 +12,32 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_mqtt_types). %% MQTT Types
-module(emqx_mqtt).
-include("emqx_mqtt.hrl"). -include("emqx_mqtt.hrl").
-export_type([version/0, qos/0, qos_name/0]). -export_type([ version/0
-export_type([connack/0, reason_code/0]). , qos/0
-export_type([properties/0, subopts/0]). , qos_name/0
]).
-export_type([ connack/0
, reason_code/0
]).
-export_type([ properties/0
, subopts/0
]).
-export_type([topic_filters/0]). -export_type([topic_filters/0]).
-export_type([packet_id/0, packet_type/0, packet/0]).
-export_type([ packet_id/0
, packet_type/0
, packet/0
]).
-type(qos() :: ?QOS_0 | ?QOS_1 | ?QOS_2). -type(qos() :: ?QOS_0 | ?QOS_1 | ?QOS_2).
-type(version() :: ?MQTT_PROTO_V3 | ?MQTT_PROTO_V4 | ?MQTT_PROTO_V5). -type(version() :: ?MQTT_PROTO_V3 | ?MQTT_PROTO_V4 | ?MQTT_PROTO_V5).
@ -38,6 +55,6 @@
qos := qos(), qos := qos(),
rc => reason_code() rc => reason_code()
}). }).
-type(topic_filters() :: [{emqx_topic:topic(), subopts()}]). -type(topic_filters() :: list({emqx_topic:topic(), subopts()})).
-type(packet() :: #mqtt_packet{}). -type(packet() :: #mqtt_packet{}).

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
%% @doc MQTTv5 capabilities %% @doc MQTTv5 capabilities
-module(emqx_mqtt_caps). -module(emqx_mqtt_caps).
@ -35,8 +37,6 @@
mqtt_shared_subscription => boolean(), mqtt_shared_subscription => boolean(),
mqtt_wildcard_subscription => boolean()}). mqtt_wildcard_subscription => boolean()}).
-export_type([caps/0]).
-define(UNLIMITED, 0). -define(UNLIMITED, 0).
-define(DEFAULT_CAPS, [{max_packet_size, ?MAX_PACKET_SIZE}, -define(DEFAULT_CAPS, [{max_packet_size, ?MAX_PACKET_SIZE},

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
%% @doc MQTT5 Properties %% @doc MQTT5 Properties
-module(emqx_mqtt_props). -module(emqx_mqtt_props).

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,7 +12,9 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
%%--------------------------------------------------------------------
%% @doc A Simple in-memory message queue. %% @doc A Simple in-memory message queue.
%% %%
%% Notice that MQTT is not a (on-disk) persistent messaging queue. %% Notice that MQTT is not a (on-disk) persistent messaging queue.
@ -42,6 +45,7 @@
%% unless `max_len' is set to `0' which implies (`infinity'). %% unless `max_len' is set to `0' which implies (`infinity').
%% %%
%% @end %% @end
%%--------------------------------------------------------------------
-module(emqx_mqueue). -module(emqx_mqueue).

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_os_mon). -module(emqx_os_mon).
@ -18,19 +20,10 @@
-include("logger.hrl"). -include("logger.hrl").
-logger_header("[OS Monitor]"). -logger_header("[OS_MON]").
-export([start_link/1]). -export([start_link/1]).
%% gen_server callbacks
-export([ init/1
, handle_call/3
, handle_cast/2
, handle_info/2
, terminate/2
, code_change/3
]).
-export([ get_cpu_check_interval/0 -export([ get_cpu_check_interval/0
, set_cpu_check_interval/1 , set_cpu_check_interval/1
, get_cpu_high_watermark/0 , get_cpu_high_watermark/0
@ -45,20 +38,29 @@
, set_procmem_high_watermark/1 , set_procmem_high_watermark/1
]). ]).
-define(OS_MON, ?MODULE). %% gen_server callbacks
-export([ init/1
, handle_call/3
, handle_cast/2
, handle_info/2
, terminate/2
, code_change/3
]).
-define(compat_windows(Expression), case os:type() of -define(compat_windows(Expression), case os:type() of
{win32, nt} -> windows; {win32, nt} -> windows;
_Unix -> Expression _Unix -> Expression
end). end).
%%------------------------------------------------------------------------------ -define(OS_MON, ?MODULE).
%% API
%%------------------------------------------------------------------------------
start_link(Opts) -> start_link(Opts) ->
gen_server:start_link({local, ?OS_MON}, ?MODULE, [Opts], []). gen_server:start_link({local, ?OS_MON}, ?MODULE, [Opts], []).
%%--------------------------------------------------------------------
%% API
%%--------------------------------------------------------------------
get_cpu_check_interval() -> get_cpu_check_interval() ->
call(get_cpu_check_interval). call(get_cpu_check_interval).
@ -95,9 +97,12 @@ get_procmem_high_watermark() ->
set_procmem_high_watermark(Float) -> set_procmem_high_watermark(Float) ->
memsup:set_procmem_high_watermark(Float). memsup:set_procmem_high_watermark(Float).
%%------------------------------------------------------------------------------ call(Req) ->
gen_server:call(?OS_MON, Req, infinity).
%%--------------------------------------------------------------------
%% gen_server callbacks %% gen_server callbacks
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
init([Opts]) -> init([Opts]) ->
_ = ?compat_windows(cpu_sup:util()), _ = ?compat_windows(cpu_sup:util()),
@ -112,49 +117,59 @@ init([Opts]) ->
handle_call(get_cpu_check_interval, _From, State) -> handle_call(get_cpu_check_interval, _From, State) ->
{reply, maps:get(cpu_check_interval, State, undefined), State}; {reply, maps:get(cpu_check_interval, State, undefined), State};
handle_call({set_cpu_check_interval, Seconds}, _From, State) -> handle_call({set_cpu_check_interval, Seconds}, _From, State) ->
{reply, ok, State#{cpu_check_interval := Seconds}}; {reply, ok, State#{cpu_check_interval := Seconds}};
handle_call(get_cpu_high_watermark, _From, State) -> handle_call(get_cpu_high_watermark, _From, State) ->
{reply, maps:get(cpu_high_watermark, State, undefined), State}; {reply, maps:get(cpu_high_watermark, State, undefined), State};
handle_call({set_cpu_high_watermark, Float}, _From, State) -> handle_call({set_cpu_high_watermark, Float}, _From, State) ->
{reply, ok, State#{cpu_high_watermark := Float}}; {reply, ok, State#{cpu_high_watermark := Float}};
handle_call(get_cpu_low_watermark, _From, State) -> handle_call(get_cpu_low_watermark, _From, State) ->
{reply, maps:get(cpu_low_watermark, State, undefined), State}; {reply, maps:get(cpu_low_watermark, State, undefined), State};
handle_call({set_cpu_low_watermark, Float}, _From, State) -> handle_call({set_cpu_low_watermark, Float}, _From, State) ->
{reply, ok, State#{cpu_low_watermark := Float}}; {reply, ok, State#{cpu_low_watermark := Float}};
handle_call(_Request, _From, State) -> handle_call(Req, _From, State) ->
{reply, ok, State}. ?LOG(error, "Unexpected call: ~p", [Req]),
{reply, ignored, State}.
handle_cast(_Request, State) -> handle_cast(Msg, State) ->
?LOG(error, "Unexpected cast: ~p", [Msg]),
{noreply, State}. {noreply, State}.
handle_info({timeout, Timer, check}, State = #{timer := Timer, handle_info({timeout, Timer, check}, State = #{timer := Timer,
cpu_high_watermark := CPUHighWatermark, cpu_high_watermark := CPUHighWatermark,
cpu_low_watermark := CPULowWatermark, cpu_low_watermark := CPULowWatermark,
is_cpu_alarm_set := IsCPUAlarmSet}) -> is_cpu_alarm_set := IsCPUAlarmSet}) ->
case ?compat_windows(cpu_sup:util()) of NState = case ?compat_windows(cpu_sup:util()) of
0 -> 0 ->
{noreply, State#{timer := undefined}}; State#{timer := undefined};
{error, Reason} -> {error, Reason} ->
?LOG(error, "Failed to get cpu utilization: ~p", [Reason]), ?LOG(error, "Failed to get cpu utilization: ~p", [Reason]),
{noreply, ensure_check_timer(State)}; ensure_check_timer(State);
windows -> windows ->
{noreply, State}; State;
Busy when Busy / 100 >= CPUHighWatermark -> Busy when Busy / 100 >= CPUHighWatermark ->
alarm_handler:set_alarm({cpu_high_watermark, Busy}), alarm_handler:set_alarm({cpu_high_watermark, Busy}),
{noreply, ensure_check_timer(State#{is_cpu_alarm_set := true})}; ensure_check_timer(State#{is_cpu_alarm_set := true});
Busy when Busy / 100 < CPULowWatermark -> Busy when Busy / 100 < CPULowWatermark ->
case IsCPUAlarmSet of case IsCPUAlarmSet of
true -> alarm_handler:clear_alarm(cpu_high_watermark); true -> alarm_handler:clear_alarm(cpu_high_watermark);
false -> ok false -> ok
end, end,
{noreply, ensure_check_timer(State#{is_cpu_alarm_set := false})}; ensure_check_timer(State#{is_cpu_alarm_set := false});
_Busy -> _Busy ->
{noreply, ensure_check_timer(State)} ensure_check_timer(State)
end. end,
{noreply, NState};
handle_info(Info, State) ->
?LOG(error, "Unexpected info: ~p", [Info]),
{noreply, State}.
terminate(_Reason, #{timer := Timer}) -> terminate(_Reason, #{timer := Timer}) ->
emqx_misc:cancel_timer(Timer). emqx_misc:cancel_timer(Timer).
@ -162,11 +177,9 @@ terminate(_Reason, #{timer := Timer}) ->
code_change(_OldVsn, State, _Extra) -> code_change(_OldVsn, State, _Extra) ->
{ok, State}. {ok, State}.
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Internal functions %% Internal functions
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
call(Req) ->
gen_server:call(?OS_MON, Req, infinity).
ensure_check_timer(State = #{cpu_check_interval := Interval}) -> ensure_check_timer(State = #{cpu_check_interval := Interval}) ->
State#{timer := emqx_misc:start_timer(timer:seconds(Interval), check)}. State#{timer := emqx_misc:start_timer(timer:seconds(Interval), check)}.

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_packet). -module(emqx_packet).
@ -40,10 +42,11 @@ protocol_name(?MQTT_PROTO_V5) ->
type_name(Type) when Type > ?RESERVED andalso Type =< ?AUTH -> type_name(Type) when Type > ?RESERVED andalso Type =< ?AUTH ->
lists:nth(Type, ?TYPE_NAMES). lists:nth(Type, ?TYPE_NAMES).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Validate MQTT Packet %% Validate MQTT Packet
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
-spec(validate(emqx_mqtt_types:packet()) -> true).
validate(?SUBSCRIBE_PACKET(_PacketId, _Properties, [])) -> validate(?SUBSCRIBE_PACKET(_PacketId, _Properties, [])) ->
error(topic_filters_invalid); error(topic_filters_invalid);
validate(?SUBSCRIBE_PACKET(PacketId, Properties, TopicFilters)) -> validate(?SUBSCRIBE_PACKET(PacketId, Properties, TopicFilters)) ->
@ -110,7 +113,8 @@ validate_qos(QoS) when ?QOS_0 =< QoS, QoS =< ?QOS_2 ->
validate_qos(_) -> error(bad_qos). validate_qos(_) -> error(bad_qos).
%% @doc From message to packet %% @doc From message to packet
-spec(from_message(emqx_mqtt_types:packet_id(), emqx_types:message()) -> emqx_mqtt_types:packet()). -spec(from_message(emqx_mqtt_types:packet_id(), emqx_types:message())
-> emqx_mqtt_types:packet()).
from_message(PacketId, #message{qos = QoS, flags = Flags, headers = Headers, from_message(PacketId, #message{qos = QoS, flags = Flags, headers = Headers,
topic = Topic, payload = Payload}) -> topic = Topic, payload = Payload}) ->
Flags1 = if Flags =:= undefined -> Flags1 = if Flags =:= undefined ->

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
%% @doc The utility functions for erlang process dictionary. %% @doc The utility functions for erlang process dictionary.
-module(emqx_pd). -module(emqx_pd).
@ -22,6 +24,12 @@
, reset_counter/1 , reset_counter/1
]). ]).
-compile({inline,
[ update_counter/2
, get_counter/1
, reset_counter/1
]}).
-type(key() :: term()). -type(key() :: term()).
-spec(update_counter(key(), number()) -> maybe(number())). -spec(update_counter(key(), number()) -> maybe(number())).

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_plugins). -module(emqx_plugins).
@ -30,9 +32,9 @@
, load_expand_plugin/1 , load_expand_plugin/1
]). ]).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% APIs %% APIs
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% @doc Init plugins' config %% @doc Init plugins' config
-spec(init() -> ok). -spec(init() -> ok).
@ -47,8 +49,8 @@ init() ->
init_config(CfgFile) -> init_config(CfgFile) ->
{ok, [AppsEnv]} = file:consult(CfgFile), {ok, [AppsEnv]} = file:consult(CfgFile),
lists:foreach(fun({AppName, Envs}) -> lists:foreach(fun({App, Envs}) ->
[application:set_env(AppName, Par, Val) || {Par, Val} <- Envs] [application:set_env(App, Par, Val) || {Par, Val} <- Envs]
end, AppsEnv). end, AppsEnv).
%% @doc Load all plugins when the broker started. %% @doc Load all plugins when the broker started.

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_pmon). -module(emqx_pmon).
@ -30,12 +32,13 @@
-export([count/1]). -export([count/1]).
-type(pmon() :: {?MODULE, map()}).
-export_type([pmon/0]). -export_type([pmon/0]).
%%------------------------------------------------------------------------------ -opaque(pmon() :: {?MODULE, map()}).
%%--------------------------------------------------------------------
%% APIs %% APIs
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
-spec(new() -> pmon()). -spec(new() -> pmon()).
new() -> new() ->

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_pool). -module(emqx_pool).
@ -47,9 +49,9 @@
-type(task() :: fun() | mfa() | {fun(), Args :: list(any())}). -type(task() :: fun() | mfa() | {fun(), Args :: list(any())}).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% APIs %% APIs
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% @doc Start pool. %% @doc Start pool.
-spec(start_link(atom(), pos_integer()) -> startlink_ret()). -spec(start_link(atom(), pos_integer()) -> startlink_ret()).
@ -87,9 +89,9 @@ cast(Msg) ->
worker() -> worker() ->
gproc_pool:pick_worker(?POOL). gproc_pool:pick_worker(?POOL).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% gen_server callbacks %% gen_server callbacks
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
init([Pool, Id]) -> init([Pool, Id]) ->
true = gproc_pool:connect_worker(Pool, {Pool, Id}), true = gproc_pool:connect_worker(Pool, {Pool, Id}),
@ -123,9 +125,9 @@ terminate(_Reason, #{pool := Pool, id := Id}) ->
code_change(_OldVsn, State, _Extra) -> code_change(_OldVsn, State, _Extra) ->
{ok, State}. {ok, State}.
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Internal functions %% Internal functions
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
run({M, F, A}) -> run({M, F, A}) ->
erlang:apply(M, F, A); erlang:apply(M, F, A);

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_pool_sup). -module(emqx_pool_sup).
@ -44,7 +46,8 @@ spec(ChildId, Args) ->
start_link() -> start_link() ->
start_link(?POOL, random, {?POOL, start_link, []}). start_link(?POOL, random, {?POOL, start_link, []}).
-spec(start_link(atom() | tuple(), atom(), mfa()) -> {ok, pid()} | {error, term()}). -spec(start_link(atom() | tuple(), atom(), mfa())
-> {ok, pid()} | {error, term()}).
start_link(Pool, Type, MFA) -> start_link(Pool, Type, MFA) ->
start_link(Pool, Type, emqx_vm:schedulers(), MFA). start_link(Pool, Type, emqx_vm:schedulers(), MFA).
@ -54,11 +57,16 @@ start_link(Pool, Type, Size, MFA) ->
supervisor:start_link(?MODULE, [Pool, Type, Size, MFA]). supervisor:start_link(?MODULE, [Pool, Type, Size, MFA]).
init([Pool, Type, Size, {M, F, Args}]) -> init([Pool, Type, Size, {M, F, Args}]) ->
ensure_pool(Pool, Type, [{size, Size}]), ok = ensure_pool(Pool, Type, [{size, Size}]),
{ok, {{one_for_one, 10, 3600}, [ {ok, {{one_for_one, 10, 3600}, [
begin begin
ensure_pool_worker(Pool, {Pool, I}, I), ensure_pool_worker(Pool, {Pool, I}, I),
{{M, I}, {M, F, [Pool, I | Args]}, transient, 5000, worker, [M]} #{id => {M, I},
start => {M, F, [Pool, I | Args]},
restart => transient,
shutdown => 5000,
type => worker,
modules => [M]}
end || I <- lists:seq(1, Size)]}}. end || I <- lists:seq(1, Size)]}}.
ensure_pool(Pool, Type, Opts) -> ensure_pool(Pool, Type, Opts) ->

View File

@ -57,6 +57,8 @@
, highest/1 , highest/1
]). ]).
-export_type([q/0]).
%%---------------------------------------------------------------------------- %%----------------------------------------------------------------------------
-type(priority() :: integer() | 'infinity'). -type(priority() :: integer() | 'infinity').
@ -64,8 +66,6 @@
-type(pqueue() :: squeue() | {pqueue, [{priority(), squeue()}]}). -type(pqueue() :: squeue() | {pqueue, [{priority(), squeue()}]}).
-type(q() :: pqueue()). -type(q() :: pqueue()).
-export_type([q/0]).
%%---------------------------------------------------------------------------- %%----------------------------------------------------------------------------
-spec(new() -> pqueue()). -spec(new() -> pqueue()).

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_psk). -module(emqx_psk).
@ -35,4 +37,4 @@ lookup(psk, ClientPSKID, _UserState) ->
Except:Error:Stacktrace -> Except:Error:Stacktrace ->
?LOG(error, "Lookup PSK failed, ~p: ~p", [{Except,Error}, Stacktrace]), ?LOG(error, "Lookup PSK failed, ~p: ~p", [{Except,Error}, Stacktrace]),
error error
end. end.

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
%% @doc MQTT5 reason codes %% @doc MQTT5 reason codes
-module(emqx_reason_codes). -module(emqx_reason_codes).
@ -20,6 +22,7 @@
-export([ name/2 -export([ name/2
, text/1 , text/1
, connack_error/1 , connack_error/1
, puback/1
]). ]).
-export([compat/2]). -export([compat/2]).
@ -159,3 +162,6 @@ connack_error(server_busy) -> ?RC_SERVER_BUSY;
connack_error(banned) -> ?RC_BANNED; connack_error(banned) -> ?RC_BANNED;
connack_error(bad_authentication_method) -> ?RC_BAD_AUTHENTICATION_METHOD; connack_error(bad_authentication_method) -> ?RC_BAD_AUTHENTICATION_METHOD;
connack_error(_) -> ?RC_NOT_AUTHORIZED. connack_error(_) -> ?RC_NOT_AUTHORIZED.
puback([]) -> ?RC_NO_MATCHING_SUBSCRIBERS;
puback(L) when is_list(L) -> ?RC_SUCCESS.

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_router). -module(emqx_router).
@ -68,9 +70,9 @@
-define(ROUTE, emqx_route). -define(ROUTE, emqx_route).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Mnesia bootstrap %% Mnesia bootstrap
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
mnesia(boot) -> mnesia(boot) ->
ok = ekka_mnesia:create_table(?ROUTE, [ ok = ekka_mnesia:create_table(?ROUTE, [
@ -83,18 +85,18 @@ mnesia(boot) ->
mnesia(copy) -> mnesia(copy) ->
ok = ekka_mnesia:copy_table(?ROUTE). ok = ekka_mnesia:copy_table(?ROUTE).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Start a router %% Start a router
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
-spec(start_link(atom(), pos_integer()) -> startlink_ret()). -spec(start_link(atom(), pos_integer()) -> startlink_ret()).
start_link(Pool, Id) -> start_link(Pool, Id) ->
gen_server:start_link({local, emqx_misc:proc_name(?MODULE, Id)}, gen_server:start_link({local, emqx_misc:proc_name(?MODULE, Id)},
?MODULE, [Pool, Id], [{hibernate_after, 1000}]). ?MODULE, [Pool, Id], [{hibernate_after, 1000}]).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Route APIs %% Route APIs
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
-spec(add_route(emqx_topic:topic()) -> ok | {error, term()}). -spec(add_route(emqx_topic:topic()) -> ok | {error, term()}).
add_route(Topic) when is_binary(Topic) -> add_route(Topic) when is_binary(Topic) ->
@ -183,9 +185,9 @@ call(Router, Msg) ->
pick(Topic) -> pick(Topic) ->
gproc_pool:pick_worker(router_pool, Topic). gproc_pool:pick_worker(router_pool, Topic).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% gen_server callbacks %% gen_server callbacks
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
init([Pool, Id]) -> init([Pool, Id]) ->
true = gproc_pool:connect_worker(Pool, {Pool, Id}), true = gproc_pool:connect_worker(Pool, {Pool, Id}),
@ -217,9 +219,9 @@ terminate(_Reason, #{pool := Pool, id := Id}) ->
code_change(_OldVsn, State, _Extra) -> code_change(_OldVsn, State, _Extra) ->
{ok, State}. {ok, State}.
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Internal functions %% Internal functions
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
insert_direct_route(Route) -> insert_direct_route(Route) ->
mnesia:async_dirty(fun mnesia:write/3, [?ROUTE, Route, sticky_write]). mnesia:async_dirty(fun mnesia:write/3, [?ROUTE, Route, sticky_write]).

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_router_helper). -module(emqx_router_helper).
@ -51,9 +53,9 @@
-define(ROUTING_NODE, emqx_routing_node). -define(ROUTING_NODE, emqx_routing_node).
-define(LOCK, {?MODULE, cleanup_routes}). -define(LOCK, {?MODULE, cleanup_routes}).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Mnesia bootstrap %% Mnesia bootstrap
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
mnesia(boot) -> mnesia(boot) ->
ok = ekka_mnesia:create_table(?ROUTING_NODE, [ ok = ekka_mnesia:create_table(?ROUTING_NODE, [
@ -66,9 +68,9 @@ mnesia(boot) ->
mnesia(copy) -> mnesia(copy) ->
ok = ekka_mnesia:copy_table(?ROUTING_NODE). ok = ekka_mnesia:copy_table(?ROUTING_NODE).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% API %% API
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% @doc Starts the router helper %% @doc Starts the router helper
-spec(start_link() -> startlink_ret()). -spec(start_link() -> startlink_ret()).
@ -86,9 +88,9 @@ monitor(Node) when is_atom(Node) ->
false -> mnesia:dirty_write(?ROUTING_NODE, #routing_node{name = Node}) false -> mnesia:dirty_write(?ROUTING_NODE, #routing_node{name = Node})
end. end.
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% gen_server callbacks %% gen_server callbacks
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
init([]) -> init([]) ->
ok = ekka:monitor(membership), ok = ekka:monitor(membership),
@ -154,9 +156,9 @@ terminate(_Reason, _State) ->
code_change(_OldVsn, State, _Extra) -> code_change(_OldVsn, State, _Extra) ->
{ok, State}. {ok, State}.
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Internal functions %% Internal functions
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
stats_fun() -> stats_fun() ->
case ets:info(?ROUTE, size) of case ets:info(?ROUTE, size) of

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_router_sup). -module(emqx_router_sup).

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
%% @doc wrap gen_rpc? %% @doc wrap gen_rpc?
-module(emqx_rpc). -module(emqx_rpc).
@ -32,7 +34,7 @@ cast(Node, Mod, Fun, Args) ->
filter_result(?RPC:cast(Node, Mod, Fun, Args)). filter_result(?RPC:cast(Node, Mod, Fun, Args)).
filter_result(Delivery) -> filter_result(Delivery) ->
case Delivery of case Delivery of
{badrpc, Reason} -> {badrpc, Reason}; {badrpc, Reason} -> {badrpc, Reason};
{badtcp, Reason} -> {badrpc, Reason}; {badtcp, Reason} -> {badrpc, Reason};
_ -> Delivery _ -> Delivery

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_sequence). -module(emqx_sequence).
@ -21,17 +23,17 @@
, delete/1 , delete/1
]). ]).
-export_type([seqid/0]).
-type(key() :: term()). -type(key() :: term()).
-type(name() :: atom()). -type(name() :: atom()).
-type(seqid() :: non_neg_integer()). -type(seqid() :: non_neg_integer()).
-export_type([seqid/0]). %%--------------------------------------------------------------------
%%------------------------------------------------------------------------------
%% APIs %% APIs
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% @doc Create a sequence. %% @doc Create a sequence.
-spec(create(name()) -> ok). -spec(create(name()) -> ok).

File diff suppressed because it is too large Load Diff

View File

@ -1,267 +0,0 @@
%% Copyright (c) 2013-2019 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).
-behaviour(gen_server).
-include("logger.hrl").
-include("types.hrl").
-logger_header("[Session Supervisor]").
-export([start_link/1]).
-export([ start_session/1
, count_sessions/0
]).
%% gen_server callbacks
-export([ init/1
, handle_call/3
, handle_cast/2
, handle_info/2
, terminate/2
, code_change/3
]).
-type(shutdown() :: brutal_kill | infinity | pos_integer()).
-record(state,
{ sessions :: #{pid() => emqx_types:client_id()}
, mfargs :: mfa()
, shutdown :: shutdown()
, clean_down :: fun()
}).
-define(SUP, ?MODULE).
-define(BATCH_EXIT, 100000).
%% @doc Start session supervisor.
-spec(start_link(map()) -> startlink_ret()).
start_link(SessSpec) when is_map(SessSpec) ->
gen_server:start_link({local, ?SUP}, ?MODULE, [SessSpec], []).
%%------------------------------------------------------------------------------
%% API
%%------------------------------------------------------------------------------
%% @doc Start a session.
-spec(start_session(map()) -> startlink_ret()).
start_session(SessAttrs) ->
gen_server:call(?SUP, {start_session, SessAttrs}, infinity).
%% @doc Count sessions.
-spec(count_sessions() -> non_neg_integer()).
count_sessions() ->
gen_server:call(?SUP, count_sessions, infinity).
%%------------------------------------------------------------------------------
%% gen_server callbacks
%%------------------------------------------------------------------------------
init([Spec]) ->
process_flag(trap_exit, true),
MFA = maps:get(start, Spec),
Shutdown = maps:get(shutdown, Spec, brutal_kill),
CleanDown = maps:get(clean_down, Spec, undefined),
State = #state{sessions = #{},
mfargs = MFA,
shutdown = Shutdown,
clean_down = CleanDown
},
{ok, State}.
handle_call({start_session, SessAttrs = #{client_id := ClientId}}, _From,
State = #state{sessions = SessMap, mfargs = {M, F, Args}}) ->
try erlang:apply(M, F, [SessAttrs | Args]) of
{ok, Pid} ->
reply({ok, Pid}, State#state{sessions = maps:put(Pid, ClientId, SessMap)});
ignore ->
reply(ignore, State);
{error, Reason} ->
reply({error, Reason}, State)
catch
_:Error:Stk ->
?LOG(error, "Failed to start session ~p: ~p, stacktrace:~n~p",
[ClientId, Error, Stk]),
reply({error, Error}, State)
end;
handle_call(count_sessions, _From, State = #state{sessions = SessMap}) ->
{reply, maps:size(SessMap), State};
handle_call(Req, _From, State) ->
?LOG(error, "Unexpected call: ~p", [Req]),
{reply, ignored, State}.
handle_cast(Msg, State) ->
?LOG(error, "Unexpected cast: ~p", [Msg]),
{noreply, State}.
handle_info({'EXIT', Pid, _Reason}, State = #state{sessions = SessMap, clean_down = CleanDown}) ->
SessPids = [Pid | drain_exit(?BATCH_EXIT, [])],
{SessItems, SessMap1} = erase_all(SessPids, SessMap),
(CleanDown =:= undefined)
orelse emqx_pool:async_submit(
fun lists:foreach/2, [CleanDown, SessItems]),
{noreply, State#state{sessions = SessMap1}};
handle_info(Info, State) ->
?LOG(error, "Unexpected info: ~p", [Info]),
{noreply, State}.
terminate(_Reason, State) ->
terminate_children(State).
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%------------------------------------------------------------------------------
%% Internal functions
%%------------------------------------------------------------------------------
drain_exit(0, Acc) ->
lists:reverse(Acc);
drain_exit(Cnt, Acc) ->
receive
{'EXIT', Pid, _Reason} ->
drain_exit(Cnt - 1, [Pid|Acc])
after 0 ->
lists:reverse(Acc)
end.
erase_all(Pids, Map) ->
lists:foldl(
fun(Pid, {Acc, M}) ->
case maps:take(Pid, M) of
{Val, M1} ->
{[{Val, Pid}|Acc], M1};
error ->
{Acc, M}
end
end, {[], Map}, Pids).
terminate_children(State = #state{sessions = SessMap, shutdown = Shutdown}) ->
{Pids, EStack0} = monitor_children(SessMap),
Sz = sets:size(Pids),
EStack =
case Shutdown of
brutal_kill ->
sets:fold(fun(P, _) -> exit(P, kill) end, ok, Pids),
wait_children(Shutdown, Pids, Sz, undefined, EStack0);
infinity ->
sets:fold(fun(P, _) -> exit(P, shutdown) end, ok, Pids),
wait_children(Shutdown, Pids, Sz, undefined, EStack0);
Time when is_integer(Time) ->
sets:fold(fun(P, _) -> exit(P, shutdown) end, ok, Pids),
TRef = erlang:start_timer(Time, self(), kill),
wait_children(Shutdown, Pids, Sz, TRef, EStack0)
end,
%% Unroll stacked errors and report them
dict:fold(fun(Reason, Pid, _) ->
report_error(connection_shutdown_error, Reason, Pid, State)
end, ok, EStack).
monitor_children(SessMap) ->
lists:foldl(
fun(Pid, {Pids, EStack}) ->
case monitor_child(Pid) of
ok ->
{sets:add_element(Pid, Pids), EStack};
{error, normal} ->
{Pids, EStack};
{error, Reason} ->
{Pids, dict:append(Reason, Pid, EStack)}
end
end, {sets:new(), dict:new()}, maps:keys(SessMap)).
%% Help function to shutdown/2 switches from link to monitor approach
monitor_child(Pid) ->
%% Do the monitor operation first so that if the child dies
%% before the monitoring is done causing a 'DOWN'-message with
%% reason noproc, we will get the real reason in the 'EXIT'-message
%% unless a naughty child has already done unlink...
erlang:monitor(process, Pid),
unlink(Pid),
receive
%% If the child dies before the unlik we must empty
%% the mail-box of the 'EXIT'-message and the 'DOWN'-message.
{'EXIT', Pid, Reason} ->
receive
{'DOWN', _, process, Pid, _} ->
{error, Reason}
end
after 0 ->
%% If a naughty child did unlink and the child dies before
%% monitor the result will be that shutdown/2 receives a
%% 'DOWN'-message with reason noproc.
%% If the child should die after the unlink there
%% will be a 'DOWN'-message with a correct reason
%% that will be handled in shutdown/2.
ok
end.
wait_children(_Shutdown, _Pids, 0, undefined, EStack) ->
EStack;
wait_children(_Shutdown, _Pids, 0, TRef, EStack) ->
%% If the timer has expired before its cancellation, we must empty the
%% mail-box of the 'timeout'-message.
erlang:cancel_timer(TRef),
receive
{timeout, TRef, kill} ->
EStack
after 0 ->
EStack
end;
%%TODO: Copied from supervisor.erl, rewrite it later.
wait_children(brutal_kill, Pids, Sz, TRef, EStack) ->
receive
{'DOWN', _MRef, process, Pid, killed} ->
wait_children(brutal_kill, sets:del_element(Pid, Pids), Sz-1, TRef, EStack);
{'DOWN', _MRef, process, Pid, Reason} ->
wait_children(brutal_kill, sets:del_element(Pid, Pids),
Sz-1, TRef, dict:append(Reason, Pid, EStack))
end;
wait_children(Shutdown, Pids, Sz, TRef, EStack) ->
receive
{'DOWN', _MRef, process, Pid, shutdown} ->
wait_children(Shutdown, sets:del_element(Pid, Pids), Sz-1, TRef, EStack);
{'DOWN', _MRef, process, Pid, normal} ->
wait_children(Shutdown, sets:del_element(Pid, Pids), Sz-1, TRef, EStack);
{'DOWN', _MRef, process, Pid, Reason} ->
wait_children(Shutdown, sets:del_element(Pid, Pids), Sz-1,
TRef, dict:append(Reason, Pid, EStack));
{timeout, TRef, kill} ->
sets:fold(fun(P, _) -> exit(P, kill) end, ok, Pids),
wait_children(Shutdown, Pids, Sz-1, undefined, EStack)
end.
report_error(Error, Reason, Pid, #state{mfargs = MFA}) ->
SupName = list_to_atom("esockd_connection_sup - " ++ pid_to_list(self())),
ErrorMsg = [{supervisor, SupName},
{errorContext, Error},
{reason, Reason},
{offender, [{pid, Pid},
{name, connection},
{mfargs, MFA}]}],
error_logger:error_report(supervisor_report, ErrorMsg).
reply(Repy, State) ->
{reply, Repy, State}.

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_shared_sub). -module(emqx_shared_sub).
@ -67,11 +69,12 @@
-define(no_ack, no_ack). -define(no_ack, no_ack).
-record(state, {pmon}). -record(state, {pmon}).
-record(emqx_shared_subscription, {group, topic, subpid}). -record(emqx_shared_subscription, {group, topic, subpid}).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Mnesia bootstrap %% Mnesia bootstrap
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
mnesia(boot) -> mnesia(boot) ->
ok = ekka_mnesia:create_table(?TAB, [ ok = ekka_mnesia:create_table(?TAB, [
@ -83,9 +86,9 @@ mnesia(boot) ->
mnesia(copy) -> mnesia(copy) ->
ok = ekka_mnesia:copy_table(?TAB). ok = ekka_mnesia:copy_table(?TAB).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% API %% API
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
-spec(start_link() -> startlink_ret()). -spec(start_link() -> startlink_ret()).
start_link() -> start_link() ->
@ -132,7 +135,7 @@ ack_enabled() ->
do_dispatch(SubPid, Topic, Msg, _Type) when SubPid =:= self() -> do_dispatch(SubPid, Topic, Msg, _Type) when SubPid =:= self() ->
%% Deadlock otherwise %% Deadlock otherwise
_ = erlang:send(SubPid, {dispatch, Topic, Msg}), _ = erlang:send(SubPid, {deliver, Topic, Msg}),
ok; ok;
do_dispatch(SubPid, Topic, Msg, Type) -> do_dispatch(SubPid, Topic, Msg, Type) ->
dispatch_per_qos(SubPid, Topic, Msg, Type). dispatch_per_qos(SubPid, Topic, Msg, Type).
@ -140,18 +143,18 @@ do_dispatch(SubPid, Topic, Msg, Type) ->
%% return either 'ok' (when everything is fine) or 'error' %% return either 'ok' (when everything is fine) or 'error'
dispatch_per_qos(SubPid, Topic, #message{qos = ?QOS_0} = Msg, _Type) -> dispatch_per_qos(SubPid, Topic, #message{qos = ?QOS_0} = Msg, _Type) ->
%% For QoS 0 message, send it as regular dispatch %% For QoS 0 message, send it as regular dispatch
_ = erlang:send(SubPid, {dispatch, Topic, Msg}), _ = erlang:send(SubPid, {deliver, Topic, Msg}),
ok; ok;
dispatch_per_qos(SubPid, Topic, Msg, retry) -> dispatch_per_qos(SubPid, Topic, Msg, retry) ->
%% Retry implies all subscribers nack:ed, send again without ack %% Retry implies all subscribers nack:ed, send again without ack
_ = erlang:send(SubPid, {dispatch, Topic, Msg}), _ = erlang:send(SubPid, {deliver, Topic, Msg}),
ok; ok;
dispatch_per_qos(SubPid, Topic, Msg, fresh) -> dispatch_per_qos(SubPid, Topic, Msg, fresh) ->
case ack_enabled() of case ack_enabled() of
true -> true ->
dispatch_with_ack(SubPid, Topic, Msg); dispatch_with_ack(SubPid, Topic, Msg);
false -> false ->
_ = erlang:send(SubPid, {dispatch, Topic, Msg}), _ = erlang:send(SubPid, {deliver, Topic, Msg}),
ok ok
end. end.
@ -159,7 +162,7 @@ dispatch_with_ack(SubPid, Topic, Msg) ->
%% For QoS 1/2 message, expect an ack %% For QoS 1/2 message, expect an ack
Ref = erlang:monitor(process, SubPid), Ref = erlang:monitor(process, SubPid),
Sender = self(), Sender = self(),
_ = erlang:send(SubPid, {dispatch, Topic, with_ack_ref(Msg, {Sender, Ref})}), _ = erlang:send(SubPid, {deliver, Topic, with_ack_ref(Msg, {Sender, Ref})}),
Timeout = case Msg#message.qos of Timeout = case Msg#message.qos of
?QOS_1 -> timer:seconds(?SHARED_SUB_QOS1_DISPATCH_TIMEOUT_SECONDS); ?QOS_1 -> timer:seconds(?SHARED_SUB_QOS1_DISPATCH_TIMEOUT_SECONDS);
?QOS_2 -> infinity ?QOS_2 -> infinity
@ -275,12 +278,12 @@ do_pick_subscriber(Group, Topic, round_robin, _ClientId, Count) ->
subscribers(Group, Topic) -> subscribers(Group, Topic) ->
ets:select(?TAB, [{{emqx_shared_subscription, Group, Topic, '$1'}, [], ['$1']}]). ets:select(?TAB, [{{emqx_shared_subscription, Group, Topic, '$1'}, [], ['$1']}]).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% gen_server callbacks %% gen_server callbacks
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
init([]) -> init([]) ->
mnesia:subscribe({table, ?TAB, simple}), {ok, _} = mnesia:subscribe({table, ?TAB, simple}),
{atomic, PMon} = mnesia:transaction(fun init_monitors/0), {atomic, PMon} = mnesia:transaction(fun init_monitors/0),
ok = emqx_tables:new(?SHARED_SUBS, [protected, bag]), ok = emqx_tables:new(?SHARED_SUBS, [protected, bag]),
ok = emqx_tables:new(?ALIVE_SUBS, [protected, set, {read_concurrency, true}]), ok = emqx_tables:new(?ALIVE_SUBS, [protected, set, {read_concurrency, true}]),
@ -345,9 +348,9 @@ terminate(_Reason, _State) ->
code_change(_OldVsn, State, _Extra) -> code_change(_OldVsn, State, _Extra) ->
{ok, State}. {ok, State}.
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Internal functions %% Internal functions
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% keep track of alive remote pids %% keep track of alive remote pids
maybe_insert_alive_tab(Pid) when ?IS_LOCAL_PID(Pid) -> ok; maybe_insert_alive_tab(Pid) when ?IS_LOCAL_PID(Pid) -> ok;

View File

@ -1,298 +0,0 @@
%% Copyright (c) 2013-2019 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).
-behaviour(gen_server).
-include("emqx.hrl").
-include("logger.hrl").
-include("types.hrl").
-logger_header("[SM]").
%% APIs
-export([start_link/0]).
-export([ open_session/1
, close_session/1
, resume_session/2
, discard_session/1
, discard_session/2
, register_session/1
, register_session/2
, unregister_session/1
, unregister_session/2
]).
-export([ get_session_attrs/1
, get_session_attrs/2
, set_session_attrs/2
, set_session_attrs/3
, get_session_stats/1
, get_session_stats/2
, set_session_stats/2
, set_session_stats/3
]).
-export([lookup_session_pids/1]).
%% Internal functions for rpc
-export([dispatch/3]).
%% Internal function for stats
-export([stats_fun/0]).
%% Internal function for emqx_session_sup
-export([clean_down/1]).
%% gen_server callbacks
-export([ init/1
, handle_call/3
, handle_cast/2
, handle_info/2
, terminate/2
, code_change/3
]).
-define(SM, ?MODULE).
%% ETS Tables for session management.
-define(SESSION_TAB, emqx_session).
-define(SESSION_P_TAB, emqx_session_p).
-define(SESSION_ATTRS_TAB, emqx_session_attrs).
-define(SESSION_STATS_TAB, emqx_session_stats).
-define(BATCH_SIZE, 100000).
%%------------------------------------------------------------------------------
%% APIs
%%------------------------------------------------------------------------------
-spec(start_link() -> startlink_ret()).
start_link() ->
gen_server:start_link({local, ?SM}, ?MODULE, [], []).
%% @doc Open a session.
-spec(open_session(map()) -> {ok, pid()} | {ok, pid(), boolean()} | {error, term()}).
open_session(SessAttrs = #{clean_start := true, client_id := ClientId, conn_pid := ConnPid}) ->
CleanStart = fun(_) ->
ok = discard_session(ClientId, ConnPid),
emqx_session_sup:start_session(SessAttrs)
end,
emqx_sm_locker:trans(ClientId, CleanStart);
open_session(SessAttrs = #{clean_start := false, client_id := ClientId}) ->
ResumeStart = fun(_) ->
case resume_session(ClientId, SessAttrs) of
{ok, SessPid} ->
{ok, SessPid, true};
{error, not_found} ->
emqx_session_sup:start_session(SessAttrs)
end
end,
emqx_sm_locker:trans(ClientId, ResumeStart).
%% @doc Discard all the sessions identified by the ClientId.
-spec(discard_session(emqx_types:client_id()) -> ok).
discard_session(ClientId) when is_binary(ClientId) ->
discard_session(ClientId, self()).
-spec(discard_session(emqx_types:client_id(), pid()) -> ok).
discard_session(ClientId, ConnPid) when is_binary(ClientId) ->
lists:foreach(
fun(SessPid) ->
try emqx_session:discard(SessPid, ConnPid)
catch
_:Error:_Stk ->
unregister_session(ClientId, SessPid),
?LOG(warning, "Failed to discard ~p: ~p", [SessPid, Error])
end
end, lookup_session_pids(ClientId)).
%% @doc Try to resume a session.
-spec(resume_session(emqx_types:client_id(), map()) -> {ok, pid()} | {error, term()}).
resume_session(ClientId, SessAttrs = #{conn_pid := ConnPid}) ->
case lookup_session_pids(ClientId) of
[] -> {error, not_found};
[SessPid] ->
ok = emqx_session:resume(SessPid, SessAttrs),
{ok, SessPid};
SessPids ->
[SessPid|StalePids] = lists:reverse(SessPids),
?LOG(error, "More than one session found: ~p", [SessPids]),
lists:foreach(fun(StalePid) ->
catch emqx_session:discard(StalePid, ConnPid)
end, StalePids),
ok = emqx_session:resume(SessPid, SessAttrs),
{ok, SessPid}
end.
%% @doc Close a session.
-spec(close_session(emqx_types:client_id() | pid()) -> ok).
close_session(ClientId) when is_binary(ClientId) ->
case lookup_session_pids(ClientId) of
[] -> ok;
[SessPid] -> close_session(SessPid);
SessPids -> lists:foreach(fun close_session/1, SessPids)
end;
close_session(SessPid) when is_pid(SessPid) ->
emqx_session:close(SessPid).
%% @doc Register a session.
-spec(register_session(emqx_types:client_id()) -> ok).
register_session(ClientId) when is_binary(ClientId) ->
register_session(ClientId, self()).
-spec(register_session(emqx_types:client_id(), pid()) -> ok).
register_session(ClientId, SessPid) when is_binary(ClientId), is_pid(SessPid) ->
Session = {ClientId, SessPid},
true = ets:insert(?SESSION_TAB, Session),
emqx_sm_registry:register_session(Session).
%% @doc Unregister a session
-spec(unregister_session(emqx_types:client_id()) -> ok).
unregister_session(ClientId) when is_binary(ClientId) ->
unregister_session(ClientId, self()).
-spec(unregister_session(emqx_types:client_id(), pid()) -> ok).
unregister_session(ClientId, SessPid) when is_binary(ClientId), is_pid(SessPid) ->
Session = {ClientId, SessPid},
true = ets:delete(?SESSION_STATS_TAB, Session),
true = ets:delete(?SESSION_ATTRS_TAB, Session),
true = ets:delete_object(?SESSION_P_TAB, Session),
true = ets:delete_object(?SESSION_TAB, Session),
emqx_sm_registry:unregister_session(Session).
%% @doc Get session attrs
-spec(get_session_attrs(emqx_types:client_id()) -> list(emqx_session:attr())).
get_session_attrs(ClientId) when is_binary(ClientId) ->
case lookup_session_pids(ClientId) of
[] -> [];
[SessPid|_] -> get_session_attrs(ClientId, SessPid)
end.
-spec(get_session_attrs(emqx_types:client_id(), pid()) -> list(emqx_session:attr())).
get_session_attrs(ClientId, SessPid) when is_binary(ClientId), is_pid(SessPid) ->
emqx_tables:lookup_value(?SESSION_ATTRS_TAB, {ClientId, SessPid}, []).
%% @doc Set session attrs
-spec(set_session_attrs(emqx_types:client_id(), list(emqx_session:attr())) -> true).
set_session_attrs(ClientId, SessAttrs) when is_binary(ClientId) ->
set_session_attrs(ClientId, self(), SessAttrs).
-spec(set_session_attrs(emqx_types:client_id(), pid(), list(emqx_session:attr())) -> true).
set_session_attrs(ClientId, SessPid, SessAttrs) when is_binary(ClientId), is_pid(SessPid) ->
Session = {ClientId, SessPid},
true = ets:insert(?SESSION_ATTRS_TAB, {Session, SessAttrs}),
proplists:get_value(clean_start, SessAttrs, true) orelse ets:insert(?SESSION_P_TAB, Session).
%% @doc Get session stats
-spec(get_session_stats(emqx_types:client_id()) -> list(emqx_stats:stats())).
get_session_stats(ClientId) when is_binary(ClientId) ->
case lookup_session_pids(ClientId) of
[] -> [];
[SessPid|_] ->
get_session_stats(ClientId, SessPid)
end.
-spec(get_session_stats(emqx_types:client_id(), pid()) -> list(emqx_stats:stats())).
get_session_stats(ClientId, SessPid) when is_binary(ClientId) ->
emqx_tables:lookup_value(?SESSION_STATS_TAB, {ClientId, SessPid}, []).
%% @doc Set session stats
-spec(set_session_stats(emqx_types:client_id(), emqx_stats:stats()) -> true).
set_session_stats(ClientId, Stats) when is_binary(ClientId) ->
set_session_stats(ClientId, self(), Stats).
-spec(set_session_stats(emqx_types:client_id(), pid(), emqx_stats:stats()) -> true).
set_session_stats(ClientId, SessPid, Stats) when is_binary(ClientId), is_pid(SessPid) ->
ets:insert(?SESSION_STATS_TAB, {{ClientId, SessPid}, Stats}).
%% @doc Lookup session pid.
-spec(lookup_session_pids(emqx_types:client_id()) -> list(pid())).
lookup_session_pids(ClientId) ->
case emqx_sm_registry:is_enabled() of
true -> emqx_sm_registry:lookup_session(ClientId);
false ->
case emqx_tables:lookup_value(?SESSION_TAB, ClientId) of
undefined -> [];
SessPid when is_pid(SessPid) -> [SessPid]
end
end.
%% @doc Dispatch a message to the session.
-spec(dispatch(emqx_types:client_id(), emqx_topic:topic(), emqx_types:message()) -> any()).
dispatch(ClientId, Topic, Msg) ->
case lookup_session_pids(ClientId) of
[SessPid|_] when is_pid(SessPid) ->
SessPid ! {dispatch, Topic, Msg};
[] ->
emqx_hooks:run('message.dropped', [#{client_id => ClientId}, Msg])
end.
%%------------------------------------------------------------------------------
%% gen_server callbacks
%%------------------------------------------------------------------------------
init([]) ->
TabOpts = [public, set, {write_concurrency, true}],
ok = emqx_tables:new(?SESSION_TAB, [{read_concurrency, true} | TabOpts]),
ok = emqx_tables:new(?SESSION_P_TAB, TabOpts),
ok = emqx_tables:new(?SESSION_ATTRS_TAB, TabOpts),
ok = emqx_tables:new(?SESSION_STATS_TAB, TabOpts),
ok = emqx_stats:update_interval(sess_stats, fun ?MODULE:stats_fun/0),
{ok, #{}}.
handle_call(Req, _From, State) ->
?LOG(error, "Unexpected call: ~p", [Req]),
{reply, ignored, State}.
handle_cast(Msg, State) ->
?LOG(error, "Unexpected cast: ~p", [Msg]),
{noreply, State}.
handle_info(Info, State) ->
?LOG(error, "Unexpected info: ~p", [Info]),
{noreply, State}.
terminate(_Reason, _State) ->
emqx_stats:cancel_update(sess_stats).
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%------------------------------------------------------------------------------
%% Internal functions
%%------------------------------------------------------------------------------
clean_down(Session = {ClientId, SessPid}) ->
case ets:member(?SESSION_TAB, ClientId)
orelse ets:member(?SESSION_ATTRS_TAB, Session) of
true ->
unregister_session(ClientId, SessPid);
false -> ok
end.
stats_fun() ->
safe_update_stats(?SESSION_TAB, 'sessions.count', 'sessions.max'),
safe_update_stats(?SESSION_P_TAB, 'sessions.persistent.count', 'sessions.persistent.max').
safe_update_stats(Tab, Stat, MaxStat) ->
case ets:info(Tab, size) of
undefined -> ok;
Size -> emqx_stats:setstat(Stat, MaxStat, Size)
end.

View File

@ -1,64 +0,0 @@
%% Copyright (c) 2013-2019 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).
-behaviour(supervisor).
-export([start_link/0]).
-export([init/1]).
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
%% 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 = #{id => manager,
start => {emqx_sm, start_link, []},
restart => permanent,
shutdown => 5000,
type => worker,
modules => [emqx_sm]
},
%% Session Sup
SessSpec = #{start => {emqx_session, start_link, []},
shutdown => brutal_kill,
clean_down => fun emqx_sm:clean_down/1
},
SessionSup = #{id => session_sup,
start => {emqx_session_sup, start_link, [SessSpec ]},
restart => transient,
shutdown => infinity,
type => supervisor,
modules => [emqx_session_sup]
},
{ok, {{rest_for_one, 10, 3600}, [Locker, Registry, Manager, SessionSup]}}.

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_stats). -module(emqx_stats).
@ -49,14 +51,18 @@
, code_change/3 , code_change/3
]). ]).
-export_type([stats/0]).
-record(update, {name, countdown, interval, func}). -record(update, {name, countdown, interval, func}).
-record(state, {timer, updates :: [#update{}], tick_ms :: timeout()}). -record(state, {
timer :: reference(),
updates :: [#update{}],
tick_ms :: timeout()
}).
-type(stats() :: list({atom(), non_neg_integer()})). -type(stats() :: list({atom(), non_neg_integer()})).
-export_type([stats/0]).
%% Connection stats %% Connection stats
-define(CONNECTION_STATS, [ -define(CONNECTION_STATS, [
'connections.count', % current connections 'connections.count', % current connections
@ -168,9 +174,9 @@ rec(Name, Secs, UpFun) ->
cast(Msg) -> cast(Msg) ->
gen_server:cast(?SERVER, Msg). gen_server:cast(?SERVER, Msg).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% gen_server callbacks %% gen_server callbacks
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
init(#{tick_ms := TickMs}) -> init(#{tick_ms := TickMs}) ->
ok = emqx_tables:new(?TAB, [public, set, {write_concurrency, true}]), ok = emqx_tables:new(?TAB, [public, set, {write_concurrency, true}]),
@ -201,7 +207,8 @@ handle_cast({setstat, Stat, MaxStat, Val}, State) ->
safe_update_element(Stat, Val), safe_update_element(Stat, Val),
{noreply, State}; {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 case lists:keyfind(Name, #update.name, Updates) of
#update{} -> #update{} ->
?LOG(warning, "Duplicated update: ~s", [Name]), ?LOG(warning, "Duplicated update: ~s", [Name]),
@ -242,9 +249,9 @@ terminate(_Reason, #state{timer = TRef}) ->
code_change(_OldVsn, State, _Extra) -> code_change(_OldVsn, State, _Extra) ->
{ok, State}. {ok, State}.
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Internal functions %% Internal functions
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
safe_update_element(Key, Val) -> safe_update_element(Key, Val) ->
try ets:update_element(?TAB, Key, {2, Val}) of try ets:update_element(?TAB, Key, {2, Val}) of
@ -256,3 +263,4 @@ safe_update_element(Key, Val) ->
error:badarg -> error:badarg ->
?LOG(warning, "Update ~p to ~p failed", [Key, Val]) ?LOG(warning, "Update ~p to ~p failed", [Key, Val])
end. end.

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,11 +12,14 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_sup). -module(emqx_sup).
-behaviour(supervisor). -behaviour(supervisor).
-include("types.hrl").
-export([ start_link/0 -export([ start_link/0
, start_child/1 , start_child/1
, start_child/2 , start_child/2
@ -28,29 +32,28 @@
| {ok, supervisor:child(), term()} | {ok, supervisor:child(), term()}
| {error, term()}). | {error, term()}).
-define(SUPERVISOR, ?MODULE). -define(SUP, ?MODULE).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% API %% API
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-spec(start_link() -> startlink_ret()).
start_link() -> start_link() ->
supervisor:start_link({local, ?SUPERVISOR}, ?MODULE, []). supervisor:start_link({local, ?SUP}, ?MODULE, []).
-spec(start_child(supervisor:child_spec()) -> startchild_ret()). -spec(start_child(supervisor:child_spec()) -> startchild_ret()).
start_child(ChildSpec) when is_tuple(ChildSpec) -> start_child(ChildSpec) when is_tuple(ChildSpec) ->
supervisor:start_child(?SUPERVISOR, ChildSpec). supervisor:start_child(?SUP, ChildSpec).
-spec(start_child(module(), worker | supervisor) -> startchild_ret()). -spec(start_child(module(), worker | supervisor) -> startchild_ret()).
start_child(Mod, worker) -> start_child(Mod, Type) ->
start_child(worker_spec(Mod)); start_child(child_spec(Mod, Type)).
start_child(Mod, supervisor) ->
start_child(supervisor_spec(Mod)).
-spec(stop_child(supervisor:child_id()) -> ok | {error, term()}). -spec(stop_child(supervisor:child_id()) -> ok | {error, term()}).
stop_child(ChildId) -> stop_child(ChildId) ->
case supervisor:terminate_child(?SUPERVISOR, ChildId) of case supervisor:terminate_child(?SUP, ChildId) of
ok -> supervisor:delete_child(?SUPERVISOR, ChildId); ok -> supervisor:delete_child(?SUP, ChildId);
Error -> Error Error -> Error
end. end.
@ -60,30 +63,37 @@ stop_child(ChildId) ->
init([]) -> init([]) ->
%% Kernel Sup %% Kernel Sup
KernelSup = supervisor_spec(emqx_kernel_sup), KernelSup = child_spec(emqx_kernel_sup, supervisor),
%% Router Sup %% Router Sup
RouterSup = supervisor_spec(emqx_router_sup), RouterSup = child_spec(emqx_router_sup, supervisor),
%% Broker Sup %% Broker Sup
BrokerSup = supervisor_spec(emqx_broker_sup), BrokerSup = child_spec(emqx_broker_sup, supervisor),
%% Session Manager %% CM Sup
SMSup = supervisor_spec(emqx_sm_sup), CMSup = child_spec(emqx_cm_sup, supervisor),
%% Connection Manager
CMSup = supervisor_spec(emqx_cm_sup),
%% Sys Sup %% Sys Sup
SysSup = supervisor_spec(emqx_sys_sup), SysSup = child_spec(emqx_sys_sup, supervisor),
{ok, {{one_for_all, 0, 1}, {ok, {{one_for_all, 0, 1},
[KernelSup, [KernelSup, RouterSup, BrokerSup, CMSup, SysSup]}}.
RouterSup,
BrokerSup,
SMSup,
CMSup,
SysSup]}}.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Internal functions %% Internal functions
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
worker_spec(M) -> child_spec(Mod, supervisor) ->
{M, {M, start_link, []}, permanent, 30000, worker, [M]}. #{id => Mod,
supervisor_spec(M) -> start => {Mod, start_link, []},
{M, {M, start_link, []}, permanent, infinity, supervisor, [M]}. restart => permanent,
shutdown => infinity,
type => supervisor,
modules => [Mod]
};
child_spec(Mod, worker) ->
#{id => Mod,
start => {Mod, start_link, []},
restart => permanent,
shutdown => 15000,
type => worker,
modules => [Mod]
}.

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_sys). -module(emqx_sys).

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_sys_mon). -module(emqx_sys_mon).
@ -43,18 +45,14 @@
-define(SYSMON, ?MODULE). -define(SYSMON, ?MODULE).
%%------------------------------------------------------------------------------ %% @doc Start the system monitor.
%% APIs
%%------------------------------------------------------------------------------
%% @doc Start system monitor
-spec(start_link(list(option())) -> startlink_ret()). -spec(start_link(list(option())) -> startlink_ret()).
start_link(Opts) -> start_link(Opts) ->
gen_server:start_link({local, ?SYSMON}, ?MODULE, [Opts], []). gen_server:start_link({local, ?SYSMON}, ?MODULE, [Opts], []).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% gen_server callbacks %% gen_server callbacks
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
init([Opts]) -> init([Opts]) ->
erlang:system_monitor(self(), parse_opt(Opts)), erlang:system_monitor(self(), parse_opt(Opts)),

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_sys_sup). -module(emqx_sys_sup).
@ -24,23 +26,28 @@ start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []). supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) -> init([]) ->
{ok, {{one_for_one, 10, 100}, [child_spec(emqx_sys, worker), Childs = [child_spec(emqx_sys),
child_spec(emqx_sys_mon, worker, [emqx_config:get_env(sysmon, [])]), child_spec(emqx_sys_mon, [config(sysmon)]),
child_spec(emqx_os_mon, worker, [emqx_config:get_env(os_mon, [])]), child_spec(emqx_os_mon, [config(os_mon)]),
child_spec(emqx_vm_mon, worker, [emqx_config:get_env(vm_mon, [])])]}}. child_spec(emqx_vm_mon, [config(vm_mon)])],
{ok, {{one_for_one, 10, 100}, Childs}}.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Internal functions %% Internal functions
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
child_spec(M, worker) -> child_spec(Mod) ->
child_spec(M, worker, []). child_spec(Mod, []).
child_spec(M, worker, A) -> child_spec(Mod, Args) ->
#{id => M, #{id => Mod,
start => {M, start_link, A}, start => {Mod, start_link, Args},
restart => permanent, restart => permanent,
shutdown => 5000, shutdown => 5000,
type => worker, type => worker,
modules => [M]}. modules => [Mod]
}.
config(Name) ->
emqx_config:get_env(Name, []).

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_tables). -module(emqx_tables).
@ -52,3 +54,4 @@ lookup_value(Tab, Key, Def) ->
catch catch
error:badarg -> Def error:badarg -> Def
end. end.

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_time). -module(emqx_time).
@ -39,3 +41,4 @@ now_ms({MegaSecs, Secs, MicroSecs}) ->
ts_from_ms(Ms) -> ts_from_ms(Ms) ->
{Ms div 1000000, Ms rem 1000000, 0}. {Ms div 1000000, Ms rem 1000000, 0}.

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_topic). -module(emqx_topic).
@ -33,19 +35,23 @@
, parse/2 , parse/2
]). ]).
-export_type([ group/0
, topic/0
, word/0
, triple/0
]).
-type(group() :: binary()). -type(group() :: binary()).
-type(topic() :: binary()). -type(topic() :: binary()).
-type(word() :: '' | '+' | '#' | binary()). -type(word() :: '' | '+' | '#' | binary()).
-type(words() :: list(word())). -type(words() :: list(word())).
-opaque(triple() :: {root | binary(), word(), binary()}). -opaque(triple() :: {root | binary(), word(), binary()}).
-export_type([group/0, topic/0, word/0, triple/0]).
-define(MAX_TOPIC_LEN, 4096). -define(MAX_TOPIC_LEN, 4096).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% APIs %% APIs
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% @doc Is wildcard topic? %% @doc Is wildcard topic?
-spec(wildcard(topic() | words()) -> true | false). -spec(wildcard(topic() | words()) -> true | false).
@ -230,3 +236,4 @@ parse(Topic, Options = #{qos := QoS}) ->
{Topic, Options#{rc => QoS}}; {Topic, Options#{rc => QoS}};
parse(Topic, Options) -> parse(Topic, Options) ->
{Topic, Options}. {Topic, Options}.

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_tracer). -module(emqx_tracer).

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_trie). -module(emqx_trie).
@ -35,9 +37,9 @@
-define(TRIE, emqx_trie). -define(TRIE, emqx_trie).
-define(TRIE_NODE, emqx_trie_node). -define(TRIE_NODE, emqx_trie_node).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Mnesia bootstrap %% Mnesia bootstrap
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% @doc Create or replicate trie tables. %% @doc Create or replicate trie tables.
-spec(mnesia(boot | copy) -> ok). -spec(mnesia(boot | copy) -> ok).
@ -64,9 +66,9 @@ mnesia(copy) ->
%% Copy trie_node table %% Copy trie_node table
ok = ekka_mnesia:copy_table(?TRIE_NODE). ok = ekka_mnesia:copy_table(?TRIE_NODE).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Trie APIs %% Trie APIs
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% @doc Insert a topic filter into the trie. %% @doc Insert a topic filter into the trie.
-spec(insert(emqx_topic:topic()) -> ok). -spec(insert(emqx_topic:topic()) -> ok).
@ -111,9 +113,9 @@ delete(Topic) when is_binary(Topic) ->
empty() -> empty() ->
ets:info(?TRIE, size) == 0. ets:info(?TRIE, size) == 0.
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Internal functions %% Internal functions
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% @private %% @private
%% @doc Add a path to the trie. %% @doc Add a path to the trie.

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_types). -module(emqx_types).
@ -32,9 +34,7 @@
, protocol/0 , protocol/0
]). ]).
-export_type([ credentials/0 -export_type([credentials/0]).
, session/0
]).
-export_type([ subscription/0 -export_type([ subscription/0
, subscriber/0 , subscriber/0
@ -65,7 +65,6 @@
share => binary(), share => binary(),
atom() => term() atom() => term()
}). }).
-type(session() :: #session{}).
-type(client_id() :: binary() | atom()). -type(client_id() :: binary() | atom()).
-type(username() :: maybe(binary())). -type(username() :: maybe(binary())).
-type(password() :: maybe(binary())). -type(password() :: maybe(binary())).
@ -105,3 +104,4 @@
-type(alarm() :: #alarm{}). -type(alarm() :: #alarm{}).
-type(plugin() :: #plugin{}). -type(plugin() :: #plugin{}).
-type(command() :: #command{}). -type(command() :: #command{}).

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_vm). -module(emqx_vm).

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,11 +12,14 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_vm_mon). -module(emqx_vm_mon).
-behaviour(gen_server). -behaviour(gen_server).
-include("logger.hrl").
%% APIs %% APIs
-export([start_link/1]). -export([start_link/1]).
@ -38,13 +42,13 @@
-define(VM_MON, ?MODULE). -define(VM_MON, ?MODULE).
%%----------------------------------------------------------------------
%% API
%%----------------------------------------------------------------------
start_link(Opts) -> start_link(Opts) ->
gen_server:start_link({local, ?VM_MON}, ?MODULE, [Opts], []). gen_server:start_link({local, ?VM_MON}, ?MODULE, [Opts], []).
%%--------------------------------------------------------------------
%% API
%%--------------------------------------------------------------------
get_check_interval() -> get_check_interval() ->
call(get_check_interval). call(get_check_interval).
@ -63,9 +67,12 @@ get_process_low_watermark() ->
set_process_low_watermark(Float) -> set_process_low_watermark(Float) ->
call({set_process_low_watermark, Float}). call({set_process_low_watermark, Float}).
%%---------------------------------------------------------------------- call(Req) ->
gen_server:call(?VM_MON, Req, infinity).
%%--------------------------------------------------------------------
%% gen_server callbacks %% gen_server callbacks
%%---------------------------------------------------------------------- %%--------------------------------------------------------------------
init([Opts]) -> init([Opts]) ->
{ok, ensure_check_timer(#{check_interval => proplists:get_value(check_interval, Opts, 30), {ok, ensure_check_timer(#{check_interval => proplists:get_value(check_interval, Opts, 30),
@ -76,43 +83,53 @@ init([Opts]) ->
handle_call(get_check_interval, _From, State) -> handle_call(get_check_interval, _From, State) ->
{reply, maps:get(check_interval, State, undefined), State}; {reply, maps:get(check_interval, State, undefined), State};
handle_call({set_check_interval, Seconds}, _From, State) -> handle_call({set_check_interval, Seconds}, _From, State) ->
{reply, ok, State#{check_interval := Seconds}}; {reply, ok, State#{check_interval := Seconds}};
handle_call(get_process_high_watermark, _From, State) -> handle_call(get_process_high_watermark, _From, State) ->
{reply, maps:get(process_high_watermark, State, undefined), State}; {reply, maps:get(process_high_watermark, State, undefined), State};
handle_call({set_process_high_watermark, Float}, _From, State) -> handle_call({set_process_high_watermark, Float}, _From, State) ->
{reply, ok, State#{process_high_watermark := Float}}; {reply, ok, State#{process_high_watermark := Float}};
handle_call(get_process_low_watermark, _From, State) -> handle_call(get_process_low_watermark, _From, State) ->
{reply, maps:get(process_low_watermark, State, undefined), State}; {reply, maps:get(process_low_watermark, State, undefined), State};
handle_call({set_process_low_watermark, Float}, _From, State) -> handle_call({set_process_low_watermark, Float}, _From, State) ->
{reply, ok, State#{process_low_watermark := Float}}; {reply, ok, State#{process_low_watermark := Float}};
handle_call(_Request, _From, State) -> handle_call(Req, _From, State) ->
{reply, ok, State}. ?LOG(error, "[VM_MON] Unexpected call: ~p", [Req]),
{reply, ignored, State}.
handle_cast(_Request, State) -> handle_cast(Msg, State) ->
?LOG(error, "[VM_MON] Unexpected cast: ~p", [Msg]),
{noreply, State}. {noreply, State}.
handle_info({timeout, Timer, check}, State = #{timer := Timer, handle_info({timeout, Timer, check},
process_high_watermark := ProcHighWatermark, State = #{timer := Timer,
process_low_watermark := ProcLowWatermark, process_high_watermark := ProcHighWatermark,
is_process_alarm_set := IsProcessAlarmSet}) -> process_low_watermark := ProcLowWatermark,
is_process_alarm_set := IsProcessAlarmSet}) ->
ProcessCount = erlang:system_info(process_count), ProcessCount = erlang:system_info(process_count),
case ProcessCount / erlang:system_info(process_limit) of NState = case ProcessCount / erlang:system_info(process_limit) of
Percent when Percent >= ProcHighWatermark -> Percent when Percent >= ProcHighWatermark ->
alarm_handler:set_alarm({too_many_processes, ProcessCount}), alarm_handler:set_alarm({too_many_processes, ProcessCount}),
{noreply, ensure_check_timer(State#{is_process_alarm_set := true})}; State#{is_process_alarm_set := true};
Percent when Percent < ProcLowWatermark -> Percent when Percent < ProcLowWatermark ->
case IsProcessAlarmSet of case IsProcessAlarmSet of
true -> alarm_handler:clear_alarm(too_many_processes); true -> alarm_handler:clear_alarm(too_many_processes);
false -> ok false -> ok
end, end,
{noreply, ensure_check_timer(State#{is_process_alarm_set := false})}; State#{is_process_alarm_set := false};
_Precent -> _Precent -> State
{noreply, ensure_check_timer(State)} end,
end. {noreply, ensure_check_timer(NState)};
handle_info(Info, State) ->
?LOG(error, "[VM_MON] Unexpected info: ~p", [Info]),
{noreply, State}.
terminate(_Reason, #{timer := Timer}) -> terminate(_Reason, #{timer := Timer}) ->
emqx_misc:cancel_timer(Timer). emqx_misc:cancel_timer(Timer).
@ -120,11 +137,10 @@ terminate(_Reason, #{timer := Timer}) ->
code_change(_OldVsn, State, _Extra) -> code_change(_OldVsn, State, _Extra) ->
{ok, State}. {ok, State}.
%%---------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Internal functions %% Internal functions
%%---------------------------------------------------------------------- %%--------------------------------------------------------------------
call(Req) ->
gen_server:call(?VM_MON, Req, infinity).
ensure_check_timer(State = #{check_interval := Interval}) -> ensure_check_timer(State = #{check_interval := Interval}) ->
State#{timer := emqx_misc:start_timer(timer:seconds(Interval), check)}. State#{timer := emqx_misc:start_timer(timer:seconds(Interval), check)}.

View File

@ -14,19 +14,19 @@
%% limitations under the License. %% limitations under the License.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-module(emqx_ws_channel). %% MQTT WebSocket Connection
-module(emqx_ws_connection).
-include("emqx.hrl"). -include("emqx.hrl").
-include("emqx_mqtt.hrl"). -include("emqx_mqtt.hrl").
-include("logger.hrl"). -include("logger.hrl").
-include("types.hrl").
-logger_header("[WS Channel]"). -logger_header("[WS Conn]").
-export([ info/1 -export([ info/1
, attrs/1 , attrs/1
, stats/1 , stats/1
, kick/1
, session/1
]). ]).
%% websocket callbacks %% websocket callbacks
@ -40,18 +40,19 @@
-record(state, { -record(state, {
request, request,
options, options,
peername, peername :: {inet:ip_address(), inet:port_number()},
sockname, sockname :: {inet:ip_address(), inet:port_number()},
proto_state, parse_state :: emqx_frame:parse_state(),
parse_state, packets :: list(emqx_mqtt:packet()),
keepalive, chan_state :: emqx_channel:channel(),
enable_stats, keepalive :: maybe(emqx_keepalive:keepalive()),
stats_timer, stats_timer :: disabled | maybe(reference()),
idle_timeout, idle_timeout :: timeout(),
shutdown shutdown
}). }).
-define(SOCK_STATS, [recv_oct, recv_cnt, send_oct, send_cnt]). -define(SOCK_STATS, [recv_oct, recv_cnt, send_oct, send_cnt]).
-define(CHAN_STATS, [recv_pkt, recv_msg, send_pkt, send_msg]).
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% API %% API
@ -61,42 +62,40 @@
info(WSPid) when is_pid(WSPid) -> info(WSPid) when is_pid(WSPid) ->
call(WSPid, info); call(WSPid, info);
info(#state{peername = Peername, info(#state{peername = Peername,
sockname = Sockname, sockname = Sockname,
proto_state = ProtoState}) -> chan_state = ChanState}) ->
ProtoInfo = emqx_protocol:info(ProtoState),
ConnInfo = #{socktype => websocket, ConnInfo = #{socktype => websocket,
conn_state => running, conn_state => running,
peername => Peername, peername => Peername,
sockname => Sockname}, sockname => Sockname
maps:merge(ProtoInfo, ConnInfo). },
ChanInfo = emqx_channel:info(ChanState),
maps:merge(ConnInfo, ChanInfo).
%% for dashboard %% for dashboard
attrs(WSPid) when is_pid(WSPid) -> attrs(WSPid) when is_pid(WSPid) ->
call(WSPid, attrs); call(WSPid, attrs);
attrs(#state{peername = Peername, attrs(#state{peername = Peername,
sockname = Sockname, sockname = Sockname,
proto_state = ProtoState}) -> chan_state = ChanState}) ->
SockAttrs = #{peername => Peername, SockAttrs = #{peername => Peername,
sockname => Sockname}, sockname => Sockname},
ProtoAttrs = emqx_protocol:attrs(ProtoState), ChanAttrs = emqx_channel:attrs(ChanState),
maps:merge(SockAttrs, ProtoAttrs). maps:merge(SockAttrs, ChanAttrs).
stats(WSPid) when is_pid(WSPid) -> stats(WSPid) when is_pid(WSPid) ->
call(WSPid, stats); call(WSPid, stats);
stats(#state{proto_state = ProtoState}) -> stats(#state{}) ->
lists:append([wsock_stats(), lists:append([chan_stats(), wsock_stats(), emqx_misc:proc_stats()]).
emqx_misc:proc_stats(),
emqx_protocol:stats(ProtoState)
]).
kick(WSPid) when is_pid(WSPid) -> %%kick(WSPid) when is_pid(WSPid) ->
call(WSPid, kick). %% call(WSPid, kick).
session(WSPid) when is_pid(WSPid) -> %%session(WSPid) when is_pid(WSPid) ->
call(WSPid, session). %% call(WSPid, session).
call(WSPid, Req) when is_pid(WSPid) -> call(WSPid, Req) when is_pid(WSPid) ->
Mref = erlang:monitor(process, WSPid), Mref = erlang:monitor(process, WSPid),
@ -148,30 +147,30 @@ websocket_init(#state{request = Req, options = Options}) ->
?LOG(error, "Illegal cookie"), ?LOG(error, "Illegal cookie"),
undefined; undefined;
Error:Reason -> Error:Reason ->
?LOG(error, ?LOG(error, "Cookie is parsed failed, Error: ~p, Reason ~p",
"Cookie is parsed failed, Error: ~p, Reason ~p",
[Error, Reason]), [Error, Reason]),
undefined undefined
end, end,
ProtoState = emqx_protocol:init(#{peername => Peername, ChanState = emqx_channel:init(#{peername => Peername,
sockname => Sockname, sockname => Sockname,
peercert => Peercert, peercert => Peercert,
sendfun => send_fun(self()), ws_cookie => WsCookie,
ws_cookie => WsCookie, conn_mod => ?MODULE}, Options),
conn_mod => ?MODULE}, Options),
Zone = proplists:get_value(zone, Options), Zone = proplists:get_value(zone, Options),
MaxSize = emqx_zone:get_env(Zone, max_packet_size, ?MAX_PACKET_SIZE), MaxSize = emqx_zone:get_env(Zone, max_packet_size, ?MAX_PACKET_SIZE),
ParseState = emqx_frame:initial_parse_state(#{max_size => MaxSize}), ParseState = emqx_frame:initial_parse_state(#{max_size => MaxSize}),
EnableStats = emqx_zone:get_env(Zone, enable_stats, true), EnableStats = emqx_zone:get_env(Zone, enable_stats, true),
StatsTimer = if EnableStats -> undefined; ?Otherwise-> disabled end,
IdleTimout = emqx_zone:get_env(Zone, idle_timeout, 30000), IdleTimout = emqx_zone:get_env(Zone, idle_timeout, 30000),
emqx_logger:set_metadata_peername(esockd_net:format(Peername)), emqx_logger:set_metadata_peername(esockd_net:format(Peername)),
ok = emqx_misc:init_proc_mng_policy(Zone), ok = emqx_misc:init_proc_mng_policy(Zone),
{ok, #state{peername = Peername, {ok, #state{peername = Peername,
sockname = Sockname, sockname = Sockname,
parse_state = ParseState, parse_state = ParseState,
proto_state = ProtoState, chan_state = ChanState,
enable_stats = EnableStats, stats_timer = StatsTimer,
idle_timeout = IdleTimout}}. idle_timeout = IdleTimout
}}.
send_fun(WsPid) -> send_fun(WsPid) ->
fun(Packet, Options) -> fun(Packet, Options) ->
@ -243,21 +242,21 @@ websocket_info({call, From, kick}, State) ->
gen_server:reply(From, ok), gen_server:reply(From, ok),
shutdown(kick, State); shutdown(kick, State);
websocket_info({call, From, session}, State = #state{proto_state = ProtoState}) -> websocket_info(Delivery, State = #state{chan_state = ChanState})
gen_server:reply(From, emqx_protocol:session(ProtoState)), when element(1, Delivery) =:= deliver ->
{ok, State}; case emqx_channel:handle_out(Delivery, ChanState) of
{ok, NChanState} ->
websocket_info({deliver, PubOrAck}, State = #state{proto_state = ProtoState}) -> {ok, State#state{chan_state = NChanState}};
case emqx_protocol:deliver(PubOrAck, ProtoState) of {ok, Packet, NChanState} ->
{ok, ProtoState1} -> handle_outgoing(Packet, State#state{chan_state = NChanState});
{ok, ensure_stats_timer(State#state{proto_state = ProtoState1})};
{error, Reason} -> {error, Reason} ->
shutdown(Reason, State) shutdown(Reason, State)
end; end;
websocket_info({timeout, Timer, emit_stats}, websocket_info({timeout, Timer, emit_stats},
State = #state{stats_timer = Timer, proto_state = ProtoState}) -> State = #state{stats_timer = Timer, chan_state = ChanState}) ->
emqx_cm:set_conn_stats(emqx_protocol:client_id(ProtoState), stats(State)), ClientId = emqx_channel:client_id(ChanState),
ok = emqx_cm:set_conn_stats(ClientId, stats(State)),
{ok, State#state{stats_timer = undefined}, hibernate}; {ok, State#state{stats_timer = undefined}, hibernate};
websocket_info({keepalive, start, Interval}, State) -> websocket_info({keepalive, start, Interval}, State) ->
@ -290,8 +289,8 @@ websocket_info({shutdown, conflict, {ClientId, NewPid}}, State) ->
?LOG(warning, "Clientid '~s' conflict with ~p", [ClientId, NewPid]), ?LOG(warning, "Clientid '~s' conflict with ~p", [ClientId, NewPid]),
shutdown(conflict, State); shutdown(conflict, State);
websocket_info({binary, Data}, State) -> %% websocket_info({binary, Data}, State) ->
{reply, {binary, Data}, State}; %% {reply, {binary, Data}, State};
websocket_info({shutdown, Reason}, State) -> websocket_info({shutdown, Reason}, State) ->
shutdown(Reason, State); shutdown(Reason, State);
@ -303,50 +302,74 @@ websocket_info(Info, State) ->
?LOG(error, "Unexpected info: ~p", [Info]), ?LOG(error, "Unexpected info: ~p", [Info]),
{ok, State}. {ok, State}.
terminate(SockError, _Req, #state{keepalive = Keepalive, terminate(SockError, _Req, #state{keepalive = Keepalive,
proto_state = ProtoState, chan_state = ChanState,
shutdown = Shutdown}) -> shutdown = Shutdown}) ->
?LOG(debug, "Terminated for ~p, sockerror: ~p", ?LOG(debug, "Terminated for ~p, sockerror: ~p",
[Shutdown, SockError]), [Shutdown, SockError]),
emqx_keepalive:cancel(Keepalive), emqx_keepalive:cancel(Keepalive),
case {ProtoState, Shutdown} of case {ChanState, Shutdown} of
{undefined, _} -> ok; {undefined, _} -> ok;
{_, {shutdown, Reason}} -> {_, {shutdown, Reason}} ->
emqx_protocol:terminate(Reason, ProtoState); emqx_channel:terminate(Reason, ChanState);
{_, Error} -> {_, Error} ->
emqx_protocol:terminate(Error, ProtoState) emqx_channel:terminate(Error, ChanState)
end. end.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
%% Internal functions %% Internal functions
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
handle_incoming(Packet, SuccFun, State = #state{proto_state = ProtoState}) -> handle_incoming(Packet = ?PACKET(Type), SuccFun,
case emqx_protocol:received(Packet, ProtoState) of State = #state{chan_state = ChanState}) ->
{ok, NProtoState} -> _ = inc_incoming_stats(Type),
SuccFun(State#state{proto_state = NProtoState}); case emqx_channel:handle_in(Packet, ChanState) of
{error, Reason} -> {ok, NChanState} ->
?LOG(error, "Protocol error: ~p", [Reason]), SuccFun(State#state{chan_state = NChanState});
shutdown(Reason, State); {ok, OutPacket, NChanState} ->
{error, Reason, NProtoState} -> %% TODO: SuccFun,
shutdown(Reason, State#state{proto_state = NProtoState}); handle_outgoing(OutPacket, State#state{chan_state = NChanState});
{stop, Error, NProtoState} -> {error, Reason, NChanState} ->
shutdown(Error, State#state{proto_state = NProtoState}) shutdown(Reason, State#state{chan_state = NChanState});
{stop, Error, NChanState} ->
shutdown(Error, State#state{chan_state = NChanState})
end. end.
handle_outgoing(Packet = ?PACKET(Type), State = #state{chan_state = ChanState}) ->
ProtoVer = emqx_channel:info(proto_ver, ChanState),
Data = emqx_frame:serialize(Packet, ProtoVer),
BinSize = iolist_size(Data),
_ = inc_outgoing_stats(Type, BinSize),
{reply, {binary, Data}, ensure_stats_timer(State)}.
inc_incoming_stats(Type) ->
emqx_pd:update_counter(recv_pkt, 1),
(Type == ?PUBLISH)
andalso emqx_pd:update_counter(recv_msg, 1).
ensure_stats_timer(State = #state{enable_stats = true, inc_outgoing_stats(Type, BinSize) ->
stats_timer = undefined, emqx_pd:update_counter(send_cnt, 1),
emqx_pd:update_counter(send_oct, BinSize),
emqx_pd:update_counter(send_pkt, 1),
(Type == ?PUBLISH)
andalso emqx_pd:update_counter(send_msg, 1).
ensure_stats_timer(State = #state{stats_timer = undefined,
idle_timeout = IdleTimeout}) -> idle_timeout = IdleTimeout}) ->
State#state{stats_timer = emqx_misc:start_timer(IdleTimeout, emit_stats)}; TRef = emqx_misc:start_timer(IdleTimeout, emit_stats),
ensure_stats_timer(State) -> State#state{stats_timer = TRef};
State. %% disabled or timer existed
ensure_stats_timer(State) -> State.
-compile({inline, [shutdown/2]}).
shutdown(Reason, State) -> shutdown(Reason, State) ->
%% Fix the issue#2591(https://github.com/emqx/emqx/issues/2591#issuecomment-500278696) %% Fix the issue#2591(https://github.com/emqx/emqx/issues/2591#issuecomment-500278696)
self() ! {stop, Reason}, %% self() ! {stop, Reason},
{ok, State}. {stop, State#state{shutdown = Reason}}.
wsock_stats() -> wsock_stats() ->
[{Key, emqx_pd:get_counter(Key)} || Key <- ?SOCK_STATS]. [{Key, emqx_pd:get_counter(Key)} || Key <- ?SOCK_STATS].
chan_stats() ->
[{Name, emqx_pd:get_counter(Name)} || Name <- ?CHAN_STATS].

View File

@ -1,4 +1,5 @@
%% Copyright (c) 2013-2019 EMQ Technologies Co., Ltd. All Rights Reserved. %%--------------------------------------------------------------------
%% Copyright (c) 2019 EMQ Technologies Co., Ltd. All Rights Reserved.
%% %%
%% Licensed under the Apache License, Version 2.0 (the "License"); %% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License. %% you may not use this file except in compliance with the License.
@ -11,6 +12,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and %% See the License for the specific language governing permissions and
%% limitations under the License. %% limitations under the License.
%%--------------------------------------------------------------------
-module(emqx_zone). -module(emqx_zone).
@ -43,28 +45,32 @@
, code_change/3 , code_change/3
]). ]).
-export_type([zone/0]).
%% dummy state %% dummy state
-record(state, {}). -record(state, {}).
-type(zone() :: atom()).
-define(TAB, ?MODULE). -define(TAB, ?MODULE).
-define(SERVER, ?MODULE). -define(SERVER, ?MODULE).
-define(KEY(Zone, Key), {?MODULE, Zone, Key}). -define(KEY(Zone, Key), {?MODULE, Zone, Key}).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% APIs %% APIs
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
-spec(start_link() -> startlink_ret()). -spec(start_link() -> startlink_ret()).
start_link() -> start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
-spec(get_env(maybe(emqx_types:zone()), atom()) -> maybe(term())). -spec(get_env(maybe(zone()), atom()) -> maybe(term())).
get_env(undefined, Key) -> get_env(undefined, Key) ->
emqx_config:get_env(Key); emqx_config:get_env(Key);
get_env(Zone, Key) -> get_env(Zone, Key) ->
get_env(Zone, Key, undefined). get_env(Zone, Key, undefined).
-spec(get_env(maybe(emqx_types:zone()), atom(), term()) -> maybe(term())). -spec(get_env(maybe(zone()), atom(), term()) -> maybe(term())).
get_env(undefined, Key, Def) -> get_env(undefined, Key, Def) ->
emqx_config:get_env(Key, Def); emqx_config:get_env(Key, Def);
get_env(Zone, Key, Def) -> get_env(Zone, Key, Def) ->
@ -73,7 +79,7 @@ get_env(Zone, Key, Def) ->
emqx_config:get_env(Key, Def) emqx_config:get_env(Key, Def)
end. end.
-spec(set_env(emqx_types:zone(), atom(), term()) -> ok). -spec(set_env(zone(), atom(), term()) -> ok).
set_env(Zone, Key, Val) -> set_env(Zone, Key, Val) ->
gen_server:cast(?SERVER, {set_env, Zone, Key, Val}). gen_server:cast(?SERVER, {set_env, Zone, Key, Val}).
@ -85,9 +91,9 @@ force_reload() ->
stop() -> stop() ->
gen_server:stop(?SERVER, normal, infinity). gen_server:stop(?SERVER, normal, infinity).
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% gen_server callbacks %% gen_server callbacks
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
init([]) -> init([]) ->
_ = do_reload(), _ = do_reload(),
@ -119,9 +125,9 @@ terminate(_Reason, _State) ->
code_change(_OldVsn, State, _Extra) -> code_change(_OldVsn, State, _Extra) ->
{ok, State}. {ok, State}.
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
%% Internal functions %% Internal functions
%%------------------------------------------------------------------------------ %%--------------------------------------------------------------------
do_reload() -> do_reload() ->
[ persistent_term:put(?KEY(Zone, Key), Val) [ persistent_term:put(?KEY(Zone, Key), Val)

View File

@ -23,11 +23,6 @@
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").
-include_lib("common_test/include/ct.hrl"). -include_lib("common_test/include/ct.hrl").
-define(CLIENT, ?CONNECT_PACKET(#mqtt_packet_connect{
client_id = <<"mqtt_client">>,
username = <<"admin">>,
password = <<"public">>})).
all() -> all() ->
[ t_ws_connect_api [ t_ws_connect_api
, t_ws_auth_failure , t_ws_auth_failure
@ -45,17 +40,13 @@ t_ws_auth_failure(_Config) ->
application:set_env(emqx, allow_anonymous, false), application:set_env(emqx, allow_anonymous, false),
WS = rfc6455_client:new("ws://127.0.0.1:8083" ++ "/mqtt", self()), WS = rfc6455_client:new("ws://127.0.0.1:8083" ++ "/mqtt", self()),
{ok, _} = rfc6455_client:open(WS), {ok, _} = rfc6455_client:open(WS),
Packet = raw_send_serialize(?CLIENT), Connect = ?CONNECT_PACKET(
ok = rfc6455_client:send_binary(WS, Packet), #mqtt_packet_connect{
{binary, CONNACK} = rfc6455_client:recv(WS), client_id = <<"mqtt_client">>,
{ok, ?CONNACK_PACKET(?CONNACK_AUTH), <<>>, _} = raw_recv_pase(CONNACK), username = <<"admin">>,
application:set_env(emqx, allow_anonymous, true), password = <<"public">>
ok. }),
ok = rfc6455_client:send_binary(WS, raw_send_serialize(Connect)),
t_ws_connect_api(_Config) ->
WS = rfc6455_client:new("ws://127.0.0.1:8083" ++ "/mqtt", self()),
{ok, _} = rfc6455_client:open(WS),
ok = rfc6455_client:send_binary(WS, raw_send_serialize(?CLIENT)),
{binary, Bin} = rfc6455_client:recv(WS), {binary, Bin} = rfc6455_client:recv(WS),
Connack = ?CONNACK_PACKET(?CONNACK_ACCEPT), Connack = ?CONNACK_PACKET(?CONNACK_ACCEPT),
{ok, Connack, <<>>, _} = raw_recv_pase(Bin), {ok, Connack, <<>>, _} = raw_recv_pase(Bin),
@ -75,7 +66,13 @@ t_ws_connect_api(_Config) ->
t_ws_other_type_frame(_Config) -> t_ws_other_type_frame(_Config) ->
WS = rfc6455_client:new("ws://127.0.0.1:8083" ++ "/mqtt", self()), WS = rfc6455_client:new("ws://127.0.0.1:8083" ++ "/mqtt", self()),
{ok, _} = rfc6455_client:open(WS), {ok, _} = rfc6455_client:open(WS),
ok = rfc6455_client:send_binary(WS, raw_send_serialize(?CLIENT)), Connect = ?CONNECT_PACKET(
#mqtt_packet_connect{
client_id = <<"mqtt_client">>,
username = <<"admin">>,
password = <<"public">>
}),
ok = rfc6455_client:send_binary(WS, raw_send_serialize(Connect)),
{binary, Bin} = rfc6455_client:recv(WS), {binary, Bin} = rfc6455_client:recv(WS),
Connack = ?CONNACK_PACKET(?CONNACK_ACCEPT), Connack = ?CONNACK_PACKET(?CONNACK_ACCEPT),
{ok, Connack, <<>>, _} = raw_recv_pase(Bin), {ok, Connack, <<>>, _} = raw_recv_pase(Bin),
@ -110,3 +107,4 @@ t_stats(StatsData) ->
?assertEqual(true, proplists:get_value(recv_pkt, StatsData) =:=1), ?assertEqual(true, proplists:get_value(recv_pkt, StatsData) =:=1),
?assertEqual(true, proplists:get_value(recv_msg, StatsData) >=0), ?assertEqual(true, proplists:get_value(recv_msg, StatsData) >=0),
?assertEqual(true, proplists:get_value(send_pkt, StatsData) =:=1). ?assertEqual(true, proplists:get_value(send_pkt, StatsData) =:=1).