Add more service modules for MQTT Version 5.0
This commit is contained in:
parent
bbb66ff92e
commit
2a4ffc6645
|
@ -1,18 +1,18 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All Rights Reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Banner
|
||||
|
@ -154,7 +154,10 @@
|
|||
%% Route
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-record(route, { topic :: topic(), dest }).
|
||||
-record(route,
|
||||
{ topic :: topic(),
|
||||
dest :: node() | {binary(), node()}
|
||||
}).
|
||||
|
||||
-type(route() :: #route{}).
|
||||
|
||||
|
|
57
src/emqx.erl
57
src/emqx.erl
|
@ -1,18 +1,18 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx).
|
||||
|
||||
|
@ -29,7 +29,7 @@
|
|||
-export([topics/0, subscriptions/1, subscribers/1, subscribed/2]).
|
||||
|
||||
%% Get/Set suboptions
|
||||
-export([getopts/2, setopts/3]).
|
||||
-export([get_subopts/2, set_subopts/3]).
|
||||
|
||||
%% Hooks API
|
||||
-export([hook/4, hook/3, unhook/2, run_hooks/2, run_hooks/3]).
|
||||
|
@ -47,12 +47,17 @@
|
|||
%%--------------------------------------------------------------------
|
||||
|
||||
%% @doc Start emqx application
|
||||
-spec(start() -> ok | {error, term()}).
|
||||
start() -> application:start(?APP).
|
||||
-spec(start() -> {ok, list(atom())} | {error, term()}).
|
||||
start() ->
|
||||
%% Check OS
|
||||
%% Check VM
|
||||
%% Check Mnesia
|
||||
application:ensure_all_started(?APP).
|
||||
|
||||
%% @doc Stop emqx application.
|
||||
-spec(stop() -> ok | {error, term()}).
|
||||
stop() -> application:stop(?APP).
|
||||
stop() ->
|
||||
application:stop(?APP).
|
||||
|
||||
%% @doc Is emqx running?
|
||||
-spec(is_running(node()) -> boolean()).
|
||||
|
@ -96,13 +101,13 @@ unsubscribe(Topic, Subscriber) ->
|
|||
%% PubSub management API
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-spec(getopts(topic() | string(), subscriber()) -> [suboption()]).
|
||||
getopts(Topic, Subscriber) ->
|
||||
emqx_broker:getopts(iolist_to_binary(Topic), list_to_subid(Subscriber)).
|
||||
-spec(get_subopts(topic() | string(), subscriber()) -> [suboption()]).
|
||||
get_subopts(Topic, Subscriber) ->
|
||||
emqx_broker:get_subopts(iolist_to_binary(Topic), list_to_subid(Subscriber)).
|
||||
|
||||
-spec(setopts(topic() | string(), subscriber(), [suboption()]) -> ok).
|
||||
setopts(Topic, Subscriber, Options) when is_list(Options) ->
|
||||
emqx_broker:setopts(iolist_to_binary(Topic), list_to_subid(Subscriber), Options).
|
||||
-spec(set_subopts(topic() | string(), subscriber(), [suboption()]) -> ok).
|
||||
set_subopts(Topic, Subscriber, Options) when is_list(Options) ->
|
||||
emqx_broker:set_subopts(iolist_to_binary(Topic), list_to_subid(Subscriber), Options).
|
||||
|
||||
-spec(topics() -> list(topic())).
|
||||
topics() -> emqx_router:topics().
|
||||
|
@ -165,7 +170,7 @@ shutdown() ->
|
|||
shutdown(normal).
|
||||
|
||||
shutdown(Reason) ->
|
||||
emqx_log:error("EMQ shutdown for ~s", [Reason]),
|
||||
emqx_logger:error("EMQ shutdown for ~s", [Reason]),
|
||||
emqx_plugins:unload(),
|
||||
lists:foreach(fun application:stop/1, [emqx, ekka, mochiweb, esockd, gproc]).
|
||||
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_access_control).
|
||||
|
||||
|
@ -28,10 +28,9 @@
|
|||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-define(TAB, ?MODULE).
|
||||
-define(SERVER, ?MODULE).
|
||||
|
||||
-define(TAB, access_control).
|
||||
|
||||
-type(password() :: undefined | binary()).
|
||||
|
||||
-record(state, {}).
|
||||
|
@ -122,7 +121,7 @@ stop() ->
|
|||
%%--------------------------------------------------------------------
|
||||
|
||||
init([]) ->
|
||||
_ = emqx_tables:create(?TAB, [set, protected, {read_concurrency, true}]),
|
||||
_ = emqx_tables:new(?TAB, [set, protected, {read_concurrency, true}]),
|
||||
{ok, #state{}}.
|
||||
|
||||
handle_call({register_mod, Type, Mod, Opts, Seq}, _From, State) ->
|
||||
|
@ -157,15 +156,15 @@ handle_call(stop, _From, State) ->
|
|||
{stop, normal, ok, State};
|
||||
|
||||
handle_call(Req, _From, State) ->
|
||||
emqx_log:error("[AccessControl] Unexpected request: ~p", [Req]),
|
||||
emqx_logger:error("[AccessControl] Unexpected request: ~p", [Req]),
|
||||
{reply, ignore, State}.
|
||||
|
||||
handle_cast(Msg, State) ->
|
||||
emqx_log:error("[AccessControl] Unexpected msg: ~p", [Msg]),
|
||||
emqx_logger:error("[AccessControl] Unexpected msg: ~p", [Msg]),
|
||||
{noreply, State}.
|
||||
|
||||
handle_info(Info, State) ->
|
||||
emqx_log:error("[AccessControl] Unexpected info: ~p", [Info]),
|
||||
emqx_logger:error("[AccessControl] Unexpected info: ~p", [Info]),
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, _State) ->
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_acl_internal).
|
||||
|
||||
|
@ -25,7 +25,7 @@
|
|||
%% ACL callbacks
|
||||
-export([init/1, check_acl/2, reload_acl/1, description/0]).
|
||||
|
||||
-define(ACL_RULE_TAB, mqtt_acl_rule).
|
||||
-define(ACL_RULE_TAB, emqx_acl_rule).
|
||||
|
||||
-record(state, {config}).
|
||||
|
||||
|
@ -48,7 +48,7 @@ all_rules() ->
|
|||
%% @doc Init internal ACL
|
||||
-spec(init([File :: string()]) -> {ok, State :: any()}).
|
||||
init([File]) ->
|
||||
_ = emqx_tables:create(?ACL_RULE_TAB, [set, public, {read_concurrency, true}]),
|
||||
_ = emqx_tables:new(?ACL_RULE_TAB, [set, public, {read_concurrency, true}]),
|
||||
{ok, load_rules_from_file(#state{config = File})}.
|
||||
|
||||
load_rules_from_file(State = #state{config = AclFile}) ->
|
||||
|
|
|
@ -92,7 +92,7 @@ handle_event({set_alarm, Alarm = #alarm{id = AlarmId,
|
|||
{summary, iolist_to_binary(Summary)},
|
||||
{ts, emqx_time:now_secs(TS)}]) of
|
||||
{'EXIT', Reason} ->
|
||||
emqx_log:error("[Alarm] Failed to encode set_alarm: ~p", [Reason]);
|
||||
emqx_logger:error("[Alarm] Failed to encode set_alarm: ~p", [Reason]);
|
||||
JSON ->
|
||||
emqx_broker:publish(alarm_msg(alert, AlarmId, JSON))
|
||||
end,
|
||||
|
@ -101,7 +101,7 @@ handle_event({set_alarm, Alarm = #alarm{id = AlarmId,
|
|||
handle_event({clear_alarm, AlarmId}, Alarms) ->
|
||||
case catch emqx_json:encode([{id, AlarmId}, {ts, emqx_time:now_secs()}]) of
|
||||
{'EXIT', Reason} ->
|
||||
emqx_log:error("[Alarm] Failed to encode clear_alarm: ~p", [Reason]);
|
||||
emqx_logger:error("[Alarm] Failed to encode clear_alarm: ~p", [Reason]);
|
||||
JSON ->
|
||||
emqx_broker:publish(alarm_msg(clear, AlarmId, JSON))
|
||||
end,
|
||||
|
|
|
@ -34,7 +34,6 @@ start(_Type, _Args) ->
|
|||
ekka:start(),
|
||||
{ok, Sup} = emqx_sup:start_link(),
|
||||
%%TODO: fixme later
|
||||
emqx_mqtt_metrics:init(),
|
||||
ok = register_acl_mod(),
|
||||
emqx_modules:load(),
|
||||
start_autocluster(),
|
||||
|
|
|
@ -62,13 +62,13 @@ passwd_hash(pbkdf2, {Salt, Password, Macfun, Iterations, Dklen}) ->
|
|||
case pbkdf2:pbkdf2(Macfun, Password, Salt, Iterations, Dklen) of
|
||||
{ok, Hexstring} -> pbkdf2:to_hex(Hexstring);
|
||||
{error, Error} ->
|
||||
emqx_log:error("[AuthMod] PasswdHash with pbkdf2 error:~p", [Error]), <<>>
|
||||
emqx_logger:error("[AuthMod] PasswdHash with pbkdf2 error:~p", [Error]), <<>>
|
||||
end;
|
||||
passwd_hash(bcrypt, {Salt, Password}) ->
|
||||
case bcrypt:hashpw(Password, Salt) of
|
||||
{ok, HashPassword} -> list_to_binary(HashPassword);
|
||||
{error, Error}->
|
||||
emqx_log:error("[AuthMod] PasswdHash with bcrypt error:~p", [Error]), <<>>
|
||||
emqx_logger:error("[AuthMod] PasswdHash with bcrypt error:~p", [Error]), <<>>
|
||||
end.
|
||||
|
||||
hexstring(<<X:128/big-unsigned-integer>>) ->
|
||||
|
|
|
@ -1,72 +1,138 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All Rights Reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
%% Banned an IP Address, ClientId?
|
||||
-module(emqx_banned).
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
-include("emqx.hrl").
|
||||
|
||||
%% Mnesia bootstrap
|
||||
-export([mnesia/1]).
|
||||
|
||||
-boot_mnesia({mnesia, [boot]}).
|
||||
-copy_mnesia({mnesia, [copy]}).
|
||||
|
||||
%% API
|
||||
-export([start_link/0]).
|
||||
-export([check/1]).
|
||||
-export([add/1, del/1]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-define(TAB, ?MODULE).
|
||||
-define(SERVER, ?MODULE).
|
||||
|
||||
-record(state, {}).
|
||||
-type(key() :: {client_id, client_id()} |
|
||||
{ipaddr, inet:ip_address()} |
|
||||
{username, username()}).
|
||||
|
||||
%%%===================================================================
|
||||
%%% API
|
||||
%%%===================================================================
|
||||
-record(state, {expiry_timer}).
|
||||
|
||||
%% @doc Starts the server
|
||||
-record(banned, {key :: key(), reason, by, desc, until}).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Mnesia bootstrap
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
mnesia(boot) ->
|
||||
ok = ekka_mnesia:create_table(?TAB, [
|
||||
{type, ordered_set},
|
||||
{disc_copies, [node()]},
|
||||
{record_name, banned},
|
||||
{attributes, record_info(fields, banned)}]);
|
||||
|
||||
mnesia(copy) ->
|
||||
ok = ekka_mnesia:copy_table(?TAB).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% API
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
%% @doc Start the banned server
|
||||
-spec(start_link() -> {ok, pid()} | ignore | {error, any()}).
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
|
||||
|
||||
%%%===================================================================
|
||||
%%% gen_server callbacks
|
||||
%%%===================================================================
|
||||
-spec(check(client()) -> boolean()).
|
||||
check(#client{client_id = ClientId,
|
||||
username = Username,
|
||||
peername = {IPAddr, _}}) ->
|
||||
ets:member(?TAB, {client_id, ClientId})
|
||||
orelse ets:member(?TAB, {username, Username})
|
||||
orelse ets:member(?TAB, {ipaddr, IPAddr}).
|
||||
|
||||
add(Record) when is_record(Record, banned) ->
|
||||
mnesia:dirty_write(?TAB, Record).
|
||||
|
||||
del(Key) ->
|
||||
mnesia:dirty_delete(?TAB, Key).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% gen_server callbacks
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
init([]) ->
|
||||
{ok, #state{}}.
|
||||
emqx_timer:seed(),
|
||||
{ok, ensure_expiry_timer(#state{})}.
|
||||
|
||||
handle_call(_Request, _From, State) ->
|
||||
Reply = ok,
|
||||
{reply, Reply, State}.
|
||||
handle_call(Req, _From, State) ->
|
||||
emqx_logger:error("[BANNED] Unexpected request: ~p", [Req]),
|
||||
{reply, ignore, State}.
|
||||
|
||||
handle_cast(_Msg, State) ->
|
||||
handle_cast(Msg, State) ->
|
||||
emqx_logger:error("[BANNED] Unexpected msg: ~p", [Msg]),
|
||||
{noreply, State}.
|
||||
|
||||
handle_info(_Info, State) ->
|
||||
handle_info({timeout, Ref, expire}, State = #state{expiry_timer = Ref}) ->
|
||||
mnesia:async_dirty(fun expire_banned_items/1, [erlang:timestamp()]),
|
||||
{noreply, ensure_expiry_timer(State), hibernate};
|
||||
|
||||
handle_info(Info, State) ->
|
||||
emqx_logger:error("[BANNED] Unexpected info: ~p", [Info]),
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, _State) ->
|
||||
ok.
|
||||
terminate(_Reason, #state{expiry_timer = Timer}) ->
|
||||
emqx_misc:cancel_timer(Timer).
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
%%%===================================================================
|
||||
%%% Internal functions
|
||||
%%%===================================================================
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
ensure_expiry_timer(State) ->
|
||||
Interval = emqx_config:get_env(banned_expiry_interval, timer:minutes(5)),
|
||||
State#state{expiry_timer = emqx_misc:start_timer(
|
||||
Interval + rand:uniform(Interval), expire)}.
|
||||
|
||||
expire_banned_items(Now) ->
|
||||
expire_banned_item(mnesia:first(?TAB), Now).
|
||||
|
||||
expire_banned_item('$end_of_table', _Now) ->
|
||||
ok;
|
||||
expire_banned_item(Key, Now) ->
|
||||
case mnesia:read(?TAB, Key) of
|
||||
[#banned{until = undefined}] -> ok;
|
||||
[B = #banned{until = Until}] when Until < Now ->
|
||||
mnesia:delete_object(?TAB, B, sticky_write);
|
||||
[] -> ok
|
||||
end,
|
||||
expire_banned_item(mnesia:next(Key), Now).
|
||||
|
||||
|
|
|
@ -103,11 +103,11 @@ qname(Node, Topic) ->
|
|||
iolist_to_binary(["Bridge:", Node, ":", Topic]).
|
||||
|
||||
handle_call(Req, _From, State) ->
|
||||
emqx_log:error("[Bridge] Unexpected request: ~p", [Req]),
|
||||
emqx_logger:error("[Bridge] Unexpected request: ~p", [Req]),
|
||||
{reply, ignore, State}.
|
||||
|
||||
handle_cast(Msg, State) ->
|
||||
emqx_log:error("[Bridge] Unexpected msg: ~p", [Msg]),
|
||||
emqx_logger:error("[Bridge] Unexpected msg: ~p", [Msg]),
|
||||
{noreply, State}.
|
||||
|
||||
handle_info({dispatch, _Topic, Msg}, State = #state{mqueue = MQ, status = down}) ->
|
||||
|
@ -118,7 +118,7 @@ handle_info({dispatch, _Topic, Msg}, State = #state{node = Node, status = up}) -
|
|||
{noreply, State, hibernate};
|
||||
|
||||
handle_info({nodedown, Node}, State = #state{node = Node, ping_down_interval = Interval}) ->
|
||||
emqx_log:warning("[Bridge] Node Down: ~s", [Node]),
|
||||
emqx_logger:warning("[Bridge] Node Down: ~s", [Node]),
|
||||
erlang:send_after(Interval, self(), ping_down_node),
|
||||
{noreply, State#state{status = down}, hibernate};
|
||||
|
||||
|
@ -126,7 +126,7 @@ handle_info({nodeup, Node}, State = #state{node = Node}) ->
|
|||
%% TODO: Really fast??
|
||||
case emqx:is_running(Node) of
|
||||
true ->
|
||||
emqx_log:warning("[Bridge] Node up: ~s", [Node]),
|
||||
emqx_logger:warning("[Bridge] Node up: ~s", [Node]),
|
||||
{noreply, dequeue(State#state{status = up})};
|
||||
false ->
|
||||
self() ! {nodedown, Node},
|
||||
|
@ -149,7 +149,7 @@ handle_info({'EXIT', _Pid, normal}, State) ->
|
|||
{noreply, State};
|
||||
|
||||
handle_info(Info, State) ->
|
||||
emqx_log:error("[Bridge] Unexpected info: ~p", [Info]),
|
||||
emqx_logger:error("[Bridge] Unexpected info: ~p", [Info]),
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, #state{pool = Pool, id = Id}) ->
|
||||
|
|
|
@ -1,24 +1,27 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_bridge_sup_sup).
|
||||
|
||||
-behavior(supervisor).
|
||||
|
||||
-export([start_link/0, bridges/0, start_bridge/2, start_bridge/3, stop_bridge/2]).
|
||||
-include("emqx.hrl").
|
||||
|
||||
-export([start_link/0, bridges/0]).
|
||||
-export([start_bridge/2, start_bridge/3, stop_bridge/2]).
|
||||
|
||||
-export([init/1]).
|
||||
|
||||
|
@ -32,27 +35,28 @@ start_link() ->
|
|||
%%--------------------------------------------------------------------
|
||||
|
||||
%% @doc List all bridges
|
||||
-spec(bridges() -> [{node(), binary(), pid()}]).
|
||||
-spec(bridges() -> [{node(), topic(), pid()}]).
|
||||
bridges() ->
|
||||
[{Node, Topic, Pid} || {?CHILD_ID(Node, Topic), Pid, supervisor, _}
|
||||
<- supervisor:which_children(?MODULE)].
|
||||
|
||||
%% @doc Start a bridge
|
||||
-spec(start_bridge(atom(), binary()) -> {ok, pid()} | {error, term()}).
|
||||
start_bridge(Node, Topic) when is_atom(Node) andalso is_binary(Topic) ->
|
||||
-spec(start_bridge(node(), topic()) -> {ok, pid()} | {error, term()}).
|
||||
start_bridge(Node, Topic) when is_atom(Node), is_binary(Topic) ->
|
||||
start_bridge(Node, Topic, []).
|
||||
|
||||
-spec(start_bridge(atom(), binary(), [emqx_bridge:option()]) -> {ok, pid()} | {error, term()}).
|
||||
-spec(start_bridge(node(), topic(), [emqx_bridge:option()])
|
||||
-> {ok, pid()} | {error, term()}).
|
||||
start_bridge(Node, _Topic, _Options) when Node =:= node() ->
|
||||
{error, bridge_to_self};
|
||||
start_bridge(Node, Topic, Options) when is_atom(Node) andalso is_binary(Topic) ->
|
||||
start_bridge(Node, Topic, Options) when is_atom(Node), is_binary(Topic) ->
|
||||
{ok, BridgeEnv} = emqx_config:get_env(bridge),
|
||||
Options1 = emqx_misc:merge_opts(BridgeEnv, Options),
|
||||
supervisor:start_child(?MODULE, bridge_spec(Node, Topic, Options1)).
|
||||
|
||||
%% @doc Stop a bridge
|
||||
-spec(stop_bridge(atom(), binary()) -> {ok, pid()} | ok).
|
||||
stop_bridge(Node, Topic) when is_atom(Node) andalso is_binary(Topic) ->
|
||||
-spec(stop_bridge(node(), topic()) -> ok | {error, term()}).
|
||||
stop_bridge(Node, Topic) when is_atom(Node), is_binary(Topic) ->
|
||||
ChildId = ?CHILD_ID(Node, Topic),
|
||||
case supervisor:terminate_child(?MODULE, ChildId) of
|
||||
ok -> supervisor:delete_child(?MODULE, ChildId);
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_broker).
|
||||
|
||||
|
@ -53,9 +53,11 @@
|
|||
%% Start a broker
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-spec(start_link(atom(), pos_integer()) -> {ok, pid()} | ignore | {error, term()}).
|
||||
-spec(start_link(atom(), pos_integer())
|
||||
-> {ok, pid()} | ignore | {error, term()}).
|
||||
start_link(Pool, Id) ->
|
||||
gen_server:start_link(?MODULE, [Pool, Id], [{hibernate_after, 2000}]).
|
||||
gen_server:start_link(emqx_misc:proc_name(?MODULE, Id),
|
||||
?MODULE, [Pool, Id], [{hibernate_after, 2000}]).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Subscriber/Unsubscribe
|
||||
|
@ -101,15 +103,32 @@ unsubscribe(Topic, Subscriber, Timeout) ->
|
|||
|
||||
-spec(publish(message()) -> delivery() | stopped).
|
||||
publish(Msg = #message{from = From}) ->
|
||||
emqx_tracer:trace(publish, From, Msg),
|
||||
%% Hook to trace?
|
||||
trace(public, From, Msg),
|
||||
case emqx_hooks:run('message.publish', [], Msg) of
|
||||
{ok, Msg1 = #message{topic = Topic}} ->
|
||||
publish(Topic, Msg1);
|
||||
{stop, Msg1} ->
|
||||
emqx_log:warning("Stop publishing: ~s", [emqx_message:format(Msg1)]),
|
||||
emqx_logger:warning("Stop publishing: ~s", [emqx_message:format(Msg1)]),
|
||||
stopped
|
||||
end.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Trace
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
trace(publish, From, _Msg) when is_atom(From) ->
|
||||
%% Dont' trace '$SYS' publish
|
||||
ignore;
|
||||
trace(public, #client{client_id = ClientId, username = Username},
|
||||
#message{topic = Topic, payload = Payload}) ->
|
||||
emqx_logger:info([{client, ClientId}, {topic, Topic}],
|
||||
"~s/~s PUBLISH to ~s: ~p", [ClientId, Username, Topic, Payload]);
|
||||
trace(public, From, #message{topic = Topic, payload = Payload})
|
||||
when is_binary(From); is_list(From) ->
|
||||
emqx_logger:info([{client, From}, {topic, Topic}],
|
||||
"~s PUBLISH to ~s: ~p", [From, Topic, Payload]).
|
||||
|
||||
publish(Topic, Msg) ->
|
||||
route(aggre(emqx_router:match_routes(Topic)), delivery(Msg)).
|
||||
|
||||
|
@ -131,16 +150,14 @@ route(Routes, Delivery) ->
|
|||
|
||||
aggre([]) ->
|
||||
[];
|
||||
aggre([{To, Dest}]) ->
|
||||
aggre([#route{topic = To, dest = Dest}]) ->
|
||||
[{To, Dest}];
|
||||
aggre(Routes) ->
|
||||
lists:foldl(
|
||||
fun({To, Node}, Acc) when is_atom(Node) ->
|
||||
fun(#route{topic = To, dest = Node}, Acc) when is_atom(Node) ->
|
||||
[{To, Node} | Acc];
|
||||
({To, {Group, _Node}}, Acc) ->
|
||||
lists:usort([{To, Group} | Acc]);
|
||||
({To, {Cluster, Group, _Node}}, Acc) ->
|
||||
lists:usort([{To, {Cluster, Group}} | Acc])
|
||||
(#route{topic = To, dest = {Group, _Node}}, Acc) ->
|
||||
lists:usort([{To, Group} | Acc])
|
||||
end, [], Routes).
|
||||
|
||||
%% @doc Forward message to another node.
|
||||
|
@ -148,7 +165,7 @@ forward(Node, To, Delivery) ->
|
|||
%% rpc:call to ensure the delivery, but the latency:(
|
||||
case emqx_rpc:call(Node, ?BROKER, dispatch, [To, Delivery]) of
|
||||
{badrpc, Reason} ->
|
||||
emqx_log:error("[Broker] Failed to forward msg to ~s: ~p", [Node, Reason]),
|
||||
emqx_logger:error("[Broker] Failed to forward msg to ~s: ~p", [Node, Reason]),
|
||||
Delivery;
|
||||
Delivery1 -> Delivery1
|
||||
end.
|
||||
|
@ -261,7 +278,7 @@ handle_call({set_subopts, Topic, Subscriber, Opts}, _From, State) ->
|
|||
end;
|
||||
|
||||
handle_call(Request, _From, State) ->
|
||||
emqx_log:error("[Broker] Unexpected request: ~p", [Request]),
|
||||
emqx_logger:error("[Broker] Unexpected request: ~p", [Request]),
|
||||
{reply, ignore, State}.
|
||||
|
||||
handle_cast({From, {subscribe, Topic, Subscriber, Options}}, State) ->
|
||||
|
@ -292,7 +309,7 @@ handle_cast({From, {unsubscribe, Topic, Subscriber}}, State) ->
|
|||
{noreply, State};
|
||||
|
||||
handle_cast(Msg, State) ->
|
||||
emqx_log:error("[Broker] Unexpected msg: ~p", [Msg]),
|
||||
emqx_logger:error("[Broker] Unexpected msg: ~p", [Msg]),
|
||||
{noreply, State}.
|
||||
|
||||
handle_info({'DOWN', _MRef, process, SubPid, _Reason},
|
||||
|
@ -321,7 +338,7 @@ handle_info({'DOWN', _MRef, process, SubPid, _Reason},
|
|||
{noreply, demonitor_subscriber(SubPid, State)};
|
||||
|
||||
handle_info(Info, State) ->
|
||||
emqx_log:error("[Broker] Unexpected info: ~p", [Info]),
|
||||
emqx_logger:error("[Broker] Unexpected info: ~p", [Info]),
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, #state{pool = Pool, id = Id}) ->
|
||||
|
|
|
@ -1,66 +1,79 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_broker_helper).
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
-export([start_link/1]).
|
||||
-export([start_link/0]).
|
||||
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-define(SERVER, ?MODULE).
|
||||
-define(HELPER, ?MODULE).
|
||||
|
||||
-record(state, {stats_fun, stats_timer}).
|
||||
-record(state, {}).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% API
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-spec(start_link(fun()) -> {ok, pid()} | ignore | {error, any()}).
|
||||
start_link(StatsFun) ->
|
||||
gen_server:start_link({local, ?SERVER}, ?MODULE, [StatsFun], []).
|
||||
-spec(start_link() -> {ok, pid()} | ignore | {error, any()}).
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?HELPER}, ?MODULE, [], []).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% gen_server callbacks
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
init([StatsFun]) ->
|
||||
{ok, TRef} = timer:send_interval(timer:seconds(1), stats),
|
||||
{ok, #state{stats_fun = StatsFun, stats_timer = TRef}}.
|
||||
init([]) ->
|
||||
emqx_stats:update_interval(broker_stats, stats_fun()),
|
||||
{ok, #state{}, hibernate}.
|
||||
|
||||
handle_call(Req, _From, State) ->
|
||||
emqx_log:error("[BrokerHelper] Unexpected request: ~p", [Req]),
|
||||
emqx_logger:error("[BrokerHelper] Unexpected request: ~p", [Req]),
|
||||
{reply, ignore, State}.
|
||||
|
||||
handle_cast(Msg, State) ->
|
||||
emqx_log:error("[BrokerHelper] Unexpected msg: ~p", [Msg]),
|
||||
emqx_logger:error("[BrokerHelper] Unexpected msg: ~p", [Msg]),
|
||||
{noreply, State}.
|
||||
|
||||
handle_info(stats, State = #state{stats_fun = StatsFun}) ->
|
||||
StatsFun(), {noreply, State, hibernate};
|
||||
|
||||
handle_info(Info, State) ->
|
||||
emqx_log:error("[BrokerHelper] Unexpected info: ~p", [Info]),
|
||||
emqx_logger:error("[BrokerHelper] Unexpected info: ~p", [Info]),
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, #state{stats_timer = TRef}) ->
|
||||
timer:cancel(TRef).
|
||||
terminate(_Reason, #state{}) ->
|
||||
emqx_stats:cancel_update(broker_stats).
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
stats_fun() ->
|
||||
fun() ->
|
||||
safe_update_stats(emqx_subscriber,
|
||||
'subscribers/count', 'subscribers/max'),
|
||||
safe_update_stats(emqx_subscription,
|
||||
'subscriptions/count', 'subscriptions/max'),
|
||||
safe_update_stats(emqx_suboptions,
|
||||
'suboptions/count', 'suboptions/max')
|
||||
end.
|
||||
|
||||
safe_update_stats(Tab, Stat, MaxStat) ->
|
||||
case ets:info(Tab, size) of
|
||||
undefined -> ok;
|
||||
Size -> emqx_stats:setstat(Stat, MaxStat, Size)
|
||||
end.
|
||||
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_broker_sup).
|
||||
|
||||
|
@ -22,7 +22,11 @@
|
|||
|
||||
-export([init/1]).
|
||||
|
||||
-define(CONCURRENCY_OPTS, [{read_concurrency, true}, {write_concurrency, true}]).
|
||||
-import(lists, [foreach/2]).
|
||||
|
||||
-define(TAB_OPTS, [public,
|
||||
{read_concurrency, true},
|
||||
{write_concurrency, true}]).
|
||||
|
||||
start_link() ->
|
||||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||
|
@ -33,24 +37,23 @@ start_link() ->
|
|||
|
||||
init([]) ->
|
||||
%% Create the pubsub tables
|
||||
lists:foreach(fun create_tab/1,
|
||||
[subscription, subscriber, suboption]),
|
||||
foreach(fun create_tab/1, [subscription, subscriber, suboption]),
|
||||
|
||||
%% Shared subscription
|
||||
Shared = {shared_sub, {emqx_shared_sub, start_link, []},
|
||||
SharedSub = {shared_sub, {emqx_shared_sub, start_link, []},
|
||||
permanent, 5000, worker, [emqx_shared_sub]},
|
||||
|
||||
%% Broker helper
|
||||
Helper = {broker_helper, {emqx_broker_helper, start_link, [stats_fun()]},
|
||||
Helper = {broker_helper, {emqx_broker_helper, start_link, []},
|
||||
permanent, 5000, worker, [emqx_broker_helper]},
|
||||
|
||||
%% Broker pool
|
||||
PoolArgs = [broker, hash, emqx_sys:schedulers() * 2,
|
||||
PoolArgs = [broker, hash, emqx_vm:schedulers() * 2,
|
||||
{emqx_broker, start_link, []}],
|
||||
|
||||
PoolSup = emqx_pool_sup:spec(broker_pool, PoolArgs),
|
||||
PoolSup = emqx_pool_sup:spec(eqmx_broker_pool, PoolArgs),
|
||||
|
||||
{ok, {{one_for_all, 0, 3600}, [Shared, Helper, PoolSup]}}.
|
||||
{ok, {{one_for_all, 0, 1}, [SharedSub, Helper, PoolSup]}}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Create tables
|
||||
|
@ -58,27 +61,15 @@ init([]) ->
|
|||
|
||||
create_tab(suboption) ->
|
||||
%% Suboption: {Topic, Sub} -> [{qos, 1}]
|
||||
emqx_tables:create(emqx_suboption, [public, set | ?CONCURRENCY_OPTS]);
|
||||
emqx_tables:new(emqx_suboption, [set | ?TAB_OPTS]);
|
||||
|
||||
create_tab(subscriber) ->
|
||||
%% Subscriber: Topic -> Sub1, Sub2, Sub3, ..., SubN
|
||||
%% duplicate_bag: o(1) insert
|
||||
emqx_tables:create(emqx_subscriber, [public, duplicate_bag | ?CONCURRENCY_OPTS]);
|
||||
emqx_tables:new(emqx_subscriber, [duplicate_bag | ?TAB_OPTS]);
|
||||
|
||||
create_tab(subscription) ->
|
||||
%% Subscription: Sub -> Topic1, Topic2, Topic3, ..., TopicN
|
||||
%% bag: o(n) insert
|
||||
emqx_tables:create(emqx_subscription, [public, bag | ?CONCURRENCY_OPTS]).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Stats function
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
stats_fun() ->
|
||||
fun() ->
|
||||
emqx_stats:setstat('subscribers/count', 'subscribers/max',
|
||||
ets:info(emqx_subscriber, size)),
|
||||
emqx_stats:setstat('subscriptions/count', 'subscriptions/max',
|
||||
ets:info(emqx_subscription, size))
|
||||
end.
|
||||
emqx_tables:new(emqx_subscription, [bag | ?TAB_OPTS]).
|
||||
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_cli).
|
||||
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_client).
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
%% API
|
||||
-export([start_link/0]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-define(SERVER, ?MODULE).
|
||||
|
||||
-record(state, {}).
|
||||
|
||||
%%%===================================================================
|
||||
%%% API
|
||||
%%%===================================================================
|
||||
|
||||
%% @doc Starts the server
|
||||
-spec(start_link() -> {ok, pid()} | ignore | {error, any()}).
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
|
||||
|
||||
%%%===================================================================
|
||||
%%% gen_server callbacks
|
||||
%%%===================================================================
|
||||
|
||||
init([]) ->
|
||||
{ok, #state{}}.
|
||||
|
||||
handle_call(_Request, _From, State) ->
|
||||
Reply = ok,
|
||||
{reply, Reply, State}.
|
||||
|
||||
handle_cast(_Msg, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, _State) ->
|
||||
ok.
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
%%%===================================================================
|
||||
%%% Internal functions
|
||||
%%%===================================================================
|
||||
|
||||
|
||||
|
||||
|
205
src/emqx_cm.erl
205
src/emqx_cm.erl
|
@ -1,18 +1,18 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_cm).
|
||||
|
||||
|
@ -22,92 +22,145 @@
|
|||
|
||||
-export([start_link/0]).
|
||||
|
||||
-export([lookup/1, reg/1, unreg/1]).
|
||||
-export([lookup_client/1, register_client/1, register_client/2,
|
||||
unregister_client/1]).
|
||||
|
||||
-export([get_client_attrs/1, lookup_client_pid/1]).
|
||||
|
||||
-export([get_client_stats/1, set_client_stats/2]).
|
||||
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-record(state, {stats_fun, stats_timer, monitors}).
|
||||
-record(state, {client_pmon}).
|
||||
|
||||
-define(SERVER, ?MODULE).
|
||||
-define(CM, ?MODULE).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% API
|
||||
%%--------------------------------------------------------------------
|
||||
%% ETS Tables.
|
||||
-define(CLIENT, emqx_client).
|
||||
-define(CLIENT_ATTRS, emqx_client_attrs).
|
||||
-define(CLIENT_STATS, emqx_client_stats).
|
||||
|
||||
%% @doc Start the client manager
|
||||
%% @doc Start the client manager.
|
||||
-spec(start_link() -> {ok, pid()} | ignore | {error, term()}).
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
|
||||
gen_server:start_link({local, ?CM}, ?MODULE, [], []).
|
||||
|
||||
%% @doc Lookup ClientPid by ClientId
|
||||
-spec(lookup(client_id()) -> pid() | undefined).
|
||||
lookup(ClientId) when is_binary(ClientId) ->
|
||||
try ets:lookup_element(client, ClientId, 2)
|
||||
%% @doc Lookup a client.
|
||||
-spec(lookup_client(client_id()) -> list({client_id(), pid()})).
|
||||
lookup_client(ClientId) when is_binary(ClientId) ->
|
||||
ets:lookup(?CLIENT, ClientId).
|
||||
|
||||
%% @doc Register a client.
|
||||
-spec(register_client(client_id() | {client_id(), pid()}) -> ok).
|
||||
register_client(ClientId) when is_binary(ClientId) ->
|
||||
register_client({ClientId, self()});
|
||||
|
||||
register_client({ClientId, ClientPid}) when is_binary(ClientId),
|
||||
is_pid(ClientPid) ->
|
||||
register_client({ClientId, ClientPid}, []).
|
||||
|
||||
-spec(register_client({client_id(), pid()}, list()) -> ok).
|
||||
register_client({ClientId, ClientPid}, Attrs) when is_binary(ClientId),
|
||||
is_pid(ClientPid) ->
|
||||
ets:insert(?CLIENT, {ClientId, ClientPid}),
|
||||
ets:insert(?CLIENT_ATTRS, {{ClientId, ClientPid}, Attrs}),
|
||||
notify({registered, ClientId, ClientPid}).
|
||||
|
||||
%% @doc Get client attrs
|
||||
-spec(get_client_attrs({client_id(), pid()}) -> list()).
|
||||
get_client_attrs({ClientId, ClientPid}) when is_binary(ClientId),
|
||||
is_pid(ClientPid) ->
|
||||
try ets:lookup_element(?CLIENT_ATTRS, {ClientId, ClientPid}, 2)
|
||||
catch
|
||||
error:badarg -> undefined
|
||||
error:badarg -> []
|
||||
end.
|
||||
|
||||
%% @doc Register a clientId
|
||||
-spec(reg(client_id()) -> ok).
|
||||
reg(ClientId) ->
|
||||
gen_server:cast(?SERVER, {reg, ClientId, self()}).
|
||||
%% @doc Unregister a client.
|
||||
-spec(unregister_client(client_id() | {client_id(), pid()}) -> ok).
|
||||
unregister_client(ClientId) when is_binary(ClientId) ->
|
||||
unregister_client({ClientId, self()});
|
||||
|
||||
%% @doc Unregister clientId with pid.
|
||||
-spec(unreg(client_id()) -> ok).
|
||||
unreg(ClientId) ->
|
||||
gen_server:cast(?SERVER, {unreg, ClientId, self()}).
|
||||
unregister_client({ClientId, ClientPid}) when is_binary(ClientId),
|
||||
is_pid(ClientPid) ->
|
||||
ets:delete(?CLIENT_STATS, {ClientId, ClientPid}),
|
||||
ets:delete(?CLIENT_ATTRS, {ClientId, ClientPid}),
|
||||
ets:delete_object(?CLIENT, {ClientId, ClientPid}),
|
||||
notify({unregistered, ClientId, ClientPid}).
|
||||
|
||||
%% @doc Lookup client pid
|
||||
-spec(lookup_client_pid(client_id()) -> pid() | undefined).
|
||||
lookup_client_pid(ClientId) when is_binary(ClientId) ->
|
||||
case lookup_client_pid(ClientId) of
|
||||
[] -> undefined;
|
||||
[{_, Pid}] -> Pid
|
||||
end.
|
||||
|
||||
%% @doc Get client stats
|
||||
-spec(get_client_stats({client_id(), pid()}) -> list(emqx_stats:stats())).
|
||||
get_client_stats({ClientId, ClientPid}) when is_binary(ClientId),
|
||||
is_pid(ClientPid) ->
|
||||
try ets:lookup_element(?CLIENT_STATS, {ClientId, ClientPid}, 2)
|
||||
catch
|
||||
error:badarg -> []
|
||||
end.
|
||||
|
||||
%% @doc Set client stats.
|
||||
-spec(set_client_stats(client_id(), list(emqx_stats:stats())) -> boolean()).
|
||||
set_client_stats(ClientId, Stats) when is_binary(ClientId) ->
|
||||
set_client_stats({ClientId, self()}, Stats);
|
||||
|
||||
set_client_stats({ClientId, ClientPid}, Stats) when is_binary(ClientId),
|
||||
is_pid(ClientPid) ->
|
||||
ets:insert(?CLIENT_STATS, {{ClientId, ClientPid}, Stats}).
|
||||
|
||||
notify(Msg) ->
|
||||
gen_server:cast(?CM, {notify, Msg}).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% gen_server callbacks
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
init([]) ->
|
||||
_ = emqx_tables:create(client, [public, set, {keypos, 2},
|
||||
{read_concurrency, true},
|
||||
{write_concurrency, true}]),
|
||||
_ = emqx_tables:create(client_attrs, [public, set,
|
||||
{write_concurrency, true}]),
|
||||
{ok, #state{monitors = dict:new()}}.
|
||||
TabOpts = [public, set, {write_concurrency, true}],
|
||||
_ = emqx_tables:new(?CLIENT, [{read_concurrency, true} | TabOpts]),
|
||||
_ = emqx_tables:new(?CLIENT_ATTRS, TabOpts),
|
||||
_ = emqx_tables:new(?CLIENT_STATS, TabOpts),
|
||||
ok = emqx_stats:update_interval(cm_stats, fun update_client_stats/0),
|
||||
{ok, #state{client_pmon = emqx_pmon:new()}}.
|
||||
|
||||
handle_call(Req, _From, State) ->
|
||||
emqx_log:error("[CM] Unexpected request: ~p", [Req]),
|
||||
emqx_logger:error("[CM] Unexpected request: ~p", [Req]),
|
||||
{reply, ignore, State}.
|
||||
|
||||
handle_cast({reg, ClientId, Pid}, State) ->
|
||||
_ = ets:insert(client, {ClientId, Pid}),
|
||||
{noreply, monitor_client(ClientId, Pid, State)};
|
||||
handle_cast({notify, {registered, ClientId, Pid}},
|
||||
State = #state{client_pmon = PMon}) ->
|
||||
{noreply, State#state{client_pmon = PMon:monitor(Pid, ClientId)}};
|
||||
|
||||
handle_cast({unreg, ClientId, Pid}, State) ->
|
||||
case lookup(ClientId) of
|
||||
Pid -> remove_client({ClientId, Pid});
|
||||
_ -> ok
|
||||
end,
|
||||
{noreply, State};
|
||||
handle_cast({notify, {unregistered, _ClientId, Pid}},
|
||||
State = #state{client_pmon = PMon}) ->
|
||||
{noreply, State#state{client_pmon = PMon:demonitor(Pid)}};
|
||||
|
||||
handle_cast(Msg, State) ->
|
||||
emqx_log:error("[CM] Unexpected msg: ~p", [Msg]),
|
||||
emqx_logger:error("[CM] Unexpected msg: ~p", [Msg]),
|
||||
{noreply, State}.
|
||||
|
||||
handle_info({'DOWN', MRef, process, DownPid, _Reason}, State) ->
|
||||
case dict:find(MRef, State#state.monitors) of
|
||||
{ok, ClientId} ->
|
||||
case lookup(ClientId) of
|
||||
DownPid -> remove_client({ClientId, DownPid});
|
||||
_ -> ok
|
||||
end,
|
||||
{noreply, erase_monitor(MRef, State)};
|
||||
error ->
|
||||
emqx_log:error("[CM] down client ~p not found", [DownPid]),
|
||||
{noreply, State}
|
||||
handle_info({'DOWN', _MRef, process, DownPid, _Reason},
|
||||
State = #state{client_pmon = PMon}) ->
|
||||
case PMon:find(DownPid) of
|
||||
undefined ->
|
||||
{noreply, State};
|
||||
ClientId ->
|
||||
unregister_client({ClientId, DownPid}),
|
||||
{noreply, State#state{client_pmon = PMon:erase(DownPid)}}
|
||||
end;
|
||||
|
||||
handle_info(Info, State) ->
|
||||
emqx_log:error("[CM] Unexpected info: ~p", [Info]),
|
||||
emqx_logger:error("[CM] Unexpected info: ~p", [Info]),
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, _State = #state{stats_timer = TRef}) ->
|
||||
timer:cancel(TRef).
|
||||
terminate(_Reason, _State = #state{}) ->
|
||||
emqx_stats:cancel_update(cm_stats).
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
@ -116,16 +169,10 @@ code_change(_OldVsn, State, _Extra) ->
|
|||
%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
remove_client(Client) ->
|
||||
ets:delete_object(client, Client),
|
||||
ets:delete(client_stats, Client),
|
||||
ets:delete(client_attrs, Client).
|
||||
|
||||
monitor_client(ClientId, Pid, State = #state{monitors = Monitors}) ->
|
||||
MRef = erlang:monitor(process, Pid),
|
||||
State#state{monitors = dict:store(MRef, ClientId, Monitors)}.
|
||||
|
||||
erase_monitor(MRef, State = #state{monitors = Monitors}) ->
|
||||
erlang:demonitor(MRef),
|
||||
State#state{monitors = dict:erase(MRef, Monitors)}.
|
||||
update_client_stats() ->
|
||||
case ets:info(?CLIENT, size) of
|
||||
undefined -> ok;
|
||||
Size ->
|
||||
emqx_stats:setstat('clients/count', 'clients/max', Size)
|
||||
end.
|
||||
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All Rights Reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-module(emqx_cm_stats).
|
||||
|
||||
-behaviour(gen_statem).
|
||||
|
||||
-include("emqx.hrl").
|
||||
|
||||
%% API
|
||||
-export([start_link/0]).
|
||||
|
||||
-export([set_client_stats/2, get_client_stats/1, del_client_stats/1]).
|
||||
|
||||
%% gen_statem callbacks
|
||||
-export([init/1, callback_mode/0, handle_event/4, terminate/3, code_change/4]).
|
||||
|
||||
-define(TAB, client_stats).
|
||||
|
||||
-record(state, {statsfun}).
|
||||
|
||||
start_link() ->
|
||||
gen_statem:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||
|
||||
-spec(set_client_stats(client_id(), emqx_stats:stats()) -> true).
|
||||
set_client_stats(ClientId, Stats) ->
|
||||
ets:insert(?TAB, {ClientId, [{'$ts', emqx_time:now_secs()}|Stats]}).
|
||||
|
||||
-spec(get_client_stats(client_id()) -> emqx_stats:stats()).
|
||||
get_client_stats(ClientId) ->
|
||||
case ets:lookup(?TAB, ClientId) of
|
||||
[{_, Stats}] -> Stats;
|
||||
[] -> []
|
||||
end.
|
||||
|
||||
-spec(del_client_stats(client_id()) -> true).
|
||||
del_client_stats(ClientId) ->
|
||||
ets:delete(?TAB, ClientId).
|
||||
|
||||
init([]) ->
|
||||
_ = emqx_tables:create(?TAB, [public, {write_concurrency, true}]),
|
||||
StatsFun = emqx_stats:statsfun('clients/count', 'clients/max'),
|
||||
{ok, idle, #state{statsfun = StatsFun}, timer:seconds(1)}.
|
||||
|
||||
callback_mode() -> handle_event_function.
|
||||
|
||||
handle_event(timeout, _Timeout, idle, State = #state{statsfun = StatsFun}) ->
|
||||
case ets:info(client, size) of
|
||||
undefined -> ok;
|
||||
Size -> StatsFun(Size)
|
||||
end,
|
||||
{next_state, idle, State, timer:seconds(1)}.
|
||||
|
||||
terminate(_Reason, _StateName, _State) ->
|
||||
ok.
|
||||
|
||||
code_change(_OldVsn, StateName, State, _Extra) ->
|
||||
{ok, StateName, State}.
|
||||
|
|
@ -1,18 +1,18 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_cm_sup).
|
||||
|
||||
|
@ -26,9 +26,7 @@ start_link() ->
|
|||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||
|
||||
init([]) ->
|
||||
Stats = {emqx_cm_stats, {emqx_cm_stats, start_link, []},
|
||||
permanent, 5000, worker, [emqx_cm_stats]},
|
||||
CM = {emqx_cm, {emqx_cm, start_link, []},
|
||||
permanent, 5000, worker, [emqx_cm]},
|
||||
{ok, {{one_for_all, 10, 3600}, [Stats, CM]}}.
|
||||
{ok, {{one_for_all, 10, 3600}, [CM]}}.
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
-define(SOCK_STATS, [recv_oct, recv_cnt, send_oct, send_cnt, send_pend]).
|
||||
|
||||
-define(LOG(Level, Format, Args, State),
|
||||
emqx_log:Level("Client(~s): " ++ Format,
|
||||
emqx_logger:Level("Client(~s): " ++ Format,
|
||||
[esockd_net:format(State#state.peername) | Args])).
|
||||
|
||||
start_link(Conn, Env) ->
|
||||
|
@ -316,7 +316,7 @@ received(Bytes, State = #state{parser = Parser,
|
|||
{more, NewParser} ->
|
||||
{noreply, run_socket(State#state{parser = NewParser}), IdleTimeout};
|
||||
{ok, Packet, Rest} ->
|
||||
emqx_mqtt_metrics:received(Packet),
|
||||
emqx_metrics:received(Packet),
|
||||
case emqx_protocol:received(Packet, ProtoState) of
|
||||
{ok, ProtoState1} ->
|
||||
received(Rest, State#state{parser = emqx_parser:initial_state(PacketSize),
|
||||
|
@ -371,7 +371,7 @@ emit_stats(undefined, State) ->
|
|||
State;
|
||||
emit_stats(ClientId, State) ->
|
||||
{reply, Stats, _, _} = handle_call(stats, undefined, State),
|
||||
emqx_stats:set_client_stats(ClientId, Stats),
|
||||
emqx_cm:set_client_stats(ClientId, Stats),
|
||||
State.
|
||||
|
||||
sock_stats(#state{connection = Conn}) ->
|
||||
|
|
108
src/emqx_ctl.erl
108
src/emqx_ctl.erl
|
@ -1,26 +1,26 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_ctl).
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
%% API Function Exports
|
||||
-export([start_link/0, register_cmd/2, register_cmd/3, unregister_cmd/1,
|
||||
lookup/1, run/1]).
|
||||
-export([start_link/0]).
|
||||
-export([register_command/2, register_command/3, unregister_command/1]).
|
||||
-export([run_command/2, lookup_command/1]).
|
||||
|
||||
%% gen_server Function Exports
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
|
@ -28,9 +28,10 @@
|
|||
|
||||
-record(state, {seq = 0}).
|
||||
|
||||
-define(SERVER, ?MODULE).
|
||||
-type(cmd() :: atom()).
|
||||
|
||||
-define(TAB, ?MODULE).
|
||||
-define(SERVER, ?MODULE).
|
||||
-define(TAB, emqx_command).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% API
|
||||
|
@ -40,47 +41,44 @@ start_link() ->
|
|||
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
|
||||
|
||||
%% @doc Register a command
|
||||
-spec(register_cmd(atom(), {module(), atom()}) -> ok).
|
||||
register_cmd(Cmd, MF) ->
|
||||
register_cmd(Cmd, MF, []).
|
||||
-spec(register_command(cmd(), {module(), atom()}) -> ok).
|
||||
register_command(Cmd, MF) when is_atom(Cmd) ->
|
||||
register_command(Cmd, MF, []).
|
||||
|
||||
%% @doc Register a command with opts
|
||||
-spec(register_cmd(atom(), {module(), atom()}, list()) -> ok).
|
||||
register_cmd(Cmd, MF, Opts) ->
|
||||
cast({register_cmd, Cmd, MF, Opts}).
|
||||
%% @doc Register a command with options
|
||||
-spec(register_command(cmd(), {module(), atom()}, list()) -> ok).
|
||||
register_command(Cmd, MF, Opts) when is_atom(Cmd) ->
|
||||
cast({register_command, Cmd, MF, Opts}).
|
||||
|
||||
%% @doc Unregister a command
|
||||
-spec(unregister_cmd(atom()) -> ok).
|
||||
unregister_cmd(Cmd) ->
|
||||
cast({unregister_cmd, Cmd}).
|
||||
-spec(unregister_command(cmd()) -> ok).
|
||||
unregister_command(Cmd) when is_atom(Cmd) ->
|
||||
cast({unregister_command, Cmd}).
|
||||
|
||||
cast(Msg) -> gen_server:cast(?SERVER, Msg).
|
||||
|
||||
%% @doc Run a command
|
||||
-spec(run([string()]) -> any()).
|
||||
run([]) -> usage(), ok;
|
||||
|
||||
run(["help"]) -> usage(), ok;
|
||||
|
||||
run([CmdS|Args]) ->
|
||||
case lookup(list_to_atom(CmdS)) of
|
||||
-spec(run_command(cmd(), [string()]) -> ok | {error, term()}).
|
||||
run_command(help, []) ->
|
||||
usage();
|
||||
run_command(Cmd, Args) when is_atom(Cmd) ->
|
||||
case lookup_command(Cmd) of
|
||||
[{Mod, Fun}] ->
|
||||
try Mod:Fun(Args) of
|
||||
_ -> ok
|
||||
catch
|
||||
_:Reason ->
|
||||
io:format("Reason:~p, get_stacktrace:~p~n",
|
||||
emqx_logger:error("[CTL] Cmd error:~p, stacktrace:~p",
|
||||
[Reason, erlang:get_stacktrace()]),
|
||||
{error, Reason}
|
||||
end;
|
||||
[] ->
|
||||
usage(),
|
||||
{error, cmd_not_found}
|
||||
usage(), {error, cmd_not_found}
|
||||
end.
|
||||
|
||||
%% @doc Lookup a command
|
||||
-spec(lookup(atom()) -> [{module(), atom()}]).
|
||||
lookup(Cmd) ->
|
||||
-spec(lookup_command(cmd()) -> [{module(), atom()}]).
|
||||
lookup_command(Cmd) when is_atom(Cmd) ->
|
||||
case ets:match(?TAB, {{'_', Cmd}, '$1', '_'}) of
|
||||
[El] -> El;
|
||||
[] -> []
|
||||
|
@ -97,30 +95,32 @@ usage() ->
|
|||
%%--------------------------------------------------------------------
|
||||
|
||||
init([]) ->
|
||||
ets:new(?TAB, [ordered_set, named_table, protected]),
|
||||
_ = emqx_tables:new(?TAB, [ordered_set, protected]),
|
||||
{ok, #state{seq = 0}}.
|
||||
|
||||
handle_call(_Request, _From, State) ->
|
||||
{reply, ok, State}.
|
||||
handle_call(Req, _From, State) ->
|
||||
emqx_logger:error("Unexpected request: ~p", [Req]),
|
||||
{reply, ignore, State}.
|
||||
|
||||
handle_cast({register_cmd, Cmd, MF, Opts}, State = #state{seq = Seq}) ->
|
||||
handle_cast({register_command, Cmd, MF, Opts}, State = #state{seq = Seq}) ->
|
||||
case ets:match(?TAB, {{'$1', Cmd}, '_', '_'}) of
|
||||
[] ->
|
||||
ets:insert(?TAB, {{Seq, Cmd}, MF, Opts});
|
||||
[] -> ets:insert(?TAB, {{Seq, Cmd}, MF, Opts});
|
||||
[[OriginSeq] | _] ->
|
||||
emqx_log:warning("[CLI] ~s is overidden by ~p", [Cmd, MF]),
|
||||
emqx_logger:warning("[CTL] cmd ~s is overidden by ~p", [Cmd, MF]),
|
||||
ets:insert(?TAB, {{OriginSeq, Cmd}, MF, Opts})
|
||||
end,
|
||||
noreply(next_seq(State));
|
||||
|
||||
handle_cast({unregister_cmd, Cmd}, State) ->
|
||||
handle_cast({unregister_command, Cmd}, State) ->
|
||||
ets:match_delete(?TAB, {{'_', Cmd}, '_', '_'}),
|
||||
noreply(State);
|
||||
|
||||
handle_cast(_Msg, State) ->
|
||||
handle_cast(Msg, State) ->
|
||||
emqx_logger:error("Unexpected msg: ~p", [Msg]),
|
||||
noreply(State).
|
||||
|
||||
handle_info(_Info, State) ->
|
||||
handle_info(Info, State) ->
|
||||
emqx_logger:error("Unexpected info: ~p", [Info]),
|
||||
noreply(State).
|
||||
|
||||
terminate(_Reason, _State) ->
|
||||
|
@ -143,7 +143,7 @@ next_seq(State = #state{seq = Seq}) ->
|
|||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
register_cmd_test_() ->
|
||||
register_command_test_() ->
|
||||
{setup,
|
||||
fun() ->
|
||||
{ok, InitState} = emqx_ctl:init([]),
|
||||
|
@ -153,7 +153,7 @@ register_cmd_test_() ->
|
|||
ok = emqx_ctl:terminate(shutdown, State)
|
||||
end,
|
||||
fun(State = #state{seq = Seq}) ->
|
||||
emqx_ctl:handle_cast({register_cmd, test0, {?MODULE, test0}, []}, State),
|
||||
emqx_ctl:handle_cast({register_command, test0, {?MODULE, test0}, []}, State),
|
||||
[?_assertMatch([{{0,test0},{?MODULE, test0}, []}], ets:lookup(?TAB, {Seq,test0}))]
|
||||
end
|
||||
}.
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_flow_control).
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
%% API
|
||||
-export([start_link/0]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-define(SERVER, ?MODULE).
|
||||
|
||||
-record(state, {}).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% API
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
%% @doc Starts the server
|
||||
-spec(start_link() -> {ok, pid()} | ignore | {error, any()}).
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% gen_server callbacks
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
init([]) ->
|
||||
{ok, #state{}}.
|
||||
|
||||
handle_call(_Request, _From, State) ->
|
||||
Reply = ok,
|
||||
{reply, Reply, State}.
|
||||
|
||||
handle_cast(_Msg, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, _State) ->
|
||||
ok.
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,24 +1,23 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_hooks).
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
%% Start
|
||||
-export([start_link/0]).
|
||||
|
||||
%% Hooks API
|
||||
|
@ -41,7 +40,7 @@
|
|||
|
||||
-record(hook, {name :: atom(), callbacks = [] :: list(#callback{})}).
|
||||
|
||||
-define(HOOK_TAB, mqtt_hook).
|
||||
-define(TAB, ?MODULE).
|
||||
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||
|
@ -104,24 +103,25 @@ run_([], _Args, Acc) ->
|
|||
|
||||
-spec(lookup(atom()) -> [#callback{}]).
|
||||
lookup(HookPoint) ->
|
||||
case ets:lookup(?HOOK_TAB, HookPoint) of
|
||||
case ets:lookup(?TAB, HookPoint) of
|
||||
[#hook{callbacks = Callbacks}] -> Callbacks;
|
||||
[] -> []
|
||||
end.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% gen_server Callbacks
|
||||
%% gen_server callbacks
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
init([]) ->
|
||||
ets:new(?HOOK_TAB, [set, protected, named_table, {keypos, #hook.name}]),
|
||||
_ = emqx_tables:new(?TAB, [set, protected, {keypos, #hook.name},
|
||||
{read_concurrency, true}]),
|
||||
{ok, #state{}}.
|
||||
|
||||
handle_call({add, HookPoint, {Tag, Function}, InitArgs, Priority}, _From, State) ->
|
||||
Callback = #callback{tag = Tag, function = Function,
|
||||
init_args = InitArgs, priority = Priority},
|
||||
{reply,
|
||||
case ets:lookup(?HOOK_TAB, HookPoint) of
|
||||
case ets:lookup(?TAB, HookPoint) of
|
||||
[#hook{callbacks = Callbacks}] ->
|
||||
case contain_(Tag, Function, Callbacks) of
|
||||
false ->
|
||||
|
@ -135,7 +135,7 @@ handle_call({add, HookPoint, {Tag, Function}, InitArgs, Priority}, _From, State)
|
|||
|
||||
handle_call({delete, HookPoint, {Tag, Function}}, _From, State) ->
|
||||
{reply,
|
||||
case ets:lookup(?HOOK_TAB, HookPoint) of
|
||||
case ets:lookup(?TAB, HookPoint) of
|
||||
[#hook{callbacks = Callbacks}] ->
|
||||
case contain_(Tag, Function, Callbacks) of
|
||||
true ->
|
||||
|
@ -167,7 +167,7 @@ code_change(_OldVsn, State, _Extra) ->
|
|||
%%--------------------------------------------------------------------
|
||||
|
||||
insert_hook_(HookPoint, Callbacks) ->
|
||||
ets:insert(?HOOK_TAB, #hook{name = HookPoint, callbacks = Callbacks}), ok.
|
||||
ets:insert(?TAB, #hook{name = HookPoint, callbacks = Callbacks}), ok.
|
||||
|
||||
add_callback_(Callback, Callbacks) ->
|
||||
lists:keymerge(#callback.priority, Callbacks, [Callback]).
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_inflight).
|
||||
|
||||
|
|
|
@ -1,22 +1,23 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_json).
|
||||
|
||||
-export([encode/1, encode/2, decode/1, decode/2]).
|
||||
-export([encode/1, encode/2, safe_encode/1, safe_encode/2]).
|
||||
-export([decode/1, decode/2, safe_decode/1, safe_decode/2]).
|
||||
|
||||
-spec(encode(jsx:json_term()) -> jsx:json_text()).
|
||||
encode(Term) ->
|
||||
|
@ -26,11 +27,41 @@ encode(Term) ->
|
|||
encode(Term, Opts) ->
|
||||
jsx:encode(Term, Opts).
|
||||
|
||||
-spec(safe_encode(jsx:json_term())
|
||||
-> {ok, jsx:json_text()} | {error, term()}).
|
||||
safe_encode(Term) ->
|
||||
safe_encode(Term, []).
|
||||
|
||||
-spec(safe_encode(jsx:json_term(), jsx_to_json:config())
|
||||
-> {ok, jsx:json_text()} | {error, term()}).
|
||||
safe_encode(Term, Opts) ->
|
||||
try encode(Term, Opts) of
|
||||
Json -> {ok, Json}
|
||||
catch
|
||||
error:Reason ->
|
||||
{error, Reason}
|
||||
end.
|
||||
|
||||
-spec(decode(jsx:json_text()) -> jsx:json_term()).
|
||||
decode(JSON) ->
|
||||
jsx:decode(JSON).
|
||||
decode(Json) ->
|
||||
jsx:decode(Json).
|
||||
|
||||
-spec(decode(jsx:json_text(), jsx_to_json:config()) -> jsx:json_term()).
|
||||
decode(JSON, Opts) ->
|
||||
jsx:decode(JSON, Opts).
|
||||
decode(Json, Opts) ->
|
||||
jsx:decode(Json, Opts).
|
||||
|
||||
-spec(safe_decode(jsx:json_text())
|
||||
-> {ok, jsx:json_term()} | {error, term()}).
|
||||
safe_decode(Json) ->
|
||||
safe_decode(Json, []).
|
||||
|
||||
-spec(safe_decode(jsx:json_text(), jsx_to_json:config())
|
||||
-> {ok, jsx:json_term()} | {error, term()}).
|
||||
safe_decode(Json, Opts) ->
|
||||
try decode(Json, Opts) of
|
||||
Term -> {ok, Term}
|
||||
catch
|
||||
error:Reason ->
|
||||
{error, Reason}
|
||||
end.
|
||||
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_keepalive).
|
||||
|
||||
|
@ -29,19 +29,21 @@
|
|||
start(_, 0, _) ->
|
||||
{ok, #keepalive{}};
|
||||
start(StatFun, TimeoutSec, TimeoutMsg) ->
|
||||
case StatFun() of
|
||||
case catch StatFun() of
|
||||
{ok, StatVal} ->
|
||||
{ok, #keepalive{statfun = StatFun, statval = StatVal,
|
||||
tsec = TimeoutSec, tmsg = TimeoutMsg,
|
||||
tref = timer(TimeoutSec, TimeoutMsg)}};
|
||||
{error, Error} ->
|
||||
{error, Error}
|
||||
{error, Error};
|
||||
{'EXIT', Reason} ->
|
||||
{error, Reason}
|
||||
end.
|
||||
|
||||
%% @doc Check keepalive, called when timeout.
|
||||
%% @doc Check keepalive, called when timeout...
|
||||
-spec(check(keepalive()) -> {ok, keepalive()} | {error, term()}).
|
||||
check(KeepAlive = #keepalive{statfun = StatFun, statval = LastVal, repeat = Repeat}) ->
|
||||
case StatFun() of
|
||||
case catch StatFun() of
|
||||
{ok, NewVal} ->
|
||||
if NewVal =/= LastVal ->
|
||||
{ok, resume(KeepAlive#keepalive{statval = NewVal, repeat = 0})};
|
||||
|
@ -51,9 +53,12 @@ check(KeepAlive = #keepalive{statfun = StatFun, statval = LastVal, repeat = Repe
|
|||
{error, timeout}
|
||||
end;
|
||||
{error, Error} ->
|
||||
{error, Error}
|
||||
{error, Error};
|
||||
{'EXIT', Reason} ->
|
||||
{error, Reason}
|
||||
end.
|
||||
|
||||
-spec(resume(keepalive()) -> keepalive()).
|
||||
resume(KeepAlive = #keepalive{tsec = TimeoutSec, tmsg = TimeoutMsg}) ->
|
||||
KeepAlive#keepalive{tref = timer(TimeoutSec, TimeoutMsg)}.
|
||||
|
||||
|
@ -64,6 +69,6 @@ cancel(#keepalive{tref = TRef}) when is_reference(TRef) ->
|
|||
cancel(_) ->
|
||||
ok.
|
||||
|
||||
timer(Sec, Msg) ->
|
||||
erlang:send_after(timer:seconds(Sec), self(), Msg).
|
||||
timer(Secs, Msg) ->
|
||||
erlang:send_after(timer:seconds(Secs), self(), Msg).
|
||||
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_kernel_sup).
|
||||
|
||||
-behaviour(supervisor).
|
||||
|
||||
-export([start_link/0]).
|
||||
|
||||
-export([init/1]).
|
||||
|
||||
start_link() ->
|
||||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||
|
||||
init([]) ->
|
||||
{ok, {{one_for_one, 10, 100},
|
||||
[child_spec(emqx_pool, supervisor),
|
||||
child_spec(emqx_alarm, worker),
|
||||
child_spec(emqx_hooks, worker),
|
||||
child_spec(emqx_stats, worker),
|
||||
child_spec(emqx_metrics, worker),
|
||||
child_spec(emqx_ctl, worker),
|
||||
child_spec(emqx_tracer, worker)]}}.
|
||||
|
||||
child_spec(M, worker) ->
|
||||
{M, {M, start_link, []}, permanent, 5000, worker, [M]};
|
||||
child_spec(M, supervisor) ->
|
||||
{M, {M, start_link, []},
|
||||
permanent, infinity, supervisor, [M]}.
|
||||
|
|
@ -1,20 +1,20 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_log).
|
||||
-module(emqx_logger).
|
||||
|
||||
-compile({no_auto_import,[error/1]}).
|
||||
|
|
@ -50,7 +50,7 @@ get_flag(Flag, #message{flags = Flags}, Default) ->
|
|||
|
||||
%% @doc Set flag
|
||||
-spec(set_flag(message_flag(), message()) -> message()).
|
||||
set_flag(Flag, Msg = #message{flags = Flags}) ->
|
||||
set_flag(Flag, Msg = #message{flags = Flags}) when is_atom(Flag) ->
|
||||
Msg#message{flags = maps:put(Flag, true, Flags)}.
|
||||
|
||||
%% @doc Unset flag
|
||||
|
|
|
@ -1,55 +1,110 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_metrics).
|
||||
|
||||
-behaviour(gen_server).
|
||||
-include("emqx_mqtt.hrl").
|
||||
|
||||
%% API Function Exports
|
||||
-export([start_link/0, create/1]).
|
||||
-export([start_link/0]).
|
||||
|
||||
-export([all/0, value/1, inc/1, inc/2, inc/3, dec/2, dec/3, set/2]).
|
||||
-export([new/1, all/0]).
|
||||
|
||||
-export([val/1, inc/1, inc/2, inc/3, dec/2, dec/3, set/2]).
|
||||
|
||||
%% Received/sent metrics
|
||||
-export([received/1, sent/1]).
|
||||
|
||||
%% gen_server Function Exports
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-record(state, {tick}).
|
||||
-record(state, {}).
|
||||
|
||||
-define(TAB, metrics).
|
||||
%% Bytes sent and received of Broker
|
||||
-define(BYTES_METRICS, [
|
||||
{counter, 'bytes/received'}, % Total bytes received
|
||||
{counter, 'bytes/sent'} % Total bytes sent
|
||||
]).
|
||||
|
||||
%% Packets sent and received of broker
|
||||
-define(PACKET_METRICS, [
|
||||
{counter, 'packets/received'}, % All Packets received
|
||||
{counter, 'packets/sent'}, % All Packets sent
|
||||
{counter, 'packets/connect'}, % CONNECT Packets received
|
||||
{counter, 'packets/connack'}, % CONNACK Packets sent
|
||||
{counter, 'packets/publish/received'}, % PUBLISH packets received
|
||||
{counter, 'packets/publish/sent'}, % PUBLISH packets sent
|
||||
{counter, 'packets/puback/received'}, % PUBACK packets received
|
||||
{counter, 'packets/puback/sent'}, % PUBACK packets sent
|
||||
{counter, 'packets/puback/missed'}, % PUBACK packets missed
|
||||
{counter, 'packets/pubrec/received'}, % PUBREC packets received
|
||||
{counter, 'packets/pubrec/sent'}, % PUBREC packets sent
|
||||
{counter, 'packets/pubrec/missed'}, % PUBREC packets missed
|
||||
{counter, 'packets/pubrel/received'}, % PUBREL packets received
|
||||
{counter, 'packets/pubrel/sent'}, % PUBREL packets sent
|
||||
{counter, 'packets/pubrel/missed'}, % PUBREL packets missed
|
||||
{counter, 'packets/pubcomp/received'}, % PUBCOMP packets received
|
||||
{counter, 'packets/pubcomp/sent'}, % PUBCOMP packets sent
|
||||
{counter, 'packets/pubcomp/missed'}, % PUBCOMP packets missed
|
||||
{counter, 'packets/subscribe'}, % SUBSCRIBE Packets received
|
||||
{counter, 'packets/suback'}, % SUBACK packets sent
|
||||
{counter, 'packets/unsubscribe'}, % UNSUBSCRIBE Packets received
|
||||
{counter, 'packets/unsuback'}, % UNSUBACK Packets sent
|
||||
{counter, 'packets/pingreq'}, % PINGREQ packets received
|
||||
{counter, 'packets/pingresp'}, % PINGRESP Packets sent
|
||||
{counter, 'packets/disconnect'}, % DISCONNECT Packets received
|
||||
{counter, 'packets/auth'} % Auth Packets received
|
||||
]).
|
||||
|
||||
%% Messages sent and received of broker
|
||||
-define(MESSAGE_METRICS, [
|
||||
{counter, 'messages/received'}, % All Messages received
|
||||
{counter, 'messages/sent'}, % All Messages sent
|
||||
{counter, 'messages/qos0/received'}, % QoS0 Messages received
|
||||
{counter, 'messages/qos0/sent'}, % QoS0 Messages sent
|
||||
{counter, 'messages/qos1/received'}, % QoS1 Messages received
|
||||
{counter, 'messages/qos1/sent'}, % QoS1 Messages sent
|
||||
{counter, 'messages/qos2/received'}, % QoS2 Messages received
|
||||
{counter, 'messages/qos2/sent'}, % QoS2 Messages sent
|
||||
{counter, 'messages/qos2/dropped'}, % QoS2 Messages dropped
|
||||
{gauge, 'messages/retained'}, % Messagea retained
|
||||
{counter, 'messages/dropped'}, % Messages dropped
|
||||
{counter, 'messages/forward'} % Messages forward
|
||||
]).
|
||||
|
||||
-define(TAB, ?MODULE).
|
||||
|
||||
-define(SERVER, ?MODULE).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% API
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
%% @doc Start the metrics server
|
||||
-spec(start_link() -> {ok, pid()} | ignore | {error, term()}).
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
|
||||
|
||||
create({gauge, Name}) ->
|
||||
%%--------------------------------------------------------------------
|
||||
%% Metrics API
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
new({gauge, Name}) ->
|
||||
ets:insert(?TAB, {{Name, 0}, 0});
|
||||
|
||||
create({counter, Name}) ->
|
||||
Schedulers = lists:seq(1, erlang:system_info(schedulers)),
|
||||
new({counter, Name}) ->
|
||||
Schedulers = lists:seq(1, emqx_vm:schedulers()),
|
||||
ets:insert(?TAB, [{{Name, I}, 0} || I <- Schedulers]).
|
||||
|
||||
|
||||
%% @doc Get all metrics
|
||||
-spec(all() -> [{atom(), non_neg_integer()}]).
|
||||
all() ->
|
||||
|
@ -63,8 +118,8 @@ all() ->
|
|||
end, #{}, ?TAB)).
|
||||
|
||||
%% @doc Get metric value
|
||||
-spec(value(atom()) -> non_neg_integer()).
|
||||
value(Metric) ->
|
||||
-spec(val(atom()) -> non_neg_integer()).
|
||||
val(Metric) ->
|
||||
lists:sum(ets:select(?TAB, [{{{Metric, '_'}, '$1'}, [], ['$1']}])).
|
||||
|
||||
%% @doc Increase counter
|
||||
|
@ -84,9 +139,9 @@ inc(Metric, Val) when is_atom(Metric) ->
|
|||
%% @doc Increase metric value
|
||||
-spec(inc(counter | gauge, atom(), pos_integer()) -> pos_integer()).
|
||||
inc(gauge, Metric, Val) ->
|
||||
ets:update_counter(?TAB, key(gauge, Metric), {2, Val});
|
||||
update_counter(key(gauge, Metric), {2, Val});
|
||||
inc(counter, Metric, Val) ->
|
||||
ets:update_counter(?TAB, key(counter, Metric), {2, Val}).
|
||||
update_counter(key(counter, Metric), {2, Val}).
|
||||
|
||||
%% @doc Decrease metric value
|
||||
-spec(dec(gauge, atom()) -> integer()).
|
||||
|
@ -96,7 +151,7 @@ dec(gauge, Metric) ->
|
|||
%% @doc Decrease metric value
|
||||
-spec(dec(gauge, atom(), pos_integer()) -> integer()).
|
||||
dec(gauge, Metric, Val) ->
|
||||
ets:update_counter(?TAB, key(gauge, Metric), {2, -Val}).
|
||||
update_counter(key(gauge, Metric), {2, -Val}).
|
||||
|
||||
%% @doc Set metric value
|
||||
set(Metric, Val) when is_atom(Metric) ->
|
||||
|
@ -104,57 +159,120 @@ set(Metric, Val) when is_atom(Metric) ->
|
|||
set(gauge, Metric, Val) ->
|
||||
ets:insert(?TAB, {key(gauge, Metric), Val}).
|
||||
|
||||
%% @doc Metric Key
|
||||
%% @doc Metric key
|
||||
key(gauge, Metric) ->
|
||||
{Metric, 0};
|
||||
key(counter, Metric) ->
|
||||
{Metric, erlang:system_info(scheduler_id)}.
|
||||
|
||||
update_counter(Key, UpOp) ->
|
||||
ets:update_counter(?TAB, Key, UpOp).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% gen_server Callbacks
|
||||
%% Receive/Sent metrics
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
%% @doc Count packets received.
|
||||
-spec(received(mqtt_packet()) -> ok).
|
||||
received(Packet) ->
|
||||
inc('packets/received'),
|
||||
received1(Packet).
|
||||
received1(?PUBLISH_PACKET(Qos, _PktId)) ->
|
||||
inc('packets/publish/received'),
|
||||
inc('messages/received'),
|
||||
qos_received(Qos);
|
||||
received1(?PACKET(Type)) ->
|
||||
received2(Type).
|
||||
received2(?CONNECT) ->
|
||||
inc('packets/connect');
|
||||
received2(?PUBACK) ->
|
||||
inc('packets/puback/received');
|
||||
received2(?PUBREC) ->
|
||||
inc('packets/pubrec/received');
|
||||
received2(?PUBREL) ->
|
||||
inc('packets/pubrel/received');
|
||||
received2(?PUBCOMP) ->
|
||||
inc('packets/pubcomp/received');
|
||||
received2(?SUBSCRIBE) ->
|
||||
inc('packets/subscribe');
|
||||
received2(?UNSUBSCRIBE) ->
|
||||
inc('packets/unsubscribe');
|
||||
received2(?PINGREQ) ->
|
||||
inc('packets/pingreq');
|
||||
received2(?DISCONNECT) ->
|
||||
inc('packets/disconnect');
|
||||
received2(_) ->
|
||||
ignore.
|
||||
qos_received(?QOS_0) ->
|
||||
inc('messages/qos0/received');
|
||||
qos_received(?QOS_1) ->
|
||||
inc('messages/qos1/received');
|
||||
qos_received(?QOS_2) ->
|
||||
inc('messages/qos2/received').
|
||||
|
||||
%% @doc Count packets received. Will not count $SYS PUBLISH.
|
||||
-spec(sent(mqtt_packet()) -> ignore | non_neg_integer()).
|
||||
sent(?PUBLISH_PACKET(_Qos, <<"$SYS/", _/binary>>, _, _)) ->
|
||||
ignore;
|
||||
sent(Packet) ->
|
||||
inc('packets/sent'),
|
||||
sent1(Packet).
|
||||
sent1(?PUBLISH_PACKET(Qos, _PktId)) ->
|
||||
inc('packets/publish/sent'),
|
||||
inc('messages/sent'),
|
||||
qos_sent(Qos);
|
||||
sent1(?PACKET(Type)) ->
|
||||
sent2(Type).
|
||||
sent2(?CONNACK) ->
|
||||
inc('packets/connack');
|
||||
sent2(?PUBACK) ->
|
||||
inc('packets/puback/sent');
|
||||
sent2(?PUBREC) ->
|
||||
inc('packets/pubrec/sent');
|
||||
sent2(?PUBREL) ->
|
||||
inc('packets/pubrel/sent');
|
||||
sent2(?PUBCOMP) ->
|
||||
inc('packets/pubcomp/sent');
|
||||
sent2(?SUBACK) ->
|
||||
inc('packets/suback');
|
||||
sent2(?UNSUBACK) ->
|
||||
inc('packets/unsuback');
|
||||
sent2(?PINGRESP) ->
|
||||
inc('packets/pingresp');
|
||||
sent2(_Type) ->
|
||||
ignore.
|
||||
qos_sent(?QOS_0) ->
|
||||
inc('messages/qos0/sent');
|
||||
qos_sent(?QOS_1) ->
|
||||
inc('messages/qos1/sent');
|
||||
qos_sent(?QOS_2) ->
|
||||
inc('messages/qos2/sent').
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% gen_server callbacks
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
init([]) ->
|
||||
emqx_time:seed(),
|
||||
% Create metrics table
|
||||
_ = ets:new(?TAB, [set, public, named_table, {write_concurrency, true}]),
|
||||
% Tick to publish metrics
|
||||
{ok, TRef} = timer:send_after(emqx_sys:sys_interval(), tick),
|
||||
{ok, #state{tick = TRef}, hibernate}.
|
||||
_ = emqx_tables:new(?TAB, [set, public, {write_concurrency, true}]),
|
||||
lists:foreach(fun new/1, ?BYTES_METRICS ++ ?PACKET_METRICS ++ ?MESSAGE_METRICS),
|
||||
{ok, #state{}, hibernate}.
|
||||
|
||||
handle_call(_Req, _From, State) ->
|
||||
{reply, error, State}.
|
||||
handle_call(Req, _From, State) ->
|
||||
emqx_logger:error("[METRICS] Unexpected request: ~p", [Req]),
|
||||
{reply, ignore, State}.
|
||||
|
||||
handle_cast(_Msg, State) ->
|
||||
handle_cast(Msg, State) ->
|
||||
emqx_logger:error("[METRICS] Unexpected msg: ~p", [Msg]),
|
||||
{noreply, State}.
|
||||
|
||||
handle_info(tick, State) ->
|
||||
% publish metric message
|
||||
[publish(Metric, Val) || {Metric, Val} <- all()],
|
||||
{noreply, State, hibernate};
|
||||
|
||||
handle_info(_Info, State) ->
|
||||
handle_info(Info, State) ->
|
||||
emqx_logger:error("[METRICS] Unexpected info: ~p", [Info]),
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, #state{tick = TRef}) ->
|
||||
%%TODO:
|
||||
timer:cancel(TRef).
|
||||
terminate(_Reason, #state{}) ->
|
||||
ok.
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
%% TODO: the depencies are not right
|
||||
|
||||
publish(Metric, Val) ->
|
||||
Msg = emqx_message:make(metrics, metric_topic(Metric), bin(Val)),
|
||||
emqx_broker:publish(emqx_message:set_flag(sys, Msg)).
|
||||
|
||||
metric_topic(Metric) ->
|
||||
emqx_topic:systop(list_to_binary(lists:concat(['metrics/', Metric]))).
|
||||
|
||||
bin(I) when is_integer(I) -> list_to_binary(integer_to_list(I)).
|
||||
|
||||
|
|
|
@ -1,25 +1,26 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_misc).
|
||||
|
||||
-export([merge_opts/2, start_timer/2, start_timer/3, cancel_timer/1,
|
||||
proc_stats/0, proc_stats/1]).
|
||||
proc_name/2, proc_stats/0, proc_stats/1]).
|
||||
|
||||
%% @doc Merge Options
|
||||
-spec(merge_opts(list(), list()) -> list()).
|
||||
merge_opts(Defaults, Options) ->
|
||||
lists:foldl(
|
||||
fun({Opt, Val}, Acc) ->
|
||||
|
@ -51,6 +52,10 @@ cancel_timer(Timer) ->
|
|||
_ -> ok
|
||||
end.
|
||||
|
||||
-spec(proc_name(atom(), pos_integer()) -> atom()).
|
||||
proc_name(Mod, Id) ->
|
||||
list_to_atom(lists:concat([Mod, "_", Id])).
|
||||
|
||||
-spec(proc_stats() -> list()).
|
||||
proc_stats() ->
|
||||
proc_stats(self()).
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_mod_presence).
|
||||
|
||||
|
@ -34,32 +34,32 @@ on_client_connected(ConnAck, Client = #client{client_id = ClientId,
|
|||
%%clean_sess = CleanSess,
|
||||
%%proto_ver = ProtoVer
|
||||
}, Env) ->
|
||||
case catch emqx_json:encode([{clientid, ClientId},
|
||||
case emqx_json:safe_encode([{clientid, ClientId},
|
||||
{username, Username},
|
||||
{ipaddress, iolist_to_binary(emqx_net:ntoa(IpAddr))},
|
||||
%%{clean_sess, CleanSess}, %%TODO:: fixme later
|
||||
%%{protocol, ProtoVer},
|
||||
{connack, ConnAck},
|
||||
{ts, emqx_time:now_secs()}]) of
|
||||
Payload when is_binary(Payload) ->
|
||||
{ok, Payload} ->
|
||||
Msg = message(qos(Env), topic(connected, ClientId), Payload),
|
||||
emqx:publish(emqx_message:set_flag(sys, Msg));
|
||||
{'EXIT', Reason} ->
|
||||
emqx_log:error("[Presence Module] json error: ~p", [Reason])
|
||||
{error, Reason} ->
|
||||
emqx_logger:error("[Presence Module] Json error: ~p", [Reason])
|
||||
end,
|
||||
{ok, Client}.
|
||||
|
||||
on_client_disconnected(Reason, #client{client_id = ClientId,
|
||||
username = Username}, Env) ->
|
||||
case catch emqx_json:encode([{clientid, ClientId},
|
||||
case emqx_json:safe_encode([{clientid, ClientId},
|
||||
{username, Username},
|
||||
{reason, reason(Reason)},
|
||||
{ts, emqx_time:now_secs()}]) of
|
||||
Payload when is_binary(Payload) ->
|
||||
{ok, Payload} ->
|
||||
Msg = message(qos(Env), topic(disconnected, ClientId), Payload),
|
||||
emqx:publish(emqx_message:set_flag(sys, Msg));
|
||||
{'EXIT', Reason} ->
|
||||
emqx_log:error("[Presence Module] json error: ~p", [Reason])
|
||||
{error, Reason} ->
|
||||
emqx_logger:error("[Presence Module] Json error: ~p", [Reason])
|
||||
end, ok.
|
||||
|
||||
unload(_Env) ->
|
||||
|
@ -67,13 +67,13 @@ unload(_Env) ->
|
|||
emqx:unhook('client.disconnected', fun ?MODULE:on_client_disconnected/3).
|
||||
|
||||
message(Qos, Topic, Payload) ->
|
||||
Msg = emqx_message:make(presence, Topic, iolist_to_binary(Payload)),
|
||||
Msg = emqx_message:make(?MODULE, Topic, iolist_to_binary(Payload)),
|
||||
emqx_message:set_header(qos, Qos, Msg).
|
||||
|
||||
topic(connected, ClientId) ->
|
||||
emqx_topic:systop(list_to_binary(["clients/", ClientId, "/connected"]));
|
||||
emqx_topic:systop(iolist_to_binary(["clients/", ClientId, "/connected"]));
|
||||
topic(disconnected, ClientId) ->
|
||||
emqx_topic:systop(list_to_binary(["clients/", ClientId, "/disconnected"])).
|
||||
emqx_topic:systop(iolist_to_binary(["clients/", ClientId, "/disconnected"])).
|
||||
|
||||
qos(Env) ->
|
||||
proplists:get_value(qos, Env, 0).
|
||||
|
|
|
@ -35,11 +35,11 @@ load(Rules0) ->
|
|||
emqx:hook('message.publish', fun ?MODULE:rewrite_publish/2, [Rules]).
|
||||
|
||||
rewrite_subscribe(_ClientId, _Username, TopicTable, Rules) ->
|
||||
emqx_log:info("Rewrite subscribe: ~p", [TopicTable]),
|
||||
emqx_logger:info("Rewrite subscribe: ~p", [TopicTable]),
|
||||
{ok, [{match_rule(Topic, Rules), Opts} || {Topic, Opts} <- TopicTable]}.
|
||||
|
||||
rewrite_unsubscribe(_ClientId, _Username, TopicTable, Rules) ->
|
||||
emqx_log:info("Rewrite unsubscribe: ~p", [TopicTable]),
|
||||
emqx_logger:info("Rewrite unsubscribe: ~p", [TopicTable]),
|
||||
{ok, [{match_rule(Topic, Rules), Opts} || {Topic, Opts} <- TopicTable]}.
|
||||
|
||||
rewrite_publish(Message = #message{topic = Topic}, Rules) ->
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_mod_subscription).
|
||||
|
||||
|
|
|
@ -1,23 +1,24 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_modules).
|
||||
|
||||
-export([load/0, unload/0]).
|
||||
|
||||
-spec(load() -> ok).
|
||||
load() ->
|
||||
lists:foreach(
|
||||
fun({Mod, Env}) ->
|
||||
|
@ -25,6 +26,7 @@ load() ->
|
|||
io:format("Load ~s module successfully.~n", [Mod])
|
||||
end, emqx_config:get_env(modules, [])).
|
||||
|
||||
-spec(unload() -> ok).
|
||||
unload() ->
|
||||
lists:foreach(
|
||||
fun({Mod, Env}) ->
|
||||
|
|
|
@ -1,159 +0,0 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-module(emqx_mqtt_metrics).
|
||||
|
||||
-include("emqx_mqtt.hrl").
|
||||
|
||||
-import(emqx_metrics, [inc/1]).
|
||||
|
||||
-export([init/0]).
|
||||
|
||||
%% Received/Sent Metrics
|
||||
-export([received/1, sent/1]).
|
||||
|
||||
%% Bytes sent and received of Broker
|
||||
-define(SYSTOP_BYTES, [
|
||||
{counter, 'bytes/received'}, % Total bytes received
|
||||
{counter, 'bytes/sent'} % Total bytes sent
|
||||
]).
|
||||
|
||||
%% Packets sent and received of Broker
|
||||
-define(SYSTOP_PACKETS, [
|
||||
{counter, 'packets/received'}, % All Packets received
|
||||
{counter, 'packets/sent'}, % All Packets sent
|
||||
{counter, 'packets/connect'}, % CONNECT Packets received
|
||||
{counter, 'packets/connack'}, % CONNACK Packets sent
|
||||
{counter, 'packets/publish/received'}, % PUBLISH packets received
|
||||
{counter, 'packets/publish/sent'}, % PUBLISH packets sent
|
||||
{counter, 'packets/puback/received'}, % PUBACK packets received
|
||||
{counter, 'packets/puback/sent'}, % PUBACK packets sent
|
||||
{counter, 'packets/puback/missed'}, % PUBACK packets missed
|
||||
{counter, 'packets/pubrec/received'}, % PUBREC packets received
|
||||
{counter, 'packets/pubrec/sent'}, % PUBREC packets sent
|
||||
{counter, 'packets/pubrec/missed'}, % PUBREC packets missed
|
||||
{counter, 'packets/pubrel/received'}, % PUBREL packets received
|
||||
{counter, 'packets/pubrel/sent'}, % PUBREL packets sent
|
||||
{counter, 'packets/pubrel/missed'}, % PUBREL packets missed
|
||||
{counter, 'packets/pubcomp/received'}, % PUBCOMP packets received
|
||||
{counter, 'packets/pubcomp/sent'}, % PUBCOMP packets sent
|
||||
{counter, 'packets/pubcomp/missed'}, % PUBCOMP packets missed
|
||||
{counter, 'packets/subscribe'}, % SUBSCRIBE Packets received
|
||||
{counter, 'packets/suback'}, % SUBACK packets sent
|
||||
{counter, 'packets/unsubscribe'}, % UNSUBSCRIBE Packets received
|
||||
{counter, 'packets/unsuback'}, % UNSUBACK Packets sent
|
||||
{counter, 'packets/pingreq'}, % PINGREQ packets received
|
||||
{counter, 'packets/pingresp'}, % PINGRESP Packets sent
|
||||
{counter, 'packets/disconnect'} % DISCONNECT Packets received
|
||||
]).
|
||||
|
||||
%% Messages sent and received of broker
|
||||
-define(SYSTOP_MESSAGES, [
|
||||
{counter, 'messages/received'}, % All Messages received
|
||||
{counter, 'messages/sent'}, % All Messages sent
|
||||
{counter, 'messages/qos0/received'}, % QoS0 Messages received
|
||||
{counter, 'messages/qos0/sent'}, % QoS0 Messages sent
|
||||
{counter, 'messages/qos1/received'}, % QoS1 Messages received
|
||||
{counter, 'messages/qos1/sent'}, % QoS1 Messages sent
|
||||
{counter, 'messages/qos2/received'}, % QoS2 Messages received
|
||||
{counter, 'messages/qos2/sent'}, % QoS2 Messages sent
|
||||
{counter, 'messages/qos2/dropped'}, % QoS2 Messages dropped
|
||||
{gauge, 'messages/retained'}, % Messagea retained
|
||||
{counter, 'messages/dropped'}, % Messages dropped
|
||||
{counter, 'messages/forward'} % Messages forward
|
||||
]).
|
||||
|
||||
% Init metrics
|
||||
init() ->
|
||||
lists:foreach(fun emqx_metrics:create/1,
|
||||
?SYSTOP_BYTES ++ ?SYSTOP_PACKETS ++ ?SYSTOP_MESSAGES).
|
||||
|
||||
%% @doc Count packets received.
|
||||
-spec(received(mqtt_packet()) -> ok).
|
||||
received(Packet) ->
|
||||
inc('packets/received'),
|
||||
received1(Packet).
|
||||
received1(?PUBLISH_PACKET(Qos, _PktId)) ->
|
||||
inc('packets/publish/received'),
|
||||
inc('messages/received'),
|
||||
qos_received(Qos);
|
||||
received1(?PACKET(Type)) ->
|
||||
received2(Type).
|
||||
received2(?CONNECT) ->
|
||||
inc('packets/connect');
|
||||
received2(?PUBACK) ->
|
||||
inc('packets/puback/received');
|
||||
received2(?PUBREC) ->
|
||||
inc('packets/pubrec/received');
|
||||
received2(?PUBREL) ->
|
||||
inc('packets/pubrel/received');
|
||||
received2(?PUBCOMP) ->
|
||||
inc('packets/pubcomp/received');
|
||||
received2(?SUBSCRIBE) ->
|
||||
inc('packets/subscribe');
|
||||
received2(?UNSUBSCRIBE) ->
|
||||
inc('packets/unsubscribe');
|
||||
received2(?PINGREQ) ->
|
||||
inc('packets/pingreq');
|
||||
received2(?DISCONNECT) ->
|
||||
inc('packets/disconnect');
|
||||
received2(_) ->
|
||||
ignore.
|
||||
qos_received(?QOS_0) ->
|
||||
inc('messages/qos0/received');
|
||||
qos_received(?QOS_1) ->
|
||||
inc('messages/qos1/received');
|
||||
qos_received(?QOS_2) ->
|
||||
inc('messages/qos2/received').
|
||||
|
||||
%% @doc Count packets received. Will not count $SYS PUBLISH.
|
||||
-spec(sent(mqtt_packet()) -> ignore | non_neg_integer()).
|
||||
sent(?PUBLISH_PACKET(_Qos, <<"$SYS/", _/binary>>, _, _)) ->
|
||||
ignore;
|
||||
sent(Packet) ->
|
||||
inc('packets/sent'),
|
||||
sent1(Packet).
|
||||
sent1(?PUBLISH_PACKET(Qos, _PktId)) ->
|
||||
inc('packets/publish/sent'),
|
||||
inc('messages/sent'),
|
||||
qos_sent(Qos);
|
||||
sent1(?PACKET(Type)) ->
|
||||
sent2(Type).
|
||||
sent2(?CONNACK) ->
|
||||
inc('packets/connack');
|
||||
sent2(?PUBACK) ->
|
||||
inc('packets/puback/sent');
|
||||
sent2(?PUBREC) ->
|
||||
inc('packets/pubrec/sent');
|
||||
sent2(?PUBREL) ->
|
||||
inc('packets/pubrel/sent');
|
||||
sent2(?PUBCOMP) ->
|
||||
inc('packets/pubcomp/sent');
|
||||
sent2(?SUBACK) ->
|
||||
inc('packets/suback');
|
||||
sent2(?UNSUBACK) ->
|
||||
inc('packets/unsuback');
|
||||
sent2(?PINGRESP) ->
|
||||
inc('packets/pingresp');
|
||||
sent2(_Type) ->
|
||||
ignore.
|
||||
qos_sent(?QOS_0) ->
|
||||
inc('messages/qos0/sent');
|
||||
qos_sent(?QOS_1) ->
|
||||
inc('messages/qos1/sent');
|
||||
qos_sent(?QOS_2) ->
|
||||
inc('messages/qos2/sent').
|
||||
|
|
@ -1,18 +1,18 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_parser).
|
||||
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_plugins).
|
||||
|
||||
|
@ -83,7 +83,7 @@ load_expand_plugin(PluginDir) ->
|
|||
end, Modules),
|
||||
case filelib:wildcard(Ebin ++ "/*.app") of
|
||||
[App|_] -> application:load(list_to_atom(filename:basename(App, ".app")));
|
||||
_ -> emqx_log:error("App file cannot be found."),
|
||||
_ -> emqx_logger:error("App file cannot be found."),
|
||||
{error, load_app_fail}
|
||||
end.
|
||||
|
||||
|
@ -128,7 +128,7 @@ with_loaded_file(File, SuccFun) ->
|
|||
{ok, Names} ->
|
||||
SuccFun(Names);
|
||||
{error, Error} ->
|
||||
emqx_log:error("[Plugins] Failed to read: ~p, error: ~p", [File, Error]),
|
||||
emqx_logger:error("[Plugins] Failed to read: ~p, error: ~p", [File, Error]),
|
||||
{error, Error}
|
||||
end.
|
||||
|
||||
|
@ -136,7 +136,7 @@ load_plugins(Names, Persistent) ->
|
|||
Plugins = list(), NotFound = Names -- names(Plugins),
|
||||
case NotFound of
|
||||
[] -> ok;
|
||||
NotFound -> emqx_log:error("[Plugins] Cannot find plugins: ~p", [NotFound])
|
||||
NotFound -> emqx_logger:error("[Plugins] Cannot find plugins: ~p", [NotFound])
|
||||
end,
|
||||
NeedToLoad = Names -- NotFound -- names(started_app),
|
||||
[load_plugin(find_plugin(Name, Plugins), Persistent) || Name <- NeedToLoad].
|
||||
|
@ -185,12 +185,12 @@ plugin(CfgFile) ->
|
|||
load(PluginName) when is_atom(PluginName) ->
|
||||
case lists:member(PluginName, names(started_app)) of
|
||||
true ->
|
||||
emqx_log:error("[Plugins] Plugin ~s is already started", [PluginName]),
|
||||
emqx_logger:error("[Plugins] Plugin ~s is already started", [PluginName]),
|
||||
{error, already_started};
|
||||
false ->
|
||||
case find_plugin(PluginName) of
|
||||
false ->
|
||||
emqx_log:error("[Plugins] Plugin ~s not found", [PluginName]),
|
||||
emqx_logger:error("[Plugins] Plugin ~s not found", [PluginName]),
|
||||
{error, not_found};
|
||||
Plugin ->
|
||||
load_plugin(Plugin, true)
|
||||
|
@ -218,12 +218,12 @@ load_app(App) ->
|
|||
start_app(App, SuccFun) ->
|
||||
case application:ensure_all_started(App) of
|
||||
{ok, Started} ->
|
||||
emqx_log:info("Started Apps: ~p", [Started]),
|
||||
emqx_log:info("Load plugin ~s successfully", [App]),
|
||||
emqx_logger:info("Started Apps: ~p", [Started]),
|
||||
emqx_logger:info("Load plugin ~s successfully", [App]),
|
||||
SuccFun(App),
|
||||
{ok, Started};
|
||||
{error, {ErrApp, Reason}} ->
|
||||
emqx_log:error("Load plugin ~s error, cannot start app ~s for ~p", [App, ErrApp, Reason]),
|
||||
emqx_logger:error("Load plugin ~s error, cannot start app ~s for ~p", [App, ErrApp, Reason]),
|
||||
{error, {ErrApp, Reason}}
|
||||
end.
|
||||
|
||||
|
@ -240,10 +240,10 @@ unload(PluginName) when is_atom(PluginName) ->
|
|||
{true, true} ->
|
||||
unload_plugin(PluginName, true);
|
||||
{false, _} ->
|
||||
emqx_log:error("Plugin ~s is not started", [PluginName]),
|
||||
emqx_logger:error("Plugin ~s is not started", [PluginName]),
|
||||
{error, not_started};
|
||||
{true, false} ->
|
||||
emqx_log:error("~s is not a plugin, cannot unload it", [PluginName]),
|
||||
emqx_logger:error("~s is not a plugin, cannot unload it", [PluginName]),
|
||||
{error, not_found}
|
||||
end.
|
||||
|
||||
|
@ -258,11 +258,11 @@ unload_plugin(App, Persistent) ->
|
|||
stop_app(App) ->
|
||||
case application:stop(App) of
|
||||
ok ->
|
||||
emqx_log:info("Stop plugin ~s successfully", [App]), ok;
|
||||
emqx_logger:info("Stop plugin ~s successfully", [App]), ok;
|
||||
{error, {not_started, App}} ->
|
||||
emqx_log:error("Plugin ~s is not started", [App]), ok;
|
||||
emqx_logger:error("Plugin ~s is not started", [App]), ok;
|
||||
{error, Reason} ->
|
||||
emqx_log:error("Stop plugin ~s error: ~p", [App]), {error, Reason}
|
||||
emqx_logger:error("Stop plugin ~s error: ~p", [App]), {error, Reason}
|
||||
end.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
|
@ -294,7 +294,7 @@ plugin_loaded(Name, true) ->
|
|||
ignore
|
||||
end;
|
||||
{error, Error} ->
|
||||
emqx_log:error("Cannot read loaded plugins: ~p", [Error])
|
||||
emqx_logger:error("Cannot read loaded plugins: ~p", [Error])
|
||||
end.
|
||||
|
||||
plugin_unloaded(_Name, false) ->
|
||||
|
@ -306,10 +306,10 @@ plugin_unloaded(Name, true) ->
|
|||
true ->
|
||||
write_loaded(lists:delete(Name, Names));
|
||||
false ->
|
||||
emqx_log:error("Cannot find ~s in loaded_file", [Name])
|
||||
emqx_logger:error("Cannot find ~s in loaded_file", [Name])
|
||||
end;
|
||||
{error, Error} ->
|
||||
emqx_log:error("Cannot read loaded_plugins: ~p", [Error])
|
||||
emqx_logger:error("Cannot read loaded_plugins: ~p", [Error])
|
||||
end.
|
||||
|
||||
read_loaded() ->
|
||||
|
@ -328,7 +328,7 @@ write_loaded(AppNames) ->
|
|||
file:write(Fd, iolist_to_binary(io_lib:format("~s.~n", [Name])))
|
||||
end, AppNames);
|
||||
{error, Error} ->
|
||||
emqx_log:error("Open File ~p Error: ~p", [File, Error]),
|
||||
emqx_logger:error("Open File ~p Error: ~p", [File, Error]),
|
||||
{error, Error}
|
||||
end.
|
||||
|
||||
|
|
|
@ -1,22 +1,24 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_pmon).
|
||||
|
||||
-export([new/0, monitor/2, monitor/3, demonitor/2, find/2, erase/2]).
|
||||
-export([new/0]).
|
||||
|
||||
-export([monitor/2, monitor/3, demonitor/2, find/2, erase/2]).
|
||||
|
||||
-compile({no_auto_import,[monitor/3]}).
|
||||
|
||||
|
@ -43,7 +45,8 @@ monitor(Pid, Val, PM = {?MODULE, [M]}) ->
|
|||
demonitor(Pid, PM = {?MODULE, [M]}) ->
|
||||
case maps:find(Pid, M) of
|
||||
{ok, {Ref, _Val}} ->
|
||||
erlang:demonitor(Ref, [flush]),
|
||||
%% Don't flush
|
||||
_ = erlang:demonitor(Ref),
|
||||
{?MODULE, [maps:remove(Pid, M)]};
|
||||
error ->
|
||||
PM
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_pool).
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
%% Start the pool supervisor
|
||||
-export([start_link/0]).
|
||||
|
||||
%% API Exports
|
||||
-export([start_link/2, submit/1, async_submit/1]).
|
||||
|
||||
%% gen_server Function Exports
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-record(state, {pool, id}).
|
||||
|
||||
-define(POOL, ?MODULE).
|
||||
|
||||
%% @doc Start Pooler Supervisor.
|
||||
start_link() ->
|
||||
emqx_pool_sup:start_link(?POOL, random, {?MODULE, start_link, []}).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% API
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-spec(start_link(atom(), pos_integer())
|
||||
-> {ok, pid()} | ignore | {error, term()}).
|
||||
start_link(Pool, Id) ->
|
||||
gen_server:start_link({local, emqx_misc:proc_name(?MODULE, Id)},
|
||||
?MODULE, [Pool, Id], []).
|
||||
|
||||
%% @doc Submit work to the pool
|
||||
-spec(submit(fun()) -> any()).
|
||||
submit(Fun) ->
|
||||
gen_server:call(worker(), {submit, Fun}, infinity).
|
||||
|
||||
%% @doc Submit work to the pool asynchronously
|
||||
-spec(async_submit(fun()) -> ok).
|
||||
async_submit(Fun) ->
|
||||
gen_server:cast(worker(), {async_submit, Fun}).
|
||||
|
||||
worker() ->
|
||||
gproc_pool:pick_worker(pool).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% gen_server callbacks
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
init([Pool, Id]) ->
|
||||
gproc_pool:connect_worker(Pool, {Pool, Id}),
|
||||
{ok, #state{pool = Pool, id = Id}}.
|
||||
|
||||
handle_call({submit, Fun}, _From, State) ->
|
||||
{reply, catch run(Fun), State};
|
||||
|
||||
handle_call(Req, _From, State) ->
|
||||
emqx_logger:error("[POOL] Unexpected request: ~p", [Req]),
|
||||
{reply, ignore, State}.
|
||||
|
||||
handle_cast({async_submit, Fun}, State) ->
|
||||
try run(Fun)
|
||||
catch _:Error ->
|
||||
emqx_logger:error("[POOL] Error: ~p, ~p", [Error, erlang:get_stacktrace()])
|
||||
end,
|
||||
{noreply, State};
|
||||
|
||||
handle_cast(Msg, State) ->
|
||||
emqx_logger:error("[POOL] Unexpected msg: ~p", [Msg]),
|
||||
{noreply, State}.
|
||||
|
||||
handle_info(Info, State) ->
|
||||
emqx_logger:error("[POOL] Unexpected info: ~p", [Info]),
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, #state{pool = Pool, id = Id}) ->
|
||||
gproc_pool:disconnect_worker(Pool, {Pool, Id}).
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
run({M, F, A}) ->
|
||||
erlang:apply(M, F, A);
|
||||
run(Fun) when is_function(Fun) ->
|
||||
Fun().
|
||||
|
|
@ -1,18 +1,18 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_pool_sup).
|
||||
|
||||
|
@ -39,12 +39,11 @@ start_link(Pool, Type, MFA) ->
|
|||
start_link(Pool, Type, Schedulers, MFA).
|
||||
|
||||
-spec(start_link(atom() | tuple(), atom(), pos_integer(), mfa()) -> {ok, pid()} | {error, term()}).
|
||||
start_link(Pool, Type, Size, MFA) when is_atom(Pool) ->
|
||||
supervisor:start_link({local, Pool}, ?MODULE, [Pool, Type, Size, MFA]);
|
||||
start_link(Pool, Type, Size, MFA) ->
|
||||
supervisor:start_link(?MODULE, [Pool, Type, Size, MFA]).
|
||||
|
||||
%% sup_name(Pool) when is_atom(Pool) ->
|
||||
%% list_to_atom(atom_to_list(Pool) ++ "_pool_sup").
|
||||
|
||||
init([Pool, Type, Size, {M, F, Args}]) ->
|
||||
ensure_pool(Pool, Type, [{size, Size}]),
|
||||
{ok, {{one_for_one, 10, 3600}, [
|
||||
|
|
|
@ -1,98 +0,0 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-module(emqx_pooler).
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
%% Start the pool supervisor
|
||||
-export([start_link/0]).
|
||||
|
||||
%% API Exports
|
||||
-export([start_link/2, submit/1, async_submit/1]).
|
||||
|
||||
%% gen_server Function Exports
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-record(state, {pool, id}).
|
||||
|
||||
%% @doc Start Pooler Supervisor.
|
||||
start_link() ->
|
||||
emqx_pool_sup:start_link(pooler, random, {?MODULE, start_link, []}).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% API
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-spec(start_link(atom(), pos_integer()) -> {ok, pid()} | ignore | {error, term()}).
|
||||
start_link(Pool, Id) ->
|
||||
gen_server:start_link({local, name(Id)}, ?MODULE, [Pool, Id], []).
|
||||
|
||||
name(Id) -> list_to_atom(lists:concat([?MODULE, "_", Id])).
|
||||
|
||||
%% @doc Submit work to pooler
|
||||
submit(Fun) -> gen_server:call(worker(), {submit, Fun}, infinity).
|
||||
|
||||
%% @doc Submit work to pooler asynchronously
|
||||
async_submit(Fun) ->
|
||||
gen_server:cast(worker(), {async_submit, Fun}).
|
||||
|
||||
worker() ->
|
||||
gproc_pool:pick_worker(pooler).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% gen_server callbacks
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
init([Pool, Id]) ->
|
||||
gproc_pool:connect_worker(Pool, {Pool, Id}),
|
||||
{ok, #state{pool = Pool, id = Id}}.
|
||||
|
||||
handle_call({submit, Fun}, _From, State) ->
|
||||
{reply, catch run(Fun), State};
|
||||
|
||||
handle_call(_Req, _From, State) ->
|
||||
{reply, ok, State}.
|
||||
|
||||
handle_cast({async_submit, Fun}, State) ->
|
||||
try run(Fun)
|
||||
catch _:Error ->
|
||||
emqx_log:error("Pooler Error: ~p, ~p", [Error, erlang:get_stacktrace()])
|
||||
end,
|
||||
{noreply, State};
|
||||
|
||||
handle_cast(_Msg, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, #state{pool = Pool, id = Id}) ->
|
||||
gproc_pool:disconnect_worker(Pool, {Pool, Id}).
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
run({M, F, A}) ->
|
||||
erlang:apply(M, F, A);
|
||||
run(Fun) when is_function(Fun) ->
|
||||
Fun().
|
||||
|
|
@ -57,7 +57,7 @@
|
|||
-define(STATS_KEYS, [recv_pkt, recv_msg, send_pkt, send_msg]).
|
||||
|
||||
-define(LOG(Level, Format, Args, State),
|
||||
emqx_log:Level([{client, State#proto_state.client_id}], "Client(~s@~s): " ++ Format,
|
||||
emqx_logger:Level([{client, State#proto_state.client_id}], "Client(~s@~s): " ++ Format,
|
||||
[State#proto_state.client_id, esockd_net:format(State#proto_state.peername) | Args])).
|
||||
|
||||
%% @doc Init protocol
|
||||
|
@ -220,7 +220,7 @@ process(?CONNECT_PACKET(Var), State0) ->
|
|||
{ok, Session} -> %% TODO:...
|
||||
SP = true, %% TODO:...
|
||||
%% TODO: Register the client
|
||||
emqx_cm:reg(clientid(State2)),
|
||||
emqx_cm:register_client(clientid(State2)),
|
||||
%%emqx_cm:reg(client(State2)),
|
||||
%% Start keepalive
|
||||
start_keepalive(KeepAlive, State2),
|
||||
|
@ -362,7 +362,7 @@ send(Msg, State = #proto_state{client_id = ClientId,
|
|||
|
||||
send(Packet = ?PACKET(Type), State = #proto_state{sendfun = SendFun, stats_data = Stats}) ->
|
||||
trace(send, Packet, State),
|
||||
emqx_mqtt_metrics:sent(Packet),
|
||||
emqx_metrics:sent(Packet),
|
||||
SendFun(Packet),
|
||||
{ok, State#proto_state{stats_data = inc_stats(send, Type, Stats)}}.
|
||||
|
||||
|
@ -398,15 +398,14 @@ stop_if_auth_failure(_RC, State) ->
|
|||
|
||||
shutdown(_Error, #proto_state{client_id = undefined}) ->
|
||||
ignore;
|
||||
shutdown(conflict, _State) ->
|
||||
%% let it down
|
||||
shutdown(conflict, _State = #proto_state{client_id = ClientId}) ->
|
||||
emqx_cm:unregister_client(ClientId),
|
||||
ignore;
|
||||
shutdown(mnesia_conflict, _State) ->
|
||||
%% let it down
|
||||
%% emqx_cm:unreg(ClientId);
|
||||
shutdown(mnesia_conflict, _State = #proto_state{client_id = ClientId}) ->
|
||||
emqx_cm:unregister_client(ClientId),
|
||||
ignore;
|
||||
|
||||
shutdown(Error, State = #proto_state{will_msg = WillMsg}) ->
|
||||
shutdown(Error, State = #proto_state{client_id = ClientId,
|
||||
will_msg = WillMsg}) ->
|
||||
?LOG(info, "Shutdown for ~p", [Error], State),
|
||||
Client = client(State),
|
||||
%% Auth failure not publish the will message
|
||||
|
@ -415,11 +414,11 @@ shutdown(Error, State = #proto_state{will_msg = WillMsg}) ->
|
|||
false -> send_willmsg(Client, WillMsg)
|
||||
end,
|
||||
emqx_hooks:run('client.disconnected', [Error], Client),
|
||||
%% let it down
|
||||
%% emqx_cm:unreg(ClientId).
|
||||
emqx_cm:unregister_client(ClientId),
|
||||
ok.
|
||||
|
||||
willmsg(Packet, State = #proto_state{mountpoint = MountPoint}) when is_record(Packet, mqtt_packet_connect) ->
|
||||
willmsg(Packet, State = #proto_state{mountpoint = MountPoint})
|
||||
when is_record(Packet, mqtt_packet_connect) ->
|
||||
case emqx_packet:to_message(Packet) of
|
||||
undefined -> undefined;
|
||||
Msg -> mount(replvar(MountPoint, State), Msg)
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
%%%-------------------------------------------------------------------
|
||||
%%% Copyright (c) 2013-2018 EMQ Enterprise, Inc. (http://emqtt.io)
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
-module(emqx_rate_limiter).
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
%% API
|
||||
-export([start_link/0]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-define(SERVER, ?MODULE).
|
||||
|
||||
-record(state, {}).
|
||||
|
||||
%%%===================================================================
|
||||
%%% API
|
||||
%%%===================================================================
|
||||
|
||||
%% @doc Starts the server
|
||||
-spec(start_link() -> {ok, pid()} | ignore | {error, any()}).
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
|
||||
|
||||
%%%===================================================================
|
||||
%%% gen_server callbacks
|
||||
%%%===================================================================
|
||||
|
||||
init([]) ->
|
||||
{ok, #state{}}.
|
||||
|
||||
handle_call(_Request, _From, State) ->
|
||||
Reply = ok,
|
||||
{reply, Reply, State}.
|
||||
|
||||
handle_cast(_Msg, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, _State) ->
|
||||
ok.
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
%%%===================================================================
|
||||
%%% Internal functions
|
||||
%%%===================================================================
|
||||
|
|
@ -1,59 +1,53 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_router).
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
-include("emqx.hrl").
|
||||
|
||||
-include_lib("ekka/include/ekka.hrl").
|
||||
|
||||
%% Mnesia Bootstrap
|
||||
%% Mnesia bootstrap
|
||||
-export([mnesia/1]).
|
||||
|
||||
-boot_mnesia({mnesia, [boot]}).
|
||||
-copy_mnesia({mnesia, [copy]}).
|
||||
|
||||
%% Start
|
||||
-export([start_link/2]).
|
||||
|
||||
%% Route APIs
|
||||
-export([add_route/2, add_route/3, get_routes/1, del_route/2, del_route/3]).
|
||||
-export([has_routes/1, match_routes/1, print_routes/1]).
|
||||
|
||||
%% Topics
|
||||
-export([topics/0]).
|
||||
|
||||
%% Route management APIs
|
||||
-export([add_route/2, add_route/3, get_routes/1, del_route/2, del_route/3]).
|
||||
|
||||
-export([has_routes/1, match_routes/1, print_routes/1]).
|
||||
|
||||
%% gen_server Function Exports
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-type(group() :: binary()).
|
||||
|
||||
-type(destination() :: node() | {group(), node()}
|
||||
| {cluster(), group(), node()}).
|
||||
-type(destination() :: node() | {binary(), node()}).
|
||||
|
||||
-record(state, {pool, id}).
|
||||
|
||||
-define(ROUTE, emqx_route).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Mnesia Bootstrap
|
||||
%% Mnesia bootstrap
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
mnesia(boot) ->
|
||||
|
@ -67,14 +61,17 @@ mnesia(copy) ->
|
|||
ok = ekka_mnesia:copy_table(?ROUTE).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Start a router
|
||||
%% Strat a Router
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-spec(start_link(atom(), pos_integer())
|
||||
-> {ok, pid()} | ignore | {error, term()}).
|
||||
start_link(Pool, Id) ->
|
||||
gen_server:start_link(?MODULE, [Pool, Id], [{hibernate_after, 10000}]).
|
||||
gen_server:start_link(emqx_misc:proc_name(?MODULE, Id),
|
||||
?MODULE, [Pool, Id], [{hibernate_after, 10000}]).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Add/Del Routes
|
||||
%% Route APIs
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
%% @doc Add a route
|
||||
|
@ -105,40 +102,24 @@ del_route(From, Topic, Dest) when is_binary(Topic) ->
|
|||
has_routes(Topic) when is_binary(Topic) ->
|
||||
ets:member(?ROUTE, Topic).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Topics
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-spec(topics() -> list(binary())).
|
||||
%% @doc Get topics
|
||||
-spec(topics() -> list(topic())).
|
||||
topics() -> mnesia:dirty_all_keys(?ROUTE).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Match routes
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
%% @doc Match routes
|
||||
%% Optimize: routing table will be replicated to all router nodes.
|
||||
-spec(match_routes(Topic:: topic()) -> [{topic(), binary() | node()}]).
|
||||
-spec(match_routes(topic()) -> [route()]).
|
||||
match_routes(Topic) when is_binary(Topic) ->
|
||||
Matched = mnesia:ets(fun emqx_trie:match/1, [Topic]),
|
||||
Routes = [ets:lookup(?ROUTE, To) || To <- [Topic | Matched]],
|
||||
[{To, Dest} || #route{topic = To, dest = Dest} <- lists:append(Routes)].
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Print routes
|
||||
%%--------------------------------------------------------------------
|
||||
lists:append([get_routes(To) || To <- [Topic | Matched]]).
|
||||
|
||||
%% @doc Print routes to a topic
|
||||
-spec(print_routes(topic()) -> ok).
|
||||
print_routes(Topic) ->
|
||||
lists:foreach(fun({To, Dest}) ->
|
||||
lists:foreach(fun(#route{topic = To, dest = Dest}) ->
|
||||
io:format("~s -> ~s~n", [To, Dest])
|
||||
end, match_routes(Topic)).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Utility functions
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
cast(Router, Msg) ->
|
||||
gen_server:cast(Router, Msg).
|
||||
|
||||
|
@ -154,7 +135,7 @@ init([Pool, Id]) ->
|
|||
{ok, #state{pool = Pool, id = Id}}.
|
||||
|
||||
handle_call(Req, _From, State) ->
|
||||
emqx_log:error("[Router] Unexpected request: ~p", [Req]),
|
||||
emqx_logger:error("[Router] Unexpected request: ~p", [Req]),
|
||||
{reply, ignore, State}.
|
||||
|
||||
handle_cast({add_route, From, Route}, State) ->
|
||||
|
@ -163,12 +144,12 @@ handle_cast({add_route, From, Route}, State) ->
|
|||
{noreply, State};
|
||||
|
||||
handle_cast({add_route, Route = #route{topic = Topic, dest = Dest}}, State) ->
|
||||
case lists:member(Route, ets:lookup(?ROUTE, Topic)) of
|
||||
case lists:member(Route, get_routes(Topic)) of
|
||||
true -> ok;
|
||||
false ->
|
||||
ok = emqx_router_helper:monitor(Dest),
|
||||
case emqx_topic:wildcard(Topic) of
|
||||
true -> trans(fun add_trie_route/1, [Route]);
|
||||
true -> log(trans(fun add_trie_route/1, [Route]));
|
||||
false -> add_direct_route(Route)
|
||||
end
|
||||
end,
|
||||
|
@ -185,18 +166,18 @@ handle_cast({del_route, Route = #route{topic = Topic}}, State) ->
|
|||
true -> ok;
|
||||
false ->
|
||||
case emqx_topic:wildcard(Topic) of
|
||||
true -> trans(fun del_trie_route/1, [Route]);
|
||||
true -> log(trans(fun del_trie_route/1, [Route]));
|
||||
false -> del_direct_route(Route)
|
||||
end
|
||||
end,
|
||||
{noreply, State};
|
||||
|
||||
handle_cast(Msg, State) ->
|
||||
emqx_log:error("[Router] Unexpected msg: ~p", [Msg]),
|
||||
emqx_logger:error("[Router] Unexpected msg: ~p", [Msg]),
|
||||
{noreply, State}.
|
||||
|
||||
handle_info(Info, State) ->
|
||||
emqx_log:error("[Router] Unexpected info: ~p", [Info]),
|
||||
emqx_logger:error("[Router] Unexpected info: ~p", [Info]),
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, #state{pool = Pool, id = Id}) ->
|
||||
|
@ -237,8 +218,10 @@ del_trie_route(Route = #route{topic = Topic}) ->
|
|||
trans(Fun, Args) ->
|
||||
case mnesia:transaction(Fun, Args) of
|
||||
{atomic, _} -> ok;
|
||||
{aborted, Error} ->
|
||||
emqx_log:error("[Router] Mnesia aborted: ~p", [Error]),
|
||||
{error, Error}
|
||||
{aborted, Error} -> {error, Error}
|
||||
end.
|
||||
|
||||
log(ok) -> ok;
|
||||
log({error, Error}) ->
|
||||
emqx_logger:error("[Router] Mnesia aborted: ~p", [Error]).
|
||||
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_router_helper).
|
||||
|
||||
|
@ -20,72 +20,73 @@
|
|||
|
||||
-include("emqx.hrl").
|
||||
|
||||
%% Mnesia Bootstrap
|
||||
%% Mnesia bootstrap
|
||||
-export([mnesia/1]).
|
||||
|
||||
-boot_mnesia({mnesia, [boot]}).
|
||||
-copy_mnesia({mnesia, [copy]}).
|
||||
|
||||
%% API
|
||||
-export([start_link/1, monitor/1]).
|
||||
-export([start_link/0, monitor/1]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-record(routing_node, {name, ts}).
|
||||
-record(routing_node, {name, const = unused}).
|
||||
|
||||
-record(state, {nodes = [], stats_fun, stats_timer}).
|
||||
-record(state, {nodes = []}).
|
||||
|
||||
-compile({no_auto_import, [monitor/1]}).
|
||||
|
||||
-define(SERVER, ?MODULE).
|
||||
|
||||
-define(TAB, emqx_routing_node).
|
||||
-define(ROUTE, emqx_route).
|
||||
-define(ROUTING_NODE, emqx_routing_node).
|
||||
|
||||
-define(LOCK, {?MODULE, clean_routes}).
|
||||
-define(LOCK, {?MODULE, cleanup_routes}).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Mnesia bootstrap
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
mnesia(boot) ->
|
||||
ok = ekka_mnesia:create_table(?TAB, [
|
||||
ok = ekka_mnesia:create_table(?ROUTING_NODE, [
|
||||
{type, set},
|
||||
{ram_copies, [node()]},
|
||||
{record_name, routing_node},
|
||||
{attributes, record_info(fields, routing_node)}]);
|
||||
|
||||
mnesia(copy) ->
|
||||
ok = ekka_mnesia:copy_table(?TAB).
|
||||
ok = ekka_mnesia:copy_table(?ROUTING_NODE).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% API
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
%% @doc Starts the router helper
|
||||
-spec(start_link(fun()) -> {ok, pid()} | ignore | {error, any()}).
|
||||
start_link(StatsFun) ->
|
||||
gen_server:start_link({local, ?SERVER}, ?MODULE, [StatsFun], []).
|
||||
-spec(start_link() -> {ok, pid()} | ignore | {error, any()}).
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
|
||||
|
||||
%% @doc Monitor routing node
|
||||
-spec(monitor(node()) -> ok).
|
||||
-spec(monitor(node() | {binary(), node()}) -> ok).
|
||||
monitor({_Group, Node}) ->
|
||||
monitor(Node);
|
||||
monitor(Node) when is_atom(Node) ->
|
||||
case ekka:is_member(Node) orelse ets:member(?TAB, Node) of
|
||||
case ekka:is_member(Node)
|
||||
orelse ets:member(?ROUTING_NODE, Node) of
|
||||
true -> ok;
|
||||
false ->
|
||||
mnesia:dirty_write(?TAB, #routing_node{name = Node, ts = os:timestamp()})
|
||||
false -> mnesia:dirty_write(?ROUTING_NODE, #routing_node{name = Node})
|
||||
end.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% gen_server callbacks
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
init([StatsFun]) ->
|
||||
init([]) ->
|
||||
ekka:monitor(membership),
|
||||
mnesia:subscribe({table, ?TAB, simple}),
|
||||
mnesia:subscribe({table, ?ROUTING_NODE, simple}),
|
||||
Nodes = lists:foldl(
|
||||
fun(Node, Acc) ->
|
||||
case ekka:is_member(Node) of
|
||||
|
@ -93,21 +94,21 @@ init([StatsFun]) ->
|
|||
false -> _ = erlang:monitor_node(Node, true),
|
||||
[Node | Acc]
|
||||
end
|
||||
end, [], mnesia:dirty_all_keys(?TAB)),
|
||||
{ok, TRef} = timer:send_interval(timer:seconds(1), stats),
|
||||
{ok, #state{nodes = Nodes, stats_fun = StatsFun, stats_timer = TRef}}.
|
||||
end, [], mnesia:dirty_all_keys(?ROUTING_NODE)),
|
||||
emqx_stats:update_interval(route_stats, stats_fun()),
|
||||
{ok, #state{nodes = Nodes}}.
|
||||
|
||||
handle_call(Req, _From, State) ->
|
||||
emqx_log:error("[RouterHelper] Unexpected request: ~p", [Req]),
|
||||
emqx_logger:error("[RouterHelper] Unexpected request: ~p", [Req]),
|
||||
{reply, ignore, State}.
|
||||
|
||||
handle_cast(Msg, State) ->
|
||||
emqx_log:error("[RouterHelper] Unexpected msg: ~p", [Msg]),
|
||||
emqx_logger:error("[RouterHelper] Unexpected msg: ~p", [Msg]),
|
||||
{noreply, State}.
|
||||
|
||||
handle_info({mnesia_table_event, {write, #routing_node{name = Node}, _}},
|
||||
State = #state{nodes = Nodes}) ->
|
||||
emqx_log:info("[RouterHelper] New routing node: ~s", [Node]),
|
||||
emqx_logger:info("[RouterHelper] New routing node: ~s", [Node]),
|
||||
case ekka:is_member(Node) orelse lists:member(Node, Nodes) of
|
||||
true -> {noreply, State};
|
||||
false -> _ = erlang:monitor_node(Node, true),
|
||||
|
@ -122,8 +123,8 @@ handle_info({nodedown, Node}, State = #state{nodes = Nodes}) ->
|
|||
fun() ->
|
||||
mnesia:transaction(fun cleanup_routes/1, [Node])
|
||||
end),
|
||||
mnesia:dirty_delete(?TAB, Node),
|
||||
handle_info(stats, State#state{nodes = lists:delete(Node, Nodes)});
|
||||
mnesia:dirty_delete(?ROUTING_NODE, Node),
|
||||
{noreply, State#state{nodes = lists:delete(Node, Nodes)}};
|
||||
|
||||
handle_info({membership, {mnesia, down, Node}}, State) ->
|
||||
handle_info({nodedown, Node}, State);
|
||||
|
@ -131,18 +132,14 @@ handle_info({membership, {mnesia, down, Node}}, State) ->
|
|||
handle_info({membership, _Event}, State) ->
|
||||
{noreply, State};
|
||||
|
||||
handle_info(stats, State = #state{stats_fun = StatsFun}) ->
|
||||
ok = StatsFun(mnesia:table_info(emqx_route, size)),
|
||||
{noreply, State, hibernate};
|
||||
|
||||
handle_info(Info, State) ->
|
||||
emqx_log:error("[RouteHelper] Unexpected info: ~p", [Info]),
|
||||
emqx_logger:error("[RouteHelper] Unexpected info: ~p", [Info]),
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, #state{stats_timer = TRef}) ->
|
||||
timer:cancel(TRef),
|
||||
terminate(_Reason, #state{}) ->
|
||||
ekka:unmonitor(membership),
|
||||
mnesia:unsubscribe({table, ?TAB, simple}).
|
||||
emqx_stats:cancel_update(route_stats),
|
||||
mnesia:unsubscribe({table, ?ROUTING_NODE, simple}).
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
@ -151,9 +148,19 @@ code_change(_OldVsn, State, _Extra) ->
|
|||
%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
stats_fun() ->
|
||||
fun() ->
|
||||
case ets:info(?ROUTE, size) of
|
||||
undefined -> ok;
|
||||
Size ->
|
||||
emqx_stats:setstat('routes/count', 'routes/max', Size),
|
||||
emqx_stats:setstat('topics/count', 'topics/max', Size)
|
||||
end
|
||||
end.
|
||||
|
||||
cleanup_routes(Node) ->
|
||||
Patterns = [#route{_ = '_', dest = Node},
|
||||
#route{_ = '_', dest = {'_', Node}}],
|
||||
[mnesia:delete_object(?TAB, R, write)
|
||||
|| Pat <- Patterns, R <- mnesia:match_object(?TAB, Pat, write)].
|
||||
[mnesia:delete_object(?ROUTE, Route, write)
|
||||
|| Pat <- Patterns, Route <- mnesia:match_object(?ROUTE, Pat, write)].
|
||||
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_router_sup).
|
||||
|
||||
|
@ -26,16 +26,14 @@ start_link() ->
|
|||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||
|
||||
init([]) ->
|
||||
StatsFun = emqx_stats:statsfun('routes/count', 'routes/max'),
|
||||
|
||||
%% Router helper
|
||||
Helper = {router_helper, {emqx_router_helper, start_link, [StatsFun]},
|
||||
Helper = {router_helper, {emqx_router_helper, start_link, []},
|
||||
permanent, 5000, worker, [emqx_router_helper]},
|
||||
|
||||
%% Router pool
|
||||
PoolSup = emqx_pool_sup:spec(router_pool,
|
||||
[router, hash, emqx_sys:schedulers(),
|
||||
RouterPool = emqx_pool_sup:spec(emqx_router_pool,
|
||||
[router, hash, emqx_vm:schedulers(),
|
||||
{emqx_router, start_link, []}]),
|
||||
|
||||
{ok, {{one_for_all, 0, 3600}, [Helper, PoolSup]}}.
|
||||
{ok, {{one_for_all, 0, 1}, [Helper, RouterPool]}}.
|
||||
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_rpc).
|
||||
|
||||
|
@ -20,12 +20,14 @@
|
|||
|
||||
-export([multicall/4]).
|
||||
|
||||
-define(RPC, rpc).
|
||||
|
||||
call(Node, Mod, Fun, Args) ->
|
||||
rpc:call(Node, Mod, Fun, Args).
|
||||
?RPC:call(Node, Mod, Fun, Args).
|
||||
|
||||
multicall(Nodes, Mod, Fun, Args) ->
|
||||
rpc:multicall(Nodes, Mod, Fun, Args).
|
||||
?RPC:multicall(Nodes, Mod, Fun, Args).
|
||||
|
||||
cast(Node, Mod, Fun, Args) ->
|
||||
rpc:cast(Node, Mod, Fun, Args).
|
||||
?RPC:cast(Node, Mod, Fun, Args).
|
||||
|
||||
|
|
|
@ -155,7 +155,7 @@
|
|||
created_at]).
|
||||
|
||||
-define(LOG(Level, Format, Args, State),
|
||||
emqx_log:Level([{client, State#state.client_id}],
|
||||
emqx_logger:Level([{client, State#state.client_id}],
|
||||
"Session(~s): " ++ Format, [State#state.client_id | Args])).
|
||||
|
||||
%% @doc Start a Session
|
||||
|
@ -296,7 +296,7 @@ init(#{clean_start := CleanStart,
|
|||
enable_stats = EnableStats,
|
||||
ignore_loop_deliver = IgnoreLoopDeliver,
|
||||
created_at = os:timestamp()},
|
||||
emqx_sm:register_session(#session{sid = ClientId, pid = self()}, info(State)),
|
||||
emqx_sm:register_session(ClientId, info(State)),
|
||||
emqx_hooks:run('session.created', [ClientId, Username]),
|
||||
io:format("Session started: ~p~n", [self()]),
|
||||
{ok, emit_stats(State), hibernate}.
|
||||
|
@ -342,7 +342,7 @@ handle_call(state, _From, State) ->
|
|||
reply(?record_to_proplist(state, State, ?STATE_KEYS), State);
|
||||
|
||||
handle_call(Req, _From, State) ->
|
||||
emqx_log:error("[Session] Unexpected request: ~p", [Req]),
|
||||
emqx_logger:error("[Session] Unexpected request: ~p", [Req]),
|
||||
{reply, ignore, State}.
|
||||
|
||||
handle_cast({subscribe, From, TopicTable, AckFun},
|
||||
|
@ -501,7 +501,7 @@ handle_cast({resume, ClientPid},
|
|||
{noreply, emit_stats(dequeue(retry_delivery(true, State1)))};
|
||||
|
||||
handle_cast(Msg, State) ->
|
||||
emqx_log:error("[Session] Unexpected msg: ~p", [Msg]),
|
||||
emqx_logger:error("[Session] Unexpected msg: ~p", [Msg]),
|
||||
{noreply, State}.
|
||||
|
||||
%% Ignore Messages delivered by self
|
||||
|
@ -551,13 +551,13 @@ handle_info({'EXIT', Pid, Reason}, State = #state{client_pid = ClientPid}) ->
|
|||
{noreply, State, hibernate};
|
||||
|
||||
handle_info(Info, State) ->
|
||||
emqx_log:error("[Session] Unexpected info: ~p", [Info]),
|
||||
emqx_logger:error("[Session] Unexpected info: ~p", [Info]),
|
||||
{noreply, State}.
|
||||
|
||||
terminate(Reason, #state{client_id = ClientId, username = Username}) ->
|
||||
|
||||
emqx_hooks:run('session.terminated', [ClientId, Username, Reason]),
|
||||
emqx_sm:unregister_session(#session{sid = ClientId, pid = self()}).
|
||||
emqx_sm:unregister_session(ClientId).
|
||||
|
||||
code_change(_OldVsn, Session, _Extra) ->
|
||||
{ok, Session}.
|
||||
|
@ -812,8 +812,7 @@ next_msg_id(State = #state{next_msg_id = Id}) ->
|
|||
emit_stats(State = #state{enable_stats = false}) ->
|
||||
State;
|
||||
emit_stats(State = #state{client_id = ClientId}) ->
|
||||
Session = #session{sid = ClientId, pid = self()},
|
||||
emqx_sm_stats:set_session_stats(Session, stats(State)),
|
||||
emqx_sm:set_session_stats(ClientId, stats(State)),
|
||||
State.
|
||||
|
||||
inc_stats(Key) -> put(Key, get(Key) + 1).
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_session_sup).
|
||||
|
||||
|
@ -24,13 +24,12 @@
|
|||
|
||||
-export([init/1]).
|
||||
|
||||
-spec(start_link() -> {ok, pid()}).
|
||||
start_link() ->
|
||||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||
|
||||
-spec(start_session(session()) -> {ok, pid()}).
|
||||
start_session(Session) ->
|
||||
supervisor:start_child(?MODULE, [Session]).
|
||||
-spec(start_session(map()) -> {ok, pid()}).
|
||||
start_session(Attrs) ->
|
||||
supervisor:start_child(?MODULE, [Attrs]).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Supervisor callbacks
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_shared_sub).
|
||||
|
||||
|
@ -20,13 +20,16 @@
|
|||
|
||||
-include("emqx.hrl").
|
||||
|
||||
%% API
|
||||
%% Mnesia bootstrap
|
||||
-export([mnesia/1]).
|
||||
|
||||
-boot_mnesia({mnesia, [boot]}).
|
||||
-copy_mnesia({mnesia, [copy]}).
|
||||
|
||||
-export([start_link/0]).
|
||||
|
||||
-export([strategy/0]).
|
||||
|
||||
-export([subscribe/3, unsubscribe/3]).
|
||||
|
||||
-export([dispatch/3]).
|
||||
|
||||
%% gen_server callbacks
|
||||
|
@ -41,18 +44,26 @@
|
|||
|
||||
-record(shared_subscription, {group, topic, subpid}).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Mnesia bootstrap
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
mnesia(boot) ->
|
||||
ok = ekka_mnesia:create_table(?TAB, [
|
||||
{type, bag},
|
||||
{ram_copies, [node()]},
|
||||
{record_name, shared_subscription},
|
||||
{attributes, record_info(fields, shared_subscription)}]);
|
||||
|
||||
mnesia(copy) ->
|
||||
ok = ekka_mnesia:copy_table(?TAB).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% API
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-spec(start_link() -> {ok, pid()} | ignore | {error, any()}).
|
||||
start_link() ->
|
||||
ok = ekka_mnesia:create_table(?TAB, [
|
||||
{type, bag},
|
||||
{ram_copies, [node()]},
|
||||
{record_name, shared_subscription},
|
||||
{attributes, record_info(fields, shared_subscription)}]),
|
||||
ok = ekka_mnesia:copy_table(?TAB),
|
||||
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
|
||||
|
||||
-spec(strategy() -> random | hash).
|
||||
|
@ -62,24 +73,17 @@ strategy() ->
|
|||
subscribe(undefined, _Topic, _SubPid) ->
|
||||
ok;
|
||||
subscribe(Group, Topic, SubPid) when is_pid(SubPid) ->
|
||||
mnesia:dirty_write(?TAB, r(Group, Topic, SubPid)),
|
||||
mnesia:dirty_write(?TAB, record(Group, Topic, SubPid)),
|
||||
gen_server:cast(?SERVER, {monitor, SubPid}).
|
||||
|
||||
unsubscribe(undefined, _Topic, _SubPid) ->
|
||||
ok;
|
||||
unsubscribe(Group, Topic, SubPid) when is_pid(SubPid) ->
|
||||
mnesia:dirty_delete_object(?TAB, r(Group, Topic, SubPid)).
|
||||
mnesia:dirty_delete_object(?TAB, record(Group, Topic, SubPid)).
|
||||
|
||||
r(Group, Topic, SubPid) ->
|
||||
record(Group, Topic, SubPid) ->
|
||||
#shared_subscription{group = Group, topic = Topic, subpid = SubPid}.
|
||||
|
||||
dispatch({Cluster, Group}, Topic, Delivery) ->
|
||||
case ekka:cluster_name() of
|
||||
Cluster ->
|
||||
dispatch(Group, Topic, Delivery);
|
||||
_ -> Delivery
|
||||
end;
|
||||
|
||||
%% TODO: ensure the delivery...
|
||||
dispatch(Group, Topic, Delivery = #delivery{message = Msg, flows = Flows}) ->
|
||||
case pick(subscribers(Group, Topic)) of
|
||||
|
@ -107,7 +111,7 @@ subscribers(Group, Topic) ->
|
|||
init([]) ->
|
||||
{atomic, PMon} = mnesia:transaction(fun init_monitors/0),
|
||||
mnesia:subscribe({table, ?TAB, simple}),
|
||||
{ok, #state{pmon = PMon}}.
|
||||
{ok, update_stats(#state{pmon = PMon})}.
|
||||
|
||||
init_monitors() ->
|
||||
mnesia:foldl(
|
||||
|
@ -116,36 +120,34 @@ init_monitors() ->
|
|||
end, emqx_pmon:new(), ?TAB).
|
||||
|
||||
handle_call(Req, _From, State) ->
|
||||
emqx_log:error("[Shared] Unexpected request: ~p", [Req]),
|
||||
emqx_logger:error("[Shared] Unexpected request: ~p", [Req]),
|
||||
{reply, ignore, State}.
|
||||
|
||||
handle_cast({monitor, SubPid}, State= #state{pmon = PMon}) ->
|
||||
{noreply, State#state{pmon = PMon:monitor(SubPid)}};
|
||||
{noreply, update_stats(State#state{pmon = PMon:monitor(SubPid)})};
|
||||
|
||||
handle_cast(Msg, State) ->
|
||||
emqx_log:error("[Shared] Unexpected msg: ~p", [Msg]),
|
||||
emqx_logger:error("[Shared] Unexpected msg: ~p", [Msg]),
|
||||
{noreply, State}.
|
||||
|
||||
handle_info({mnesia_table_event, {write, NewRecord, _}}, State = #state{pmon = PMon}) ->
|
||||
emqx_log:info("Shared subscription created: ~p", [NewRecord]),
|
||||
#shared_subscription{subpid = SubPid} = NewRecord,
|
||||
{noreply, State#state{pmon = PMon:monitor(SubPid)}};
|
||||
{noreply, update_stats(State#state{pmon = PMon:monitor(SubPid)})};
|
||||
|
||||
handle_info({mnesia_table_event, {delete_object, OldRecord, _}}, State = #state{pmon = PMon}) ->
|
||||
emqx_log:info("Shared subscription deleted: ~p", [OldRecord]),
|
||||
#shared_subscription{subpid = SubPid} = OldRecord,
|
||||
{noreply, State#state{pmon = PMon:demonitor(SubPid)}};
|
||||
{noreply, update_stats(State#state{pmon = PMon:demonitor(SubPid)})};
|
||||
|
||||
handle_info({mnesia_table_event, _Event}, State) ->
|
||||
{noreply, State};
|
||||
|
||||
handle_info({'DOWN', _MRef, process, SubPid, _Reason}, State = #state{pmon = PMon}) ->
|
||||
emqx_log:info("Shared subscription down: ~p", [SubPid]),
|
||||
emqx_logger:info("Shared subscription down: ~p", [SubPid]),
|
||||
mnesia:async_dirty(fun cleanup_down/1, [SubPid]),
|
||||
{noreply, State#state{pmon = PMon:erase(SubPid)}};
|
||||
{noreply, update_stats(State#state{pmon = PMon:erase(SubPid)})};
|
||||
|
||||
handle_info(Info, State) ->
|
||||
emqx_log:error("[Shared] Unexpected info: ~p", [Info]),
|
||||
emqx_logger:error("[Shared] Unexpected info: ~p", [Info]),
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, _State) ->
|
||||
|
@ -164,3 +166,8 @@ cleanup_down(SubPid) ->
|
|||
mnesia:delete_object(?TAB, Record)
|
||||
end, mnesia:match_object(Pat)).
|
||||
|
||||
update_stats(State) ->
|
||||
emqx_stats:setstat('subscriptions/shared/count',
|
||||
'subscriptions/shared/max',
|
||||
ets:info(?TAB, size)), State.
|
||||
|
||||
|
|
237
src/emqx_sm.erl
237
src/emqx_sm.erl
|
@ -1,18 +1,18 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_sm).
|
||||
|
||||
|
@ -24,7 +24,8 @@
|
|||
|
||||
-export([open_session/1, lookup_session/1, close_session/1]).
|
||||
-export([resume_session/1, resume_session/2, discard_session/1, discard_session/2]).
|
||||
-export([register_session/2, unregister_session/1]).
|
||||
-export([register_session/2, get_session_attrs/1, unregister_session/1]).
|
||||
-export([get_session_stats/1, set_session_stats/2]).
|
||||
|
||||
%% Internal functions for rpc
|
||||
-export([dispatch/3]).
|
||||
|
@ -32,18 +33,22 @@
|
|||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-record(state, {pmon}).
|
||||
-record(state, {session_pmon}).
|
||||
|
||||
-define(SM, ?MODULE).
|
||||
|
||||
%% ETS Tables
|
||||
-define(SESSION, emqx_session).
|
||||
-define(SESSION_P, emqx_persistent_session).
|
||||
-define(SESSION_ATTRS, emqx_session_attrs).
|
||||
-define(SESSION_STATS, emqx_session_stats).
|
||||
|
||||
-spec(start_link() -> {ok, pid()} | ignore | {error, term()}).
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Open Session
|
||||
%%--------------------------------------------------------------------
|
||||
gen_server:start_link({local, ?SM}, ?MODULE, [], []).
|
||||
|
||||
%% @doc Open a session.
|
||||
-spec(open_session(map()) -> {ok, pid()} | {error, term()}).
|
||||
open_session(Attrs = #{clean_start := true,
|
||||
client_id := ClientId, client_pid := ClientPid}) ->
|
||||
CleanStart = fun(_) ->
|
||||
|
@ -66,81 +71,113 @@ open_session(Attrs = #{clean_start := false,
|
|||
end,
|
||||
emqx_sm_locker:trans(ClientId, ResumeStart).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Discard Session
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
%% @doc Discard all the sessions identified by the ClientId.
|
||||
-spec(discard_session(map()) -> ok).
|
||||
discard_session(ClientId) when is_binary(ClientId) ->
|
||||
discard_session(ClientId, self()).
|
||||
|
||||
discard_session(ClientId, ClientPid) when is_binary(ClientId) ->
|
||||
lists:foreach(
|
||||
fun(#session{pid = SessionPid}) ->
|
||||
fun({_ClientId, SessionPid}) ->
|
||||
case catch emqx_session:discard(SessionPid, ClientPid) of
|
||||
{'EXIT', Error} ->
|
||||
emqx_log:error("[SM] Failed to discard ~p: ~p", [SessionPid, Error]);
|
||||
emqx_logger:error("[SM] Failed to discard ~p: ~p", [SessionPid, Error]);
|
||||
ok -> ok
|
||||
end
|
||||
end, lookup_session(ClientId)).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Resume Session
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
%% @doc Try to resume a session.
|
||||
-spec(resume_session(client_id()) -> {ok, pid()} | {error, term()}).
|
||||
resume_session(ClientId) ->
|
||||
resume_session(ClientId, self()).
|
||||
|
||||
resume_session(ClientId, ClientPid) ->
|
||||
case lookup_session(ClientId) of
|
||||
[] -> {error, not_found};
|
||||
[#session{pid = SessionPid}] ->
|
||||
[{_ClientId, SessionPid}] ->
|
||||
ok = emqx_session:resume(SessionPid, ClientPid),
|
||||
{ok, SessionPid};
|
||||
Sessions ->
|
||||
[#session{pid = SessionPid}|StaleSessions] = lists:reverse(Sessions),
|
||||
emqx_log:error("[SM] More than one session found: ~p", [Sessions]),
|
||||
lists:foreach(fun(#session{pid = Pid}) ->
|
||||
catch emqx_session:discard(Pid, ClientPid)
|
||||
[{_, SessionPid}|StaleSessions] = lists:reverse(Sessions),
|
||||
emqx_logger:error("[SM] More than one session found: ~p", [Sessions]),
|
||||
lists:foreach(fun({_, StalePid}) ->
|
||||
catch emqx_session:discard(StalePid, ClientPid)
|
||||
end, StaleSessions),
|
||||
ok = emqx_session:resume(SessionPid, ClientPid),
|
||||
{ok, SessionPid}
|
||||
end.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Close a session
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
close_session(#session{pid = SessionPid}) ->
|
||||
%% @doc Close a session.
|
||||
-spec(close_session({client_id(), pid()} | pid()) -> ok).
|
||||
close_session({_ClientId, SessionPid}) ->
|
||||
emqx_session:close(SessionPid);
|
||||
close_session(SessionPid) when is_pid(SessionPid) ->
|
||||
emqx_session:close(SessionPid).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Create/Delete a session
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc Register a session with attributes.
|
||||
-spec(register_session(client_id() | {client_id(), pid()},
|
||||
list(emqx_session:attribute())) -> ok).
|
||||
register_session(ClientId, Attrs) when is_binary(ClientId) ->
|
||||
register_session({ClientId, self()}, Attrs);
|
||||
|
||||
register_session(Session, Attrs) when is_record(Session, session) ->
|
||||
ets:insert(session, Session),
|
||||
ets:insert(session_attrs, {Session, Attrs}),
|
||||
register_session(Session = {ClientId, SessionPid}, Attrs)
|
||||
when is_binary(ClientId), is_pid(SessionPid) ->
|
||||
ets:insert(?SESSION, Session),
|
||||
ets:insert(?SESSION_ATTRS, {Session, Attrs}),
|
||||
case proplists:get_value(clean_start, Attrs, true) of
|
||||
true -> ok;
|
||||
false -> ets:insert(?SESSION_P, Session)
|
||||
end,
|
||||
emqx_sm_registry:register_session(Session),
|
||||
gen_server:cast(?MODULE, {registered, Session}).
|
||||
notify({registered, ClientId, SessionPid}).
|
||||
|
||||
unregister_session(Session) when is_record(Session, session) ->
|
||||
%% @doc Get session attrs
|
||||
-spec(get_session_attrs({client_id(), pid()})
|
||||
-> list(emqx_session:attribute())).
|
||||
get_session_attrs(Session = {ClientId, SessionPid})
|
||||
when is_binary(ClientId), is_pid(SessionPid) ->
|
||||
safe_lookup_element(?SESSION_ATTRS, Session, []).
|
||||
|
||||
%% @doc Unregister a session
|
||||
-spec(unregister_session(client_id() | {client_id(), pid()}) -> ok).
|
||||
unregister_session(ClientId) when is_binary(ClientId) ->
|
||||
unregister_session({ClientId, self()});
|
||||
|
||||
unregister_session(Session = {ClientId, SessionPid})
|
||||
when is_binary(ClientId), is_pid(SessionPid) ->
|
||||
emqx_sm_registry:unregister_session(Session),
|
||||
emqx_sm_stats:del_session_stats(Session),
|
||||
ets:delete(session_attrs, Session),
|
||||
ets:delete_object(session, Session),
|
||||
gen_server:cast(?MODULE, {unregistered, Session}).
|
||||
ets:delete(?SESSION_STATS, Session),
|
||||
ets:delete(?SESSION_ATTRS, Session),
|
||||
ets:delete_object(?SESSION_P, Session),
|
||||
ets:delete_object(?SESSION, Session),
|
||||
notify({unregistered, ClientId, SessionPid}).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Lookup a session from registry
|
||||
%%--------------------------------------------------------------------
|
||||
%% @doc Get session stats
|
||||
-spec(get_session_stats({client_id(), pid()}) -> list(emqx_stats:stats())).
|
||||
get_session_stats(Session = {ClientId, SessionPid})
|
||||
when is_binary(ClientId), is_pid(SessionPid) ->
|
||||
safe_lookup_element(?SESSION_STATS, Session, []).
|
||||
|
||||
%% @doc Set session stats
|
||||
-spec(set_session_stats(client_id() | {client_id(), pid()},
|
||||
emqx_stats:stats()) -> ok).
|
||||
set_session_stats(ClientId, Stats) when is_binary(ClientId) ->
|
||||
set_session_stats({ClientId, self()}, Stats);
|
||||
|
||||
set_session_stats(Session = {ClientId, SessionPid}, Stats)
|
||||
when is_binary(ClientId), is_pid(SessionPid) ->
|
||||
ets:insert(?SESSION_STATS, {Session, Stats}).
|
||||
|
||||
%% @doc Lookup a session from registry
|
||||
-spec(lookup_session(client_id()) -> list({client_id(), pid()})).
|
||||
lookup_session(ClientId) ->
|
||||
emqx_sm_registry:lookup_session(ClientId).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Dispatch by client Id
|
||||
%%--------------------------------------------------------------------
|
||||
case emqx_sm_registry:is_enabled() of
|
||||
true -> emqx_sm_registry:lookup_session(ClientId);
|
||||
false -> ets:lookup(?SESSION, ClientId)
|
||||
end.
|
||||
|
||||
%% @doc Dispatch a message to the session.
|
||||
-spec(dispatch(client_id(), topic(), message()) -> any()).
|
||||
dispatch(ClientId, Topic, Msg) ->
|
||||
case lookup_session_pid(ClientId) of
|
||||
Pid when is_pid(Pid) ->
|
||||
|
@ -149,60 +186,82 @@ dispatch(ClientId, Topic, Msg) ->
|
|||
emqx_hooks:run('message.dropped', [ClientId, Msg])
|
||||
end.
|
||||
|
||||
%% @doc Lookup session pid.
|
||||
-spec(lookup_session_pid(client_id()) -> pid() | undefined).
|
||||
lookup_session_pid(ClientId) ->
|
||||
try ets:lookup_element(session, ClientId, #session.pid)
|
||||
catch error:badarg ->
|
||||
undefined
|
||||
safe_lookup_element(?SESSION, ClientId, undefined).
|
||||
|
||||
safe_lookup_element(Tab, Key, Default) ->
|
||||
try ets:lookup_element(Tab, Key, 2)
|
||||
catch
|
||||
error:badarg -> Default
|
||||
end.
|
||||
|
||||
notify(Event) -> gen_server:cast(?SM, {notify, Event}).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% gen_server callbacks
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
init([]) ->
|
||||
_ = emqx_tables:create(session, [public, set, {keypos, 2},
|
||||
{read_concurrency, true},
|
||||
{write_concurrency, true}]),
|
||||
_ = emqx_tables:create(session_attrs, [public, set,
|
||||
{write_concurrency, true}]),
|
||||
{ok, #state{pmon = emqx_pmon:new()}}.
|
||||
TabOpts = [public, set, {write_concurrency, true}],
|
||||
_ = emqx_tables:new(?SESSION, [{read_concurrency, true} | TabOpts]),
|
||||
_ = emqx_tables:new(?SESSION_P, TabOpts),
|
||||
_ = emqx_tables:new(?SESSION_ATTRS, TabOpts),
|
||||
_ = emqx_tables:new(?SESSION_STATS, TabOpts),
|
||||
emqx_stats:update_interval(sm_stats, stats_fun()),
|
||||
{ok, #state{session_pmon = emqx_pmon:new()}}.
|
||||
|
||||
handle_call(Req, _From, State) ->
|
||||
emqx_log:error("[SM] Unexpected request: ~p", [Req]),
|
||||
emqx_logger:error("[SM] Unexpected request: ~p", [Req]),
|
||||
{reply, ignore, State}.
|
||||
|
||||
handle_cast({registered, #session{sid = ClientId, pid = SessionPid}},
|
||||
State = #state{pmon = PMon}) ->
|
||||
{noreply, State#state{pmon = PMon:monitor(SessionPid, ClientId)}};
|
||||
handle_cast({notify, {registered, ClientId, SessionPid}},
|
||||
State = #state{session_pmon = PMon}) ->
|
||||
{noreply, State#state{session_pmon = PMon:monitor(SessionPid, ClientId)}};
|
||||
|
||||
handle_cast({unregistered, #session{sid = _ClientId, pid = SessionPid}},
|
||||
State = #state{pmon = PMon}) ->
|
||||
{noreply, State#state{pmon = PMon:erase(SessionPid)}};
|
||||
handle_cast({notify, {unregistered, _ClientId, SessionPid}},
|
||||
State = #state{session_pmon = PMon}) ->
|
||||
{noreply, State#state{session_pmon = PMon:demonitor(SessionPid)}};
|
||||
|
||||
handle_cast(Msg, State) ->
|
||||
emqx_log:error("[SM] Unexpected msg: ~p", [Msg]),
|
||||
emqx_logger:error("[SM] Unexpected msg: ~p", [Msg]),
|
||||
{noreply, State}.
|
||||
|
||||
handle_info({'DOWN', _MRef, process, DownPid, _Reason},
|
||||
State = #state{pmon = PMon}) ->
|
||||
State = #state{session_pmon = PMon}) ->
|
||||
case PMon:find(DownPid) of
|
||||
{ok, ClientId} ->
|
||||
case ets:lookup(session, ClientId) of
|
||||
[] -> ok;
|
||||
_ -> unregister_session(#session{sid = ClientId, pid = DownPid})
|
||||
end,
|
||||
{noreply, State};
|
||||
undefined ->
|
||||
{noreply, State}
|
||||
{noreply, State};
|
||||
ClientId ->
|
||||
unregister_session({ClientId, DownPid}),
|
||||
{noreply, State#state{session_pmon = PMon:erase(DownPid)}}
|
||||
end;
|
||||
|
||||
handle_info(Info, State) ->
|
||||
emqx_log:error("[SM] Unexpected info: ~p", [Info]),
|
||||
emqx_logger:error("[SM] Unexpected info: ~p", [Info]),
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, _State) ->
|
||||
ok.
|
||||
emqx_stats:cancel_update(cm_stats).
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
stats_fun() ->
|
||||
fun () ->
|
||||
safe_update_stats(?SESSION, 'sessions/count', 'sessions/max'),
|
||||
safe_update_stats(?SESSION_P, 'sessions/persistent/count',
|
||||
'sessions/persistent/max')
|
||||
end.
|
||||
|
||||
safe_update_stats(Tab, Stat, MaxStat) ->
|
||||
case ets:info(Tab, size) of
|
||||
undefined -> ok;
|
||||
Size -> emqx_stats:setstat(Stat, MaxStat, Size)
|
||||
end.
|
||||
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_sm_locker).
|
||||
|
||||
|
@ -32,8 +32,11 @@ start_link() ->
|
|||
trans(ClientId, Fun) ->
|
||||
trans(ClientId, Fun, undefined).
|
||||
|
||||
-spec(trans(client_id(), fun(([node()]) -> any()),
|
||||
-spec(trans(client_id() | undefined,
|
||||
fun(([node()]) -> any()),
|
||||
ekka_locker:piggyback()) -> any()).
|
||||
trans(undefined, Fun, _Piggyback) ->
|
||||
Fun([]);
|
||||
trans(ClientId, Fun, Piggyback) ->
|
||||
case lock(ClientId, Piggyback) of
|
||||
{true, Nodes} ->
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_sm_registry).
|
||||
|
||||
|
@ -23,58 +23,73 @@
|
|||
%% API
|
||||
-export([start_link/0]).
|
||||
|
||||
-export([is_enabled/0]).
|
||||
|
||||
-export([register_session/1, lookup_session/1, unregister_session/1]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-define(SERVER, ?MODULE).
|
||||
-define(REGISTRY, ?MODULE).
|
||||
|
||||
-define(TAB, session_registry).
|
||||
-define(TAB, emqx_session_registry).
|
||||
|
||||
-define(LOCK, {?MODULE, cleanup_sessions}).
|
||||
|
||||
-record(global_session, {sid, pid}).
|
||||
|
||||
-record(state, {}).
|
||||
|
||||
-type(session_pid() :: pid()).
|
||||
|
||||
%% @doc Start the session manager.
|
||||
-spec(start_link() -> {ok, pid()} | ignore | {error, term()}).
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?REGISTRY}, ?MODULE, [], []).
|
||||
|
||||
-spec(is_enabled() -> boolean()).
|
||||
is_enabled() ->
|
||||
ets:info(?TAB, name) =/= undefined.
|
||||
|
||||
-spec(lookup_session(client_id()) -> list({client_id(), session_pid()})).
|
||||
lookup_session(ClientId) ->
|
||||
[{ClientId, SessionPid} || #global_session{pid = SessionPid}
|
||||
<- mnesia:dirty_read(?TAB, ClientId)].
|
||||
|
||||
-spec(register_session({client_id(), session_pid()}) -> ok).
|
||||
register_session({ClientId, SessionPid}) when is_binary(ClientId),
|
||||
is_pid(SessionPid) ->
|
||||
mnesia:dirty_write(?TAB, record(ClientId, SessionPid)).
|
||||
|
||||
-spec(unregister_session({client_id(), session_pid()}) -> ok).
|
||||
unregister_session({ClientId, SessionPid}) when is_binary(ClientId),
|
||||
is_pid(SessionPid) ->
|
||||
mnesia:dirty_delete_object(?TAB, record(ClientId, SessionPid)).
|
||||
|
||||
record(ClientId, SessionPid) ->
|
||||
#global_session{sid = ClientId, pid = SessionPid}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% API
|
||||
%% gen_server callbacks
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
start_link() ->
|
||||
init([]) ->
|
||||
ok = ekka_mnesia:create_table(?TAB, [
|
||||
{type, bag},
|
||||
{ram_copies, [node()]},
|
||||
{record_name, session},
|
||||
{attributes, record_info(fields, session)}]),
|
||||
{record_name, global_session},
|
||||
{attributes, record_info(fields, global_session)}]),
|
||||
ok = ekka_mnesia:copy_table(?TAB),
|
||||
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
|
||||
|
||||
-spec(lookup_session(client_id()) -> list(session())).
|
||||
lookup_session(ClientId) ->
|
||||
mnesia:dirty_read(?TAB, ClientId).
|
||||
|
||||
-spec(register_session(session()) -> ok).
|
||||
register_session(Session) when is_record(Session, session) ->
|
||||
mnesia:dirty_write(?TAB, Session).
|
||||
|
||||
-spec(unregister_session(session()) -> ok).
|
||||
unregister_session(Session) when is_record(Session, session) ->
|
||||
mnesia:dirty_delete_object(?TAB, Session).
|
||||
|
||||
%%%===================================================================
|
||||
%%% gen_server callbacks
|
||||
%%%===================================================================
|
||||
|
||||
init([]) ->
|
||||
ekka:monitor(membership),
|
||||
{ok, #state{}}.
|
||||
|
||||
handle_call(_Request, _From, State) ->
|
||||
Reply = ok,
|
||||
{reply, Reply, State}.
|
||||
handle_call(Req, _From, State) ->
|
||||
emqx_logger:error("[Registry] Unexpected request: ~p", [Req]),
|
||||
{reply, ignore, State}.
|
||||
|
||||
handle_cast(_Msg, State) ->
|
||||
handle_cast(Msg, State) ->
|
||||
emqx_logger:error("[Registry] Unexpected msg: ~p", [Msg]),
|
||||
{noreply, State}.
|
||||
|
||||
handle_info({membership, {mnesia, down, Node}}, State) ->
|
||||
|
@ -87,7 +102,8 @@ handle_info({membership, {mnesia, down, Node}}, State) ->
|
|||
handle_info({membership, _Event}, State) ->
|
||||
{noreply, State};
|
||||
|
||||
handle_info(_Info, State) ->
|
||||
handle_info(Info, State) ->
|
||||
emqx_logger:error("[Registry] Unexpected info: ~p", [Info]),
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, _State) ->
|
||||
|
@ -101,7 +117,7 @@ code_change(_OldVsn, State, _Extra) ->
|
|||
%%--------------------------------------------------------------------
|
||||
|
||||
cleanup_sessions(Node) ->
|
||||
Pat = [{#session{pid = '$1', _ = '_'},
|
||||
Pat = [{#global_session{pid = '$1', _ = '_'},
|
||||
[{'==', {node, '$1'}, Node}], ['$_']}],
|
||||
lists:foreach(fun(Session) ->
|
||||
mnesia:delete_object(?TAB, Session)
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-module(emqx_sm_stats).
|
||||
|
||||
-behaviour(gen_statem).
|
||||
|
||||
-include("emqx.hrl").
|
||||
|
||||
%% API
|
||||
-export([start_link/0]).
|
||||
|
||||
-export([set_session_stats/2, get_session_stats/1, del_session_stats/1]).
|
||||
|
||||
%% gen_statem callbacks
|
||||
-export([init/1, callback_mode/0, handle_event/4, terminate/3, code_change/4]).
|
||||
|
||||
-define(TAB, session_stats).
|
||||
|
||||
-record(state, {statsfun}).
|
||||
|
||||
start_link() ->
|
||||
gen_statem:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||
|
||||
-spec(set_session_stats(session(), emqx_stats:stats()) -> true).
|
||||
set_session_stats(Session, Stats) when is_record(Session, session) ->
|
||||
ets:insert(?TAB, {Session, [{'$ts', emqx_time:now_secs()}|Stats]}).
|
||||
|
||||
-spec(get_session_stats(session()) -> emqx_stats:stats()).
|
||||
get_session_stats(Session) ->
|
||||
case ets:lookup(?TAB, Session) of
|
||||
[{_, Stats}] -> Stats;
|
||||
[] -> []
|
||||
end.
|
||||
|
||||
-spec(del_session_stats(session()) -> true).
|
||||
del_session_stats(Session) ->
|
||||
ets:delete(?TAB, Session).
|
||||
|
||||
init([]) ->
|
||||
_ = emqx_tables:create(?TAB, [public, {write_concurrency, true}]),
|
||||
StatsFun = emqx_stats:statsfun('sessions/count', 'sessions/max'),
|
||||
{ok, idle, #state{statsfun = StatsFun}, timer:seconds(1)}.
|
||||
|
||||
callback_mode() -> handle_event_function.
|
||||
|
||||
handle_event(timeout, _Timeout, idle, State = #state{statsfun = StatsFun}) ->
|
||||
case ets:info(session, size) of
|
||||
undefined -> ok;
|
||||
Size -> StatsFun(Size)
|
||||
end,
|
||||
{next_state, idle, State, timer:seconds(1)}.
|
||||
|
||||
terminate(_Reason, _StateName, _State) ->
|
||||
ok.
|
||||
|
||||
code_change(_OldVsn, StateName, State, _Extra) ->
|
||||
{ok, StateName, State}.
|
||||
|
|
@ -1,18 +1,18 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_sm_sup).
|
||||
|
||||
|
@ -26,12 +26,17 @@ start_link() ->
|
|||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||
|
||||
init([]) ->
|
||||
Childs = [child(M) || M <- [emqx_sm_locker,
|
||||
emqx_sm_registry,
|
||||
emqx_sm_stats,
|
||||
emqx_sm]],
|
||||
{ok, {{one_for_all, 10, 3600}, Childs}}.
|
||||
%% Session Locker
|
||||
Locker = {locker, {emqx_sm_locker, start_link, []},
|
||||
permanent, 5000, worker, [emqx_sm_locker]},
|
||||
|
||||
child(M) ->
|
||||
{M, {M, start_link, []}, permanent, 5000, worker, [M]}.
|
||||
%% Session Registry
|
||||
Registry = {registry, {emqx_sm_registry, start_link, []},
|
||||
permanent, 5000, worker, [emqx_sm_registry]},
|
||||
|
||||
%% Session Manager
|
||||
Manager = {manager, {emqx_sm, start_link, []},
|
||||
permanent, 5000, worker, [emqx_sm]},
|
||||
|
||||
{ok, {{one_for_rest, 10, 3600}, [Locker, Registry, Manager]}}.
|
||||
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All Rights Reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_stats).
|
||||
|
||||
|
@ -20,69 +20,78 @@
|
|||
|
||||
-include("emqx.hrl").
|
||||
|
||||
-export([start_link/0, stop/0]).
|
||||
-export([start_link/0]).
|
||||
|
||||
%% Get all Stats
|
||||
%% Get all stats
|
||||
-export([all/0]).
|
||||
|
||||
%% Statistics API.
|
||||
%% Stats API.
|
||||
-export([statsfun/1, statsfun/2, getstats/0, getstat/1, setstat/2, setstat/3]).
|
||||
|
||||
-export([update_interval/2, update_interval/3, cancel_update/1]).
|
||||
|
||||
%% gen_server Function Exports
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-record(state, {tick}).
|
||||
-record(update, {name, countdown, interval, func}).
|
||||
|
||||
-record(state, {timer, updates :: #update{}}).
|
||||
|
||||
-type(stats() :: list({atom(), non_neg_integer()})).
|
||||
|
||||
-export_type([stats/0]).
|
||||
|
||||
-define(STATS_TAB, stats).
|
||||
|
||||
%% $SYS Topics for Clients
|
||||
-define(SYSTOP_CLIENTS, [
|
||||
%% Client stats
|
||||
-define(CLIENT_STATS, [
|
||||
'clients/count', % clients connected current
|
||||
'clients/max' % max clients connected
|
||||
'clients/max' % maximum clients connected
|
||||
]).
|
||||
|
||||
%% $SYS Topics for Sessions
|
||||
-define(SYSTOP_SESSIONS, [
|
||||
%% Session stats
|
||||
-define(SESSION_STATS, [
|
||||
'sessions/count',
|
||||
'sessions/max'
|
||||
'sessions/max',
|
||||
'sessions/persistent/count',
|
||||
'sessions/persistent/max'
|
||||
]).
|
||||
|
||||
%% $SYS Topics for Subscribers
|
||||
-define(SYSTOP_PUBSUB, [
|
||||
'topics/count', % ...
|
||||
'topics/max', % ...
|
||||
'subscribers/count', % ...
|
||||
'subscribers/max', % ...
|
||||
'subscriptions/count', % ...
|
||||
'subscriptions/max', % ...
|
||||
'routes/count', % ...
|
||||
'routes/max' % ...
|
||||
%% Subscribers, Subscriptions stats
|
||||
-define(PUBSUB_STATS, [
|
||||
'topics/count',
|
||||
'topics/max',
|
||||
'subscribers/count',
|
||||
'subscribers/max',
|
||||
'subscriptions/count',
|
||||
'subscriptions/max'
|
||||
]).
|
||||
|
||||
%% $SYS Topic for retained
|
||||
-define(SYSTOP_RETAINED, [
|
||||
-define(ROUTE_STATS, [
|
||||
'routes/count',
|
||||
'routes/max'
|
||||
]).
|
||||
|
||||
%% Retained stats
|
||||
-define(RETAINED_STATS, [
|
||||
'retained/count',
|
||||
'retained/max'
|
||||
]).
|
||||
|
||||
-define(TAB, ?MODULE).
|
||||
-define(SERVER, ?MODULE).
|
||||
|
||||
%% @doc Start stats server
|
||||
-spec(start_link() -> {ok, pid()} | ignore | {error, term()}).
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% API
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
%% @doc Start stats server
|
||||
-spec(start_link() -> {ok, pid()} | ignore | {error, term()}).
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||
|
||||
stop() ->
|
||||
gen_server:call(?MODULE, stop).
|
||||
|
||||
all() -> ets:tab2list(?STATS_TAB).
|
||||
%% Get all stats.
|
||||
-spec(all() -> stats()).
|
||||
all() -> getstats().
|
||||
|
||||
%% @doc Generate stats fun
|
||||
-spec(statsfun(Stat :: atom()) -> fun()).
|
||||
|
@ -94,71 +103,117 @@ statsfun(Stat, MaxStat) ->
|
|||
fun(Val) -> setstat(Stat, MaxStat, Val) end.
|
||||
|
||||
%% @doc Get all statistics
|
||||
-spec(getstats() -> [{atom(), non_neg_integer()}]).
|
||||
-spec(getstats() -> stats()).
|
||||
getstats() ->
|
||||
lists:sort(ets:tab2list(?STATS_TAB)).
|
||||
case ets:info(?TAB, name) of
|
||||
undefined -> [];
|
||||
_ -> ets:tab2list(?TAB)
|
||||
end.
|
||||
|
||||
%% @doc Get stats by name
|
||||
-spec(getstat(atom()) -> non_neg_integer() | undefined).
|
||||
getstat(Name) ->
|
||||
case ets:lookup(?STATS_TAB, Name) of
|
||||
case ets:lookup(?TAB, Name) of
|
||||
[{Name, Val}] -> Val;
|
||||
[] -> undefined
|
||||
end.
|
||||
|
||||
%% @doc Set broker stats
|
||||
%% @doc Set stats
|
||||
-spec(setstat(Stat :: atom(), Val :: pos_integer()) -> boolean()).
|
||||
setstat(Stat, Val) ->
|
||||
ets:update_element(?STATS_TAB, Stat, {2, Val}).
|
||||
setstat(Stat, Val) when is_integer(Val) ->
|
||||
safe_update_element(Stat, Val).
|
||||
|
||||
%% @doc Set stats with max
|
||||
-spec(setstat(Stat :: atom(), MaxStat :: atom(), Val :: pos_integer()) -> boolean()).
|
||||
setstat(Stat, MaxStat, Val) ->
|
||||
gen_server:cast(?MODULE, {setstat, Stat, MaxStat, Val}).
|
||||
%% @doc Set stats with max value.
|
||||
-spec(setstat(Stat :: atom(), MaxStat :: atom(),
|
||||
Val :: pos_integer()) -> boolean()).
|
||||
setstat(Stat, MaxStat, Val) when is_integer(Val) ->
|
||||
cast({setstat, Stat, MaxStat, Val}).
|
||||
|
||||
-spec(update_interval(atom(), fun()) -> ok).
|
||||
update_interval(Name, UpFun) ->
|
||||
update_interval(Name, 1, UpFun).
|
||||
|
||||
-spec(update_interval(atom(), pos_integer(), fun()) -> ok).
|
||||
update_interval(Name, Secs, UpFun) when is_integer(Secs), Secs >= 1 ->
|
||||
cast({update_interval, rec(Name, Secs, UpFun)}).
|
||||
|
||||
-spec(cancel_update(atom()) -> ok).
|
||||
cancel_update(Name) ->
|
||||
cast({cancel_update, Name}).
|
||||
|
||||
rec(Name, Secs, UpFun) ->
|
||||
#update{name = Name, countdown = Secs, interval = Secs, func = UpFun}.
|
||||
|
||||
cast(Msg) ->
|
||||
gen_server:cast(?SERVER, Msg).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% gen_server callbacks
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
init([]) ->
|
||||
emqx_time:seed(),
|
||||
_ = emqx_tables:create(?STATS_TAB, [set, public, named_table,
|
||||
{write_concurrency, true}]),
|
||||
Topics = ?SYSTOP_CLIENTS ++ ?SYSTOP_SESSIONS ++ ?SYSTOP_PUBSUB ++ ?SYSTOP_RETAINED,
|
||||
ets:insert(?STATS_TAB, [{Topic, 0} || Topic <- Topics]),
|
||||
% Tick to publish stats
|
||||
{ok, TRef} = timer:send_after(emqx_sys:sys_interval(), tick),
|
||||
{ok, #state{tick = TRef}, hibernate}.
|
||||
_ = emqx_tables:new(?TAB, [set, public, {write_concurrency, true}]),
|
||||
Stats = lists:append([?CLIENT_STATS, ?SESSION_STATS, ?PUBSUB_STATS,
|
||||
?ROUTE_STATS, ?RETAINED_STATS]),
|
||||
ets:insert(?TAB, [{Name, 0} || Name <- Stats]),
|
||||
{ok, start_timer(#state{updates = []}), hibernate}.
|
||||
|
||||
handle_call(stop, _From, State) ->
|
||||
{stop, normal, ok, State};
|
||||
start_timer(State) ->
|
||||
State#state{timer = emqx_misc:start_timer(timer:seconds(1), tick)}.
|
||||
|
||||
handle_call(_Request, _From, State) ->
|
||||
{reply, error, State}.
|
||||
handle_call(Req, _From, State) ->
|
||||
emqx_logger:error("[STATS] Unexpected request: ~p", [Req]),
|
||||
{reply, ignore, State}.
|
||||
|
||||
%% atomic
|
||||
handle_cast({setstat, Stat, MaxStat, Val}, State) ->
|
||||
MaxVal = ets:lookup_element(?STATS_TAB, MaxStat, 2),
|
||||
if
|
||||
Val > MaxVal ->
|
||||
ets:update_element(?STATS_TAB, MaxStat, {2, Val});
|
||||
true -> ok
|
||||
try ets:lookup_element(?TAB, MaxStat, 2) of
|
||||
MaxVal when Val > MaxVal ->
|
||||
ets:update_element(?TAB, MaxStat, {2, Val});
|
||||
_ -> ok
|
||||
catch
|
||||
error:badarg ->
|
||||
ets:insert(?TAB, {MaxStat, Val})
|
||||
end,
|
||||
ets:update_element(?STATS_TAB, Stat, {2, Val}),
|
||||
safe_update_element(Stat, Val),
|
||||
{noreply, State};
|
||||
|
||||
handle_cast(_Msg, State) ->
|
||||
handle_cast({update_interval, Update = #update{name = Name}},
|
||||
State = #state{updates = Updates}) ->
|
||||
case lists:keyfind(Name, #update.name, Updates) of
|
||||
#update{} ->
|
||||
emqx_logger:error("[STATS]: Duplicated update: ~s", [Name]),
|
||||
{noreply, State};
|
||||
false ->
|
||||
{noreply, State#state{updates = [Update | Updates]}}
|
||||
end;
|
||||
|
||||
handle_cast({cancel_update, Name}, State = #state{updates = Updates}) ->
|
||||
{noreply, State#state{updates = lists:keydelete(Name, #update.name, Updates)}};
|
||||
|
||||
handle_cast(Msg, State) ->
|
||||
emqx_logger:error("[STATS] Unexpected msg: ~p", [Msg]),
|
||||
{noreply, State}.
|
||||
|
||||
%% Interval Tick.
|
||||
handle_info(tick, State) ->
|
||||
[publish(Stat, Val) || {Stat, Val} <- ets:tab2list(?STATS_TAB)],
|
||||
{noreply, State, hibernate};
|
||||
handle_info({timeout, TRef, tick}, State = #state{timer = TRef,
|
||||
updates = Updates}) ->
|
||||
lists:foldl(
|
||||
fun(Update = #update{name = Name, countdown = C, interval = I,
|
||||
func = UpFun}, Acc) when C =< 0 ->
|
||||
try UpFun()
|
||||
catch _:Error ->
|
||||
emqx_logger:error("[STATS] Update ~s error: ~p", [Name, Error])
|
||||
end,
|
||||
[Update#update{countdown = I} | Acc];
|
||||
(Update = #update{countdown = C}, Acc) ->
|
||||
[Update#update{countdown = C - 1} | Acc]
|
||||
end, [], Updates),
|
||||
{noreply, start_timer(State), hibernate};
|
||||
|
||||
handle_info(_Info, State) ->
|
||||
handle_info(Info, State) ->
|
||||
emqx_logger:error("[STATS] Unexpected info: ~p", [Info]),
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, #state{tick = TRef}) ->
|
||||
terminate(_Reason, #state{timer = TRef}) ->
|
||||
timer:cancel(TRef).
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
|
@ -168,12 +223,10 @@ code_change(_OldVsn, State, _Extra) ->
|
|||
%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
publish(Stat, Val) ->
|
||||
Msg = emqx_message:make(stats, stats_topic(Stat), bin(Val)),
|
||||
emqx:publish(emqx_message:set_flag(sys, Msg)).
|
||||
|
||||
stats_topic(Stat) ->
|
||||
emqx_topic:systop(list_to_binary(lists:concat(['stats/', Stat]))).
|
||||
|
||||
bin(I) when is_integer(I) -> list_to_binary(integer_to_list(I)).
|
||||
safe_update_element(Key, Val) ->
|
||||
try ets:update_element(?TAB, Key, {2, Val})
|
||||
catch
|
||||
error:badarg ->
|
||||
ets:insert_new(?TAB, {Key, Val})
|
||||
end.
|
||||
|
||||
|
|
107
src/emqx_sup.erl
107
src/emqx_sup.erl
|
@ -1,18 +1,18 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All Rights Reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_sup).
|
||||
|
||||
|
@ -20,7 +20,6 @@
|
|||
|
||||
-export([start_link/0, start_child/1, start_child/2, stop_child/1]).
|
||||
|
||||
%% Supervisor callbacks
|
||||
-export([init/1]).
|
||||
|
||||
-type(startchild_ret() :: {ok, supervisor:child()}
|
||||
|
@ -29,20 +28,24 @@
|
|||
|
||||
-define(SUPERVISOR, ?MODULE).
|
||||
|
||||
-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).
|
||||
|
||||
start_link() ->
|
||||
supervisor:start_link({local, ?SUPERVISOR}, ?MODULE, []).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% API
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-spec(start_child(supervisor:child_spec()) -> startchild_ret()).
|
||||
start_child(ChildSpec) when is_tuple(ChildSpec) ->
|
||||
supervisor:start_child(?SUPERVISOR, ChildSpec).
|
||||
|
||||
-spec(start_child(atom(), worker | supervisor) -> startchild_ret()).
|
||||
start_child(Mod, Type) when Type == worker orelse Type == supervisor ->
|
||||
start_child(?CHILD(Mod, Type)).
|
||||
-spec(start_child(module(), worker | supervisor) -> startchild_ret()).
|
||||
start_child(Mod, worker) ->
|
||||
start_child(worker_spec(Mod));
|
||||
start_child(Mod, supervisor) ->
|
||||
start_child(supervisor_spec(Mod)).
|
||||
|
||||
-spec(stop_child(supervisor:child_id()) -> ok | {error, any()}).
|
||||
-spec(stop_child(supervisor:child_id()) -> ok | {error, term()}).
|
||||
stop_child(ChildId) ->
|
||||
case supervisor:terminate_child(?SUPERVISOR, ChildId) of
|
||||
ok -> supervisor:delete_child(?SUPERVISOR, ChildId);
|
||||
|
@ -54,24 +57,44 @@ stop_child(ChildId) ->
|
|||
%%--------------------------------------------------------------------
|
||||
|
||||
init([]) ->
|
||||
{ok, {{one_for_all, 10, 3600},
|
||||
[?CHILD(emqx_ctl, worker),
|
||||
?CHILD(emqx_hooks, worker),
|
||||
?CHILD(emqx_stats, worker),
|
||||
?CHILD(emqx_metrics, worker),
|
||||
?CHILD(emqx_sys, worker),
|
||||
?CHILD(emqx_router_sup, supervisor),
|
||||
?CHILD(emqx_broker_sup, supervisor),
|
||||
?CHILD(emqx_pooler, supervisor),
|
||||
?CHILD(emqx_tracer_sup, supervisor),
|
||||
?CHILD(emqx_cm_sup, supervisor),
|
||||
?CHILD(emqx_sm_sup, supervisor),
|
||||
?CHILD(emqx_session_sup, supervisor),
|
||||
?CHILD(emqx_ws_connection_sup, supervisor),
|
||||
?CHILD(emqx_alarm, worker),
|
||||
?CHILD(emqx_mod_sup, supervisor),
|
||||
?CHILD(emqx_bridge_sup_sup, supervisor),
|
||||
?CHILD(emqx_access_control, worker),
|
||||
?CHILD(emqx_sysmon_sup, supervisor)]
|
||||
}}.
|
||||
%% Kernel Sup
|
||||
KernelSup = supervisor_spec(emqx_kernel_sup),
|
||||
%% Router Sup
|
||||
RouterSup = supervisor_spec(emqx_router_sup),
|
||||
%% Broker Sup
|
||||
BrokerSup = supervisor_spec(emqx_broker_sup),
|
||||
%% BridgeSup
|
||||
BridgeSup = supervisor_spec(emqx_bridge_sup_sup),
|
||||
%% AccessControl
|
||||
AccessControl = worker_spec(emqx_access_control),
|
||||
%% Session Manager
|
||||
SMSup = supervisor_spec(emqx_sm_sup),
|
||||
%% Session Sup
|
||||
SessionSup = supervisor_spec(emqx_session_sup),
|
||||
%% Connection Manager
|
||||
CMSup = supervisor_spec(emqx_cm_sup),
|
||||
%% WebSocket Connection Sup
|
||||
WSConnSup = supervisor_spec(emqx_ws_connection_sup),
|
||||
%% Sys Sup
|
||||
SysSup = supervisor_spec(emqx_sys_sup),
|
||||
{ok, {{one_for_all, 0, 1},
|
||||
[KernelSup,
|
||||
RouterSup,
|
||||
BrokerSup,
|
||||
BridgeSup,
|
||||
AccessControl,
|
||||
SMSup,
|
||||
SessionSup,
|
||||
CMSup,
|
||||
WSConnSup,
|
||||
SysSup]}}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
worker_spec(M) ->
|
||||
{M, {M, start_link, []}, permanent, 30000, worker, [M]}.
|
||||
supervisor_spec(M) ->
|
||||
{M, {M, start_link, []}, permanent, infinity, supervisor, [M]}.
|
||||
|
||||
|
|
159
src/emqx_sys.erl
159
src/emqx_sys.erl
|
@ -1,18 +1,18 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All Rights Reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_sys).
|
||||
|
||||
|
@ -22,8 +22,6 @@
|
|||
|
||||
-export([start_link/0]).
|
||||
|
||||
-export([schedulers/0]).
|
||||
|
||||
-export([version/0, uptime/0, datetime/0, sysdescr/0, sys_interval/0]).
|
||||
|
||||
-export([info/0]).
|
||||
|
@ -31,33 +29,29 @@
|
|||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-record(state, {started_at, heartbeat, sys_ticker, version, sysdescr}).
|
||||
-import(emqx_topic, [systop/1]).
|
||||
-import(emqx_misc, [start_timer/2]).
|
||||
|
||||
-record(state, {start_time, heartbeat, ticker, version, sysdescr}).
|
||||
|
||||
-define(APP, emqx).
|
||||
-define(SYS, ?MODULE).
|
||||
|
||||
-define(SERVER, ?MODULE).
|
||||
|
||||
%% $SYS Topics of Broker
|
||||
-define(SYSTOP_BROKERS, [
|
||||
-define(INFO_KEYS, [
|
||||
version, % Broker version
|
||||
uptime, % Broker uptime
|
||||
datetime, % Broker local datetime
|
||||
sysdescr % Broker description
|
||||
]).
|
||||
|
||||
-spec(start_link() -> {ok, pid()} | ignore | {error, any()}).
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?SYS}, ?MODULE, [], []).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% API
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-spec(start_link() -> {ok, pid()} | ignore | {error, any()}).
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
|
||||
|
||||
%% @doc Get schedulers
|
||||
-spec(schedulers() -> pos_integer()).
|
||||
schedulers() ->
|
||||
erlang:system_info(schedulers).
|
||||
|
||||
%% @doc Get sys version
|
||||
-spec(version() -> string()).
|
||||
version() ->
|
||||
|
@ -70,7 +64,7 @@ sysdescr() ->
|
|||
|
||||
%% @doc Get sys uptime
|
||||
-spec(uptime() -> string()).
|
||||
uptime() -> gen_server:call(?SERVER, uptime).
|
||||
uptime() -> gen_server:call(?SYS, uptime).
|
||||
|
||||
%% @doc Get sys datetime
|
||||
-spec(datetime() -> string()).
|
||||
|
@ -80,6 +74,8 @@ datetime() ->
|
|||
io_lib:format(
|
||||
"~4..0w-~2..0w-~2..0w ~2..0w:~2..0w:~2..0w", [Y, M, D, H, MM, S])).
|
||||
|
||||
%% @doc Get sys interval
|
||||
-spec(sys_interval() -> pos_integer()).
|
||||
sys_interval() ->
|
||||
application:get_env(?APP, sys_interval, 60000).
|
||||
|
||||
|
@ -96,44 +92,49 @@ info() ->
|
|||
%%--------------------------------------------------------------------
|
||||
|
||||
init([]) ->
|
||||
Tick = fun(I, M) ->
|
||||
{ok, TRef} = timer:send_interval(I, M), TRef
|
||||
end,
|
||||
{ok, #state{started_at = os:timestamp(),
|
||||
heartbeat = Tick(1000, heartbeat),
|
||||
sys_ticker = Tick(sys_interval(), tick),
|
||||
State = #state{start_time = erlang:timestamp(),
|
||||
version = iolist_to_binary(version()),
|
||||
sysdescr = iolist_to_binary(sysdescr())}, hibernate}.
|
||||
sysdescr = iolist_to_binary(sysdescr())},
|
||||
{ok, heartbeat(tick(State))}.
|
||||
|
||||
heartbeat(State) ->
|
||||
State#state{heartbeat = start_timer(timer:seconds(1), heartbeat)}.
|
||||
tick(State) ->
|
||||
State#state{ticker = start_timer(sys_interval(), tick)}.
|
||||
|
||||
handle_call(uptime, _From, State) ->
|
||||
{reply, uptime(State), State};
|
||||
|
||||
handle_call(Req, _From, State) ->
|
||||
emqx_log:error("[SYS] Unexpected request: ~p", [Req]),
|
||||
emqx_logger:error("[SYS] Unexpected request: ~p", [Req]),
|
||||
{reply, ignore, State}.
|
||||
|
||||
handle_cast(Msg, State) ->
|
||||
emqx_log:error("[SYS] Unexpected msg: ~p", [Msg]),
|
||||
emqx_logger:error("[SYS] Unexpected msg: ~p", [Msg]),
|
||||
{noreply, State}.
|
||||
|
||||
handle_info(heartbeat, State) ->
|
||||
handle_info({timeout, TRef, heartbeat}, State = #state{heartbeat = TRef}) ->
|
||||
publish(uptime, iolist_to_binary(uptime(State))),
|
||||
publish(datetime, iolist_to_binary(datetime())),
|
||||
{noreply, State, hibernate};
|
||||
{noreply, heartbeat(State)};
|
||||
|
||||
handle_info(tick, State = #state{version = Version, sysdescr = Descr}) ->
|
||||
retain(brokers),
|
||||
retain(version, Version),
|
||||
retain(sysdescr, Descr),
|
||||
{noreply, State, hibernate};
|
||||
handle_info({timeout, TRef, tick}, State = #state{ticker = TRef,
|
||||
version = Version,
|
||||
sysdescr = Descr}) ->
|
||||
publish(version, Version),
|
||||
publish(sysdescr, Descr),
|
||||
publish(brokers, ekka_mnesia:running_nodes()),
|
||||
publish(stats, emqx_stats:all()),
|
||||
publish(metrics, emqx_metrics:all()),
|
||||
{noreply, tick(State), hibernate};
|
||||
|
||||
handle_info(Info, State) ->
|
||||
emqx_log:error("[SYS] Unexpected info: ~p", [Info]),
|
||||
emqx_logger:error("[SYS] Unexpected info: ~p", [Info]),
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, #state{heartbeat = Hb, sys_ticker = TRef}) ->
|
||||
timer:cancel(Hb),
|
||||
timer:cancel(TRef).
|
||||
terminate(_Reason, #state{heartbeat = HBRef, ticker = TRef}) ->
|
||||
emqx_misc:cancel_timer(HBRef),
|
||||
emqx_misc:cancel_timer(TRef).
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
@ -142,24 +143,9 @@ code_change(_OldVsn, State, _Extra) ->
|
|||
%% Internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
retain(brokers) ->
|
||||
Payload = list_to_binary(string:join([atom_to_list(N) ||
|
||||
N <- ekka_mnesia:running_nodes()], ",")),
|
||||
Msg = emqx_message:make(broker, <<"$SYS/brokers">>, Payload),
|
||||
emqx:publish(emqx_message:set_flag(sys, emqx_message:set_flag(retain, Msg))).
|
||||
|
||||
retain(Topic, Payload) when is_binary(Payload) ->
|
||||
Msg = emqx_message:make(broker, emqx_topic:systop(Topic), Payload),
|
||||
emqx:publish(emqx_message:set_flag(sys, emqx_message:set_flag(retain, Msg))).
|
||||
|
||||
publish(Topic, Payload) when is_binary(Payload) ->
|
||||
Msg = emqx_message:make(broker, emqx_topic:systop(Topic), Payload),
|
||||
emqx:publish(emqx_message:set_flag(sys, Msg)).
|
||||
|
||||
uptime(#state{started_at = Ts}) ->
|
||||
Secs = timer:now_diff(os:timestamp(), Ts) div 1000000,
|
||||
uptime(#state{start_time = Ts}) ->
|
||||
Secs = timer:now_diff(erlang:timestamp(), Ts) div 1000000,
|
||||
lists:flatten(uptime(seconds, Secs)).
|
||||
|
||||
uptime(seconds, Secs) when Secs < 60 ->
|
||||
[integer_to_list(Secs), " seconds"];
|
||||
uptime(seconds, Secs) ->
|
||||
|
@ -175,3 +161,38 @@ uptime(hours, H) ->
|
|||
uptime(days, D) ->
|
||||
[integer_to_list(D), " days,"].
|
||||
|
||||
publish(uptime, Uptime) ->
|
||||
safe_publish(systop(uptime), [sys], Uptime);
|
||||
publish(datetime, Datetime) ->
|
||||
safe_publish(systop(datatype), [sys], Datetime);
|
||||
publish(version, Version) ->
|
||||
safe_publish(systop(version), [sys, retain], Version);
|
||||
publish(sysdescr, Descr) ->
|
||||
safe_publish(systop(sysdescr), [sys, retain], Descr);
|
||||
publish(brokers, Nodes) ->
|
||||
Payload = string:join([atom_to_list(N) || N <- Nodes], ","),
|
||||
safe_publish(<<"$SYS/brokers">>, [sys, retain], Payload);
|
||||
publish(stats, Stats) ->
|
||||
[begin
|
||||
Topic = systop(lists:concat(['stats/', Stat])),
|
||||
safe_publish(Topic, [sys], integer_to_binary(Val))
|
||||
end || {Stat, Val} <- Stats, is_atom(Stat), is_integer(Val)];
|
||||
publish(metrics, Metrics) ->
|
||||
[begin
|
||||
Topic = systop(lists:concat(['metrics/', Metric])),
|
||||
safe_publish(Topic, [sys], integer_to_binary(Val))
|
||||
end || {Metric, Val} <- Metrics, is_atom(Metric), is_integer(Val)].
|
||||
|
||||
safe_publish(Topic, Flags, Payload) ->
|
||||
try do_publish(Topic, Flags, Payload)
|
||||
catch
|
||||
_:Error ->
|
||||
emqx_logger:error("[SYS] Publish error: ~p", [Error])
|
||||
end.
|
||||
|
||||
do_publish(Topic, Flags, Payload) ->
|
||||
Msg0 = emqx_message:make(?SYS, Topic, iolist_to_binary(Payload)),
|
||||
emqx_broker:publish(lists:foldl(fun(Flag, Msg) ->
|
||||
emqx_message:set_flag(Flag, Msg)
|
||||
end, Msg0, Flags)).
|
||||
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_sysmon).
|
||||
-module(emqx_sys_mon).
|
||||
|
||||
-behavior(gen_server).
|
||||
|
||||
|
@ -23,31 +23,34 @@
|
|||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-record(state, {tickref, events = [], tracelog}).
|
||||
|
||||
%%-define(LOG_FMT, [{formatter_config, [time, " ", message, "\n"]}]).
|
||||
-record(state, {timer, events}).
|
||||
|
||||
-define(LOG(Msg, ProcInfo),
|
||||
emqx_log:warning([{sysmon, true}], "[SYSMON] ~s~n~p", [WarnMsg, ProcInfo])).
|
||||
emqx_logger:warning([{sysmon, true}],
|
||||
"[SYSMON] ~s~n~p", [WarnMsg, ProcInfo])).
|
||||
|
||||
-define(LOG(Msg, ProcInfo, PortInfo),
|
||||
emqx_log:warning([{sysmon, true}], "[SYSMON] ~s~n~p~n~p", [WarnMsg, ProcInfo, PortInfo])).
|
||||
emqx_logger:warning([{sysmon, true}],
|
||||
"[SYSMON] ~s~n~p~n~p", [WarnMsg, ProcInfo, PortInfo])).
|
||||
|
||||
-define(SYSMON, ?MODULE).
|
||||
|
||||
%% @doc Start system monitor
|
||||
-spec(start_link(Opts :: list(tuple())) -> {ok, pid()} | ignore | {error, term()}).
|
||||
-spec(start_link(Opts :: list(tuple()))
|
||||
-> {ok, pid()} | ignore | {error, term()}).
|
||||
start_link(Opts) ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [Opts], []).
|
||||
gen_server:start_link({local, ?SYSMON}, ?MODULE, [Opts], []).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% gen_server Callbacks
|
||||
%% gen_server callbacks
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
init([Opts]) ->
|
||||
erlang:system_monitor(self(), parse_opt(Opts)),
|
||||
{ok, TRef} = timer:send_interval(timer:seconds(1), reset),
|
||||
%%TODO: don't trace for performance issue.
|
||||
%%{ok, TraceLog} = start_tracelog(proplists:get_value(logfile, Opts)),
|
||||
{ok, #state{tickref = TRef}}.
|
||||
{ok, start_timer(#state{events = []})}.
|
||||
|
||||
start_timer(State) ->
|
||||
State#state{timer = emqx_misc:start_timer(timer:seconds(2), tick)}.
|
||||
|
||||
parse_opt(Opts) ->
|
||||
parse_opt(Opts, []).
|
||||
|
@ -75,53 +78,53 @@ parse_opt([_Opt|Opts], Acc) ->
|
|||
parse_opt(Opts, Acc).
|
||||
|
||||
handle_call(Req, _From, State) ->
|
||||
emqx_log:error("[SYSMON] Unexpected request: ~p", [Req]),
|
||||
emqx_logger:error("[SYSMON] Unexpected request: ~p", [Req]),
|
||||
{reply, ignore, State}.
|
||||
|
||||
handle_cast(Msg, State) ->
|
||||
emqx_log:error("[SYSMON] Unexpected msg: ~p", [Msg]),
|
||||
emqx_logger:error("[SYSMON] Unexpected msg: ~p", [Msg]),
|
||||
{noreply, State}.
|
||||
|
||||
handle_info({monitor, Pid, long_gc, Info}, State) ->
|
||||
suppress({long_gc, Pid}, fun() ->
|
||||
WarnMsg = io_lib:format("long_gc warning: pid = ~p, info: ~p", [Pid, Info]),
|
||||
?LOG(WarnMsg, procinfo(Pid)),
|
||||
publish(long_gc, WarnMsg)
|
||||
safe_publish(long_gc, WarnMsg)
|
||||
end, State);
|
||||
|
||||
handle_info({monitor, Pid, long_schedule, Info}, State) when is_pid(Pid) ->
|
||||
suppress({long_schedule, Pid}, fun() ->
|
||||
WarnMsg = io_lib:format("long_schedule warning: pid = ~p, info: ~p", [Pid, Info]),
|
||||
?LOG(WarnMsg, procinfo(Pid)),
|
||||
publish(long_schedule, WarnMsg)
|
||||
safe_publish(long_schedule, WarnMsg)
|
||||
end, State);
|
||||
|
||||
handle_info({monitor, Port, long_schedule, Info}, State) when is_port(Port) ->
|
||||
suppress({long_schedule, Port}, fun() ->
|
||||
WarnMsg = io_lib:format("long_schedule warning: port = ~p, info: ~p", [Port, Info]),
|
||||
?LOG(WarnMsg, erlang:port_info(Port)),
|
||||
publish(long_schedule, WarnMsg)
|
||||
safe_publish(long_schedule, WarnMsg)
|
||||
end, State);
|
||||
|
||||
handle_info({monitor, Pid, large_heap, Info}, State) ->
|
||||
suppress({large_heap, Pid}, fun() ->
|
||||
WarnMsg = io_lib:format("large_heap warning: pid = ~p, info: ~p", [Pid, Info]),
|
||||
?LOG(WarnMsg, procinfo(Pid)),
|
||||
publish(large_heap, WarnMsg)
|
||||
safe_publish(large_heap, WarnMsg)
|
||||
end, State);
|
||||
|
||||
handle_info({monitor, SusPid, busy_port, Port}, State) ->
|
||||
suppress({busy_port, Port}, fun() ->
|
||||
WarnMsg = io_lib:format("busy_port warning: suspid = ~p, port = ~p", [SusPid, Port]),
|
||||
?LOG(WarnMsg, procinfo(SusPid), erlang:port_info(Port)),
|
||||
publish(busy_port, WarnMsg)
|
||||
safe_publish(busy_port, WarnMsg)
|
||||
end, State);
|
||||
|
||||
handle_info({monitor, SusPid, busy_dist_port, Port}, State) ->
|
||||
suppress({busy_dist_port, Port}, fun() ->
|
||||
WarnMsg = io_lib:format("busy_dist_port warning: suspid = ~p, port = ~p", [SusPid, Port]),
|
||||
?LOG(WarnMsg, procinfo(SusPid), erlang:port_info(Port)),
|
||||
publish(busy_dist_port, WarnMsg)
|
||||
safe_publish(busy_dist_port, WarnMsg)
|
||||
end, State);
|
||||
|
||||
handle_info(reset, State) ->
|
||||
|
@ -131,9 +134,8 @@ handle_info(Info, State) ->
|
|||
lager:error("[SYSMON] Unexpected Info: ~p", [Info]),
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, #state{tickref = TRef, tracelog = TraceLog}) ->
|
||||
timer:cancel(TRef),
|
||||
cancel_tracelog(TraceLog).
|
||||
terminate(_Reason, #state{timer = TRef}) ->
|
||||
emqx_misc:cancel_timer(TRef).
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
@ -152,20 +154,13 @@ procinfo(Pid) ->
|
|||
{Info, GcInfo} -> Info ++ GcInfo
|
||||
end.
|
||||
|
||||
publish(Sysmon, WarnMsg) ->
|
||||
Msg = emqx_message:make(sysmon, topic(Sysmon), iolist_to_binary(WarnMsg)),
|
||||
emqx:publish(emqx_message:set_flag(sys, Msg)).
|
||||
|
||||
topic(Sysmon) ->
|
||||
emqx_topic:systop(list_to_binary(lists:concat(['sysmon/', Sysmon]))).
|
||||
|
||||
%% start_tracelog(undefined) ->
|
||||
%% {ok, undefined};
|
||||
%% start_tracelog(LogFile) ->
|
||||
%% lager:trace_file(LogFile, [{sysmon, true}], info, ?LOG_FMT).
|
||||
|
||||
cancel_tracelog(undefined) ->
|
||||
ok;
|
||||
cancel_tracelog(TraceLog) ->
|
||||
lager:stop_trace(TraceLog).
|
||||
safe_publish(Event, WarnMsg) ->
|
||||
try
|
||||
Topic = emqx_topic:systop(lists:concat(['sysmon/', Event])),
|
||||
Msg = emqx_message:make(?SYSMON, Topic, iolist_to_binary(WarnMsg)),
|
||||
emqx_broker:publish(emqx_message:set_flag(sys, Msg))
|
||||
catch
|
||||
_:Error ->
|
||||
emqx_logger:error("[SYSMON] Publish error: ~p", [Error])
|
||||
end.
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_sys_sup).
|
||||
|
||||
-behaviour(supervisor).
|
||||
|
||||
-export([start_link/0]).
|
||||
|
||||
-export([init/1]).
|
||||
|
||||
start_link() ->
|
||||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||
|
||||
init([]) ->
|
||||
Sys = {sys, {emqx_sys, start_link, []},
|
||||
permanent, 5000, worker, [emqx_sys]},
|
||||
|
||||
{ok, Env} = emqx_config:get_env(sysmon),
|
||||
Sysmon = {sys_mon, {emqx_sysmon, start_link, [Env]},
|
||||
permanent, 5000, worker, [emqx_sys_mon]},
|
||||
{ok, {{one_for_one, 10, 100}, [Sys, Sysmon]}}.
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-module(emqx_sysmon_sup).
|
||||
|
||||
-behaviour(supervisor).
|
||||
|
||||
%% API
|
||||
-export([start_link/0]).
|
||||
|
||||
%% Supervisor callbacks
|
||||
-export([init/1]).
|
||||
|
||||
start_link() ->
|
||||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||
|
||||
init([]) ->
|
||||
{ok, Env} = emqx_config:get_env(sysmon),
|
||||
Sysmon = {sysmon, {emqx_sysmon, start_link, [Env]},
|
||||
permanent, 5000, worker, [emqx_sysmon]},
|
||||
{ok, {{one_for_one, 10, 100}, [Sysmon]}}.
|
||||
|
|
@ -1,24 +1,25 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_tables).
|
||||
|
||||
-export([create/2]).
|
||||
-export([new/2]).
|
||||
|
||||
create(Tab, Opts) ->
|
||||
%% Create a named_table ets.
|
||||
new(Tab, Opts) ->
|
||||
case ets:info(Tab, name) of
|
||||
undefined ->
|
||||
ets:new(Tab, lists:usort([named_table | Opts]));
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_time).
|
||||
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_topic).
|
||||
|
||||
|
@ -143,11 +143,10 @@ word(<<"#">>) -> '#';
|
|||
word(Bin) -> Bin.
|
||||
|
||||
%% @doc '$SYS' Topic.
|
||||
systop(Name) when is_atom(Name) ->
|
||||
list_to_binary(lists:concat(["$SYS/brokers/", node(), "/", Name]));
|
||||
|
||||
systop(Name) when is_atom(Name); is_list(Name) ->
|
||||
iolist_to_binary(lists:concat(["$SYS/brokers/", node(), "/", Name]));
|
||||
systop(Name) when is_binary(Name) ->
|
||||
list_to_binary(["$SYS/brokers/", atom_to_list(node()), "/", Name]).
|
||||
iolist_to_binary(["$SYS/brokers/", atom_to_list(node()), "/", Name]).
|
||||
|
||||
-spec(feed_var(binary(), binary(), binary()) -> binary()).
|
||||
feed_var(Var, Val, Topic) ->
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_tracer).
|
||||
|
||||
|
@ -22,9 +22,7 @@
|
|||
|
||||
-export([start_link/0]).
|
||||
|
||||
-export([trace/3]).
|
||||
|
||||
-export([start_trace/2, stop_trace/1, all_traces/0]).
|
||||
-export([start_trace/2, lookup_traces/0, stop_trace/1]).
|
||||
|
||||
%% gen_server Function Exports
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
|
@ -36,29 +34,15 @@
|
|||
|
||||
-define(OPTIONS, [{formatter_config, [time, " [",severity,"] ", message, "\n"]}]).
|
||||
|
||||
-define(TRACER, ?MODULE).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Start the tracer
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-spec(start_link() -> {ok, pid()}).
|
||||
-spec(start_link() -> {ok, pid()} | ignore | {error, term()}).
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Trace
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
trace(publish, From, _Msg) when is_atom(From) ->
|
||||
%% Dont' trace '$SYS' publish
|
||||
ignore;
|
||||
trace(publish, #client{client_id = ClientId, username = Username},
|
||||
#message{topic = Topic, payload = Payload}) ->
|
||||
emqx_log:info([{client, ClientId}, {topic, Topic}],
|
||||
"~s/~s PUBLISH to ~s: ~p", [ClientId, Username, Topic, Payload]);
|
||||
trace(publish, From, #message{topic = Topic, payload = Payload})
|
||||
when is_binary(From); is_list(From) ->
|
||||
emqx_log:info([{client, From}, {topic, Topic}],
|
||||
"~s PUBLISH to ~s: ~p", [From, Topic, Payload]).
|
||||
gen_server:start_link({local, ?TRACER}, ?MODULE, [], []).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Start/Stop Trace
|
||||
|
@ -68,11 +52,11 @@ trace(publish, From, #message{topic = Topic, payload = Payload})
|
|||
-spec(start_trace(trace_who(), string()) -> ok | {error, term()}).
|
||||
start_trace({client, ClientId}, LogFile) ->
|
||||
start_trace({start_trace, {client, ClientId}, LogFile});
|
||||
|
||||
start_trace({topic, Topic}, LogFile) ->
|
||||
start_trace({start_trace, {topic, Topic}, LogFile}).
|
||||
|
||||
start_trace(Req) -> gen_server:call(?MODULE, Req, infinity).
|
||||
start_trace(Req) ->
|
||||
gen_server:call(?MODULE, Req, infinity).
|
||||
|
||||
%% @doc Stop tracing client or topic.
|
||||
-spec(stop_trace(trace_who()) -> ok | {error, term()}).
|
||||
|
@ -81,25 +65,31 @@ stop_trace({client, ClientId}) ->
|
|||
stop_trace({topic, Topic}) ->
|
||||
gen_server:call(?MODULE, {stop_trace, {topic, Topic}}).
|
||||
|
||||
%% @doc Lookup all traces.
|
||||
-spec(all_traces() -> [{Who :: trace_who(), LogFile :: string()}]).
|
||||
all_traces() ->
|
||||
gen_server:call(?MODULE, all_traces).
|
||||
%% @doc Lookup all traces
|
||||
-spec(lookup_traces() -> [{Who :: trace_who(), LogFile :: string()}]).
|
||||
lookup_traces() ->
|
||||
gen_server:call(?TRACER, lookup_traces).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% gen_server callbacks
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
init([]) ->
|
||||
{ok, #state{level = emqx_config:get_env(trace_level, debug), traces = #{}}}.
|
||||
Level = emqx_config:get_env(trace_level, debug),
|
||||
{ok, #state{level = Level, traces = #{}}}.
|
||||
|
||||
handle_call({start_trace, Who, LogFile}, _From, State = #state{level = Level, traces = Traces}) ->
|
||||
case lager:trace_file(LogFile, [Who], Level, ?OPTIONS) of
|
||||
handle_call({start_trace, Who, LogFile}, _From,
|
||||
State = #state{level = Level, traces = Traces}) ->
|
||||
case catch lager:trace_file(LogFile, [Who], Level, ?OPTIONS) of
|
||||
{ok, exists} ->
|
||||
{reply, {error, existed}, State};
|
||||
{reply, {error, alread_existed}, State};
|
||||
{ok, Trace} ->
|
||||
{reply, ok, State#state{traces = maps:put(Who, {Trace, LogFile}, Traces)}};
|
||||
{error, Error} ->
|
||||
{error, Reason} ->
|
||||
emqx_logger:error("[TRACER] trace error: ~p", [Reason]),
|
||||
{reply, {error, Reason}, State};
|
||||
{'EXIT', Error} ->
|
||||
emqx_logger:error("[TRACER] trace exit: ~p", [Error]),
|
||||
{reply, {error, Error}, State}
|
||||
end;
|
||||
|
||||
|
@ -112,23 +102,23 @@ handle_call({stop_trace, Who}, _From, State = #state{traces = Traces}) ->
|
|||
end,
|
||||
{reply, ok, State#state{traces = maps:remove(Who, Traces)}};
|
||||
error ->
|
||||
{reply, {error, not_found}, State}
|
||||
{reply, {error, trance_not_found}, State}
|
||||
end;
|
||||
|
||||
handle_call(all_traces, _From, State = #state{traces = Traces}) ->
|
||||
handle_call(lookup_traces, _From, State = #state{traces = Traces}) ->
|
||||
{reply, [{Who, LogFile} || {Who, {_Trace, LogFile}}
|
||||
<- maps:to_list(Traces)], State};
|
||||
|
||||
handle_call(Req, _From, State) ->
|
||||
emqx_log:error("[TRACE] Unexpected Call: ~p", [Req]),
|
||||
emqx_logger:error("[TRACER] Unexpected request: ~p", [Req]),
|
||||
{reply, ignore, State}.
|
||||
|
||||
handle_cast(Msg, State) ->
|
||||
emqx_log:error("[TRACE] Unexpected Cast: ~p", [Msg]),
|
||||
emqx_logger:error("[TRACER] Unexpected msg: ~p", [Msg]),
|
||||
{noreply, State}.
|
||||
|
||||
handle_info(Info, State) ->
|
||||
emqx_log:error("[TRACE] Unexpected Info: ~p", [Info]),
|
||||
emqx_logger:error("[TRACER] Unexpected info: ~p", [Info]),
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, _State) ->
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-module(emqx_tracer_sup).
|
||||
|
||||
-behaviour(supervisor).
|
||||
|
||||
-export([start_link/0]).
|
||||
|
||||
-export([init/1]).
|
||||
|
||||
start_link() ->
|
||||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||
|
||||
init([]) ->
|
||||
Tracer = {tracer, {emqx_tracer, start_link, []},
|
||||
permanent, 5000, worker, [emqx_tracer]},
|
||||
{ok, {{one_for_one, 10, 3600}, [Tracer]}}.
|
||||
|
|
@ -1,18 +1,18 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%
|
||||
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%% you may not use this file except in compliance with the License.
|
||||
%% You may obtain a copy of the License at
|
||||
%%
|
||||
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%
|
||||
%% Unless required by applicable law or agreed to in writing, software
|
||||
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%% See the License for the specific language governing permissions and
|
||||
%% limitations under the License.
|
||||
%%--------------------------------------------------------------------
|
||||
%%%===================================================================
|
||||
%%% Copyright (c) 2013-2018 EMQ Inc. All rights reserved.
|
||||
%%%
|
||||
%%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||
%%% you may not use this file except in compliance with the License.
|
||||
%%% You may obtain a copy of the License at
|
||||
%%%
|
||||
%%% http://www.apache.org/licenses/LICENSE-2.0
|
||||
%%%
|
||||
%%% Unless required by applicable law or agreed to in writing, software
|
||||
%%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
%%% See the License for the specific language governing permissions and
|
||||
%%% limitations under the License.
|
||||
%%%===================================================================
|
||||
|
||||
-module(emqx_trie).
|
||||
|
||||
|
@ -24,39 +24,39 @@
|
|||
-boot_mnesia({mnesia, [boot]}).
|
||||
-copy_mnesia({mnesia, [copy]}).
|
||||
|
||||
%% Trie API
|
||||
%% Trie APIs
|
||||
-export([insert/1, match/1, lookup/1, delete/1]).
|
||||
|
||||
%% Tables
|
||||
%% Mnesia tables
|
||||
-define(TRIE, emqx_trie).
|
||||
-define(TRIE_NODE, emqx_trie_node).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Mnesia Bootstrap
|
||||
%% Mnesia bootstrap
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
%% @doc Create or replicate trie tables.
|
||||
-spec(mnesia(boot | copy) -> ok).
|
||||
mnesia(boot) ->
|
||||
%% Trie Table
|
||||
%% Trie table
|
||||
ok = ekka_mnesia:create_table(?TRIE, [
|
||||
{ram_copies, [node()]},
|
||||
{record_name, trie},
|
||||
{attributes, record_info(fields, trie)}]),
|
||||
%% Trie Node Table
|
||||
%% Trie node table
|
||||
ok = ekka_mnesia:create_table(?TRIE_NODE, [
|
||||
{ram_copies, [node()]},
|
||||
{record_name, trie_node},
|
||||
{attributes, record_info(fields, trie_node)}]);
|
||||
|
||||
mnesia(copy) ->
|
||||
%% Copy Trie Table
|
||||
%% Copy trie table
|
||||
ok = ekka_mnesia:copy_table(?TRIE),
|
||||
%% Copy Trie Node Table
|
||||
%% Copy trie_node table
|
||||
ok = ekka_mnesia:copy_table(?TRIE_NODE).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Trie API
|
||||
%% Trie APIs
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
%% @doc Insert a topic into the trie
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
-record(wsocket_state, {peername, client_pid, max_packet_size, parser}).
|
||||
|
||||
-define(WSLOG(Level, Format, Args, State),
|
||||
emqx_log:Level("WsClient(~s): " ++ Format,
|
||||
emqx_logger:Level("WsClient(~s): " ++ Format,
|
||||
[esockd_net:format(State#wsocket_state.peername) | Args])).
|
||||
|
||||
|
||||
|
@ -38,7 +38,7 @@ handle_request(Req) ->
|
|||
%%--------------------------------------------------------------------
|
||||
|
||||
handle_request('GET', "/mqtt", Req) ->
|
||||
emqx_log:debug("WebSocket Connection from: ~s", [Req:get(peer)]),
|
||||
emqx_logger:debug("WebSocket Connection from: ~s", [Req:get(peer)]),
|
||||
Upgrade = Req:get_header_value("Upgrade"),
|
||||
Proto = check_protocol_header(Req),
|
||||
case {is_websocket(Upgrade), Proto} of
|
||||
|
@ -56,19 +56,19 @@ handle_request('GET', "/mqtt", Req) ->
|
|||
max_packet_size = PacketSize,
|
||||
client_pid = ClientPid});
|
||||
{error, Reason} ->
|
||||
emqx_log:error("Get peername with error ~s", [Reason]),
|
||||
emqx_logger:error("Get peername with error ~s", [Reason]),
|
||||
Req:respond({400, [], <<"Bad Request">>})
|
||||
end;
|
||||
{false, _} ->
|
||||
emqx_log:error("Not WebSocket: Upgrade = ~s", [Upgrade]),
|
||||
emqx_logger:error("Not WebSocket: Upgrade = ~s", [Upgrade]),
|
||||
Req:respond({400, [], <<"Bad Request">>});
|
||||
{_, Proto} ->
|
||||
emqx_log:error("WebSocket with error Protocol: ~s", [Proto]),
|
||||
emqx_logger:error("WebSocket with error Protocol: ~s", [Proto]),
|
||||
Req:respond({400, [], <<"Bad WebSocket Protocol">>})
|
||||
end;
|
||||
|
||||
handle_request(Method, Path, Req) ->
|
||||
emqx_log:error("Unexpected WS Request: ~s ~s", [Method, Path]),
|
||||
emqx_logger:error("Unexpected WS Request: ~s ~s", [Method, Path]),
|
||||
Req:not_found().
|
||||
|
||||
is_websocket(Upgrade) ->
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
-define(SOCK_STATS, [recv_oct, recv_cnt, send_oct, send_cnt, send_pend]).
|
||||
|
||||
-define(WSLOG(Level, Format, Args, State),
|
||||
emqx_log:Level("WsClient(~s): " ++ Format,
|
||||
emqx_logger:Level("WsClient(~s): " ++ Format,
|
||||
[esockd_net:format(State#wsclient_state.peername) | Args])).
|
||||
|
||||
%% @doc Start WebSocket Client.
|
||||
|
@ -140,7 +140,7 @@ handle_call(Req, _From, State) ->
|
|||
reply({error, unexpected_request}, State).
|
||||
|
||||
handle_cast({received, Packet}, State = #wsclient_state{proto_state = ProtoState}) ->
|
||||
emqx_mqtt_metrics:received(Packet),
|
||||
emqx_metrics:received(Packet),
|
||||
case emqx_protocol:received(Packet, ProtoState) of
|
||||
{ok, ProtoState1} ->
|
||||
{noreply, gc(State#wsclient_state{proto_state = ProtoState1}), hibernate};
|
||||
|
@ -290,7 +290,7 @@ emit_stats(undefined, State) ->
|
|||
State;
|
||||
emit_stats(ClientId, State) ->
|
||||
{reply, Stats, _, _} = handle_call(stats, undefined, State),
|
||||
emqx_stats:set_client_stats(ClientId, Stats),
|
||||
emqx_cm:set_client_stats(ClientId, Stats),
|
||||
State.
|
||||
|
||||
wsock_stats(#wsclient_state{connection = Conn}) ->
|
||||
|
|
Loading…
Reference in New Issue