Merge pull request #12325 from qzhuyan/dev/william/quic-lstner-reload-binding

feat(quic): support reload with new binding port
This commit is contained in:
William Yang 2024-01-15 13:57:50 +01:00 committed by GitHub
commit 8b05d36121
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 72 additions and 19 deletions

View File

@ -24,7 +24,7 @@ IsQuicSupp = fun() ->
end,
Bcrypt = {bcrypt, {git, "https://github.com/emqx/erlang-bcrypt.git", {tag, "0.6.0"}}},
Quicer = {quicer, {git, "https://github.com/emqx/quic.git", {tag, "0.0.311"}}}.
Quicer = {quicer, {git, "https://github.com/emqx/quic.git", {tag, "0.0.312"}}}.
Dialyzer = fun(Config) ->
{dialyzer, OldDialyzerConfig} = lists:keyfind(dialyzer, 1, Config),

View File

@ -469,26 +469,23 @@ do_update_listener(Type, Name, OldConf, NewConf) when
ok = ranch:set_protocol_options(Id, WsOpts),
%% No-op if the listener was not suspended.
ranch:resume_listener(Id);
do_update_listener(quic = Type, Name, _OldConf, NewConf) ->
do_update_listener(quic = Type, Name, OldConf, NewConf) ->
case quicer:listener(listener_id(Type, Name)) of
{ok, ListenerPid} ->
case quicer_listener:reload(ListenerPid, to_quicer_listener_opts(NewConf)) of
ListenOn = quic_listen_on(maps:get(bind, NewConf)),
case quicer_listener:reload(ListenerPid, ListenOn, to_quicer_listener_opts(NewConf)) of
ok ->
ok;
{error, _} = Error ->
%% @TODO: prefer: case quicer_listener:reload(ListenerPid, to_quicer_listener_opts(OldConf)) of
case quicer_listener:unlock(ListenerPid, 3000) of
Error ->
case
quic_listener_conf_rollback(
ListenerPid, to_quicer_listener_opts(OldConf), Error
)
of
ok ->
?ELOG("Failed to reload QUIC listener ~p, but Rollback success\n", [
Error
]),
{skip, Error};
RestoreErr ->
?ELOG(
"Failed to reload QUIC listener ~p, and Rollback failed as well\n",
[Error]
),
{error, {rollback_fail, RestoreErr}}
E ->
E
end
end;
E ->
@ -991,7 +988,7 @@ quic_listen_on(Bind) ->
Port
end.
-spec to_quicer_listener_opts(map()) -> quicer:listener_opts().
-spec to_quicer_listener_opts(map()) -> map().
to_quicer_listener_opts(Opts) ->
DefAcceptors = erlang:system_info(schedulers_online) * 8,
SSLOpts = maps:from_list(ssl_opts(Opts)),
@ -1018,3 +1015,27 @@ to_quicer_listener_opts(Opts) ->
),
%% @NOTE: Optional options take precedence over required options
maps:merge(Opts2, optional_quic_listener_opts(Opts)).
-spec quic_listener_conf_rollback(
pid(),
map(),
Error :: {error, _, _} | {error, _}
) -> ok | {error, any()}.
quic_listener_conf_rollback(ListenerPid, #{bind := Bind} = Conf, Error) ->
ListenOn = quic_listen_on(Bind),
case quicer_listener:reload(ListenerPid, ListenOn, Conf) of
ok ->
?ELOG(
"Failed to reload QUIC listener ~p, but Rollback success\n",
[
Error
]
),
ok;
RestoreErr ->
?ELOG(
"Failed to reload QUIC listener ~p, and Rollback failed as well\n",
[Error]
),
{error, {rollback_fail, RestoreErr}}
end.

View File

@ -444,14 +444,45 @@ t_quic_update_opts(Config) ->
| ClientSSLOpts
]),
%% Change the listener port
NewPort = emqx_common_test_helpers:select_free_port(ListenerType),
{ok, _} = emqx:update_config(
[listeners, ListenerType, updated],
{update, #{
<<"bind">> => format_bind({Host, NewPort})
}}
),
%% Connect to old port fail
?assertExceptionOneOf(
{exit, _},
{error, _},
ConnectFun(Host, Port, [
{cacertfile, filename:join(PrivDir, "ca-next.pem")},
{certfile, filename:join(PrivDir, "client.pem")},
{keyfile, filename:join(PrivDir, "client.key")}
| ClientSSLOpts
])
),
%% Connect to new port successfully.
C4 = ConnectFun(Host, NewPort, [
{cacertfile, filename:join(PrivDir, "ca-next.pem")},
{certfile, filename:join(PrivDir, "client.pem")},
{keyfile, filename:join(PrivDir, "client.key")}
| ClientSSLOpts
]),
%% Both pre- and post-update clients should be alive.
?assertEqual(pong, emqtt:ping(C1)),
?assertEqual(pong, emqtt:ping(C2)),
?assertEqual(pong, emqtt:ping(C3)),
?assertEqual(pong, emqtt:ping(C4)),
ok = emqtt:stop(C1),
ok = emqtt:stop(C2),
ok = emqtt:stop(C3)
ok = emqtt:stop(C3),
ok = emqtt:stop(C4)
end).
t_quic_update_opts_fail(Config) ->

1
changes/feat-12325.en.md Normal file
View File

@ -0,0 +1 @@
QUIC listener supports reload the listener binding without disrupting existing connections.

View File

@ -795,7 +795,7 @@ defmodule EMQXUmbrella.MixProject do
defp quicer_dep() do
if enable_quicer?(),
# in conflict with emqx and emqtt
do: [{:quicer, github: "emqx/quic", tag: "0.0.311", override: true}],
do: [{:quicer, github: "emqx/quic", tag: "0.0.312", override: true}],
else: []
end

View File

@ -36,7 +36,7 @@ assert_otp() ->
end.
quicer() ->
{quicer, {git, "https://github.com/emqx/quic.git", {tag, "0.0.311"}}}.
{quicer, {git, "https://github.com/emqx/quic.git", {tag, "0.0.312"}}}.
jq() ->
{jq, {git, "https://github.com/emqx/jq", {tag, "v0.3.12"}}}.