This is an automated email from the ASF dual-hosted git repository.
liujun pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-dubbo.git
The following commit(s) were added to refs/heads/master by this push:
new 35f1914 Merge pull request #3341, start to use IdleStateHandler in
Netty4.
35f1914 is described below
commit 35f1914782c11283a7c36efb44a8d71729b34124
Author: Ian Luo <[email protected]>
AuthorDate: Thu Jan 31 10:25:30 2019 +0800
Merge pull request #3341, start to use IdleStateHandler in Netty4.
---
.../org/apache/dubbo/common/utils/UrlUtils.java | 13 ++
.../java/org/apache/dubbo/remoting/Client.java | 4 +-
.../org/apache/dubbo/remoting/IdleSensible.java | 37 ++++++
.../java/org/apache/dubbo/remoting/Server.java | 4 +-
.../support/header/HeaderExchangeClient.java | 71 ++++++-----
.../support/header/HeaderExchangeServer.java | 56 ++++-----
.../support/header/ReconnectTimerTask.java | 13 +-
.../dubbo/remoting/transport/AbstractClient.java | 135 +--------------------
.../support/header/HeartbeatHandlerTest.java | 3 +
.../transport/netty/ClientReconnectTest.java | 31 +----
.../remoting/transport/netty4/NettyClient.java | 12 +-
.../transport/netty4/NettyClientHandler.java | 35 +++++-
.../remoting/transport/netty4/NettyServer.java | 12 ++
.../transport/netty4/NettyServerHandler.java | 20 ++-
.../transport/netty4/ClientReconnectTest.java | 4 +-
15 files changed, 209 insertions(+), 241 deletions(-)
diff --git
a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/UrlUtils.java
b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/UrlUtils.java
index 5c157c5..23b5033 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/UrlUtils.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/UrlUtils.java
@@ -467,6 +467,19 @@ public class UrlUtils {
PROVIDERS_CATEGORY.equals(url.getParameter(CATEGORY_KEY,
PROVIDERS_CATEGORY));
}
+ public static int getHeartbeat(URL url) {
+ return url.getParameter(Constants.HEARTBEAT_KEY,
Constants.DEFAULT_HEARTBEAT);
+ }
+
+ public static int getIdleTimeout(URL url) {
+ int heartBeat = getHeartbeat(url);
+ int idleTimeout = url.getParameter(Constants.HEARTBEAT_TIMEOUT_KEY,
heartBeat * 3);
+ if (idleTimeout < heartBeat * 2) {
+ throw new IllegalStateException("idleTimeout < heartbeatInterval *
2");
+ }
+ return idleTimeout;
+ }
+
/**
* Check if the given value matches the given pattern. The pattern
supports wildcard "*".
*
diff --git
a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Client.java
b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Client.java
index b8ccad2..7f15535 100644
---
a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Client.java
+++
b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Client.java
@@ -25,7 +25,7 @@ import org.apache.dubbo.common.Resetable;
*
* @see
org.apache.dubbo.remoting.Transporter#connect(org.apache.dubbo.common.URL,
ChannelHandler)
*/
-public interface Client extends Endpoint, Channel, Resetable {
+public interface Client extends Endpoint, Channel, Resetable, IdleSensible {
/**
* reconnect.
@@ -35,4 +35,4 @@ public interface Client extends Endpoint, Channel, Resetable {
@Deprecated
void reset(org.apache.dubbo.common.Parameters parameters);
-}
\ No newline at end of file
+}
diff --git
a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/IdleSensible.java
b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/IdleSensible.java
new file mode 100644
index 0000000..14e371e
--- /dev/null
+++
b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/IdleSensible.java
@@ -0,0 +1,37 @@
+/*
+ * 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;
+
+/**
+ * Indicate whether the implementation (for both server and client) has the
ability to sense and handle idle connection.
+ * If the server has the ability to handle idle connection, it should close
the connection when it happens, and if
+ * the client has the ability to handle idle connection, it should send the
heartbeat to the server.
+ */
+public interface IdleSensible {
+ /**
+ * Whether the implementation can sense and handle the idle connection. By
default it's false, the implementation
+ * relies on dedicated timer to take care of idle connection.
+ *
+ * @return whether has the ability to handle idle connection
+ */
+ default boolean canHandleIdle() {
+ return false;
+ }
+}
diff --git
a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Server.java
b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Server.java
index f413cb0..c6ecf16 100755
---
a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Server.java
+++
b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/Server.java
@@ -28,7 +28,7 @@ import java.util.Collection;
*
* @see
org.apache.dubbo.remoting.Transporter#bind(org.apache.dubbo.common.URL,
ChannelHandler)
*/
-public interface Server extends Endpoint, Resetable {
+public interface Server extends Endpoint, Resetable, IdleSensible {
/**
* is bound.
@@ -55,4 +55,4 @@ public interface Server extends Endpoint, Resetable {
@Deprecated
void reset(org.apache.dubbo.common.Parameters parameters);
-}
\ No newline at end of file
+}
diff --git
a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeClient.java
b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeClient.java
index 65862b6..3e57fba 100644
---
a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeClient.java
+++
b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeClient.java
@@ -33,6 +33,9 @@ import java.net.InetSocketAddress;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
+import static org.apache.dubbo.common.utils.UrlUtils.getHeartbeat;
+import static org.apache.dubbo.common.utils.UrlUtils.getIdleTimeout;
+
/**
* DefaultMessageClient
*/
@@ -40,31 +43,21 @@ public class HeaderExchangeClient implements ExchangeClient
{
private final Client client;
private final ExchangeChannel channel;
- private int heartbeat;
- private int idleTimeout;
-
- private static final HashedWheelTimer IDLE_CHECK_TIMER = new
HashedWheelTimer(new NamedThreadFactory("dubbo-client-idleCheck", true), 1,
- TimeUnit.SECONDS, Constants.TICKS_PER_WHEEL);
+ private static final HashedWheelTimer IDLE_CHECK_TIMER = new
HashedWheelTimer(
+ new NamedThreadFactory("dubbo-client-idleCheck", true), 1,
TimeUnit.SECONDS, Constants.TICKS_PER_WHEEL);
private HeartbeatTimerTask heartBeatTimerTask;
-
private ReconnectTimerTask reconnectTimerTask;
- public HeaderExchangeClient(Client client, boolean needHeartbeat) {
+ public HeaderExchangeClient(Client client, boolean startTimer) {
Assert.notNull(client, "Client can't be null");
this.client = client;
this.channel = new HeaderExchangeChannel(client);
- String dubbo =
client.getUrl().getParameter(Constants.DUBBO_VERSION_KEY);
-
- this.heartbeat = client.getUrl().getParameter(Constants.HEARTBEAT_KEY,
dubbo != null &&
- dubbo.startsWith("1.0.") ? Constants.DEFAULT_HEARTBEAT : 0);
- this.idleTimeout =
client.getUrl().getParameter(Constants.HEARTBEAT_TIMEOUT_KEY, heartbeat * 3);
- if (idleTimeout < heartbeat * 2) {
- throw new IllegalStateException("idleTimeout < heartbeatInterval *
2");
- }
- if (needHeartbeat) {
- startIdleCheckTask();
+ if (startTimer) {
+ URL url = client.getUrl();
+ startReconnectTask(url);
+ startHeartBeatTask(url);
}
}
@@ -145,6 +138,7 @@ public class HeaderExchangeClient implements ExchangeClient
{
@Override
public void reset(URL url) {
client.reset(url);
+ // FIXME, should cancel and restart timer tasks if parameters in the
new URL are different?
}
@Override
@@ -178,25 +172,34 @@ public class HeaderExchangeClient implements
ExchangeClient {
return channel.hasAttribute(key);
}
- private void startIdleCheckTask() {
- AbstractTimerTask.ChannelProvider cp = () ->
Collections.singletonList(HeaderExchangeClient.this);
-
- long heartbeatTick = calculateLeastDuration(heartbeat);
- long heartbeatTimeoutTick = calculateLeastDuration(idleTimeout);
- HeartbeatTimerTask heartBeatTimerTask = new HeartbeatTimerTask(cp,
heartbeatTick, heartbeat);
- ReconnectTimerTask reconnectTimerTask = new ReconnectTimerTask(cp,
heartbeatTimeoutTick, idleTimeout);
-
- this.heartBeatTimerTask = heartBeatTimerTask;
- this.reconnectTimerTask = reconnectTimerTask;
+ private void startHeartBeatTask(URL url) {
+ if (!client.canHandleIdle()) {
+ AbstractTimerTask.ChannelProvider cp = () ->
Collections.singletonList(HeaderExchangeClient.this);
+ int heartbeat = getHeartbeat(url);
+ long heartbeatTick = calculateLeastDuration(heartbeat);
+ this.heartBeatTimerTask = new HeartbeatTimerTask(cp,
heartbeatTick, heartbeat);
+ IDLE_CHECK_TIMER.newTimeout(heartBeatTimerTask, heartbeatTick,
TimeUnit.MILLISECONDS);
+ }
+ }
- // init task and start timer.
- IDLE_CHECK_TIMER.newTimeout(heartBeatTimerTask, heartbeatTick,
TimeUnit.MILLISECONDS);
- IDLE_CHECK_TIMER.newTimeout(reconnectTimerTask, heartbeatTimeoutTick,
TimeUnit.MILLISECONDS);
+ private void startReconnectTask(URL url) {
+ if (shouldReconnect(url)) {
+ AbstractTimerTask.ChannelProvider cp = () ->
Collections.singletonList(HeaderExchangeClient.this);
+ int idleTimeout = getIdleTimeout(url);
+ long heartbeatTimeoutTick = calculateLeastDuration(idleTimeout);
+ this.reconnectTimerTask = new ReconnectTimerTask(cp,
heartbeatTimeoutTick, idleTimeout);
+ IDLE_CHECK_TIMER.newTimeout(reconnectTimerTask,
heartbeatTimeoutTick, TimeUnit.MILLISECONDS);
+ }
}
private void doClose() {
- heartBeatTimerTask.cancel();
- reconnectTimerTask.cancel();
+ if (heartBeatTimerTask != null) {
+ heartBeatTimerTask.cancel();
+ }
+
+ if (reconnectTimerTask != null) {
+ reconnectTimerTask.cancel();
+ }
}
/**
@@ -210,6 +213,10 @@ public class HeaderExchangeClient implements
ExchangeClient {
}
}
+ private boolean shouldReconnect(URL url) {
+ return url.getParameter(Constants.RECONNECT_KEY, true);
+ }
+
@Override
public String toString() {
return "HeaderExchangeClient [channel=" + channel + "]";
diff --git
a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeServer.java
b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeServer.java
index 65b0836..8a74e23 100644
---
a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeServer.java
+++
b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/HeaderExchangeServer.java
@@ -25,6 +25,7 @@ import org.apache.dubbo.common.timer.HashedWheelTimer;
import org.apache.dubbo.common.utils.Assert;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.NamedThreadFactory;
+import org.apache.dubbo.common.utils.UrlUtils;
import org.apache.dubbo.remoting.Channel;
import org.apache.dubbo.remoting.ChannelHandler;
import org.apache.dubbo.remoting.RemotingException;
@@ -49,8 +50,6 @@ public class HeaderExchangeServer implements ExchangeServer {
protected final Logger logger = LoggerFactory.getLogger(getClass());
private final Server server;
- private int heartbeat;
- private int idleTimeout;
private AtomicBoolean closed = new AtomicBoolean(false);
private static final HashedWheelTimer IDLE_CHECK_TIMER = new
HashedWheelTimer(new NamedThreadFactory("dubbo-server-idleCheck", true), 1,
@@ -61,13 +60,7 @@ public class HeaderExchangeServer implements ExchangeServer {
public HeaderExchangeServer(Server server) {
Assert.notNull(server, "server == null");
this.server = server;
- this.heartbeat = server.getUrl().getParameter(Constants.HEARTBEAT_KEY,
0);
- this.idleTimeout =
server.getUrl().getParameter(Constants.HEARTBEAT_TIMEOUT_KEY, heartbeat * 3);
- if (idleTimeout < heartbeat * 2) {
- throw new IllegalStateException("idleTimeout < heartbeatInterval *
2");
- }
-
- startIdleCheckTask();
+ startIdleCheckTask(getUrl());
}
public Server getServer() {
@@ -154,7 +147,9 @@ public class HeaderExchangeServer implements ExchangeServer
{
}
private void cancelCloseTask() {
- closeTimerTask.cancel();
+ if (closeTimerTask != null) {
+ closeTimerTask.cancel();
+ }
}
@Override
@@ -210,21 +205,13 @@ public class HeaderExchangeServer implements
ExchangeServer {
public void reset(URL url) {
server.reset(url);
try {
- if (url.hasParameter(Constants.HEARTBEAT_KEY)
- || url.hasParameter(Constants.HEARTBEAT_TIMEOUT_KEY)) {
- int h = url.getParameter(Constants.HEARTBEAT_KEY, heartbeat);
- int t = url.getParameter(Constants.HEARTBEAT_TIMEOUT_KEY, h *
3);
- if (t < h * 2) {
- throw new IllegalStateException("idleTimeout <
heartbeatInterval * 2");
- }
- if (h != heartbeat || t != idleTimeout) {
- heartbeat = h;
- idleTimeout = t;
-
- // we need cancel the exist closeTimeout first.
- cancelCloseTask();
- startIdleCheckTask();
- }
+ int currHeartbeat = UrlUtils.getHeartbeat(getUrl());
+ int currIdleTimeout = UrlUtils.getIdleTimeout(getUrl());
+ int heartbeat = UrlUtils.getHeartbeat(url);
+ int idleTimeout = UrlUtils.getIdleTimeout(url);
+ if (currHeartbeat != heartbeat || currIdleTimeout != idleTimeout) {
+ cancelCloseTask();
+ startIdleCheckTask(url);
}
} catch (Throwable t) {
logger.error(t.getMessage(), t);
@@ -266,15 +253,16 @@ public class HeaderExchangeServer implements
ExchangeServer {
}
}
- private void startIdleCheckTask() {
- AbstractTimerTask.ChannelProvider cp = () ->
unmodifiableCollection(HeaderExchangeServer.this.getChannels());
-
- long idleTimeoutTick = calculateLeastDuration(idleTimeout);
- CloseTimerTask closeTimerTask = new CloseTimerTask(cp,
idleTimeoutTick, idleTimeout);
- this.closeTimerTask = closeTimerTask;
+ private void startIdleCheckTask(URL url) {
+ if (!server.canHandleIdle()) {
+ AbstractTimerTask.ChannelProvider cp = () ->
unmodifiableCollection(HeaderExchangeServer.this.getChannels());
+ int idleTimeout = UrlUtils.getIdleTimeout(url);
+ long idleTimeoutTick = calculateLeastDuration(idleTimeout);
+ CloseTimerTask closeTimerTask = new CloseTimerTask(cp,
idleTimeoutTick, idleTimeout);
+ this.closeTimerTask = closeTimerTask;
- // init task and start timer.
- IDLE_CHECK_TIMER.newTimeout(closeTimerTask, idleTimeoutTick,
TimeUnit.MILLISECONDS);
+ // init task and start timer.
+ IDLE_CHECK_TIMER.newTimeout(closeTimerTask, idleTimeoutTick,
TimeUnit.MILLISECONDS);
+ }
}
-
}
diff --git
a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/ReconnectTimerTask.java
b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/ReconnectTimerTask.java
index 3c0e938..5fd8c90 100644
---
a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/ReconnectTimerTask.java
+++
b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/support/header/ReconnectTimerTask.java
@@ -41,9 +41,18 @@ public class ReconnectTimerTask extends AbstractTimerTask {
try {
Long lastRead = lastRead(channel);
Long now = now();
+
+ // Rely on reconnect timer to reconnect when
AbstractClient.doConnect fails to init the connection
+ if (!channel.isConnected()) {
+ try {
+ logger.info("Initial connection to " + channel);
+ ((Client) channel).reconnect();
+ } catch (Exception e) {
+ logger.error("Fail to connect to " + channel, e);
+ }
// check pong at client
- if (lastRead != null && now - lastRead > idleTimeout) {
- logger.warn("Close channel " + channel + ", because heartbeat
read idle time out: "
+ } else if (lastRead != null && now - lastRead > idleTimeout) {
+ logger.warn("Reconnect to channel " + channel + ", because
heartbeat read idle time out: "
+ idleTimeout + "ms");
try {
((Client) channel).reconnect();
diff --git
a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractClient.java
b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractClient.java
index 7280b50..4138398 100644
---
a/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractClient.java
+++
b/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/transport/AbstractClient.java
@@ -24,9 +24,7 @@ import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.store.DataStore;
import org.apache.dubbo.common.utils.ExecutorUtil;
-import org.apache.dubbo.common.utils.NamedThreadFactory;
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.Client;
@@ -35,12 +33,6 @@ import
org.apache.dubbo.remoting.transport.dispatcher.ChannelHandlers;
import java.net.InetSocketAddress;
import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@@ -51,31 +43,14 @@ public abstract class AbstractClient extends
AbstractEndpoint implements Client
protected static final String CLIENT_THREAD_POOL_NAME =
"DubboClientHandler";
private static final Logger logger =
LoggerFactory.getLogger(AbstractClient.class);
- private static final AtomicInteger CLIENT_THREAD_POOL_ID = new
AtomicInteger();
- private static final ScheduledThreadPoolExecutor reconnectExecutorService
= new ScheduledThreadPoolExecutor(2, new
NamedThreadFactory("DubboClientReconnectTimer", true));
private final Lock connectLock = new ReentrantLock();
- private final boolean send_reconnect;
- private final AtomicInteger reconnect_count = new AtomicInteger(0);
- // Reconnection error log has been called before?
- private final AtomicBoolean reconnect_error_log_flag = new
AtomicBoolean(false);
- // reconnect warning period. Reconnect warning interval (log warning after
how many times) //for test
- private final int reconnect_warning_period;
- private final long shutdown_timeout;
+ private final boolean needReconnect;
protected volatile ExecutorService executor;
- private volatile ScheduledFuture<?> reconnectExecutorFuture = null;
- // the last successed connected time
- private long lastConnectedTime = System.currentTimeMillis();
-
public AbstractClient(URL url, ChannelHandler handler) throws
RemotingException {
super(url, handler);
- send_reconnect = url.getParameter(Constants.SEND_RECONNECT_KEY, false);
-
- shutdown_timeout = url.getParameter(Constants.SHUTDOWN_TIMEOUT_KEY,
Constants.DEFAULT_SHUTDOWN_TIMEOUT);
-
- // The default reconnection interval is 2s, 1800 means warning
interval is 1 hour.
- reconnect_warning_period = url.getParameter("reconnect.waring.period",
1800);
+ needReconnect = url.getParameter(Constants.SEND_RECONNECT_KEY, false);
try {
doOpen();
@@ -118,105 +93,6 @@ public abstract class AbstractClient extends
AbstractEndpoint implements Client
return ChannelHandlers.wrap(handler, url);
}
- /**
- * @param url
- * @return 0-false
- */
- private static int getReconnectParam(URL url) {
- int reconnect;
- String param = url.getParameter(Constants.RECONNECT_KEY);
- if (StringUtils.isEmpty(param) || "true".equalsIgnoreCase(param)) {
- reconnect = Constants.DEFAULT_RECONNECT_PERIOD;
- } else if ("false".equalsIgnoreCase(param)) {
- reconnect = 0;
- } else {
- try {
- reconnect = Integer.parseInt(param);
- } catch (Exception e) {
- throw new IllegalArgumentException("reconnect param must be
nonnegative integer or false/true. input is:" + param);
- }
- if (reconnect < 0) {
- throw new IllegalArgumentException("reconnect param must be
nonnegative integer or false/true. input is:" + param);
- }
- }
- return reconnect;
- }
-
- /**
- * init reconnect thread
- */
- private synchronized void initConnectStatusCheckCommand() {
- //reconnect=false to close reconnect
- int reconnect = getReconnectParam(getUrl());
- if (reconnect > 0 && (reconnectExecutorFuture == null ||
reconnectExecutorFuture.isCancelled())) {
- Runnable connectStatusCheckCommand = new Runnable() {
- @Override
- public void run() {
- try {
- if (cancelFutureIfOffline()) return;
-
- if (!isConnected()) {
- connect();
- } else {
- lastConnectedTime = System.currentTimeMillis();
- }
- } catch (Throwable t) {
- String errorMsg = "client reconnect to " +
getUrl().getAddress() + " find error . url: " + getUrl();
- // wait registry sync provider list
- if (System.currentTimeMillis() - lastConnectedTime >
shutdown_timeout) {
- if (!reconnect_error_log_flag.get()) {
- reconnect_error_log_flag.set(true);
- logger.error(errorMsg, t);
- return;
- }
- }
- if (reconnect_count.getAndIncrement() %
reconnect_warning_period == 0) {
- logger.warn(errorMsg, t);
- }
- }
- }
-
- private boolean cancelFutureIfOffline() {
- /**
- * If the provider service is detected offline,
- * the client should not attempt to connect again.
- *
- * issue:
https://github.com/apache/incubator-dubbo/issues/3158
- */
- if(isClosed()) {
- ScheduledFuture<?> future = reconnectExecutorFuture;
- if(future != null && !future.isCancelled()){
- /**
- * Client has been destroyed and
- * scheduled task should be cancelled.
- */
- future.cancel(true);
- }
- return true;
- }
- return false;
- }
- };
-
- reconnectExecutorFuture =
reconnectExecutorService.scheduleWithFixedDelay(connectStatusCheckCommand,
reconnect, reconnect, TimeUnit.MILLISECONDS);
- }
- }
-
- private synchronized void destroyConnectStatusCheckCommand() {
- try {
- if (reconnectExecutorFuture != null &&
!reconnectExecutorFuture.isDone()) {
- reconnectExecutorFuture.cancel(true);
- reconnectExecutorService.purge();
- }
- } catch (Throwable e) {
- logger.warn(e.getMessage(), e);
- }
- }
-
- protected ExecutorService createExecutor() {
- return Executors.newCachedThreadPool(new
NamedThreadFactory(CLIENT_THREAD_POOL_NAME +
CLIENT_THREAD_POOL_ID.incrementAndGet() + "-" + getUrl().getAddress(), true));
- }
-
public InetSocketAddress getConnectAddress() {
return new
InetSocketAddress(NetUtils.filterLocalHost(getUrl().getHost()),
getUrl().getPort());
}
@@ -286,7 +162,7 @@ public abstract class AbstractClient extends
AbstractEndpoint implements Client
@Override
public void send(Object message, boolean sent) throws RemotingException {
- if (send_reconnect && !isConnected()) {
+ if (needReconnect && !isConnected()) {
connect();
}
Channel channel = getChannel();
@@ -303,7 +179,7 @@ public abstract class AbstractClient extends
AbstractEndpoint implements Client
if (isConnected()) {
return;
}
- initConnectStatusCheckCommand();
+
doConnect();
if (!isConnected()) {
throw new RemotingException(this, "Failed connect to server "
+ getRemoteAddress() + " from " + getClass().getSimpleName() + " "
@@ -316,8 +192,6 @@ public abstract class AbstractClient extends
AbstractEndpoint implements Client
+ ", channel is " + this.getChannel());
}
}
- reconnect_count.set(0);
- reconnect_error_log_flag.set(false);
} catch (RemotingException e) {
throw e;
} catch (Throwable e) {
@@ -332,7 +206,6 @@ public abstract class AbstractClient extends
AbstractEndpoint implements Client
public void disconnect() {
connectLock.lock();
try {
- destroyConnectStatusCheckCommand();
try {
Channel channel = getChannel();
if (channel != null) {
diff --git
a/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/exchange/support/header/HeartbeatHandlerTest.java
b/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/exchange/support/header/HeartbeatHandlerTest.java
index 13b1f99..52c3e9d 100644
---
a/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/exchange/support/header/HeartbeatHandlerTest.java
+++
b/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/exchange/support/header/HeartbeatHandlerTest.java
@@ -53,6 +53,9 @@ public class HeartbeatHandlerTest {
server.close();
server = null;
}
+
+ // wait for timer to finish
+ Thread.sleep(2000);
}
@Test
diff --git
a/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/ClientReconnectTest.java
b/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/ClientReconnectTest.java
index 16160b4..4cb7eb6 100644
---
a/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/ClientReconnectTest.java
+++
b/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/ClientReconnectTest.java
@@ -18,7 +18,6 @@ package org.apache.dubbo.remoting.transport.netty;
import org.apache.dubbo.common.Constants;
import org.apache.dubbo.common.utils.DubboAppender;
-import org.apache.dubbo.common.utils.LogUtil;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.remoting.Channel;
import org.apache.dubbo.remoting.Client;
@@ -27,7 +26,6 @@ import org.apache.dubbo.remoting.Server;
import org.apache.dubbo.remoting.exchange.Exchangers;
import org.apache.dubbo.remoting.exchange.support.ExchangeHandlerAdapter;
-import org.apache.log4j.Level;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -36,9 +34,6 @@ import org.junit.jupiter.api.Test;
* Client reconnect test
*/
public class ClientReconnectTest {
- public static void main(String[] args) {
- System.out.println(3 % 1);
- }
@BeforeEach
public void clear() {
@@ -73,31 +68,9 @@ public class ClientReconnectTest {
}
}
- /**
- * Reconnect log check, when the time is not enough for shutdown time,
there is no error log, but there must be a warn log
- */
- @Test
- public void testReconnectWarnLog() throws RemotingException,
InterruptedException {
- int port = NetUtils.getAvailablePort();
- DubboAppender.doStart();
- String url = "exchange://127.0.0.1:" + port +
"/client.reconnect.test?check=false&client=netty3&"
- + Constants.RECONNECT_KEY + "=" + 1; //1ms reconnect, ensure
that there is enough frequency to reconnect
- try {
- Exchangers.connect(url);
- } catch (Exception e) {
-
- //do nothing
- }
- Thread.sleep(1500);
- //Time is not long enough to produce a error log
- Assertions.assertEquals(0, LogUtil.findMessage(Level.ERROR, "client
reconnect to "), "no error message ");
- //The first reconnection failed to have a warn log
- Assertions.assertEquals(1, LogUtil.findMessage(Level.WARN, "client
reconnect to "), "must have one warn message ");
- DubboAppender.doStop();
- }
- public Client startClient(int port, int reconnectPeriod) throws
RemotingException {
- final String url = "exchange://127.0.0.1:" + port +
"/client.reconnect.test?check=false&client=netty3&" + Constants.RECONNECT_KEY +
"=" + reconnectPeriod;
+ public Client startClient(int port, int heartbeat) throws
RemotingException {
+ final String url = "exchange://127.0.0.1:" + port +
"/client.reconnect.test?check=false&client=netty3&" + Constants.HEARTBEAT_KEY +
"=" + heartbeat;
return Exchangers.connect(url);
}
diff --git
a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyClient.java
b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyClient.java
index 4ea6fe1..464087e 100644
---
a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyClient.java
+++
b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyClient.java
@@ -22,6 +22,7 @@ import org.apache.dubbo.common.Version;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.common.utils.UrlUtils;
import org.apache.dubbo.remoting.ChannelHandler;
import org.apache.dubbo.remoting.RemotingException;
import org.apache.dubbo.remoting.transport.AbstractClient;
@@ -34,9 +35,10 @@ import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.concurrent.DefaultThreadFactory;
-import java.util.concurrent.TimeUnit;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
/**
* NettyClient.
@@ -76,10 +78,12 @@ public class NettyClient extends AbstractClient {
@Override
protected void initChannel(Channel ch) throws Exception {
+ int heartbeatInterval = UrlUtils.getIdleTimeout(getUrl());
NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(),
getUrl(), NettyClient.this);
ch.pipeline()//.addLast("logging",new
LoggingHandler(LogLevel.INFO))//for debug
.addLast("decoder", adapter.getDecoder())
.addLast("encoder", adapter.getEncoder())
+ .addLast("client-idle-handler", new
IdleStateHandler(heartbeatInterval, 0, 0, MILLISECONDS))
.addLast("handler", nettyClientHandler);
}
});
@@ -90,7 +94,7 @@ public class NettyClient extends AbstractClient {
long start = System.currentTimeMillis();
ChannelFuture future = bootstrap.connect(getConnectAddress());
try {
- boolean ret = future.awaitUninterruptibly(getConnectTimeout(),
TimeUnit.MILLISECONDS);
+ boolean ret = future.awaitUninterruptibly(getConnectTimeout(),
MILLISECONDS);
if (ret && future.isSuccess()) {
Channel newChannel = future.channel();
@@ -162,4 +166,8 @@ public class NettyClient extends AbstractClient {
return NettyChannel.getOrAddChannel(c, getUrl(), this);
}
+ @Override
+ public boolean canHandleIdle() {
+ return true;
+ }
}
diff --git
a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyClientHandler.java
b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyClientHandler.java
index 265d6ef..c707708 100644
---
a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyClientHandler.java
+++
b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyClientHandler.java
@@ -16,20 +16,26 @@
*/
package org.apache.dubbo.remoting.transport.netty4;
-import io.netty.channel.ChannelDuplexHandler;
-import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelPromise;
import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.Version;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.remoting.ChannelHandler;
import org.apache.dubbo.remoting.exchange.Request;
import org.apache.dubbo.remoting.exchange.Response;
+import io.netty.channel.ChannelDuplexHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelPromise;
+import io.netty.handler.timeout.IdleStateEvent;
+
/**
* NettyClientHandler
*/
@io.netty.channel.ChannelHandler.Sharable
public class NettyClientHandler extends ChannelDuplexHandler {
+ private static final Logger logger =
LoggerFactory.getLogger(NettyClient.class);
private final URL url;
@@ -106,6 +112,27 @@ public class NettyClientHandler extends
ChannelDuplexHandler {
}
@Override
+ public void userEventTriggered(ChannelHandlerContext ctx, Object evt)
throws Exception {
+ if (evt instanceof IdleStateEvent) {
+ try {
+ NettyChannel channel =
NettyChannel.getOrAddChannel(ctx.channel(), url, handler);
+ if (logger.isDebugEnabled()) {
+ logger.debug("IdleStateEvent triggered, send heartbeat to
channel " + channel);
+ }
+ Request req = new Request();
+ req.setVersion(Version.getProtocolVersion());
+ req.setTwoWay(true);
+ req.setEvent(Request.HEARTBEAT_EVENT);
+ channel.send(req);
+ } finally {
+ NettyChannel.removeChannelIfDisconnected(ctx.channel());
+ }
+ } else {
+ super.userEventTriggered(ctx, evt);
+ }
+ }
+
+ @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(),
url, handler);
@@ -129,4 +156,4 @@ public class NettyClientHandler extends
ChannelDuplexHandler {
response.setErrorMessage(StringUtils.toString(t));
return response;
}
-}
\ No newline at end of file
+}
diff --git
a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyServer.java
b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyServer.java
index a8da94e..62b6c55 100644
---
a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyServer.java
+++
b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyServer.java
@@ -22,6 +22,7 @@ import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.ExecutorUtil;
import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.common.utils.UrlUtils;
import org.apache.dubbo.remoting.Channel;
import org.apache.dubbo.remoting.ChannelHandler;
import org.apache.dubbo.remoting.RemotingException;
@@ -38,6 +39,7 @@ import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.concurrent.DefaultThreadFactory;
import java.net.InetSocketAddress;
@@ -45,6 +47,8 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
/**
* NettyServer
*/
@@ -84,10 +88,13 @@ public class NettyServer extends AbstractServer implements
Server {
.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) throws
Exception {
+ // FIXME: should we use getTimeout()?
+ int idleTimeout = UrlUtils.getIdleTimeout(getUrl());
NettyCodecAdapter adapter = new
NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);
ch.pipeline()//.addLast("logging",new
LoggingHandler(LogLevel.INFO))//for debug
.addLast("decoder", adapter.getDecoder())
.addLast("encoder", adapter.getEncoder())
+ .addLast("server-idle-handler", new
IdleStateHandler(0, 0, idleTimeout, MILLISECONDS))
.addLast("handler", nettyServerHandler);
}
});
@@ -158,6 +165,11 @@ public class NettyServer extends AbstractServer implements
Server {
}
@Override
+ public boolean canHandleIdle() {
+ return true;
+ }
+
+ @Override
public boolean isBound() {
return channel.isActive();
}
diff --git
a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyServerHandler.java
b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyServerHandler.java
index 56d4ff6..ea5b324 100644
---
a/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyServerHandler.java
+++
b/dubbo-remoting/dubbo-remoting-netty4/src/main/java/org/apache/dubbo/remoting/transport/netty4/NettyServerHandler.java
@@ -17,6 +17,8 @@
package org.apache.dubbo.remoting.transport.netty4;
import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.remoting.Channel;
import org.apache.dubbo.remoting.ChannelHandler;
@@ -24,6 +26,7 @@ import org.apache.dubbo.remoting.ChannelHandler;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
+import io.netty.handler.timeout.IdleStateEvent;
import java.net.InetSocketAddress;
import java.util.Map;
@@ -34,6 +37,7 @@ import java.util.concurrent.ConcurrentHashMap;
*/
@io.netty.channel.ChannelHandler.Sharable
public class NettyServerHandler extends ChannelDuplexHandler {
+ private static final Logger logger =
LoggerFactory.getLogger(NettyServerHandler.class);
private final Map<String, Channel> channels = new
ConcurrentHashMap<String, Channel>(); // <ip:port, channel>
@@ -103,6 +107,20 @@ public class NettyServerHandler extends
ChannelDuplexHandler {
}
@Override
+ public void userEventTriggered(ChannelHandlerContext ctx, Object evt)
throws Exception {
+ if (evt instanceof IdleStateEvent) {
+ NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(),
url, handler);
+ try {
+ logger.info("IdleStateEvent triggered, close channel " +
channel);
+ channel.close();
+ } finally {
+ NettyChannel.removeChannelIfDisconnected(ctx.channel());
+ }
+ }
+ super.userEventTriggered(ctx, evt);
+ }
+
+ @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(),
url, handler);
@@ -112,4 +130,4 @@ public class NettyServerHandler extends
ChannelDuplexHandler {
NettyChannel.removeChannelIfDisconnected(ctx.channel());
}
}
-}
\ No newline at end of file
+}
diff --git
a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ClientReconnectTest.java
b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ClientReconnectTest.java
index eae6674..f21918d 100644
---
a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ClientReconnectTest.java
+++
b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ClientReconnectTest.java
@@ -71,8 +71,8 @@ public class ClientReconnectTest {
}
- public Client startClient(int port, int reconnectPeriod) throws
RemotingException {
- final String url = "exchange://127.0.0.1:" + port +
"/client.reconnect.test?client=netty4&check=false&" + Constants.RECONNECT_KEY +
"=" + reconnectPeriod;
+ public Client startClient(int port, int heartbeat) throws
RemotingException {
+ final String url = "exchange://127.0.0.1:" + port +
"/client.reconnect.test?client=netty4&check=false&" + Constants.HEARTBEAT_KEY +
"=" + heartbeat;
return Exchangers.connect(url);
}