Add emqx_mqtt module
This commit is contained in:
parent
f007f69abe
commit
9976327c8d
|
@ -76,7 +76,6 @@
|
||||||
-type(message_from() :: #{zone := atom(),
|
-type(message_from() :: #{zone := atom(),
|
||||||
node := atom(),
|
node := atom(),
|
||||||
clientid := binary(),
|
clientid := binary(),
|
||||||
protocol := protocol(),
|
|
||||||
connector => atom(),
|
connector => atom(),
|
||||||
peername => {inet:ip_address(), inet:port_number()},
|
peername => {inet:ip_address(), inet:port_number()},
|
||||||
username => binary(),
|
username => binary(),
|
||||||
|
@ -99,8 +98,14 @@
|
||||||
{ id :: message_id(), %% Global unique id
|
{ id :: message_id(), %% Global unique id
|
||||||
from :: message_from(), %% Message from
|
from :: message_from(), %% Message from
|
||||||
sender :: pid(), %% The pid of the sender/publisher
|
sender :: pid(), %% The pid of the sender/publisher
|
||||||
flags :: message_flags(), %% Message flags
|
packet_id,
|
||||||
|
dup :: boolean(), %% Dup flag
|
||||||
|
qos :: 0 | 1 | 2, %% QoS
|
||||||
|
sys :: boolean(), %% $SYS flag
|
||||||
|
retain :: boolean(), %% Retain flag
|
||||||
|
flags = [], %% :: message_flags(), %% Message flags
|
||||||
headers :: message_headers(), %% Message headers
|
headers :: message_headers(), %% Message headers
|
||||||
|
protocol :: protocol(),
|
||||||
topic :: binary(), %% Message topic
|
topic :: binary(), %% Message topic
|
||||||
properties :: map(), %% Message user properties
|
properties :: map(), %% Message user properties
|
||||||
payload :: binary(), %% Message payload
|
payload :: binary(), %% Message payload
|
||||||
|
|
88
src/emqx.erl
88
src/emqx.erl
|
@ -18,16 +18,9 @@
|
||||||
|
|
||||||
-include("emqx.hrl").
|
-include("emqx.hrl").
|
||||||
|
|
||||||
-include("emqx_mqtt.hrl").
|
|
||||||
|
|
||||||
%% Start/Stop Application
|
%% Start/Stop Application
|
||||||
-export([start/0, env/1, env/2, is_running/1, stop/0]).
|
-export([start/0, env/1, env/2, is_running/1, stop/0]).
|
||||||
|
|
||||||
%% Start/Stop Listeners
|
|
||||||
-export([start_listeners/0, start_listener/1, listeners/0,
|
|
||||||
stop_listeners/0, stop_listener/1,
|
|
||||||
restart_listeners/0, restart_listener/1]).
|
|
||||||
|
|
||||||
%% PubSub API
|
%% PubSub API
|
||||||
-export([subscribe/1, subscribe/2, subscribe/3, publish/1,
|
-export([subscribe/1, subscribe/2, subscribe/3, publish/1,
|
||||||
unsubscribe/1, unsubscribe/2]).
|
unsubscribe/1, unsubscribe/2]).
|
||||||
|
@ -47,8 +40,6 @@
|
||||||
%% Shutdown and reboot
|
%% Shutdown and reboot
|
||||||
-export([shutdown/0, shutdown/1, reboot/0]).
|
-export([shutdown/0, shutdown/1, reboot/0]).
|
||||||
|
|
||||||
-type(listener() :: {atom(), esockd:listen_on(), [esockd:option()]}).
|
|
||||||
|
|
||||||
-define(APP, ?MODULE).
|
-define(APP, ?MODULE).
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
@ -80,85 +71,6 @@ is_running(Node) ->
|
||||||
Pid when is_pid(Pid) -> true
|
Pid when is_pid(Pid) -> true
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% Start/Stop Listeners
|
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
|
|
||||||
%% @doc Start Listeners.
|
|
||||||
-spec(start_listeners() -> ok).
|
|
||||||
start_listeners() -> lists:foreach(fun start_listener/1, env(listeners, [])).
|
|
||||||
|
|
||||||
%% Start mqtt listener
|
|
||||||
-spec(start_listener(listener()) -> {ok, pid()} | {error, any()}).
|
|
||||||
start_listener({tcp, ListenOn, Opts}) ->
|
|
||||||
start_listener('mqtt:tcp', ListenOn, Opts);
|
|
||||||
|
|
||||||
%% Start mqtt(SSL) listener
|
|
||||||
start_listener({ssl, ListenOn, Opts}) ->
|
|
||||||
start_listener('mqtt:ssl', ListenOn, Opts);
|
|
||||||
|
|
||||||
%% Start http listener
|
|
||||||
start_listener({Proto, ListenOn, Opts}) when Proto == http; Proto == ws ->
|
|
||||||
{ok, _} = mochiweb:start_http('mqtt:ws', ListenOn, Opts, {emqx_ws, handle_request, []});
|
|
||||||
|
|
||||||
%% Start https listener
|
|
||||||
start_listener({Proto, ListenOn, Opts}) when Proto == https; Proto == wss ->
|
|
||||||
{ok, _} = mochiweb:start_http('mqtt:wss', ListenOn, Opts, {emqx_ws, handle_request, []}).
|
|
||||||
|
|
||||||
start_listener(Proto, ListenOn, Opts) ->
|
|
||||||
Env = lists:append(emqx:env(client, []), emqx:env(protocol, [])),
|
|
||||||
MFArgs = {emqx_connection, start_link, [Env]},
|
|
||||||
{ok, _} = esockd:open(Proto, ListenOn, merge_sockopts(Opts), MFArgs).
|
|
||||||
|
|
||||||
listeners() ->
|
|
||||||
[Listener || Listener = {{Proto, _}, _Pid} <- esockd:listeners(), is_mqtt(Proto)].
|
|
||||||
|
|
||||||
is_mqtt('mqtt:tcp') -> true;
|
|
||||||
is_mqtt('mqtt:ssl') -> true;
|
|
||||||
is_mqtt('mqtt:ws') -> true;
|
|
||||||
is_mqtt('mqtt:wss') -> true;
|
|
||||||
is_mqtt(_Proto) -> false.
|
|
||||||
|
|
||||||
%% @doc Stop Listeners
|
|
||||||
-spec(stop_listeners() -> ok).
|
|
||||||
stop_listeners() -> lists:foreach(fun stop_listener/1, env(listeners, [])).
|
|
||||||
|
|
||||||
-spec(stop_listener(listener()) -> ok | {error, any()}).
|
|
||||||
stop_listener({tcp, ListenOn, _Opts}) ->
|
|
||||||
esockd:close('mqtt:tcp', ListenOn);
|
|
||||||
stop_listener({ssl, ListenOn, _Opts}) ->
|
|
||||||
esockd:close('mqtt:ssl', ListenOn);
|
|
||||||
stop_listener({Proto, ListenOn, _Opts}) when Proto == http; Proto == ws ->
|
|
||||||
mochiweb:stop_http('mqtt:ws', ListenOn);
|
|
||||||
stop_listener({Proto, ListenOn, _Opts}) when Proto == https; Proto == wss ->
|
|
||||||
mochiweb:stop_http('mqtt:wss', ListenOn);
|
|
||||||
% stop_listener({Proto, ListenOn, _Opts}) when Proto == api ->
|
|
||||||
% mochiweb:stop_http('mqtt:api', ListenOn);
|
|
||||||
stop_listener({Proto, ListenOn, _Opts}) ->
|
|
||||||
esockd:close(Proto, ListenOn).
|
|
||||||
|
|
||||||
%% @doc Restart Listeners
|
|
||||||
-spec(restart_listeners() -> ok).
|
|
||||||
restart_listeners() -> lists:foreach(fun restart_listener/1, env(listeners, [])).
|
|
||||||
|
|
||||||
-spec(restart_listener(listener()) -> any()).
|
|
||||||
restart_listener({tcp, ListenOn, _Opts}) ->
|
|
||||||
esockd:reopen('mqtt:tcp', ListenOn);
|
|
||||||
restart_listener({ssl, ListenOn, _Opts}) ->
|
|
||||||
esockd:reopen('mqtt:ssl', ListenOn);
|
|
||||||
restart_listener({Proto, ListenOn, _Opts}) when Proto == http; Proto == ws ->
|
|
||||||
mochiweb:restart_http('mqtt:ws', ListenOn);
|
|
||||||
restart_listener({Proto, ListenOn, _Opts}) when Proto == https; Proto == wss ->
|
|
||||||
mochiweb:restart_http('mqtt:wss', ListenOn);
|
|
||||||
restart_listener({Proto, ListenOn, _Opts}) when Proto == api ->
|
|
||||||
mochiweb:restart_http('mqtt:api', ListenOn);
|
|
||||||
restart_listener({Proto, ListenOn, _Opts}) ->
|
|
||||||
esockd:reopen(Proto, ListenOn).
|
|
||||||
|
|
||||||
merge_sockopts(Options) ->
|
|
||||||
SockOpts = emqx_misc:merge_opts(
|
|
||||||
?MQTT_SOCKOPTS, proplists:get_value(sockopts, Options, [])),
|
|
||||||
emqx_misc:merge_opts(Options, [{sockopts, SockOpts}]).
|
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% PubSub API
|
%% PubSub API
|
||||||
|
|
|
@ -26,13 +26,15 @@
|
||||||
-define(APP, emqx).
|
-define(APP, emqx).
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% Application Callbacks
|
%% Application callbacks
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
start(_Type, _Args) ->
|
start(_Type, _Args) ->
|
||||||
print_banner(),
|
print_banner(),
|
||||||
ekka:start(),
|
ekka:start(),
|
||||||
{ok, Sup} = emqx_sup:start_link(),
|
{ok, Sup} = emqx_sup:start_link(),
|
||||||
|
%%TODO: fixme later
|
||||||
|
emqx_mqtt_metrics:init(),
|
||||||
ok = register_acl_mod(),
|
ok = register_acl_mod(),
|
||||||
emqx_modules:load(),
|
emqx_modules:load(),
|
||||||
start_autocluster(),
|
start_autocluster(),
|
||||||
|
@ -43,7 +45,7 @@ start(_Type, _Args) ->
|
||||||
-spec(stop(State :: term()) -> term()).
|
-spec(stop(State :: term()) -> term()).
|
||||||
stop(_State) ->
|
stop(_State) ->
|
||||||
emqx_modules:unload(),
|
emqx_modules:unload(),
|
||||||
catch emqx:stop_listeners().
|
catch emqx_mqtt:shutdown().
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% Print Banner
|
%% Print Banner
|
||||||
|
@ -78,5 +80,5 @@ start_autocluster() ->
|
||||||
after_autocluster() ->
|
after_autocluster() ->
|
||||||
emqx_plugins:init(),
|
emqx_plugins:init(),
|
||||||
emqx_plugins:load(),
|
emqx_plugins:load(),
|
||||||
emqx:start_listeners().
|
emqx_mqtt:bootstrap().
|
||||||
|
|
||||||
|
|
|
@ -167,11 +167,11 @@ dequeue(State = #state{mqueue = MQ}) ->
|
||||||
{empty, MQ1} ->
|
{empty, MQ1} ->
|
||||||
State#state{mqueue = MQ1};
|
State#state{mqueue = MQ1};
|
||||||
{{value, Msg}, MQ1} ->
|
{{value, Msg}, MQ1} ->
|
||||||
handle_info({dispatch, Msg#mqtt_message.topic, Msg}, State),
|
handle_info({dispatch, Msg#message.topic, Msg}, State),
|
||||||
dequeue(State#state{mqueue = MQ1})
|
dequeue(State#state{mqueue = MQ1})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
transform(Msg = #mqtt_message{topic = Topic}, #state{topic_prefix = Prefix,
|
transform(Msg = #message{topic = Topic}, #state{topic_prefix = Prefix,
|
||||||
topic_suffix = Suffix}) ->
|
topic_suffix = Suffix}) ->
|
||||||
Msg#mqtt_message{topic = <<Prefix/binary, Topic/binary, Suffix/binary>>}.
|
Msg#message{topic = <<Prefix/binary, Topic/binary, Suffix/binary>>}.
|
||||||
|
|
||||||
|
|
|
@ -203,7 +203,7 @@ handle_info({suback, PacketId, GrantedQos}, State) ->
|
||||||
|
|
||||||
%% Fastlane
|
%% Fastlane
|
||||||
handle_info({dispatch, _Topic, Message}, State) ->
|
handle_info({dispatch, _Topic, Message}, State) ->
|
||||||
handle_info({deliver, Message#mqtt_message{qos = ?QOS_0}}, State);
|
handle_info({deliver, Message#message{qos = ?QOS_0}}, State);
|
||||||
|
|
||||||
handle_info({deliver, Message}, State) ->
|
handle_info({deliver, Message}, State) ->
|
||||||
with_proto(
|
with_proto(
|
||||||
|
@ -316,7 +316,7 @@ received(Bytes, State = #state{parser = Parser,
|
||||||
{more, NewParser} ->
|
{more, NewParser} ->
|
||||||
{noreply, run_socket(State#state{parser = NewParser}), IdleTimeout};
|
{noreply, run_socket(State#state{parser = NewParser}), IdleTimeout};
|
||||||
{ok, Packet, Rest} ->
|
{ok, Packet, Rest} ->
|
||||||
emqx_metrics:received(Packet),
|
emqx_mqtt_metrics:received(Packet),
|
||||||
case emqx_protocol:received(Packet, ProtoState) of
|
case emqx_protocol:received(Packet, ProtoState) of
|
||||||
{ok, ProtoState1} ->
|
{ok, ProtoState1} ->
|
||||||
received(Rest, State#state{parser = emqx_parser:initial_state(PacketSize),
|
received(Rest, State#state{parser = emqx_parser:initial_state(PacketSize),
|
||||||
|
|
|
@ -30,21 +30,21 @@
|
||||||
-type(msg_from() :: atom() | {binary(), undefined | binary()}).
|
-type(msg_from() :: atom() | {binary(), undefined | binary()}).
|
||||||
|
|
||||||
%% @doc Make a message
|
%% @doc Make a message
|
||||||
-spec(make(msg_from(), binary(), binary()) -> mqtt_message()).
|
-spec(make(msg_from(), binary(), binary()) -> message()).
|
||||||
make(From, Topic, Payload) ->
|
make(From, Topic, Payload) ->
|
||||||
make(From, ?QOS_0, Topic, Payload).
|
make(From, ?QOS_0, Topic, Payload).
|
||||||
|
|
||||||
-spec(make(msg_from(), mqtt_qos(), binary(), binary()) -> mqtt_message()).
|
-spec(make(msg_from(), mqtt_qos(), binary(), binary()) -> message()).
|
||||||
make(From, Qos, Topic, Payload) ->
|
make(From, Qos, Topic, Payload) ->
|
||||||
#mqtt_message{id = msgid(),
|
#message{id = msgid(),
|
||||||
from = From,
|
from = From,
|
||||||
qos = ?QOS_I(Qos),
|
qos = ?QOS_I(Qos),
|
||||||
topic = Topic,
|
topic = Topic,
|
||||||
payload = Payload,
|
payload = Payload,
|
||||||
timestamp = os:timestamp()}.
|
timestamp = os:timestamp()}.
|
||||||
|
|
||||||
%% @doc Message from Packet
|
%% @doc Message from Packet
|
||||||
-spec(from_packet(mqtt_packet()) -> mqtt_message()).
|
-spec(from_packet(mqtt_packet()) -> message()).
|
||||||
from_packet(#mqtt_packet{header = #mqtt_packet_header{type = ?PUBLISH,
|
from_packet(#mqtt_packet{header = #mqtt_packet_header{type = ?PUBLISH,
|
||||||
retain = Retain,
|
retain = Retain,
|
||||||
qos = Qos,
|
qos = Qos,
|
||||||
|
@ -52,14 +52,14 @@ from_packet(#mqtt_packet{header = #mqtt_packet_header{type = ?PUBLISH,
|
||||||
variable = #mqtt_packet_publish{topic_name = Topic,
|
variable = #mqtt_packet_publish{topic_name = Topic,
|
||||||
packet_id = PacketId},
|
packet_id = PacketId},
|
||||||
payload = Payload}) ->
|
payload = Payload}) ->
|
||||||
#mqtt_message{id = msgid(),
|
#message{id = msgid(),
|
||||||
packet_id = PacketId,
|
packet_id = PacketId,
|
||||||
qos = Qos,
|
qos = Qos,
|
||||||
retain = Retain,
|
retain = Retain,
|
||||||
dup = Dup,
|
dup = Dup,
|
||||||
topic = Topic,
|
topic = Topic,
|
||||||
payload = Payload,
|
payload = Payload,
|
||||||
timestamp = os:timestamp()};
|
timestamp = os:timestamp()};
|
||||||
|
|
||||||
from_packet(#mqtt_packet_connect{will_flag = false}) ->
|
from_packet(#mqtt_packet_connect{will_flag = false}) ->
|
||||||
undefined;
|
undefined;
|
||||||
|
@ -70,33 +70,33 @@ from_packet(#mqtt_packet_connect{client_id = ClientId,
|
||||||
will_qos = Qos,
|
will_qos = Qos,
|
||||||
will_topic = Topic,
|
will_topic = Topic,
|
||||||
will_msg = Msg}) ->
|
will_msg = Msg}) ->
|
||||||
#mqtt_message{id = msgid(),
|
#message{id = msgid(),
|
||||||
topic = Topic,
|
topic = Topic,
|
||||||
from = {ClientId, Username},
|
from = {ClientId, Username},
|
||||||
retain = Retain,
|
retain = Retain,
|
||||||
qos = Qos,
|
qos = Qos,
|
||||||
dup = false,
|
dup = false,
|
||||||
payload = Msg,
|
payload = Msg,
|
||||||
timestamp = os:timestamp()}.
|
timestamp = os:timestamp()}.
|
||||||
|
|
||||||
from_packet(ClientId, Packet) ->
|
from_packet(ClientId, Packet) ->
|
||||||
Msg = from_packet(Packet),
|
Msg = from_packet(Packet),
|
||||||
Msg#mqtt_message{from = ClientId}.
|
Msg#message{from = ClientId}.
|
||||||
|
|
||||||
from_packet(Username, ClientId, Packet) ->
|
from_packet(Username, ClientId, Packet) ->
|
||||||
Msg = from_packet(Packet),
|
Msg = from_packet(Packet),
|
||||||
Msg#mqtt_message{from = {ClientId, Username}}.
|
Msg#message{from = {ClientId, Username}}.
|
||||||
|
|
||||||
msgid() -> emqx_guid:gen().
|
msgid() -> emqx_guid:gen().
|
||||||
|
|
||||||
%% @doc Message to Packet
|
%% @doc Message to Packet
|
||||||
-spec(to_packet(mqtt_message()) -> mqtt_packet()).
|
-spec(to_packet(message()) -> mqtt_packet()).
|
||||||
to_packet(#mqtt_message{packet_id = PkgId,
|
to_packet(#message{packet_id = PkgId,
|
||||||
qos = Qos,
|
qos = Qos,
|
||||||
retain = Retain,
|
retain = Retain,
|
||||||
dup = Dup,
|
dup = Dup,
|
||||||
topic = Topic,
|
topic = Topic,
|
||||||
payload = Payload}) ->
|
payload = Payload}) ->
|
||||||
|
|
||||||
#mqtt_packet{header = #mqtt_packet_header{type = ?PUBLISH,
|
#mqtt_packet{header = #mqtt_packet_header{type = ?PUBLISH,
|
||||||
qos = Qos,
|
qos = Qos,
|
||||||
|
@ -111,40 +111,42 @@ to_packet(#mqtt_message{packet_id = PkgId,
|
||||||
payload = Payload}.
|
payload = Payload}.
|
||||||
|
|
||||||
%% @doc set dup, retain flag
|
%% @doc set dup, retain flag
|
||||||
-spec(set_flag(mqtt_message()) -> mqtt_message()).
|
-spec(set_flag(message()) -> message()).
|
||||||
set_flag(Msg) ->
|
set_flag(Msg) ->
|
||||||
Msg#mqtt_message{dup = true, retain = true}.
|
Msg#message{dup = true, retain = true}.
|
||||||
|
|
||||||
-spec(set_flag(atom(), mqtt_message()) -> mqtt_message()).
|
-spec(set_flag(atom(), message()) -> message()).
|
||||||
set_flag(dup, Msg = #mqtt_message{dup = false}) ->
|
set_flag(dup, Msg = #message{dup = false}) ->
|
||||||
Msg#mqtt_message{dup = true};
|
Msg#message{dup = true};
|
||||||
set_flag(sys, Msg = #mqtt_message{sys = false}) ->
|
set_flag(sys, Msg = #message{sys = false}) ->
|
||||||
Msg#mqtt_message{sys = true};
|
Msg#message{sys = true};
|
||||||
set_flag(retain, Msg = #mqtt_message{retain = false}) ->
|
set_flag(retain, Msg = #message{retain = false}) ->
|
||||||
Msg#mqtt_message{retain = true};
|
Msg#message{retain = true};
|
||||||
set_flag(Flag, Msg) when Flag =:= dup orelse Flag =:= retain -> Msg.
|
set_flag(Flag, Msg) when Flag =:= dup;
|
||||||
|
Flag =:= retain;
|
||||||
|
Flag =:= sys -> Msg.
|
||||||
|
|
||||||
%% @doc Unset dup, retain flag
|
%% @doc Unset dup, retain flag
|
||||||
-spec(unset_flag(mqtt_message()) -> mqtt_message()).
|
-spec(unset_flag(message()) -> message()).
|
||||||
unset_flag(Msg) ->
|
unset_flag(Msg) ->
|
||||||
Msg#mqtt_message{dup = false, retain = false}.
|
Msg#message{dup = false, retain = false}.
|
||||||
|
|
||||||
-spec(unset_flag(dup | retain | atom(), mqtt_message()) -> mqtt_message()).
|
-spec(unset_flag(dup | retain | atom(), message()) -> message()).
|
||||||
unset_flag(dup, Msg = #mqtt_message{dup = true}) ->
|
unset_flag(dup, Msg = #message{dup = true}) ->
|
||||||
Msg#mqtt_message{dup = false};
|
Msg#message{dup = false};
|
||||||
unset_flag(retain, Msg = #mqtt_message{retain = true}) ->
|
unset_flag(retain, Msg = #message{retain = true}) ->
|
||||||
Msg#mqtt_message{retain = false};
|
Msg#message{retain = false};
|
||||||
unset_flag(Flag, Msg) when Flag =:= dup orelse Flag =:= retain -> Msg.
|
unset_flag(Flag, Msg) when Flag =:= dup orelse Flag =:= retain -> Msg.
|
||||||
|
|
||||||
%% @doc Format MQTT Message
|
%% @doc Format MQTT Message
|
||||||
format(#mqtt_message{id = MsgId, packet_id = PktId, from = {ClientId, Username},
|
format(#message{id = MsgId, packet_id = PktId, from = {ClientId, Username},
|
||||||
qos = Qos, retain = Retain, dup = Dup, topic =Topic}) ->
|
qos = Qos, retain = Retain, dup = Dup, topic =Topic}) ->
|
||||||
io_lib:format("Message(Q~p, R~p, D~p, MsgId=~p, PktId=~p, From=~s/~s, Topic=~s)",
|
io_lib:format("Message(Q~p, R~p, D~p, MsgId=~p, PktId=~p, From=~s/~s, Topic=~s)",
|
||||||
[i(Qos), i(Retain), i(Dup), MsgId, PktId, Username, ClientId, Topic]);
|
[i(Qos), i(Retain), i(Dup), MsgId, PktId, Username, ClientId, Topic]);
|
||||||
|
|
||||||
%% TODO:...
|
%% TODO:...
|
||||||
format(#mqtt_message{id = MsgId, packet_id = PktId, from = From,
|
format(#message{id = MsgId, packet_id = PktId, from = From,
|
||||||
qos = Qos, retain = Retain, dup = Dup, topic =Topic}) ->
|
qos = Qos, retain = Retain, dup = Dup, topic =Topic}) ->
|
||||||
io_lib:format("Message(Q~p, R~p, D~p, MsgId=~p, PktId=~p, From=~s, Topic=~s)",
|
io_lib:format("Message(Q~p, R~p, D~p, MsgId=~p, PktId=~p, From=~s, Topic=~s)",
|
||||||
[i(Qos), i(Retain), i(Dup), MsgId, PktId, From, Topic]).
|
[i(Qos), i(Retain), i(Dup), MsgId, PktId, From, Topic]).
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
|
|
||||||
-record(state, {tick}).
|
-record(state, {tick}).
|
||||||
|
|
||||||
-define(TAB, ?MODULE).
|
-define(TAB, metrics).
|
||||||
|
|
||||||
-define(SERVER, ?MODULE).
|
-define(SERVER, ?MODULE).
|
||||||
|
|
||||||
|
@ -117,9 +117,10 @@ key(counter, Metric) ->
|
||||||
init([]) ->
|
init([]) ->
|
||||||
emqx_time:seed(),
|
emqx_time:seed(),
|
||||||
% Create metrics table
|
% Create metrics table
|
||||||
ets:new(?TAB, [set, public, named_table, {write_concurrency, true}]),
|
_ = ets:new(?TAB, [set, public, named_table, {write_concurrency, true}]),
|
||||||
% Tick to publish metrics
|
% Tick to publish metrics
|
||||||
{ok, #state{tick = emqx_broker:start_tick(tick)}, hibernate}.
|
{ok, TRef} = timer:send_after(emqx_sys:sys_interval(), tick),
|
||||||
|
{ok, #state{tick = TRef}, hibernate}.
|
||||||
|
|
||||||
handle_call(_Req, _From, State) ->
|
handle_call(_Req, _From, State) ->
|
||||||
{reply, error, State}.
|
{reply, error, State}.
|
||||||
|
@ -136,7 +137,8 @@ handle_info(_Info, State) ->
|
||||||
{noreply, State}.
|
{noreply, State}.
|
||||||
|
|
||||||
terminate(_Reason, #state{tick = TRef}) ->
|
terminate(_Reason, #state{tick = TRef}) ->
|
||||||
emqx_broker:stop_tick(TRef).
|
%%TODO:
|
||||||
|
timer:cancel(TRef).
|
||||||
|
|
||||||
code_change(_OldVsn, State, _Extra) ->
|
code_change(_OldVsn, State, _Extra) ->
|
||||||
{ok, State}.
|
{ok, State}.
|
||||||
|
@ -145,6 +147,8 @@ code_change(_OldVsn, State, _Extra) ->
|
||||||
%% Internal functions
|
%% Internal functions
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
%% TODO: the depencies are not right
|
||||||
|
|
||||||
publish(Metric, Val) ->
|
publish(Metric, Val) ->
|
||||||
Msg = emqx_message:make(metrics, metric_topic(Metric), bin(Val)),
|
Msg = emqx_message:make(metrics, metric_topic(Metric), bin(Val)),
|
||||||
emqx_broker:publish(emqx_message:set_flag(sys, Msg)).
|
emqx_broker:publish(emqx_message:set_flag(sys, Msg)).
|
||||||
|
|
|
@ -42,7 +42,7 @@ rewrite_unsubscribe(_ClientId, _Username, TopicTable, Rules) ->
|
||||||
lager:info("Rewrite unsubscribe: ~p", [TopicTable]),
|
lager:info("Rewrite unsubscribe: ~p", [TopicTable]),
|
||||||
{ok, [{match_rule(Topic, Rules), Opts} || {Topic, Opts} <- TopicTable]}.
|
{ok, [{match_rule(Topic, Rules), Opts} || {Topic, Opts} <- TopicTable]}.
|
||||||
|
|
||||||
rewrite_publish(Message=#mqtt_message{topic = Topic}, Rules) ->
|
rewrite_publish(Message = #message{topic = Topic}, Rules) ->
|
||||||
%%TODO: this will not work if the client is always online.
|
%%TODO: this will not work if the client is always online.
|
||||||
RewriteTopic =
|
RewriteTopic =
|
||||||
case get({rewrite, Topic}) of
|
case get({rewrite, Topic}) of
|
||||||
|
@ -52,7 +52,7 @@ rewrite_publish(Message=#mqtt_message{topic = Topic}, Rules) ->
|
||||||
DestTopic ->
|
DestTopic ->
|
||||||
DestTopic
|
DestTopic
|
||||||
end,
|
end,
|
||||||
{ok, Message#mqtt_message{topic = RewriteTopic}}.
|
{ok, Message#message{topic = RewriteTopic}}.
|
||||||
|
|
||||||
unload(_) ->
|
unload(_) ->
|
||||||
emqx:unhook('client.subscribe', fun ?MODULE:rewrite_subscribe/4),
|
emqx:unhook('client.subscribe', fun ?MODULE:rewrite_subscribe/4),
|
||||||
|
|
|
@ -0,0 +1,115 @@
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright © 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).
|
||||||
|
|
||||||
|
-include("emqx_mqtt.hrl").
|
||||||
|
|
||||||
|
-export([bootstrap/0, shutdown/0]).
|
||||||
|
|
||||||
|
-export([start_listeners/0, start_listener/1]).
|
||||||
|
-export([stop_listeners/0, stop_listener/1]).
|
||||||
|
-export([restart_listeners/0, restart_listener/1]).
|
||||||
|
-export([listeners/0]).
|
||||||
|
|
||||||
|
-type(listener() :: {atom(), esockd:listen_on(), [esockd:option()]}).
|
||||||
|
|
||||||
|
bootstrap() ->
|
||||||
|
start_listeners().
|
||||||
|
|
||||||
|
shutdown() ->
|
||||||
|
stop_listeners().
|
||||||
|
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Start/Stop Listeners
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
%% @doc Start Listeners.
|
||||||
|
-spec(start_listeners() -> ok).
|
||||||
|
start_listeners() ->
|
||||||
|
lists:foreach(fun start_listener/1, emqx:env(listeners, [])).
|
||||||
|
|
||||||
|
%% Start mqtt listener
|
||||||
|
-spec(start_listener(listener()) -> {ok, pid()} | {error, any()}).
|
||||||
|
start_listener({tcp, ListenOn, Opts}) ->
|
||||||
|
start_listener('mqtt:tcp', ListenOn, Opts);
|
||||||
|
|
||||||
|
%% Start mqtt(SSL) listener
|
||||||
|
start_listener({ssl, ListenOn, Opts}) ->
|
||||||
|
start_listener('mqtt:ssl', ListenOn, Opts);
|
||||||
|
|
||||||
|
%% Start http listener
|
||||||
|
start_listener({Proto, ListenOn, Opts}) when Proto == http; Proto == ws ->
|
||||||
|
{ok, _} = mochiweb:start_http('mqtt:ws', ListenOn, Opts, {emqx_ws, handle_request, []});
|
||||||
|
|
||||||
|
%% Start https listener
|
||||||
|
start_listener({Proto, ListenOn, Opts}) when Proto == https; Proto == wss ->
|
||||||
|
{ok, _} = mochiweb:start_http('mqtt:wss', ListenOn, Opts, {emqx_ws, handle_request, []}).
|
||||||
|
|
||||||
|
start_listener(Proto, ListenOn, Opts) ->
|
||||||
|
Env = lists:append(emqx:env(client, []), emqx:env(protocol, [])),
|
||||||
|
MFArgs = {emqx_connection, start_link, [Env]},
|
||||||
|
{ok, _} = esockd:open(Proto, ListenOn, merge_sockopts(Opts), MFArgs).
|
||||||
|
|
||||||
|
listeners() ->
|
||||||
|
[Listener || Listener = {{Proto, _}, _Pid} <- esockd:listeners(), is_mqtt(Proto)].
|
||||||
|
|
||||||
|
is_mqtt('mqtt:tcp') -> true;
|
||||||
|
is_mqtt('mqtt:ssl') -> true;
|
||||||
|
is_mqtt('mqtt:ws') -> true;
|
||||||
|
is_mqtt('mqtt:wss') -> true;
|
||||||
|
is_mqtt(_Proto) -> false.
|
||||||
|
|
||||||
|
%% @doc Stop Listeners
|
||||||
|
-spec(stop_listeners() -> ok).
|
||||||
|
stop_listeners() -> lists:foreach(fun stop_listener/1, emqx:env(listeners, [])).
|
||||||
|
|
||||||
|
-spec(stop_listener(listener()) -> ok | {error, any()}).
|
||||||
|
stop_listener({tcp, ListenOn, _Opts}) ->
|
||||||
|
esockd:close('mqtt:tcp', ListenOn);
|
||||||
|
stop_listener({ssl, ListenOn, _Opts}) ->
|
||||||
|
esockd:close('mqtt:ssl', ListenOn);
|
||||||
|
stop_listener({Proto, ListenOn, _Opts}) when Proto == http; Proto == ws ->
|
||||||
|
mochiweb:stop_http('mqtt:ws', ListenOn);
|
||||||
|
stop_listener({Proto, ListenOn, _Opts}) when Proto == https; Proto == wss ->
|
||||||
|
mochiweb:stop_http('mqtt:wss', ListenOn);
|
||||||
|
% stop_listener({Proto, ListenOn, _Opts}) when Proto == api ->
|
||||||
|
% mochiweb:stop_http('mqtt:api', ListenOn);
|
||||||
|
stop_listener({Proto, ListenOn, _Opts}) ->
|
||||||
|
esockd:close(Proto, ListenOn).
|
||||||
|
|
||||||
|
%% @doc Restart Listeners
|
||||||
|
-spec(restart_listeners() -> ok).
|
||||||
|
restart_listeners() -> lists:foreach(fun restart_listener/1, emqx:env(listeners, [])).
|
||||||
|
|
||||||
|
-spec(restart_listener(listener()) -> any()).
|
||||||
|
restart_listener({tcp, ListenOn, _Opts}) ->
|
||||||
|
esockd:reopen('mqtt:tcp', ListenOn);
|
||||||
|
restart_listener({ssl, ListenOn, _Opts}) ->
|
||||||
|
esockd:reopen('mqtt:ssl', ListenOn);
|
||||||
|
restart_listener({Proto, ListenOn, _Opts}) when Proto == http; Proto == ws ->
|
||||||
|
mochiweb:restart_http('mqtt:ws', ListenOn);
|
||||||
|
restart_listener({Proto, ListenOn, _Opts}) when Proto == https; Proto == wss ->
|
||||||
|
mochiweb:restart_http('mqtt:wss', ListenOn);
|
||||||
|
restart_listener({Proto, ListenOn, _Opts}) when Proto == api ->
|
||||||
|
mochiweb:restart_http('mqtt:api', ListenOn);
|
||||||
|
restart_listener({Proto, ListenOn, _Opts}) ->
|
||||||
|
esockd:reopen(Proto, ListenOn).
|
||||||
|
|
||||||
|
merge_sockopts(Options) ->
|
||||||
|
SockOpts = emqx_misc:merge_opts(
|
||||||
|
?MQTT_SOCKOPTS, proplists:get_value(sockopts, Options, [])),
|
||||||
|
emqx_misc:merge_opts(Options, [{sockopts, SockOpts}]).
|
|
@ -1,29 +0,0 @@
|
||||||
%%--------------------------------------------------------------------
|
|
||||||
%% Copyright © 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_app).
|
|
||||||
|
|
||||||
-behaviour(application).
|
|
||||||
|
|
||||||
-export([start/2, stop/1]).
|
|
||||||
|
|
||||||
start(_Type, _Args) ->
|
|
||||||
emqx_mqtt_metrics:init(),
|
|
||||||
emqx_mqtt_sup:start_link().
|
|
||||||
|
|
||||||
stop(_State) ->
|
|
||||||
ok.
|
|
||||||
|
|
|
@ -154,8 +154,8 @@ stats(#mqueue{type = Type, q = Q, max_len = MaxLen, len = Len, dropped = Dropped
|
||||||
end} | [{max_len, MaxLen}, {dropped, Dropped}]].
|
end} | [{max_len, MaxLen}, {dropped, Dropped}]].
|
||||||
|
|
||||||
%% @doc Enqueue a message.
|
%% @doc Enqueue a message.
|
||||||
-spec(in(mqtt_message(), mqueue()) -> mqueue()).
|
-spec(in(message(), mqueue()) -> mqueue()).
|
||||||
in(#mqtt_message{qos = ?QOS_0}, MQ = #mqueue{qos0 = false}) ->
|
in(#message{qos = ?QOS_0}, MQ = #mqueue{qos0 = false}) ->
|
||||||
MQ;
|
MQ;
|
||||||
in(Msg, MQ = #mqueue{type = simple, q = Q, len = Len, max_len = 0}) ->
|
in(Msg, MQ = #mqueue{type = simple, q = Q, len = Len, max_len = 0}) ->
|
||||||
MQ#mqueue{q = queue:in(Msg, Q), len = Len + 1};
|
MQ#mqueue{q = queue:in(Msg, Q), len = Len + 1};
|
||||||
|
@ -166,7 +166,7 @@ in(Msg, MQ = #mqueue{type = simple, q = Q, len = Len, max_len = MaxLen, dropped
|
||||||
in(Msg, MQ = #mqueue{type = simple, q = Q, len = Len}) ->
|
in(Msg, MQ = #mqueue{type = simple, q = Q, len = Len}) ->
|
||||||
maybe_set_alarm(MQ#mqueue{q = queue:in(Msg, Q), len = Len + 1});
|
maybe_set_alarm(MQ#mqueue{q = queue:in(Msg, Q), len = Len + 1});
|
||||||
|
|
||||||
in(Msg = #mqtt_message{topic = Topic}, MQ = #mqueue{type = priority, q = Q,
|
in(Msg = #message{topic = Topic}, MQ = #mqueue{type = priority, q = Q,
|
||||||
priorities = Priorities,
|
priorities = Priorities,
|
||||||
max_len = 0}) ->
|
max_len = 0}) ->
|
||||||
case lists:keysearch(Topic, 1, Priorities) of
|
case lists:keysearch(Topic, 1, Priorities) of
|
||||||
|
@ -176,7 +176,7 @@ in(Msg = #mqtt_message{topic = Topic}, MQ = #mqueue{type = priority, q = Q,
|
||||||
{Pri, MQ1} = insert_p(Topic, 0, MQ),
|
{Pri, MQ1} = insert_p(Topic, 0, MQ),
|
||||||
MQ1#mqueue{q = ?PQUEUE:in(Msg, Pri, Q)}
|
MQ1#mqueue{q = ?PQUEUE:in(Msg, Pri, Q)}
|
||||||
end;
|
end;
|
||||||
in(Msg = #mqtt_message{topic = Topic}, MQ = #mqueue{type = priority, q = Q,
|
in(Msg = #message{topic = Topic}, MQ = #mqueue{type = priority, q = Q,
|
||||||
priorities = Priorities,
|
priorities = Priorities,
|
||||||
max_len = MaxLen}) ->
|
max_len = MaxLen}) ->
|
||||||
case lists:keysearch(Topic, 1, Priorities) of
|
case lists:keysearch(Topic, 1, Priorities) of
|
||||||
|
|
|
@ -120,7 +120,7 @@ client(#proto_state{client_id = ClientId,
|
||||||
connected_at = Time}) ->
|
connected_at = Time}) ->
|
||||||
WillTopic = if
|
WillTopic = if
|
||||||
WillMsg =:= undefined -> undefined;
|
WillMsg =:= undefined -> undefined;
|
||||||
true -> WillMsg#mqtt_message.topic
|
true -> WillMsg#message.topic
|
||||||
end,
|
end,
|
||||||
#mqtt_client{client_id = ClientId,
|
#mqtt_client{client_id = ClientId,
|
||||||
client_pid = ClientPid,
|
client_pid = ClientPid,
|
||||||
|
@ -352,18 +352,18 @@ with_puback(Type, Packet = ?PUBLISH_PACKET(_Qos, PacketId),
|
||||||
?LOG(error, "PUBLISH ~p error: ~p", [PacketId, Error], State)
|
?LOG(error, "PUBLISH ~p error: ~p", [PacketId, Error], State)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec(send(mqtt_message() | mqtt_packet(), proto_state()) -> {ok, proto_state()}).
|
-spec(send(message() | mqtt_packet(), proto_state()) -> {ok, proto_state()}).
|
||||||
send(Msg, State = #proto_state{client_id = ClientId,
|
send(Msg, State = #proto_state{client_id = ClientId,
|
||||||
username = Username,
|
username = Username,
|
||||||
mountpoint = MountPoint,
|
mountpoint = MountPoint,
|
||||||
is_bridge = IsBridge})
|
is_bridge = IsBridge})
|
||||||
when is_record(Msg, mqtt_message) ->
|
when is_record(Msg, message) ->
|
||||||
emqx_hooks:run('message.delivered', [ClientId, Username], Msg),
|
emqx_hooks:run('message.delivered', [ClientId, Username], Msg),
|
||||||
send(emqx_message:to_packet(unmount(MountPoint, clean_retain(IsBridge, Msg))), State);
|
send(emqx_message:to_packet(unmount(MountPoint, clean_retain(IsBridge, Msg))), State);
|
||||||
|
|
||||||
send(Packet = ?PACKET(Type), State = #proto_state{sendfun = SendFun, stats_data = Stats}) ->
|
send(Packet = ?PACKET(Type), State = #proto_state{sendfun = SendFun, stats_data = Stats}) ->
|
||||||
trace(send, Packet, State),
|
trace(send, Packet, State),
|
||||||
emqx_metrics:sent(Packet),
|
emqx_mqtt_metrics:sent(Packet),
|
||||||
SendFun(Packet),
|
SendFun(Packet),
|
||||||
{ok, State#proto_state{stats_data = inc_stats(send, Type, Stats)}}.
|
{ok, State#proto_state{stats_data = inc_stats(send, Type, Stats)}}.
|
||||||
|
|
||||||
|
@ -439,7 +439,7 @@ maybe_set_clientid(State) ->
|
||||||
send_willmsg(_Client, undefined) ->
|
send_willmsg(_Client, undefined) ->
|
||||||
ignore;
|
ignore;
|
||||||
send_willmsg(#mqtt_client{client_id = ClientId, username = Username}, WillMsg) ->
|
send_willmsg(#mqtt_client{client_id = ClientId, username = Username}, WillMsg) ->
|
||||||
emqx_broker:publish(WillMsg#mqtt_message{from = {ClientId, Username}}).
|
emqx_broker:publish(WillMsg#message{from = {ClientId, Username}}).
|
||||||
|
|
||||||
start_keepalive(0, _State) -> ignore;
|
start_keepalive(0, _State) -> ignore;
|
||||||
|
|
||||||
|
@ -570,10 +570,10 @@ sp(false) -> 0.
|
||||||
%% The retained flag should be propagated for bridge.
|
%% The retained flag should be propagated for bridge.
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
clean_retain(false, Msg = #mqtt_message{retain = true, headers = Headers}) ->
|
clean_retain(false, Msg = #message{retain = true, headers = Headers}) ->
|
||||||
case lists:member(retained, Headers) of
|
case lists:member(retained, Headers) of
|
||||||
true -> Msg;
|
true -> Msg;
|
||||||
false -> Msg#mqtt_message{retain = false}
|
false -> Msg#message{retain = false}
|
||||||
end;
|
end;
|
||||||
clean_retain(_IsBridge, Msg) ->
|
clean_retain(_IsBridge, Msg) ->
|
||||||
Msg.
|
Msg.
|
||||||
|
@ -596,16 +596,16 @@ feed_var({<<"%u">>, Username}, MountPoint) ->
|
||||||
|
|
||||||
mount(undefined, Any) ->
|
mount(undefined, Any) ->
|
||||||
Any;
|
Any;
|
||||||
mount(MountPoint, Msg = #mqtt_message{topic = Topic}) ->
|
mount(MountPoint, Msg = #message{topic = Topic}) ->
|
||||||
Msg#mqtt_message{topic = <<MountPoint/binary, Topic/binary>>};
|
Msg#message{topic = <<MountPoint/binary, Topic/binary>>};
|
||||||
mount(MountPoint, TopicTable) when is_list(TopicTable) ->
|
mount(MountPoint, TopicTable) when is_list(TopicTable) ->
|
||||||
[{<<MountPoint/binary, Topic/binary>>, Opts} || {Topic, Opts} <- TopicTable].
|
[{<<MountPoint/binary, Topic/binary>>, Opts} || {Topic, Opts} <- TopicTable].
|
||||||
|
|
||||||
unmount(undefined, Any) ->
|
unmount(undefined, Any) ->
|
||||||
Any;
|
Any;
|
||||||
unmount(MountPoint, Msg = #mqtt_message{topic = Topic}) ->
|
unmount(MountPoint, Msg = #message{topic = Topic}) ->
|
||||||
case catch split_binary(Topic, byte_size(MountPoint)) of
|
case catch split_binary(Topic, byte_size(MountPoint)) of
|
||||||
{MountPoint, Topic0} -> Msg#mqtt_message{topic = Topic0};
|
{MountPoint, Topic0} -> Msg#message{topic = Topic0};
|
||||||
_ -> Msg
|
_ -> Msg
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
|
@ -185,15 +185,15 @@ subscribe(Session, PacketId, TopicTable) -> %%TODO: the ack function??...
|
||||||
|
|
||||||
%% @doc Publish Message
|
%% @doc Publish Message
|
||||||
-spec(publish(pid(), message()) -> ok | {error, term()}).
|
-spec(publish(pid(), message()) -> ok | {error, term()}).
|
||||||
publish(_Session, Msg = #mqtt_message{qos = ?QOS_0}) ->
|
publish(_Session, Msg = #message{qos = ?QOS_0}) ->
|
||||||
%% Publish QoS0 Directly
|
%% Publish QoS0 Directly
|
||||||
emqx_broker:publish(Msg), ok;
|
emqx_broker:publish(Msg), ok;
|
||||||
|
|
||||||
publish(_Session, Msg = #mqtt_message{qos = ?QOS_1}) ->
|
publish(_Session, Msg = #message{qos = ?QOS_1}) ->
|
||||||
%% Publish QoS1 message directly for client will PubAck automatically
|
%% Publish QoS1 message directly for client will PubAck automatically
|
||||||
emqx_broker:publish(Msg), ok;
|
emqx_broker:publish(Msg), ok;
|
||||||
|
|
||||||
publish(Session, Msg = #mqtt_message{qos = ?QOS_2}) ->
|
publish(Session, Msg = #message{qos = ?QOS_2}) ->
|
||||||
%% Publish QoS2 to Session
|
%% Publish QoS2 to Session
|
||||||
gen_server:call(Session, {publish, Msg}, ?TIMEOUT).
|
gen_server:call(Session, {publish, Msg}, ?TIMEOUT).
|
||||||
|
|
||||||
|
@ -313,7 +313,7 @@ binding(ClientPid) ->
|
||||||
handle_pre_hibernate(State) ->
|
handle_pre_hibernate(State) ->
|
||||||
{hibernate, emqx_gc:reset_conn_gc_count(#state.force_gc_count, emit_stats(State))}.
|
{hibernate, emqx_gc:reset_conn_gc_count(#state.force_gc_count, emit_stats(State))}.
|
||||||
|
|
||||||
handle_call({publish, Msg = #mqtt_message{qos = ?QOS_2, packet_id = PacketId}}, _From,
|
handle_call({publish, Msg = #message{qos = ?QOS_2, packet_id = PacketId}}, _From,
|
||||||
State = #state{awaiting_rel = AwaitingRel,
|
State = #state{awaiting_rel = AwaitingRel,
|
||||||
await_rel_timer = Timer,
|
await_rel_timer = Timer,
|
||||||
await_rel_timeout = Timeout}) ->
|
await_rel_timeout = Timeout}) ->
|
||||||
|
@ -510,12 +510,12 @@ handle_cast(Msg, State) ->
|
||||||
{noreply, State}.
|
{noreply, State}.
|
||||||
|
|
||||||
%% Ignore Messages delivered by self
|
%% Ignore Messages delivered by self
|
||||||
handle_info({dispatch, _Topic, #mqtt_message{from = {ClientId, _}}},
|
handle_info({dispatch, _Topic, #message{from = {ClientId, _}}},
|
||||||
State = #state{client_id = ClientId, ignore_loop_deliver = true}) ->
|
State = #state{client_id = ClientId, ignore_loop_deliver = true}) ->
|
||||||
{noreply, State};
|
{noreply, State};
|
||||||
|
|
||||||
%% Dispatch Message
|
%% Dispatch Message
|
||||||
handle_info({dispatch, Topic, Msg}, State) when is_record(Msg, mqtt_message) ->
|
handle_info({dispatch, Topic, Msg}, State) when is_record(Msg, message) ->
|
||||||
{noreply, gc(dispatch(tune_qos(Topic, reset_dup(Msg), State), State))};
|
{noreply, gc(dispatch(tune_qos(Topic, reset_dup(Msg), State), State))};
|
||||||
|
|
||||||
%% Do nothing if the client has been disconnected.
|
%% Do nothing if the client has been disconnected.
|
||||||
|
@ -604,7 +604,7 @@ retry_delivery(Force, [{Type, Msg, Ts} | Msgs], Now,
|
||||||
if
|
if
|
||||||
Force orelse (Diff >= Interval) ->
|
Force orelse (Diff >= Interval) ->
|
||||||
case {Type, Msg} of
|
case {Type, Msg} of
|
||||||
{publish, Msg = #mqtt_message{packet_id = PacketId}} ->
|
{publish, Msg = #message{packet_id = PacketId}} ->
|
||||||
redeliver(Msg, State),
|
redeliver(Msg, State),
|
||||||
Inflight1 = Inflight:update(PacketId, {publish, Msg, Now}),
|
Inflight1 = Inflight:update(PacketId, {publish, Msg, Now}),
|
||||||
retry_delivery(Force, Msgs, Now, State#state{inflight = Inflight1});
|
retry_delivery(Force, Msgs, Now, State#state{inflight = Inflight1});
|
||||||
|
@ -631,7 +631,7 @@ expire_awaiting_rel(State = #state{awaiting_rel = AwaitingRel}) ->
|
||||||
expire_awaiting_rel([], _Now, State) ->
|
expire_awaiting_rel([], _Now, State) ->
|
||||||
State#state{await_rel_timer = undefined};
|
State#state{await_rel_timer = undefined};
|
||||||
|
|
||||||
expire_awaiting_rel([{PacketId, Msg = #mqtt_message{timestamp = TS}} | Msgs],
|
expire_awaiting_rel([{PacketId, Msg = #message{timestamp = TS}} | Msgs],
|
||||||
Now, State = #state{awaiting_rel = AwaitingRel,
|
Now, State = #state{awaiting_rel = AwaitingRel,
|
||||||
await_rel_timeout = Timeout}) ->
|
await_rel_timeout = Timeout}) ->
|
||||||
case (timer:now_diff(Now, TS) div 1000) of
|
case (timer:now_diff(Now, TS) div 1000) of
|
||||||
|
@ -651,8 +651,8 @@ sortfun(inflight) ->
|
||||||
fun({_, _, Ts1}, {_, _, Ts2}) -> Ts1 < Ts2 end;
|
fun({_, _, Ts1}, {_, _, Ts2}) -> Ts1 < Ts2 end;
|
||||||
|
|
||||||
sortfun(awaiting_rel) ->
|
sortfun(awaiting_rel) ->
|
||||||
fun({_, #mqtt_message{timestamp = Ts1}},
|
fun({_, #message{timestamp = Ts1}},
|
||||||
{_, #mqtt_message{timestamp = Ts2}}) ->
|
{_, #message{timestamp = Ts2}}) ->
|
||||||
Ts1 < Ts2
|
Ts1 < Ts2
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
@ -677,17 +677,17 @@ dispatch(Msg, State = #state{client_id = ClientId, client_pid = undefined}) ->
|
||||||
end;
|
end;
|
||||||
|
|
||||||
%% Deliver qos0 message directly to client
|
%% Deliver qos0 message directly to client
|
||||||
dispatch(Msg = #mqtt_message{qos = ?QOS0}, State) ->
|
dispatch(Msg = #message{qos = ?QOS0}, State) ->
|
||||||
deliver(Msg, State), State;
|
deliver(Msg, State), State;
|
||||||
|
|
||||||
dispatch(Msg = #mqtt_message{qos = QoS},
|
dispatch(Msg = #message{qos = QoS},
|
||||||
State = #state{next_msg_id = MsgId, inflight = Inflight})
|
State = #state{next_msg_id = MsgId, inflight = Inflight})
|
||||||
when QoS =:= ?QOS1 orelse QoS =:= ?QOS2 ->
|
when QoS =:= ?QOS1 orelse QoS =:= ?QOS2 ->
|
||||||
case Inflight:is_full() of
|
case Inflight:is_full() of
|
||||||
true ->
|
true ->
|
||||||
enqueue_msg(Msg, State);
|
enqueue_msg(Msg, State);
|
||||||
false ->
|
false ->
|
||||||
Msg1 = Msg#mqtt_message{packet_id = MsgId},
|
Msg1 = Msg#message{packet_id = MsgId},
|
||||||
deliver(Msg1, State),
|
deliver(Msg1, State),
|
||||||
await(Msg1, next_msg_id(State))
|
await(Msg1, next_msg_id(State))
|
||||||
end.
|
end.
|
||||||
|
@ -700,8 +700,8 @@ enqueue_msg(Msg, State = #state{mqueue = Q}) ->
|
||||||
%% Deliver
|
%% Deliver
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
redeliver(Msg = #mqtt_message{qos = QoS}, State) ->
|
redeliver(Msg = #message{qos = QoS}, State) ->
|
||||||
deliver(Msg#mqtt_message{dup = if QoS =:= ?QOS2 -> false; true -> true end}, State);
|
deliver(Msg#message{dup = if QoS =:= ?QOS2 -> false; true -> true end}, State);
|
||||||
|
|
||||||
redeliver({pubrel, PacketId}, #state{client_pid = Pid}) ->
|
redeliver({pubrel, PacketId}, #state{client_pid = Pid}) ->
|
||||||
Pid ! {redeliver, {?PUBREL, PacketId}}.
|
Pid ! {redeliver, {?PUBREL, PacketId}}.
|
||||||
|
@ -715,7 +715,7 @@ deliver(Msg, #state{client_pid = Pid, binding = remote}) ->
|
||||||
%% Awaiting ACK for QoS1/QoS2 Messages
|
%% Awaiting ACK for QoS1/QoS2 Messages
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
await(Msg = #mqtt_message{packet_id = PacketId},
|
await(Msg = #message{packet_id = PacketId},
|
||||||
State = #state{inflight = Inflight,
|
State = #state{inflight = Inflight,
|
||||||
retry_timer = RetryTimer,
|
retry_timer = RetryTimer,
|
||||||
retry_interval = Interval}) ->
|
retry_interval = Interval}) ->
|
||||||
|
@ -780,13 +780,13 @@ dequeue2(State = #state{mqueue = Q}) ->
|
||||||
%% Tune QoS
|
%% Tune QoS
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
tune_qos(Topic, Msg = #mqtt_message{qos = PubQoS},
|
tune_qos(Topic, Msg = #message{qos = PubQoS},
|
||||||
#state{subscriptions = SubMap, upgrade_qos = UpgradeQoS}) ->
|
#state{subscriptions = SubMap, upgrade_qos = UpgradeQoS}) ->
|
||||||
case maps:find(Topic, SubMap) of
|
case maps:find(Topic, SubMap) of
|
||||||
{ok, SubQoS} when UpgradeQoS andalso (SubQoS > PubQoS) ->
|
{ok, SubQoS} when UpgradeQoS andalso (SubQoS > PubQoS) ->
|
||||||
Msg#mqtt_message{qos = SubQoS};
|
Msg#message{qos = SubQoS};
|
||||||
{ok, SubQoS} when (not UpgradeQoS) andalso (SubQoS < PubQoS) ->
|
{ok, SubQoS} when (not UpgradeQoS) andalso (SubQoS < PubQoS) ->
|
||||||
Msg#mqtt_message{qos = SubQoS};
|
Msg#message{qos = SubQoS};
|
||||||
{ok, _} ->
|
{ok, _} ->
|
||||||
Msg;
|
Msg;
|
||||||
error ->
|
error ->
|
||||||
|
@ -797,8 +797,8 @@ tune_qos(Topic, Msg = #mqtt_message{qos = PubQoS},
|
||||||
%% Reset Dup
|
%% Reset Dup
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
reset_dup(Msg = #mqtt_message{dup = true}) ->
|
reset_dup(Msg = #message{dup = true}) ->
|
||||||
Msg#mqtt_message{dup = false};
|
Msg#message{dup = false};
|
||||||
reset_dup(Msg) -> Msg.
|
reset_dup(Msg) -> Msg.
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
|
@ -163,7 +163,8 @@ init([]) ->
|
||||||
Topics = ?SYSTOP_CLIENTS ++ ?SYSTOP_SESSIONS ++ ?SYSTOP_PUBSUB ++ ?SYSTOP_RETAINED,
|
Topics = ?SYSTOP_CLIENTS ++ ?SYSTOP_SESSIONS ++ ?SYSTOP_PUBSUB ++ ?SYSTOP_RETAINED,
|
||||||
ets:insert(?STATS_TAB, [{Topic, 0} || Topic <- Topics]),
|
ets:insert(?STATS_TAB, [{Topic, 0} || Topic <- Topics]),
|
||||||
% Tick to publish stats
|
% Tick to publish stats
|
||||||
{ok, #state{tick = emqx_broker:start_tick(tick)}, hibernate}.
|
{ok, TRef} = timer:send_after(emqx_sys:sys_interval(), tick),
|
||||||
|
{ok, #state{tick = TRef}, hibernate}.
|
||||||
|
|
||||||
handle_call(stop, _From, State) ->
|
handle_call(stop, _From, State) ->
|
||||||
{stop, normal, ok, State};
|
{stop, normal, ok, State};
|
||||||
|
@ -194,7 +195,7 @@ handle_info(_Info, State) ->
|
||||||
{noreply, State}.
|
{noreply, State}.
|
||||||
|
|
||||||
terminate(_Reason, #state{tick = TRef}) ->
|
terminate(_Reason, #state{tick = TRef}) ->
|
||||||
emqx_broker:stop_tick(TRef).
|
timer:cancel(TRef).
|
||||||
|
|
||||||
code_change(_OldVsn, State, _Extra) ->
|
code_change(_OldVsn, State, _Extra) ->
|
||||||
{ok, State}.
|
{ok, State}.
|
||||||
|
|
|
@ -54,16 +54,17 @@ stop_child(ChildId) ->
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
init([]) ->
|
init([]) ->
|
||||||
{ok, {{one_for_all, 0, 1},
|
{ok, {{one_for_all, 10, 3600},
|
||||||
[?CHILD(emqx_ctl, worker),
|
[?CHILD(emqx_ctl, worker),
|
||||||
?CHILD(emqx_hooks, worker),
|
?CHILD(emqx_hooks, worker),
|
||||||
?CHILD(emqx_locker, worker),
|
?CHILD(emqx_locker, worker),
|
||||||
?CHILD(emqx_stats, worker),
|
?CHILD(emqx_stats, worker),
|
||||||
?CHILD(emqx_metrics, worker),
|
?CHILD(emqx_metrics, worker),
|
||||||
|
?CHILD(emqx_sys, worker),
|
||||||
?CHILD(emqx_router_sup, supervisor),
|
?CHILD(emqx_router_sup, supervisor),
|
||||||
?CHILD(emqx_broker_sup, supervisor),
|
?CHILD(emqx_broker_sup, supervisor),
|
||||||
?CHILD(emqx_pooler, supervisor),
|
?CHILD(emqx_pooler, supervisor),
|
||||||
?CHILD(emqx_trace_sup, supervisor),
|
?CHILD(emqx_tracer_sup, supervisor),
|
||||||
?CHILD(emqx_cm_sup, supervisor),
|
?CHILD(emqx_cm_sup, supervisor),
|
||||||
?CHILD(emqx_sm_sup, supervisor),
|
?CHILD(emqx_sm_sup, supervisor),
|
||||||
?CHILD(emqx_session_sup, supervisor),
|
?CHILD(emqx_session_sup, supervisor),
|
||||||
|
|
|
@ -140,7 +140,7 @@ handle_call(Req, _From, State) ->
|
||||||
reply({error, unexpected_request}, State).
|
reply({error, unexpected_request}, State).
|
||||||
|
|
||||||
handle_cast({received, Packet}, State = #wsclient_state{proto_state = ProtoState}) ->
|
handle_cast({received, Packet}, State = #wsclient_state{proto_state = ProtoState}) ->
|
||||||
emqx_metrics:received(Packet),
|
emqx_mqtt_metrics:received(Packet),
|
||||||
case emqx_protocol:received(Packet, ProtoState) of
|
case emqx_protocol:received(Packet, ProtoState) of
|
||||||
{ok, ProtoState1} ->
|
{ok, ProtoState1} ->
|
||||||
{noreply, gc(State#wsclient_state{proto_state = ProtoState1}), hibernate};
|
{noreply, gc(State#wsclient_state{proto_state = ProtoState1}), hibernate};
|
||||||
|
@ -178,7 +178,7 @@ handle_info({suback, PacketId, GrantedQos}, State) ->
|
||||||
|
|
||||||
%% Fastlane
|
%% Fastlane
|
||||||
handle_info({dispatch, _Topic, Message}, State) ->
|
handle_info({dispatch, _Topic, Message}, State) ->
|
||||||
handle_info({deliver, Message#mqtt_message{qos = ?QOS_0}}, State);
|
handle_info({deliver, Message#message{qos = ?QOS_0}}, State);
|
||||||
|
|
||||||
handle_info({deliver, Message}, State) ->
|
handle_info({deliver, Message}, State) ->
|
||||||
with_proto(
|
with_proto(
|
||||||
|
|
Loading…
Reference in New Issue