diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/DubboShutdownHook.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/DubboShutdownHook.java index 6dc9a0f54a89..44218a5f02d0 100644 --- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/DubboShutdownHook.java +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/DubboShutdownHook.java @@ -22,6 +22,7 @@ import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.common.utils.Assert; +import org.apache.dubbo.remoting.exchange.PortUnificationExchanger; import org.apache.dubbo.rpc.GracefulShutdown; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ModuleModel; @@ -93,6 +94,8 @@ private void doDestroy() { for (GracefulShutdown gracefulShutdown : gracefulShutdowns) { gracefulShutdown.readonly(); } + // close all exchangers to reject new requests + PortUnificationExchanger.goaway(); boolean hasModuleBindSpring = false; // check if any modules are bound to Spring diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/GoAwayEvent.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/GoAwayEvent.java new file mode 100644 index 000000000000..e3e2e2fc9361 --- /dev/null +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/GoAwayEvent.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +package org.apache.dubbo.remoting.api; + +/** + * Event used to trigger graceful shutdown for protocols that support it. + * For HTTP/2 (Triple protocol), this event triggers sending a GOAWAY frame + * to notify clients that the server is shutting down. + * + *

This event is fired through Netty's user event mechanism and is handled + * by protocol-specific handlers (e.g., TripleServerConnectionHandler for Triple protocol). + * Protocols that don't support GOAWAY (e.g., Dubbo protocol) will simply ignore this event. + */ +public class GoAwayEvent { + + /** + * Singleton instance of GoAwayEvent. + */ + public static final GoAwayEvent INSTANCE = new GoAwayEvent(); + + private GoAwayEvent() { + // Private constructor to enforce singleton pattern + } +} diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/pu/AbstractPortUnificationServer.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/pu/AbstractPortUnificationServer.java index c3a1513328e2..8ef7d7f0be4b 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/pu/AbstractPortUnificationServer.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/api/pu/AbstractPortUnificationServer.java @@ -103,4 +103,6 @@ public Map getSupportedHandlers() { // this getter is just used by implementation of this class return supportedHandlers; } + + public abstract void goaway(); } diff --git a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/PortUnificationExchanger.java b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/PortUnificationExchanger.java index 58da07715ff7..32f6d89bd2ac 100644 --- a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/PortUnificationExchanger.java +++ b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/PortUnificationExchanger.java @@ -37,7 +37,8 @@ public class PortUnificationExchanger { private static final ErrorTypeAwareLogger log = LoggerFactory.getErrorTypeAwareLogger(PortUnificationExchanger.class); - private static final ConcurrentMap servers = new ConcurrentHashMap<>(); + + private static final ConcurrentMap servers = new ConcurrentHashMap<>(); public static RemotingServer bind(URL url, ChannelHandler handler) { ConcurrentHashMapUtils.computeIfAbsent(servers, url.getAddress(), addr -> { @@ -52,7 +53,7 @@ public static RemotingServer bind(URL url, ChannelHandler handler) { }); servers.computeIfPresent(url.getAddress(), (addr, server) -> { - ((AbstractPortUnificationServer) server).addSupportedProtocol(url, handler); + server.addSupportedProtocol(url, handler); return server; }); return servers.get(url.getAddress()); @@ -69,9 +70,9 @@ public static AbstractConnectionClient connect(URL url, ChannelHandler handler) } public static void close() { - final ArrayList toClose = new ArrayList<>(servers.values()); + final ArrayList toClose = new ArrayList<>(servers.values()); servers.clear(); - for (RemotingServer server : toClose) { + for (AbstractPortUnificationServer server : toClose) { try { server.close(); } catch (Throwable throwable) { @@ -80,8 +81,18 @@ public static void close() { } } + public static void goaway() { + for (AbstractPortUnificationServer server : servers.values()) { + try { + server.goaway(); + } catch (Throwable throwable) { + log.error(PROTOCOL_ERROR_CLOSE_SERVER, "", "", "Goaway all port unification server failed", throwable); + } + } + } + // for test - public static ConcurrentMap getServers() { + public static ConcurrentMap getServers() { return servers; } diff --git a/dubbo-remoting/dubbo-remoting-netty/src/main/java/org/apache/dubbo/remoting/transport/netty/NettyPortUnificationServer.java b/dubbo-remoting/dubbo-remoting-netty/src/main/java/org/apache/dubbo/remoting/transport/netty/NettyPortUnificationServer.java index f616dcb9327c..0b8cdc98308d 100644 --- a/dubbo-remoting/dubbo-remoting-netty/src/main/java/org/apache/dubbo/remoting/transport/netty/NettyPortUnificationServer.java +++ b/dubbo-remoting/dubbo-remoting-netty/src/main/java/org/apache/dubbo/remoting/transport/netty/NettyPortUnificationServer.java @@ -71,6 +71,16 @@ public void addSupportedProtocol(URL url, ChannelHandler handler) { super.addSupportedProtocol(url, ChannelHandlers.wrap(handler, url)); } + @Override + public void goaway() { + // Netty 3 does not support HTTP/2 (Triple protocol), so we only need to mark channels as closing. + // The actual graceful shutdown for Dubbo protocol is handled by sending READONLY_EVENT + // through DubboGracefulShutdown.readonly() before this method is called. + for (Channel channel : getChannels()) { + channel.startClose(); + } + } + @Override public void close() { if (channel != null) { diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyPortUnificationServer.java b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyPortUnificationServer.java index 8bbdfb188fc3..2b3b2645efbf 100644 --- a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyPortUnificationServer.java +++ b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyPortUnificationServer.java @@ -24,6 +24,7 @@ import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.Constants; import org.apache.dubbo.remoting.RemotingException; +import org.apache.dubbo.remoting.api.GoAwayEvent; import org.apache.dubbo.remoting.api.WireProtocol; import org.apache.dubbo.remoting.api.pu.AbstractPortUnificationServer; import org.apache.dubbo.remoting.transport.dispatcher.ChannelHandlers; @@ -87,6 +88,20 @@ public void close() { } } + @Override + public void goaway() { + for (Channel channel : getChannels()) { + channel.startClose(); + // Fire GoAwayEvent through Netty pipeline to trigger protocol-specific graceful shutdown + // For HTTP/2 (Triple protocol), TripleServerConnectionHandler will handle this event + // and send Http2GoAwayFrame to notify clients + // For other protocols (e.g., Dubbo), this event will be ignored + if (channel instanceof NettyChannel) { + ((NettyChannel) channel).getNioChannel().pipeline().fireUserEventTriggered(GoAwayEvent.INSTANCE); + } + } + } + public void bind() throws Throwable { if (channel == null) { doOpen(); diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProxyProtocol.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProxyProtocol.java index 6123f22ff2fc..a325ce36af04 100644 --- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProxyProtocol.java +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/protocol/AbstractProxyProtocol.java @@ -16,14 +16,10 @@ */ package org.apache.dubbo.rpc.protocol; -import org.apache.dubbo.common.Parameters; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.StringUtils; -import org.apache.dubbo.remoting.Channel; -import org.apache.dubbo.remoting.ChannelHandler; import org.apache.dubbo.remoting.Constants; -import org.apache.dubbo.remoting.RemotingException; import org.apache.dubbo.remoting.RemotingServer; import org.apache.dubbo.rpc.Exporter; import org.apache.dubbo.rpc.Invocation; @@ -33,8 +29,6 @@ import org.apache.dubbo.rpc.Result; import org.apache.dubbo.rpc.RpcException; -import java.net.InetSocketAddress; -import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Objects; @@ -211,68 +205,4 @@ public Map getAttributes() { return attributes; } } - - protected abstract class RemotingServerAdapter implements RemotingServer { - - public abstract Object getDelegateServer(); - - /** - * @return - */ - @Override - public boolean isBound() { - return false; - } - - @Override - public Collection getChannels() { - return null; - } - - @Override - public Channel getChannel(InetSocketAddress remoteAddress) { - return null; - } - - @Override - public void reset(Parameters parameters) {} - - @Override - public void reset(URL url) {} - - @Override - public URL getUrl() { - return null; - } - - @Override - public ChannelHandler getChannelHandler() { - return null; - } - - @Override - public InetSocketAddress getLocalAddress() { - return null; - } - - @Override - public void send(Object message) throws RemotingException {} - - @Override - public void send(Object message, boolean sent) throws RemotingException {} - - @Override - public void close() {} - - @Override - public void close(int timeout) {} - - @Override - public void startClose() {} - - @Override - public boolean isClosed() { - return false; - } - } } diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/TripleServerConnectionHandler.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/TripleServerConnectionHandler.java index 06c517208efe..4606575f4ae8 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/TripleServerConnectionHandler.java +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/transport/TripleServerConnectionHandler.java @@ -18,6 +18,7 @@ import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.remoting.api.GoAwayEvent; import java.io.IOException; import java.net.SocketException; @@ -97,7 +98,13 @@ private boolean isQuiteException(Throwable t) { @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - super.userEventTriggered(ctx, evt); + if (evt instanceof GoAwayEvent) { + // Handle GoAwayEvent by initiating graceful shutdown + // This will send HTTP/2 GOAWAY frame to notify clients + close(ctx, ctx.newPromise()); + } else { + super.userEventTriggered(ctx, evt); + } } @Override