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) {

Reply via email to