This is an automated email from the ASF dual-hosted git repository.
zhangzicheng pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git
The following commit(s) were added to refs/heads/master by this push:
new 78dad37cf issues #4686: nacos client offline (#4890)
78dad37cf is described below
commit 78dad37cfbb0f30f7182dfefe441cd70cd706057
Author: zhengpeng <[email protected]>
AuthorDate: Mon Jul 24 22:06:06 2023 +0800
issues #4686: nacos client offline (#4890)
Co-authored-by: xiaoyu <[email protected]>
Co-authored-by: dragon-zhang <[email protected]>
---
.../ShenyuClientURIExecutorSubscriber.java | 6 +-
.../core/shutdown/ShenyuClientShutdownHook.java | 4 +-
.../client/core/shutdown/ShutdownHookManager.java | 197 +++++++++++++++++++++
.../nacos/NacosClientRegisterRepository.java | 35 +++-
4 files changed, 236 insertions(+), 6 deletions(-)
diff --git
a/shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/disruptor/subcriber/ShenyuClientURIExecutorSubscriber.java
b/shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/disruptor/subcriber/ShenyuClientURIExecutorSubscriber.java
index 57bb1b044..e7b5eb5f8 100644
---
a/shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/disruptor/subcriber/ShenyuClientURIExecutorSubscriber.java
+++
b/shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/disruptor/subcriber/ShenyuClientURIExecutorSubscriber.java
@@ -18,6 +18,7 @@
package org.apache.shenyu.client.core.disruptor.subcriber;
import com.google.common.base.Stopwatch;
+import org.apache.shenyu.client.core.shutdown.ShutdownHookManager;
import org.apache.shenyu.client.core.shutdown.ShenyuClientShutdownHook;
import org.apache.shenyu.register.client.api.ShenyuClientRegisterRepository;
import org.apache.shenyu.register.common.dto.URIRegisterDTO;
@@ -83,13 +84,12 @@ public class ShenyuClientURIExecutorSubscriber implements
ExecutorTypeSubscriber
}
ShenyuClientShutdownHook.delayOtherHooks();
shenyuClientRegisterRepository.persistURI(uriRegisterDTO);
- //active offline when shutdown, not now
- Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+ ShutdownHookManager.get().addShutdownHook(new Thread(() -> {
final URIRegisterDTO offlineDTO = new URIRegisterDTO();
BeanUtils.copyProperties(uriRegisterDTO, offlineDTO);
offlineDTO.setEventType(EventType.OFFLINE);
shenyuClientRegisterRepository.offline(offlineDTO);
- }));
+ }), 2);
}
}
}
diff --git
a/shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/shutdown/ShenyuClientShutdownHook.java
b/shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/shutdown/ShenyuClientShutdownHook.java
index 94ed5aed6..e784f6a87 100644
---
a/shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/shutdown/ShenyuClientShutdownHook.java
+++
b/shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/shutdown/ShenyuClientShutdownHook.java
@@ -55,7 +55,7 @@ public class ShenyuClientShutdownHook {
public ShenyuClientShutdownHook(final ShenyuClientRegisterRepository
repository, final ShenyuRegisterCenterConfig config) {
String name = String.join("-", hookNamePrefix,
String.valueOf(hookId.incrementAndGet()));
- Runtime.getRuntime().addShutdownHook(new
Thread(repository::closeRepository, name));
+ ShutdownHookManager.get().addShutdownHook(new
Thread(repository::closeRepository, name), 1);
LOG.info("Add hook {}", name);
ShenyuClientShutdownHook.props = config.getProps();
}
@@ -68,7 +68,7 @@ public class ShenyuClientShutdownHook {
*/
public static void set(final ShenyuClientRegisterRepository repository,
final Properties props) {
String name = String.join("-", hookNamePrefix,
String.valueOf(hookId.incrementAndGet()));
- Runtime.getRuntime().addShutdownHook(new
Thread(repository::closeRepository, name));
+ ShutdownHookManager.get().addShutdownHook(new
Thread(repository::closeRepository, name), 1);
LOG.info("Add hook {}", name);
ShenyuClientShutdownHook.props = props;
}
diff --git
a/shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/shutdown/ShutdownHookManager.java
b/shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/shutdown/ShutdownHookManager.java
new file mode 100644
index 000000000..e8aef67f0
--- /dev/null
+++
b/shenyu-client/shenyu-client-core/src/main/java/org/apache/shenyu/client/core/shutdown/ShutdownHookManager.java
@@ -0,0 +1,197 @@
+/*
+ * 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.shenyu.client.core.shutdown;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * HookManager.
+ */
+public final class ShutdownHookManager {
+
+ private static final ShutdownHookManager MGR = new ShutdownHookManager();
+
+ private static final Logger LOG =
LoggerFactory.getLogger(ShutdownHookManager.class);
+
+ private Set<HookEntry> hooks =
+ Collections.synchronizedSet(new HashSet<HookEntry>());
+
+ private AtomicBoolean shutdownInProgress = new AtomicBoolean(false);
+
+ private ShutdownHookManager() {
+
+ }
+
+ static {
+ Runtime.getRuntime().addShutdownHook(
+ new Thread(() -> {
+ MGR.shutdownInProgress.set(true);
+ for (Runnable hook : MGR.getShutdownHooksInOrder()) {
+ try {
+ hook.run();
+ } catch (Throwable ex) {
+ LOG.error(ex.getMessage(), ex);
+ }
+ }
+ })
+ );
+ }
+
+ /**
+ * Return <code>ShutdownHookManager</code> singleton.
+ *
+ * @return <code>ShutdownHookManager</code> singleton.
+ */
+ public static ShutdownHookManager get() {
+ return MGR;
+ }
+
+ /**
+ * Returns the list of shutdownHooks in order of execution,
+ * Highest priority first.
+ *
+ * @return the list of shutdownHooks in order of execution.
+ */
+ List<Runnable> getShutdownHooksInOrder() {
+ List<HookEntry> list;
+ synchronized (MGR.hooks) {
+ list = new ArrayList<HookEntry>(MGR.hooks);
+ }
+ Collections.sort(list, (o1, o2) -> o2.priority - o1.priority);
+ List<Runnable> ordered = new ArrayList<Runnable>();
+ for (HookEntry entry : list) {
+ ordered.add(entry.hook);
+ }
+ return ordered;
+ }
+
+ /**
+ * Adds a shutdownHook with default priority zero, the higher the priority
+ * the earlier will run. ShutdownHooks with same priority run
+ * in a non-deterministic order.
+ *
+ * @param shutdownHook shutdownHook <code>Runnable</code>
+ */
+ public void addShutdownHook(final Runnable shutdownHook) {
+ if (shutdownHook == null) {
+ throw new IllegalArgumentException("shutdownHook cannot be NULL");
+ }
+ if (shutdownInProgress.get()) {
+ throw new IllegalStateException("Shutdown in progress, cannot add
a shutdownHook");
+ }
+ hooks.add(new HookEntry(shutdownHook, 0));
+ }
+
+ /**
+ * Adds a shutdownHook with a priority, the higher the priority
+ * the earlier will run. ShutdownHooks with same priority run
+ * in a non-deterministic order.
+ *
+ * @param shutdownHook shutdownHook <code>Runnable</code>
+ * @param priority priority of the shutdownHook.
+ */
+ public void addShutdownHook(final Runnable shutdownHook, final int
priority) {
+ if (shutdownHook == null) {
+ throw new IllegalArgumentException("shutdownHook cannot be NULL");
+ }
+ if (shutdownInProgress.get()) {
+ throw new IllegalStateException("Shutdown in progress, cannot add
a shutdownHook");
+ }
+ hooks.add(new HookEntry(shutdownHook, priority));
+ }
+
+ /**
+ * Removes a shutdownHook.
+ * @param shutdownHook shutdownHook to remove.
+ * @return TRUE if the shutdownHook was registered and removed,FALSE
otherwise.
+ */
+ public boolean removeShutdownHook(final Runnable shutdownHook) {
+ if (shutdownInProgress.get()) {
+ throw new IllegalStateException("Shutdown in progress, cannot
remove a shutdownHook");
+ }
+ return hooks.remove(new HookEntry(shutdownHook, 0));
+ }
+
+ /**
+ * Indicates if a shutdownHook is registered or not.
+ *
+ * @param shutdownHook shutdownHook to check if registered.
+ * @return TRUE/FALSE depending if the shutdownHook is is registered.
+ */
+ public boolean hasShutdownHook(final Runnable shutdownHook) {
+ return hooks.contains(new HookEntry(shutdownHook, 0));
+ }
+
+ /**
+ * Indicates if shutdown is in progress or not.
+ *
+ * @return TRUE if the shutdown is in progress, otherwise FALSE.
+ */
+ public boolean isShutdownInProgress() {
+ return shutdownInProgress.get();
+ }
+
+ /**
+ * clear all registered shutdownHooks.
+ */
+ public void clearShutdownHooks() {
+ hooks.clear();
+ }
+
+ /**
+ * Private structure to store ShutdownHook and its priority.
+ */
+ private static class HookEntry {
+
+ private Runnable hook;
+
+ private int priority;
+
+ HookEntry(final Runnable hook, final int priority) {
+ this.hook = hook;
+ this.priority = priority;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ HookEntry hookEntry = (HookEntry) o;
+ return priority == hookEntry.priority && Objects.equals(hook,
hookEntry.hook);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(hook, priority);
+ }
+ }
+
+}
diff --git
a/shenyu-register-center/shenyu-register-client/shenyu-register-client-nacos/src/main/java/org/apache/shenyu/register/client/nacos/NacosClientRegisterRepository.java
b/shenyu-register-center/shenyu-register-client/shenyu-register-client-nacos/src/main/java/org/apache/shenyu/register/client/nacos/NacosClientRegisterRepository.java
index f4d9f81da..0ec6b7a90 100644
---
a/shenyu-register-center/shenyu-register-client/shenyu-register-client-nacos/src/main/java/org/apache/shenyu/register/client/nacos/NacosClientRegisterRepository.java
+++
b/shenyu-register-center/shenyu-register-client/shenyu-register-client-nacos/src/main/java/org/apache/shenyu/register/client/nacos/NacosClientRegisterRepository.java
@@ -34,6 +34,7 @@ import org.apache.shenyu.common.constant.NacosPathConstants;
import org.apache.shenyu.common.exception.ShenyuException;
import org.apache.shenyu.common.utils.ContextPathUtils;
import org.apache.shenyu.common.utils.GsonUtils;
+import org.apache.shenyu.common.utils.LogUtils;
import org.apache.shenyu.register.client.api.ShenyuClientRegisterRepository;
import org.apache.shenyu.register.common.config.ShenyuRegisterCenterConfig;
import org.apache.shenyu.register.common.dto.MetaDataRegisterDTO;
@@ -91,6 +92,16 @@ public class NacosClientRegisterRepository implements
ShenyuClientRegisterReposi
}
}
+ @Override
+ public void offline(final URIRegisterDTO offlineDTO) {
+ String rpcType = offlineDTO.getRpcType();
+ String contextPath =
ContextPathUtils.buildRealNode(offlineDTO.getContextPath(),
offlineDTO.getAppName());
+ String host = offlineDTO.getHost();
+ int port = offlineDTO.getPort();
+ unRegisterService(rpcType, contextPath, host, port, offlineDTO);
+ LogUtils.info(LOGGER, "{} nacos client unRegister uri success: {}",
rpcType, offlineDTO);
+ }
+
@Override
public void closeRepository() {
try {
@@ -135,7 +146,6 @@ public class NacosClientRegisterRepository implements
ShenyuClientRegisterReposi
metadataMap.put(Constants.CONTEXT_PATH, contextPath);
metadataMap.put(URI_META_DATA,
GsonUtils.getInstance().toJson(registerDTO));
instance.setMetadata(metadataMap);
-
String serviceName =
RegisterPathConstants.buildServiceInstancePath(rpcType);
try {
namingService.registerInstance(serviceName, instance);
@@ -145,6 +155,29 @@ public class NacosClientRegisterRepository implements
ShenyuClientRegisterReposi
LOGGER.info("register service uri success: {}", serviceName);
}
+ private synchronized void unRegisterService(final String rpcType,
+ final String contextPath,
+ final String host,
+ final int port,
+ final URIRegisterDTO
offlineDTO) {
+ Instance instance = new Instance();
+ instance.setEphemeral(true);
+ instance.setIp(host);
+ instance.setPort(port);
+ Map<String, String> metadataMap = new HashMap<>();
+ metadataMap.put(Constants.CONTEXT_PATH, contextPath);
+ metadataMap.put(URI_META_DATA,
GsonUtils.getInstance().toJson(offlineDTO));
+ instance.setMetadata(metadataMap);
+ String serviceName =
RegisterPathConstants.buildServiceInstancePath(rpcType);
+ try {
+ LOGGER.info("unRegisterService instance:{}", instance);
+ namingService.deregisterInstance(serviceName, instance);
+ } catch (NacosException e) {
+ throw new ShenyuException(e);
+ }
+ LOGGER.info("unregister service uri success: {}", serviceName);
+ }
+
private synchronized void registerConfig(final String rpcType,
final String contextPath,
final MetaDataRegisterDTO
metadata) {