fix(emqx_frame): poor large frame concatenation performance
piror to this change, binary concatenation eats most of the CPU
This commit is contained in:
parent
1e3f7319ac
commit
de43da881a
|
@ -475,7 +475,7 @@ terminate(Reason, State = #state{channel = Channel, transport = Transport,
|
||||||
E : C : S ->
|
E : C : S ->
|
||||||
?tp(warning, unclean_terminate, #{exception => E, context => C, stacktrace => S})
|
?tp(warning, unclean_terminate, #{exception => E, context => C, stacktrace => S})
|
||||||
end,
|
end,
|
||||||
?tp(debug, terminate, #{}),
|
?tp(info, terminate, #{reason => Reason}),
|
||||||
maybe_raise_excption(Reason).
|
maybe_raise_excption(Reason).
|
||||||
|
|
||||||
%% close socket, discard new state, always return ok.
|
%% close socket, discard new state, always return ok.
|
||||||
|
|
|
@ -40,6 +40,8 @@
|
||||||
, serialize_opts/0
|
, serialize_opts/0
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
-define(Q(BYTES, Q), {BYTES, Q}).
|
||||||
|
|
||||||
-type(options() :: #{strict_mode => boolean(),
|
-type(options() :: #{strict_mode => boolean(),
|
||||||
max_size => 1..?MAX_PACKET_SIZE,
|
max_size => 1..?MAX_PACKET_SIZE,
|
||||||
version => emqx_types:version()
|
version => emqx_types:version()
|
||||||
|
@ -50,12 +52,12 @@
|
||||||
-type(parse_result() :: {more, parse_state()}
|
-type(parse_result() :: {more, parse_state()}
|
||||||
| {ok, emqx_types:packet(), binary(), parse_state()}).
|
| {ok, emqx_types:packet(), binary(), parse_state()}).
|
||||||
|
|
||||||
-type(cont_state() :: {Stage :: len | body,
|
-type(cont_state() ::
|
||||||
State :: #{hdr := #mqtt_packet_header{},
|
{Stage :: len | body,
|
||||||
len := {pos_integer(), non_neg_integer()} | non_neg_integer(),
|
State :: #{hdr := #mqtt_packet_header{},
|
||||||
rest => binary()
|
len := {pos_integer(), non_neg_integer()} | non_neg_integer(),
|
||||||
}
|
rest => binary() | ?Q(non_neg_integer(), queue:queue(binary()))
|
||||||
}).
|
}}).
|
||||||
|
|
||||||
-type(serialize_opts() :: options()).
|
-type(serialize_opts() :: options()).
|
||||||
|
|
||||||
|
@ -117,9 +119,19 @@ parse(Bin, {{len, #{hdr := Header,
|
||||||
parse_remaining_len(Bin, Header, Multiplier, Length, Options);
|
parse_remaining_len(Bin, Header, Multiplier, Length, Options);
|
||||||
parse(Bin, {{body, #{hdr := Header,
|
parse(Bin, {{body, #{hdr := Header,
|
||||||
len := Length,
|
len := Length,
|
||||||
rest := Rest}
|
rest := Body}
|
||||||
}, Options}) when is_binary(Bin) ->
|
}, Options}) when is_binary(Bin) ->
|
||||||
parse_frame(<<Rest/binary, Bin/binary>>, Header, Length, Options).
|
BodyBytes = body_bytes(Body),
|
||||||
|
{NewBodyPart, Tail} = split(BodyBytes + size(Bin) - Length, Bin),
|
||||||
|
NewBody = append_body(Body, NewBodyPart),
|
||||||
|
parse_frame(NewBody, Tail, Header, Length, Options).
|
||||||
|
|
||||||
|
%% split given binary with the first N bytes
|
||||||
|
split(N, Bin) when N =< 0 ->
|
||||||
|
{Bin, <<>>};
|
||||||
|
split(N, Bin) when N =< size(Bin) ->
|
||||||
|
<<H:N/binary, T/binary>> = Bin,
|
||||||
|
{H, T}.
|
||||||
|
|
||||||
parse_remaining_len(<<>>, Header, Options) ->
|
parse_remaining_len(<<>>, Header, Options) ->
|
||||||
{more, {{len, #{hdr => Header, len => {1, 0}}}, Options}};
|
{more, {{len, #{hdr => Header, len => {1, 0}}}, Options}};
|
||||||
|
@ -132,7 +144,8 @@ parse_remaining_len(_Bin, _Header, _Multiplier, Length, #{max_size := MaxSize})
|
||||||
parse_remaining_len(<<>>, Header, Multiplier, Length, Options) ->
|
parse_remaining_len(<<>>, Header, Multiplier, Length, Options) ->
|
||||||
{more, {{len, #{hdr => Header, len => {Multiplier, Length}}}, Options}};
|
{more, {{len, #{hdr => Header, len => {Multiplier, Length}}}, Options}};
|
||||||
%% Match DISCONNECT without payload
|
%% Match DISCONNECT without payload
|
||||||
parse_remaining_len(<<0:8, Rest/binary>>, Header = #mqtt_packet_header{type = ?DISCONNECT}, 1, 0, Options) ->
|
parse_remaining_len(<<0:8, Rest/binary>>,
|
||||||
|
Header = #mqtt_packet_header{type = ?DISCONNECT}, 1, 0, Options) ->
|
||||||
Packet = packet(Header, #mqtt_packet_disconnect{reason_code = ?RC_SUCCESS}),
|
Packet = packet(Header, #mqtt_packet_disconnect{reason_code = ?RC_SUCCESS}),
|
||||||
{ok, Packet, Rest, ?none(Options)};
|
{ok, Packet, Rest, ?none(Options)};
|
||||||
%% Match PINGREQ.
|
%% Match PINGREQ.
|
||||||
|
@ -149,16 +162,35 @@ parse_remaining_len(<<1:1, Len:7, Rest/binary>>, Header, Multiplier, Value, Opti
|
||||||
parse_remaining_len(<<0:1, Len:7, Rest/binary>>, Header, Multiplier, Value,
|
parse_remaining_len(<<0:1, Len:7, Rest/binary>>, Header, Multiplier, Value,
|
||||||
Options = #{max_size := MaxSize}) ->
|
Options = #{max_size := MaxSize}) ->
|
||||||
FrameLen = Value + Len * Multiplier,
|
FrameLen = Value + Len * Multiplier,
|
||||||
if
|
case FrameLen > MaxSize of
|
||||||
FrameLen > MaxSize -> error(frame_too_large);
|
true -> error(frame_too_large);
|
||||||
true -> parse_frame(Rest, Header, FrameLen, Options)
|
false -> parse_frame(Rest, Header, FrameLen, Options)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
parse_frame(Bin, Header, 0, Options) ->
|
body_bytes(B) when is_binary(B) -> size(B);
|
||||||
{ok, packet(Header), Bin, ?none(Options)};
|
body_bytes(?Q(Bytes, _)) -> Bytes.
|
||||||
parse_frame(Bin, Header, Length, Options) ->
|
|
||||||
case Bin of
|
append_body(H, T) when is_binary(H) andalso size(H) < 1024 ->
|
||||||
<<FrameBin:Length/binary, Rest/binary>> ->
|
<<H/binary, T/binary>>;
|
||||||
|
append_body(H, T) when is_binary(H) ->
|
||||||
|
Bytes = size(H) + size(T),
|
||||||
|
?Q(Bytes, queue:from_list([H, T]));
|
||||||
|
append_body(?Q(Bytes, Q), T) ->
|
||||||
|
?Q(Bytes + iolist_size(T), queue:in(T, Q)).
|
||||||
|
|
||||||
|
flatten_body(Body, Tail) when is_binary(Body) -> <<Body/binary, Tail/binary>>;
|
||||||
|
flatten_body(?Q(_, Q), Tail) -> iolist_to_binary([queue:to_list(Q), Tail]).
|
||||||
|
|
||||||
|
parse_frame(Body, Header, Length, Options) ->
|
||||||
|
%% already appended
|
||||||
|
parse_frame(Body, _SplitTail = <<>>, Header, Length, Options).
|
||||||
|
|
||||||
|
parse_frame(Body, Tail, Header, 0, Options) ->
|
||||||
|
{ok, packet(Header), flatten_body(Body, Tail), ?none(Options)};
|
||||||
|
parse_frame(Body, Tail, Header, Length, Options) ->
|
||||||
|
case body_bytes(Body) >= Length of
|
||||||
|
true ->
|
||||||
|
<<FrameBin:Length/binary, Rest/binary>> = flatten_body(Body, Tail),
|
||||||
case parse_packet(Header, FrameBin, Options) of
|
case parse_packet(Header, FrameBin, Options) of
|
||||||
{Variable, Payload} ->
|
{Variable, Payload} ->
|
||||||
{ok, packet(Header, Variable, Payload), Rest, ?none(Options)};
|
{ok, packet(Header, Variable, Payload), Rest, ?none(Options)};
|
||||||
|
@ -167,8 +199,11 @@ parse_frame(Bin, Header, Length, Options) ->
|
||||||
Variable ->
|
Variable ->
|
||||||
{ok, packet(Header, Variable), Rest, ?none(Options)}
|
{ok, packet(Header, Variable), Rest, ?none(Options)}
|
||||||
end;
|
end;
|
||||||
TooShortBin ->
|
false ->
|
||||||
{more, {{body, #{hdr => Header, len => Length, rest => TooShortBin}}, Options}}
|
{more, {{body, #{hdr => Header,
|
||||||
|
len => Length,
|
||||||
|
rest => append_body(Body, Tail)
|
||||||
|
}}, Options}}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-compile({inline, [packet/1, packet/2, packet/3]}).
|
-compile({inline, [packet/1, packet/2, packet/3]}).
|
||||||
|
|
Loading…
Reference in New Issue