From 509bced309440310a9b8301e1d07ba1312768654 Mon Sep 17 00:00:00 2001 From: Weizhan Yun Date: Sun, 23 Oct 2022 21:19:26 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E7=BA=BF=E7=A8=8B?= =?UTF-8?q?=E4=B8=BA=E5=AE=88=E6=8A=A4=E7=BA=BF=E7=A8=8B=EF=BC=8C=E4=BF=9D?= =?UTF-8?q?=E8=AF=81=E5=9C=A8main=E7=BA=BF=E7=A8=8B=E9=80=80=E5=87=BA?= =?UTF-8?q?=E6=97=B6=E5=BA=94=E7=94=A8=E7=A8=8B=E5=BA=8F=E8=83=BD=E8=BF=87?= =?UTF-8?q?=E6=AD=A3=E5=B8=B8=E5=85=B3=E9=97=AD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/remote/RpcScheduledExecutor.java | 13 ++++- .../http/AbstractHttpClientFactory.java | 52 +++++++++++-------- .../common/http/HttpClientBeanHolder.java | 8 +-- .../common/task/engine/TaskExecuteWorker.java | 8 ++- .../distro/task/DistroTaskEngineHolder.java | 9 +++- .../nacos/istio/common/EventProcessor.java | 40 +++++++------- .../misc/NamingExecuteTaskDispatcher.java | 10 +++- .../nacos/sys/file/WatchFileCenter.java | 22 +++++--- 8 files changed, 105 insertions(+), 57 deletions(-) diff --git a/api/src/main/java/com/alibaba/nacos/api/remote/RpcScheduledExecutor.java b/api/src/main/java/com/alibaba/nacos/api/remote/RpcScheduledExecutor.java index 5fb8dd3b870..05ed29500e4 100644 --- a/api/src/main/java/com/alibaba/nacos/api/remote/RpcScheduledExecutor.java +++ b/api/src/main/java/com/alibaba/nacos/api/remote/RpcScheduledExecutor.java @@ -17,6 +17,8 @@ package com.alibaba.nacos.api.remote; import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicLong; /** * rpc scheduler executor . @@ -33,7 +35,16 @@ public class RpcScheduledExecutor extends ScheduledThreadPoolExecutor { "com.alibaba.nacos.remote.ServerCommonScheduler"); public RpcScheduledExecutor(int corePoolSize, final String threadName) { - super(corePoolSize, r -> new Thread(r, threadName)); + super(corePoolSize, new ThreadFactory() { + private AtomicLong index = new AtomicLong(); + + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r, threadName + "." + index.getAndIncrement()); + thread.setDaemon(true); + return thread; + } + }); } } diff --git a/common/src/main/java/com/alibaba/nacos/common/http/AbstractHttpClientFactory.java b/common/src/main/java/com/alibaba/nacos/common/http/AbstractHttpClientFactory.java index 6ffd23483b2..4aa777c9bcd 100644 --- a/common/src/main/java/com/alibaba/nacos/common/http/AbstractHttpClientFactory.java +++ b/common/src/main/java/com/alibaba/nacos/common/http/AbstractHttpClientFactory.java @@ -16,6 +16,7 @@ package com.alibaba.nacos.common.http; +import com.alibaba.nacos.common.executor.NameThreadFactory; import com.alibaba.nacos.common.http.client.NacosAsyncRestTemplate; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.http.client.request.DefaultAsyncHttpClientRequest; @@ -24,7 +25,9 @@ import com.alibaba.nacos.common.tls.TlsFileWatcher; import com.alibaba.nacos.common.tls.TlsHelper; import com.alibaba.nacos.common.tls.TlsSystemConfig; + import java.util.function.BiConsumer; + import org.apache.http.client.config.RequestConfig; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; @@ -58,6 +61,10 @@ */ public abstract class AbstractHttpClientFactory implements HttpClientFactory { + private static final String ASYNC_THREAD_NAME = "nacos-http-async-client"; + + private static final String AYNC_IO_REACTOR_NAME = ASYNC_THREAD_NAME + "#I/O Reactor"; + @Override public NacosRestTemplate createNacosRestTemplate() { HttpClientConfig httpClientConfig = buildHttpClientConfig(); @@ -75,68 +82,67 @@ public NacosRestTemplate createNacosRestTemplate() { @Override public NacosAsyncRestTemplate createNacosAsyncRestTemplate() { final HttpClientConfig originalRequestConfig = buildHttpClientConfig(); - final DefaultConnectingIOReactor ioreactor = getIoReactor(); + final DefaultConnectingIOReactor ioreactor = getIoReactor(AYNC_IO_REACTOR_NAME); final RequestConfig defaultConfig = getRequestConfig(); return new NacosAsyncRestTemplate(assignLogger(), new DefaultAsyncHttpClientRequest( - HttpAsyncClients.custom() - .addInterceptorLast(new RequestContent(true)) - .setDefaultIOReactorConfig(getIoReactorConfig()) - .setDefaultRequestConfig(defaultConfig) + HttpAsyncClients.custom().addInterceptorLast(new RequestContent(true)) + .setThreadFactory(new NameThreadFactory(ASYNC_THREAD_NAME)) + .setDefaultIOReactorConfig(getIoReactorConfig()).setDefaultRequestConfig(defaultConfig) .setMaxConnTotal(originalRequestConfig.getMaxConnTotal()) .setMaxConnPerRoute(originalRequestConfig.getMaxConnPerRoute()) .setUserAgent(originalRequestConfig.getUserAgent()) - .setConnectionManager(getConnectionManager(originalRequestConfig, ioreactor)) - .build(), ioreactor, defaultConfig)); + .setConnectionManager(getConnectionManager(originalRequestConfig, ioreactor)).build(), + ioreactor, defaultConfig)); } - private DefaultConnectingIOReactor getIoReactor() { + private DefaultConnectingIOReactor getIoReactor(String threadName) { final DefaultConnectingIOReactor ioreactor; try { - ioreactor = new DefaultConnectingIOReactor(getIoReactorConfig()); + ioreactor = new DefaultConnectingIOReactor(getIoReactorConfig(), new NameThreadFactory(threadName)); } catch (IOReactorException e) { assignLogger().error("[NHttpClientConnectionManager] Create DefaultConnectingIOReactor failed", e); throw new IllegalStateException(); } - + // if the handle return true, then the exception thrown by IOReactor will be ignore, and will not finish the IOReactor. ioreactor.setExceptionHandler(new IOReactorExceptionHandler() { - + @Override public boolean handle(IOException ex) { assignLogger().warn("[NHttpClientConnectionManager] handle IOException, ignore it.", ex); return true; } - + @Override public boolean handle(RuntimeException ex) { assignLogger().warn("[NHttpClientConnectionManager] handle RuntimeException, ignore it.", ex); return true; } }); - + return ioreactor; } /** - * create the {@link NHttpClientConnectionManager}, the code mainly from {@link HttpAsyncClientBuilder#build()}. - * we add the {@link IOReactorExceptionHandler} to handle the {@link IOException} and {@link RuntimeException} - * thrown by the {@link org.apache.http.impl.nio.reactor.BaseIOReactor} when process the event of Network. - * Using this way to avoid the {@link DefaultConnectingIOReactor} killed by unknown error of network. + * create the {@link NHttpClientConnectionManager}, the code mainly from {@link HttpAsyncClientBuilder#build()}. we + * add the {@link IOReactorExceptionHandler} to handle the {@link IOException} and {@link RuntimeException} thrown + * by the {@link org.apache.http.impl.nio.reactor.BaseIOReactor} when process the event of Network. Using this way + * to avoid the {@link DefaultConnectingIOReactor} killed by unknown error of network. * * @param originalRequestConfig request config. - * @param ioreactor I/O reactor. + * @param ioreactor I/O reactor. * @return {@link NHttpClientConnectionManager}. */ - private NHttpClientConnectionManager getConnectionManager(HttpClientConfig originalRequestConfig, DefaultConnectingIOReactor ioreactor) { + private NHttpClientConnectionManager getConnectionManager(HttpClientConfig originalRequestConfig, + DefaultConnectingIOReactor ioreactor) { SSLContext sslcontext = SSLContexts.createDefault(); HostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(); SchemeIOSessionStrategy sslStrategy = new SSLIOSessionStrategy(sslcontext, null, null, hostnameVerifier); Registry registry = RegistryBuilder.create() - .register("http", NoopIOSessionStrategy.INSTANCE) - .register("https", sslStrategy) - .build(); - final PoolingNHttpClientConnectionManager poolingmgr = new PoolingNHttpClientConnectionManager(ioreactor, registry); + .register("http", NoopIOSessionStrategy.INSTANCE).register("https", sslStrategy).build(); + final PoolingNHttpClientConnectionManager poolingmgr = new PoolingNHttpClientConnectionManager(ioreactor, + registry); int maxTotal = originalRequestConfig.getMaxConnTotal(); if (maxTotal > 0) { diff --git a/common/src/main/java/com/alibaba/nacos/common/http/HttpClientBeanHolder.java b/common/src/main/java/com/alibaba/nacos/common/http/HttpClientBeanHolder.java index 873df0fb878..964fa04a2ee 100644 --- a/common/src/main/java/com/alibaba/nacos/common/http/HttpClientBeanHolder.java +++ b/common/src/main/java/com/alibaba/nacos/common/http/HttpClientBeanHolder.java @@ -38,8 +38,7 @@ public final class HttpClientBeanHolder { private static final Map SINGLETON_REST = new HashMap<>(10); - private static final Map SINGLETON_ASYNC_REST = new HashMap<>( - 10); + private static final Map SINGLETON_ASYNC_REST = new HashMap<>(10); private static final AtomicBoolean ALREADY_SHUTDOWN = new AtomicBoolean(false); @@ -101,11 +100,14 @@ private static void shutdown() { return; } LOGGER.warn("[HttpClientBeanHolder] Start destroying common HttpClient"); + try { shutdown(DefaultHttpClientFactory.class.getName()); } catch (Exception ex) { - LOGGER.error("An exception occurred when the common HTTP client was closed : {}", ExceptionUtil.getStackTrace(ex)); + LOGGER.error("An exception occurred when the common HTTP client was closed : {}", + ExceptionUtil.getStackTrace(ex)); } + LOGGER.warn("[HttpClientBeanHolder] Destruction of the end"); } diff --git a/common/src/main/java/com/alibaba/nacos/common/task/engine/TaskExecuteWorker.java b/common/src/main/java/com/alibaba/nacos/common/task/engine/TaskExecuteWorker.java index f220d753407..32f1a139f97 100644 --- a/common/src/main/java/com/alibaba/nacos/common/task/engine/TaskExecuteWorker.java +++ b/common/src/main/java/com/alibaba/nacos/common/task/engine/TaskExecuteWorker.java @@ -48,6 +48,8 @@ public final class TaskExecuteWorker implements NacosTaskProcessor, Closeable { private final AtomicBoolean closed; + private final InnerWorker realWorker; + public TaskExecuteWorker(final String name, final int mod, final int total) { this(name, mod, total, null); } @@ -57,7 +59,8 @@ public TaskExecuteWorker(final String name, final int mod, final int total, fina this.queue = new ArrayBlockingQueue<>(QUEUE_CAPACITY); this.closed = new AtomicBoolean(false); this.log = null == logger ? LoggerFactory.getLogger(TaskExecuteWorker.class) : logger; - new InnerWorker(name).start(); + realWorker = new InnerWorker(this.name); + realWorker.start(); } public String getName() { @@ -95,6 +98,7 @@ public String status() { public void shutdown() throws NacosException { queue.clear(); closed.compareAndSet(false, true); + realWorker.interrupt(); } /** @@ -119,7 +123,7 @@ public void run() { log.warn("task {} takes {}ms", task, duration); } } catch (Throwable e) { - log.error("[TASK-FAILED] " + e.toString(), e); + log.error("[TASK-FAILED] " + e, e); } } } diff --git a/core/src/main/java/com/alibaba/nacos/core/distributed/distro/task/DistroTaskEngineHolder.java b/core/src/main/java/com/alibaba/nacos/core/distributed/distro/task/DistroTaskEngineHolder.java index 8cfe9ea03d5..59ce106afa2 100644 --- a/core/src/main/java/com/alibaba/nacos/core/distributed/distro/task/DistroTaskEngineHolder.java +++ b/core/src/main/java/com/alibaba/nacos/core/distributed/distro/task/DistroTaskEngineHolder.java @@ -21,6 +21,7 @@ import com.alibaba.nacos.core.distributed.distro.task.delay.DistroDelayTaskExecuteEngine; import com.alibaba.nacos.core.distributed.distro.task.delay.DistroDelayTaskProcessor; import com.alibaba.nacos.core.distributed.distro.task.execute.DistroExecuteTaskExecuteEngine; +import org.springframework.beans.factory.DisposableBean; import org.springframework.stereotype.Component; /** @@ -29,7 +30,7 @@ * @author xiweng.yy */ @Component -public class DistroTaskEngineHolder { +public class DistroTaskEngineHolder implements DisposableBean { private final DistroDelayTaskExecuteEngine delayTaskExecuteEngine = new DistroDelayTaskExecuteEngine(); @@ -51,4 +52,10 @@ public DistroExecuteTaskExecuteEngine getExecuteWorkersManager() { public void registerNacosTaskProcessor(Object key, NacosTaskProcessor nacosTaskProcessor) { this.delayTaskExecuteEngine.addProcessor(key, nacosTaskProcessor); } + + @Override + public void destroy() throws Exception { + this.delayTaskExecuteEngine.shutdown(); + this.executeWorkersManager.shutdown(); + } } diff --git a/istio/src/main/java/com/alibaba/nacos/istio/common/EventProcessor.java b/istio/src/main/java/com/alibaba/nacos/istio/common/EventProcessor.java index b7b7222b73d..5f75abdd56f 100644 --- a/istio/src/main/java/com/alibaba/nacos/istio/common/EventProcessor.java +++ b/istio/src/main/java/com/alibaba/nacos/istio/common/EventProcessor.java @@ -39,17 +39,17 @@ */ @Component public class EventProcessor implements ApplicationListener { - + private static final int MAX_WAIT_EVENT_TIME = 100; - + private NacosMcpService nacosMcpService; - + private NacosXdsService nacosXdsService; - + private NacosResourceManager resourceManager; - + private final BlockingQueue events; - + public EventProcessor() { events = new ArrayBlockingQueue<>(20); } @@ -68,11 +68,13 @@ public void notify(Event event) { Thread.currentThread().interrupt(); } } - - public void handleEvents() { - new Consumer("handle events").start(); + + private void handleEvents() { + Consumer handleEvents = new Consumer("handle events"); + handleEvents.setDaemon(true); + handleEvents.start(); } - + @Override public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) { if (contextRefreshedEvent.getApplicationContext().getParent() == null) { @@ -80,13 +82,13 @@ public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) { handleEvents(); } } - + private class Consumer extends Thread { - + Consumer(String name) { setName(name); } - + @Override @SuppressWarnings("InfiniteLoopStatement") public void run() { @@ -115,23 +117,23 @@ public void run() { } } } - + private boolean hasClientConnection() { return nacosMcpService.hasClientConnection() || nacosXdsService.hasClientConnection(); } - + private boolean needNewTask(boolean hasNewEvent, Future task) { return hasNewEvent && (task == null || task.isDone()); } - + private class EventHandleTask implements Callable { - + private final Event event; - + EventHandleTask(Event event) { this.event = event; } - + @Override public Void call() throws Exception { ResourceSnapshot snapshot = resourceManager.createResourceSnapshot(); diff --git a/naming/src/main/java/com/alibaba/nacos/naming/misc/NamingExecuteTaskDispatcher.java b/naming/src/main/java/com/alibaba/nacos/naming/misc/NamingExecuteTaskDispatcher.java index 3dbf24ed3d0..f74dc0b5e20 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/misc/NamingExecuteTaskDispatcher.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/misc/NamingExecuteTaskDispatcher.java @@ -19,13 +19,16 @@ import com.alibaba.nacos.common.task.AbstractExecuteTask; import com.alibaba.nacos.common.task.engine.NacosExecuteTaskExecuteEngine; import com.alibaba.nacos.sys.env.EnvUtil; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.stereotype.Component; /** * Naming execute task dispatcher. * * @author xiweng.yy */ -public class NamingExecuteTaskDispatcher { +@Component +public class NamingExecuteTaskDispatcher implements DisposableBean { private static final NamingExecuteTaskDispatcher INSTANCE = new NamingExecuteTaskDispatcher(); @@ -46,4 +49,9 @@ public void dispatchAndExecuteTask(Object dispatchTag, AbstractExecuteTask task) public String workersStatus() { return executeEngine.workersStatus(); } + + @Override + public void destroy() throws Exception { + executeEngine.shutdown(); + } } diff --git a/sys/src/main/java/com/alibaba/nacos/sys/file/WatchFileCenter.java b/sys/src/main/java/com/alibaba/nacos/sys/file/WatchFileCenter.java index 71d6e1c58fa..0c3bb88c455 100644 --- a/sys/src/main/java/com/alibaba/nacos/sys/file/WatchFileCenter.java +++ b/sys/src/main/java/com/alibaba/nacos/sys/file/WatchFileCenter.java @@ -25,6 +25,7 @@ import org.slf4j.LoggerFactory; import java.io.File; +import java.io.IOException; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Path; @@ -58,7 +59,7 @@ public class WatchFileCenter { */ private static final int MAX_WATCH_FILE_JOB = Integer.getInteger("nacos.watch-file.max-dirs", 16); - private static final Map MANAGER = new HashMap(MAX_WATCH_FILE_JOB); + private static final Map MANAGER = new HashMap<>(MAX_WATCH_FILE_JOB); private static final FileSystem FILE_SYSTEM = FileSystems.getDefault(); @@ -154,15 +155,15 @@ public static synchronized boolean deregisterWatcher(final String path, final Fi private static class WatchDirJob extends Thread { - private ExecutorService callBackExecutor; + private final ExecutorService callBackExecutor; private final String paths; - private WatchService watchService; + private final WatchService watchService; private volatile boolean watch = true; - private Set watchers = new ConcurrentHashSet<>(); + private final Set watchers = new ConcurrentHashSet<>(); public WatchDirJob(String paths) throws NacosException { setName(paths); @@ -172,8 +173,8 @@ public WatchDirJob(String paths) throws NacosException { throw new IllegalArgumentException("Must be a file directory : " + paths); } - this.callBackExecutor = ExecutorFactory - .newSingleExecutorService(new NameThreadFactory("com.alibaba.nacos.sys.file.watch-" + paths)); + this.callBackExecutor = ExecutorFactory.newSingleExecutorService( + new NameThreadFactory("com.alibaba.nacos.sys.file.watch-" + paths)); try { WatchService service = FILE_SYSTEM.newWatchService(); @@ -191,6 +192,13 @@ void addSubscribe(final FileWatcher watcher) { void shutdown() { watch = false; + + //fix issue[https://github.com/alibaba/nacos/issues/9393] + try { + watchService.close(); + } catch (IOException ignore) { + } + ThreadUtils.shutdownThreadPool(this.callBackExecutor); } @@ -210,7 +218,7 @@ public void run() { callBackExecutor.execute(() -> { for (WatchEvent event : events) { WatchEvent.Kind kind = event.kind(); - + // Since the OS's event cache may be overflow, a backstop is needed if (StandardWatchEventKinds.OVERFLOW.equals(kind)) { eventOverflow(); From c8f03a38e98a622312286293bf754d9ae452e1f9 Mon Sep 17 00:00:00 2001 From: Weizhan Yun Date: Wed, 2 Nov 2022 15:19:29 +0800 Subject: [PATCH 2/2] graceful shutdown naming executetask engine. --- .../naming/misc/GracefulShutdownListener.java | 67 +++++++++++++++++++ .../misc/NamingExecuteTaskDispatcher.java | 6 +- ...cos.core.listener.NacosApplicationListener | 17 +++++ 3 files changed, 85 insertions(+), 5 deletions(-) create mode 100644 naming/src/main/java/com/alibaba/nacos/naming/misc/GracefulShutdownListener.java create mode 100644 naming/src/main/resources/META-INF/services/com.alibaba.nacos.core.listener.NacosApplicationListener diff --git a/naming/src/main/java/com/alibaba/nacos/naming/misc/GracefulShutdownListener.java b/naming/src/main/java/com/alibaba/nacos/naming/misc/GracefulShutdownListener.java new file mode 100644 index 00000000000..667bbb1dcbe --- /dev/null +++ b/naming/src/main/java/com/alibaba/nacos/naming/misc/GracefulShutdownListener.java @@ -0,0 +1,67 @@ +/* + * Copyright 1999-2022 Alibaba Group Holding Ltd. + * + * Licensed 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 com.alibaba.nacos.naming.misc; + +import com.alibaba.nacos.core.listener.NacosApplicationListener; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.env.ConfigurableEnvironment; + +/** + * graceful shutdown listenner. + * @author Weizhanâ–ªYun + * @date 2022/11/2 14:40 + */ +public class GracefulShutdownListener implements NacosApplicationListener { + + @Override + public void starting() { + + } + + @Override + public void environmentPrepared(ConfigurableEnvironment environment) { + + } + + @Override + public void contextPrepared(ConfigurableApplicationContext context) { + + } + + @Override + public void contextLoaded(ConfigurableApplicationContext context) { + + } + + @Override + public void started(ConfigurableApplicationContext context) { + + } + + @Override + public void running(ConfigurableApplicationContext context) { + + } + + @Override + public void failed(ConfigurableApplicationContext context, Throwable exception) { + try { + NamingExecuteTaskDispatcher.getInstance().destroy(); + } catch (Exception ignore) { + } + } +} diff --git a/naming/src/main/java/com/alibaba/nacos/naming/misc/NamingExecuteTaskDispatcher.java b/naming/src/main/java/com/alibaba/nacos/naming/misc/NamingExecuteTaskDispatcher.java index f74dc0b5e20..7287cf7843e 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/misc/NamingExecuteTaskDispatcher.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/misc/NamingExecuteTaskDispatcher.java @@ -19,16 +19,13 @@ import com.alibaba.nacos.common.task.AbstractExecuteTask; import com.alibaba.nacos.common.task.engine.NacosExecuteTaskExecuteEngine; import com.alibaba.nacos.sys.env.EnvUtil; -import org.springframework.beans.factory.DisposableBean; -import org.springframework.stereotype.Component; /** * Naming execute task dispatcher. * * @author xiweng.yy */ -@Component -public class NamingExecuteTaskDispatcher implements DisposableBean { +public class NamingExecuteTaskDispatcher { private static final NamingExecuteTaskDispatcher INSTANCE = new NamingExecuteTaskDispatcher(); @@ -50,7 +47,6 @@ public String workersStatus() { return executeEngine.workersStatus(); } - @Override public void destroy() throws Exception { executeEngine.shutdown(); } diff --git a/naming/src/main/resources/META-INF/services/com.alibaba.nacos.core.listener.NacosApplicationListener b/naming/src/main/resources/META-INF/services/com.alibaba.nacos.core.listener.NacosApplicationListener new file mode 100644 index 00000000000..8a971ec4759 --- /dev/null +++ b/naming/src/main/resources/META-INF/services/com.alibaba.nacos.core.listener.NacosApplicationListener @@ -0,0 +1,17 @@ +# +# Copyright 1999-2022 Alibaba Group Holding Ltd. +# +# Licensed 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. +# + +com.alibaba.nacos.naming.misc.GracefulShutdownListener \ No newline at end of file