fix(stomp): backoff outgoung hear-beat timer interval
This commit is contained in:
parent
f7760232e4
commit
e4e8590a77
|
@ -43,7 +43,7 @@
|
||||||
]).
|
]).
|
||||||
|
|
||||||
%% for protocol
|
%% for protocol
|
||||||
-export([send/4, heartbeat/2]).
|
-export([send/4, heartbeat/2, statfun/3]).
|
||||||
|
|
||||||
%% for mgmt
|
%% for mgmt
|
||||||
-export([call/2, call/3]).
|
-export([call/2, call/3]).
|
||||||
|
@ -157,6 +157,7 @@ init_state(Transport, Socket, ProtoEnv) ->
|
||||||
{ok, Sockname} = Transport:ensure_ok_or_exit(sockname, [Socket]),
|
{ok, Sockname} = Transport:ensure_ok_or_exit(sockname, [Socket]),
|
||||||
|
|
||||||
SendFun = {fun ?MODULE:send/4, [Transport, Socket, self()]},
|
SendFun = {fun ?MODULE:send/4, [Transport, Socket, self()]},
|
||||||
|
StatFun = {fun ?MODULE:statfun/3, [Transport, Socket]},
|
||||||
HrtBtFun = {fun ?MODULE:heartbeat/2, [Transport, Socket]},
|
HrtBtFun = {fun ?MODULE:heartbeat/2, [Transport, Socket]},
|
||||||
Parser = emqx_stomp_frame:init_parer_state(ProtoEnv),
|
Parser = emqx_stomp_frame:init_parer_state(ProtoEnv),
|
||||||
|
|
||||||
|
@ -168,6 +169,7 @@ init_state(Transport, Socket, ProtoEnv) ->
|
||||||
peername => Peername,
|
peername => Peername,
|
||||||
sockname => Sockname,
|
sockname => Sockname,
|
||||||
peercert => Peercert,
|
peercert => Peercert,
|
||||||
|
statfun => StatFun,
|
||||||
sendfun => SendFun,
|
sendfun => SendFun,
|
||||||
heartfun => HrtBtFun,
|
heartfun => HrtBtFun,
|
||||||
conn_mod => ?MODULE
|
conn_mod => ?MODULE
|
||||||
|
@ -218,8 +220,15 @@ send(Frame, Transport, Sock, ConnPid) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
heartbeat(Transport, Sock) ->
|
heartbeat(Transport, Sock) ->
|
||||||
|
?LOG(debug, "SEND heartbeat: \\n"),
|
||||||
Transport:send(Sock, <<$\n>>).
|
Transport:send(Sock, <<$\n>>).
|
||||||
|
|
||||||
|
statfun(Stat, Transport, Sock) ->
|
||||||
|
case Transport:getstat(Sock, [Stat]) of
|
||||||
|
{ok, [{Stat, Val}]} -> {ok, Val};
|
||||||
|
{error, Error} -> {error, Error}
|
||||||
|
end.
|
||||||
|
|
||||||
handle_call(info, _From, State) ->
|
handle_call(info, _From, State) ->
|
||||||
{reply, info(State), State};
|
{reply, info(State), State};
|
||||||
|
|
||||||
|
|
|
@ -23,9 +23,10 @@
|
||||||
, check/3
|
, check/3
|
||||||
, info/1
|
, info/1
|
||||||
, interval/2
|
, interval/2
|
||||||
|
, reset/3
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-record(heartbeater, {interval, statval, repeat}).
|
-record(heartbeater, {interval, statval, repeat, repeat_max}).
|
||||||
|
|
||||||
-type name() :: incoming | outgoing.
|
-type name() :: incoming | outgoing.
|
||||||
|
|
||||||
|
@ -42,19 +43,23 @@ init({0, 0}) ->
|
||||||
#{};
|
#{};
|
||||||
init({Cx, Cy}) ->
|
init({Cx, Cy}) ->
|
||||||
maps:filter(fun(_, V) -> V /= undefined end,
|
maps:filter(fun(_, V) -> V /= undefined end,
|
||||||
#{incoming => heartbeater(Cx),
|
#{incoming => heartbeater(incoming, Cx),
|
||||||
outgoing => heartbeater(Cy)
|
outgoing => heartbeater(outgoing, Cy)
|
||||||
}).
|
}).
|
||||||
|
|
||||||
heartbeater(0) ->
|
heartbeater(_, 0) ->
|
||||||
undefined;
|
undefined;
|
||||||
heartbeater(I) ->
|
heartbeater(N, I) ->
|
||||||
#heartbeater{
|
#heartbeater{
|
||||||
interval = I,
|
interval = I,
|
||||||
statval = 0,
|
statval = 0,
|
||||||
repeat = 0
|
repeat = 0,
|
||||||
|
repeat_max = repeat_max(N)
|
||||||
}.
|
}.
|
||||||
|
|
||||||
|
repeat_max(incoming) -> 1;
|
||||||
|
repeat_max(outgoing) -> 0.
|
||||||
|
|
||||||
-spec check(name(), pos_integer(), heartbeat())
|
-spec check(name(), pos_integer(), heartbeat())
|
||||||
-> {ok, heartbeat()}
|
-> {ok, heartbeat()}
|
||||||
| {error, timeout}.
|
| {error, timeout}.
|
||||||
|
@ -67,11 +72,12 @@ check(Name, NewVal, HrtBt) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
check(NewVal, HrtBter = #heartbeater{statval = OldVal,
|
check(NewVal, HrtBter = #heartbeater{statval = OldVal,
|
||||||
repeat = Repeat}) ->
|
repeat = Repeat,
|
||||||
|
repeat_max = Max}) ->
|
||||||
if
|
if
|
||||||
NewVal =/= OldVal ->
|
NewVal =/= OldVal ->
|
||||||
{ok, HrtBter#heartbeater{statval = NewVal, repeat = 0}};
|
{ok, HrtBter#heartbeater{statval = NewVal, repeat = 0}};
|
||||||
Repeat < 1 ->
|
Repeat < Max ->
|
||||||
{ok, HrtBter#heartbeater{repeat = Repeat + 1}};
|
{ok, HrtBter#heartbeater{repeat = Repeat + 1}};
|
||||||
true -> {error, timeout}
|
true -> {error, timeout}
|
||||||
end.
|
end.
|
||||||
|
@ -89,3 +95,10 @@ interval(Type, HrtBt) ->
|
||||||
undefined -> undefined;
|
undefined -> undefined;
|
||||||
#heartbeater{interval = Intv} -> Intv
|
#heartbeater{interval = Intv} -> Intv
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
reset(Type, StatVal, HrtBt) ->
|
||||||
|
case maps:get(Type, HrtBt, undefined) of
|
||||||
|
undefined -> HrtBt;
|
||||||
|
HrtBter ->
|
||||||
|
HrtBt#{Type => HrtBter#heartbeater{statval = StatVal, repeat = 0}}
|
||||||
|
end.
|
||||||
|
|
|
@ -69,6 +69,8 @@
|
||||||
sendfun :: {function(), list()},
|
sendfun :: {function(), list()},
|
||||||
%% Heartbeat function
|
%% Heartbeat function
|
||||||
heartfun :: {function(), list()},
|
heartfun :: {function(), list()},
|
||||||
|
%% Get Socket stat function
|
||||||
|
statfun :: {function(), list()},
|
||||||
%% The confs for the connection
|
%% The confs for the connection
|
||||||
%% TODO: put these configs into a public mem?
|
%% TODO: put these configs into a public mem?
|
||||||
allow_anonymous :: maybe(boolean()),
|
allow_anonymous :: maybe(boolean()),
|
||||||
|
@ -77,6 +79,9 @@
|
||||||
|
|
||||||
-define(DEFAULT_SUB_ACK, <<"auto">>).
|
-define(DEFAULT_SUB_ACK, <<"auto">>).
|
||||||
|
|
||||||
|
-define(INCOMING_TIMER_BACKOFF, 1.25).
|
||||||
|
-define(OUTCOMING_TIMER_BACKOFF, 0.75).
|
||||||
|
|
||||||
-define(TIMER_TABLE, #{
|
-define(TIMER_TABLE, #{
|
||||||
incoming_timer => incoming,
|
incoming_timer => incoming,
|
||||||
outgoing_timer => outgoing,
|
outgoing_timer => outgoing,
|
||||||
|
@ -108,6 +113,7 @@
|
||||||
%% @doc Init protocol
|
%% @doc Init protocol
|
||||||
init(ConnInfo = #{peername := {PeerHost, _Port},
|
init(ConnInfo = #{peername := {PeerHost, _Port},
|
||||||
sockname := {_Host, SockPort},
|
sockname := {_Host, SockPort},
|
||||||
|
statfun := StatFun,
|
||||||
sendfun := SendFun,
|
sendfun := SendFun,
|
||||||
heartfun := HeartFun}, Opts) ->
|
heartfun := HeartFun}, Opts) ->
|
||||||
|
|
||||||
|
@ -132,6 +138,7 @@ init(ConnInfo = #{peername := {PeerHost, _Port},
|
||||||
clientinfo = ClientInfo,
|
clientinfo = ClientInfo,
|
||||||
heartfun = HeartFun,
|
heartfun = HeartFun,
|
||||||
sendfun = SendFun,
|
sendfun = SendFun,
|
||||||
|
statfun = StatFun,
|
||||||
timers = #{},
|
timers = #{},
|
||||||
transaction = #{},
|
transaction = #{},
|
||||||
allow_anonymous = AllowAnonymous,
|
allow_anonymous = AllowAnonymous,
|
||||||
|
@ -231,6 +238,8 @@ received(#stomp_frame{command = <<"CONNECT">>, headers = Headers},
|
||||||
default_user(State)
|
default_user(State)
|
||||||
) of
|
) of
|
||||||
true ->
|
true ->
|
||||||
|
Heartbeats = parse_heartbeats(
|
||||||
|
header(<<"heart-beat">>, Headers, <<"0,0">>)),
|
||||||
ClientId = emqx_guid:to_base62(emqx_guid:gen()),
|
ClientId = emqx_guid:to_base62(emqx_guid:gen()),
|
||||||
emqx_logger:set_metadata_clientid(ClientId),
|
emqx_logger:set_metadata_clientid(ClientId),
|
||||||
ConnInfo = State#pstate.conninfo,
|
ConnInfo = State#pstate.conninfo,
|
||||||
|
@ -238,6 +247,7 @@ received(#stomp_frame{command = <<"CONNECT">>, headers = Headers},
|
||||||
NConnInfo = ConnInfo#{
|
NConnInfo = ConnInfo#{
|
||||||
proto_ver => Version,
|
proto_ver => Version,
|
||||||
clientid => ClientId,
|
clientid => ClientId,
|
||||||
|
keepalive => element(1, Heartbeats) div 1000,
|
||||||
username => Login
|
username => Login
|
||||||
},
|
},
|
||||||
NClitInfo = ClitInfo#{
|
NClitInfo = ClitInfo#{
|
||||||
|
@ -250,7 +260,6 @@ received(#stomp_frame{command = <<"CONNECT">>, headers = Headers},
|
||||||
emqx_cm:discard_session(ClientId),
|
emqx_cm:discard_session(ClientId),
|
||||||
emqx_cm:register_channel(ClientId, ConnPid, NConnInfo)
|
emqx_cm:register_channel(ClientId, ConnPid, NConnInfo)
|
||||||
end),
|
end),
|
||||||
Heartbeats = parse_heartbeats(header(<<"heart-beat">>, Headers, <<"0,0">>)),
|
|
||||||
NState = start_heartbeart_timer(
|
NState = start_heartbeart_timer(
|
||||||
Heartbeats,
|
Heartbeats,
|
||||||
State#pstate{
|
State#pstate{
|
||||||
|
@ -454,11 +463,18 @@ timeout(_TRef, {incoming, NewVal},
|
||||||
|
|
||||||
timeout(_TRef, {outgoing, NewVal},
|
timeout(_TRef, {outgoing, NewVal},
|
||||||
State = #pstate{heart_beats = HrtBt,
|
State = #pstate{heart_beats = HrtBt,
|
||||||
|
statfun = {StatFun, StatArgs},
|
||||||
heartfun = {Fun, Args}}) ->
|
heartfun = {Fun, Args}}) ->
|
||||||
case emqx_stomp_heartbeat:check(outgoing, NewVal, HrtBt) of
|
case emqx_stomp_heartbeat:check(outgoing, NewVal, HrtBt) of
|
||||||
{error, timeout} ->
|
{error, timeout} ->
|
||||||
_ = erlang:apply(Fun, Args),
|
_ = erlang:apply(Fun, Args),
|
||||||
{ok, State};
|
case erlang:apply(StatFun, [send_oct] ++ StatArgs) of
|
||||||
|
{ok, NewVal2} ->
|
||||||
|
NHrtBt = emqx_stomp_heartbeat:reset(outgoing, NewVal2, HrtBt),
|
||||||
|
{ok, reset_timer(outgoing_timer, State#pstate{heart_beats = NHrtBt})};
|
||||||
|
{error, Reason} ->
|
||||||
|
{shutdown, {error, {get_stats_error, Reason}}, State}
|
||||||
|
end;
|
||||||
{ok, NHrtBt} ->
|
{ok, NHrtBt} ->
|
||||||
{ok, reset_timer(outgoing_timer, State#pstate{heart_beats = NHrtBt})}
|
{ok, reset_timer(outgoing_timer, State#pstate{heart_beats = NHrtBt})}
|
||||||
end;
|
end;
|
||||||
|
@ -633,7 +649,11 @@ reverse_heartbeats({Cx, Cy}) ->
|
||||||
start_heartbeart_timer(Heartbeats, State) ->
|
start_heartbeart_timer(Heartbeats, State) ->
|
||||||
ensure_timer(
|
ensure_timer(
|
||||||
[incoming_timer, outgoing_timer],
|
[incoming_timer, outgoing_timer],
|
||||||
State#pstate{heart_beats = emqx_stomp_heartbeat:init(Heartbeats)}).
|
State#pstate{heart_beats = emqx_stomp_heartbeat:init(backoff(Heartbeats))}).
|
||||||
|
|
||||||
|
backoff({Cx, Cy}) ->
|
||||||
|
{erlang:ceil(Cx * ?INCOMING_TIMER_BACKOFF),
|
||||||
|
erlang:ceil(Cy * ?OUTCOMING_TIMER_BACKOFF)}.
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
%% pub & sub helpers
|
%% pub & sub helpers
|
||||||
|
@ -768,3 +788,4 @@ interval(outgoing_timer, #pstate{heart_beats = HrtBt}) ->
|
||||||
emqx_stomp_heartbeat:interval(outgoing, HrtBt);
|
emqx_stomp_heartbeat:interval(outgoing, HrtBt);
|
||||||
interval(clean_trans_timer, _) ->
|
interval(clean_trans_timer, _) ->
|
||||||
?TRANS_TIMEOUT.
|
?TRANS_TIMEOUT.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue