Forráskód Böngészése

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

feat(quic): support reload with new binding port
William Yang 2 éve
szülő
commit
8b05d36121

+ 1 - 1
apps/emqx/rebar.config.script

@@ -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),

+ 36 - 15
apps/emqx/src/emqx_listeners.erl

@@ -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.

+ 32 - 1
apps/emqx/test/emqx_listeners_SUITE.erl

@@ -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 - 0
changes/feat-12325.en.md

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

+ 1 - 1
mix.exs

@@ -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
 

+ 1 - 1
rebar.config.erl

@@ -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"}}}.