diff --git a/epmet-admin/epmet-admin-server/pom.xml b/epmet-admin/epmet-admin-server/pom.xml index 1bd83b7e49..cc3884aa4e 100644 --- a/epmet-admin/epmet-admin-server/pom.xml +++ b/epmet-admin/epmet-admin-server/pom.xml @@ -132,6 +132,8 @@ false + + true false @@ -170,6 +172,9 @@ false + + false + false @@ -203,6 +208,8 @@ false + + true true @@ -238,6 +245,9 @@ false + + false + true diff --git a/epmet-admin/epmet-admin-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java b/epmet-admin/epmet-admin-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..7d9a1fef05 --- /dev/null +++ b/epmet-admin/epmet-admin-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-admin/epmet-admin-server/src/main/resources/bootstrap.yml b/epmet-admin/epmet-admin-server/src/main/resources/bootstrap.yml index 74c6bc2567..fc45940737 100644 --- a/epmet-admin/epmet-admin-server/src/main/resources/bootstrap.yml +++ b/epmet-admin/epmet-admin-server/src/main/resources/bootstrap.yml @@ -61,6 +61,8 @@ spring: #不把自己注册到注册中心的地址 register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@ diff --git a/epmet-auth/pom.xml b/epmet-auth/pom.xml index 08b0f4b480..496d5901fb 100644 --- a/epmet-auth/pom.xml +++ b/epmet-auth/pom.xml @@ -183,6 +183,8 @@ false + + true wxcb6ce2ed0c5ae54c @@ -238,6 +240,8 @@ false + + false wxcb6ce2ed0c5ae54c @@ -290,6 +294,8 @@ false + + true wxcb6ce2ed0c5ae54c @@ -345,6 +351,9 @@ false + + false + wxcb6ce2ed0c5ae54c c7f74941ee97fa9b2e1065772d34c397 diff --git a/epmet-auth/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java b/epmet-auth/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..7d9a1fef05 --- /dev/null +++ b/epmet-auth/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-gateway/pom.xml b/epmet-gateway/pom.xml index 42e93411bf..e09410668d 100644 --- a/epmet-gateway/pom.xml +++ b/epmet-gateway/pom.xml @@ -124,6 +124,9 @@ false + + true + @@ -255,6 +258,9 @@ false + + + false @@ -389,6 +395,9 @@ false + + true + lb://epmet-auth-server @@ -488,6 +497,10 @@ false + + + false + lb://epmet-auth-server diff --git a/epmet-gateway/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java b/epmet-gateway/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..7d9a1fef05 --- /dev/null +++ b/epmet-gateway/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-gateway/src/main/resources/bootstrap.yml b/epmet-gateway/src/main/resources/bootstrap.yml index ca78ab64aa..a5276aa300 100644 --- a/epmet-gateway/src/main/resources/bootstrap.yml +++ b/epmet-gateway/src/main/resources/bootstrap.yml @@ -358,6 +358,8 @@ spring: #不把自己注册到注册中心的地址 register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@ diff --git a/epmet-module/data-aggregator/data-aggregator-server/pom.xml b/epmet-module/data-aggregator/data-aggregator-server/pom.xml index b9835924ff..64f67470f9 100644 --- a/epmet-module/data-aggregator/data-aggregator-server/pom.xml +++ b/epmet-module/data-aggregator/data-aggregator-server/pom.xml @@ -221,6 +221,8 @@ false + + true false @@ -335,6 +337,8 @@ false + + false false @@ -449,6 +453,8 @@ false + + true true @@ -564,6 +570,9 @@ false + + false + true diff --git a/epmet-module/data-aggregator/data-aggregator-server/src/main/java/com/epmet/dataaggre/config/NacosServiceListListenerRegisterer.java b/epmet-module/data-aggregator/data-aggregator-server/src/main/java/com/epmet/dataaggre/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..a97d477908 --- /dev/null +++ b/epmet-module/data-aggregator/data-aggregator-server/src/main/java/com/epmet/dataaggre/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.dataaggre.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-module/data-aggregator/data-aggregator-server/src/main/resources/bootstrap.yml b/epmet-module/data-aggregator/data-aggregator-server/src/main/resources/bootstrap.yml index 102666f3d7..934dcae2c5 100644 --- a/epmet-module/data-aggregator/data-aggregator-server/src/main/resources/bootstrap.yml +++ b/epmet-module/data-aggregator/data-aggregator-server/src/main/resources/bootstrap.yml @@ -37,6 +37,8 @@ spring: #不把自己注册到注册中心的地址 register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@ diff --git a/epmet-module/data-report/data-report-server/pom.xml b/epmet-module/data-report/data-report-server/pom.xml index f1ab408d0a..43c7bffcae 100644 --- a/epmet-module/data-report/data-report-server/pom.xml +++ b/epmet-module/data-report/data-report-server/pom.xml @@ -167,6 +167,8 @@ false + + true false @@ -219,6 +221,8 @@ false + + false false @@ -271,6 +275,8 @@ false + + true true @@ -323,6 +329,9 @@ false + + false + true diff --git a/epmet-module/data-report/data-report-server/src/main/java/com/epmet/datareport/config/NacosServiceListListenerRegisterer.java b/epmet-module/data-report/data-report-server/src/main/java/com/epmet/datareport/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..37ad4c96f7 --- /dev/null +++ b/epmet-module/data-report/data-report-server/src/main/java/com/epmet/datareport/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.datareport.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-module/data-report/data-report-server/src/main/resources/bootstrap.yml b/epmet-module/data-report/data-report-server/src/main/resources/bootstrap.yml index 3ecb0ec3d1..2ee366e369 100644 --- a/epmet-module/data-report/data-report-server/src/main/resources/bootstrap.yml +++ b/epmet-module/data-report/data-report-server/src/main/resources/bootstrap.yml @@ -37,6 +37,8 @@ spring: #不把自己注册到注册中心的地址 register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@ diff --git a/epmet-module/data-statistical/data-statistical-server/pom.xml b/epmet-module/data-statistical/data-statistical-server/pom.xml index 97f6c5eb85..714dfa2c01 100644 --- a/epmet-module/data-statistical/data-statistical-server/pom.xml +++ b/epmet-module/data-statistical/data-statistical-server/pom.xml @@ -241,6 +241,8 @@ false + + true false @@ -361,6 +363,8 @@ false + + false false @@ -481,6 +485,8 @@ false + + true false @@ -597,6 +603,8 @@ false + + false false diff --git a/epmet-module/data-statistical/data-statistical-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java b/epmet-module/data-statistical/data-statistical-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..7d9a1fef05 --- /dev/null +++ b/epmet-module/data-statistical/data-statistical-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-module/data-statistical/data-statistical-server/src/main/resources/bootstrap.yml b/epmet-module/data-statistical/data-statistical-server/src/main/resources/bootstrap.yml index 7723414608..1f2089ea27 100644 --- a/epmet-module/data-statistical/data-statistical-server/src/main/resources/bootstrap.yml +++ b/epmet-module/data-statistical/data-statistical-server/src/main/resources/bootstrap.yml @@ -37,6 +37,8 @@ spring: #不把自己注册到注册中心的地址 register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@ diff --git a/epmet-module/epmet-activiti/epmet-activiti-server/pom.xml b/epmet-module/epmet-activiti/epmet-activiti-server/pom.xml index b87470fd09..39153b082b 100644 --- a/epmet-module/epmet-activiti/epmet-activiti-server/pom.xml +++ b/epmet-module/epmet-activiti/epmet-activiti-server/pom.xml @@ -181,6 +181,8 @@ false + + true false @@ -213,6 +215,8 @@ false + + false false @@ -242,6 +246,8 @@ false + + true true @@ -271,6 +277,8 @@ false + + false true diff --git a/epmet-module/epmet-activiti/epmet-activiti-server/src/main/resources/bootstrap.yml b/epmet-module/epmet-activiti/epmet-activiti-server/src/main/resources/bootstrap.yml index 811f0c352b..f31026085f 100644 --- a/epmet-module/epmet-activiti/epmet-activiti-server/src/main/resources/bootstrap.yml +++ b/epmet-module/epmet-activiti/epmet-activiti-server/src/main/resources/bootstrap.yml @@ -50,6 +50,8 @@ spring: #不把自己注册到注册中心的地址 register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@ diff --git a/epmet-module/epmet-common-service/common-service-server/pom.xml b/epmet-module/epmet-common-service/common-service-server/pom.xml index 204c98cfb0..4e27c15d03 100644 --- a/epmet-module/epmet-common-service/common-service-server/pom.xml +++ b/epmet-module/epmet-common-service/common-service-server/pom.xml @@ -123,6 +123,8 @@ false + + true false @@ -169,6 +171,8 @@ false + + false false @@ -213,6 +217,8 @@ false + + true true @@ -254,6 +260,8 @@ false + + false true diff --git a/epmet-module/epmet-common-service/common-service-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java b/epmet-module/epmet-common-service/common-service-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..7d9a1fef05 --- /dev/null +++ b/epmet-module/epmet-common-service/common-service-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-module/epmet-common-service/common-service-server/src/main/resources/bootstrap.yml b/epmet-module/epmet-common-service/common-service-server/src/main/resources/bootstrap.yml index ff4ec82dcf..3da9f56074 100644 --- a/epmet-module/epmet-common-service/common-service-server/src/main/resources/bootstrap.yml +++ b/epmet-module/epmet-common-service/common-service-server/src/main/resources/bootstrap.yml @@ -49,6 +49,8 @@ spring: #不把自己注册到注册中心的地址 register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@ diff --git a/epmet-module/epmet-demo/epmet-demo-server/pom.xml b/epmet-module/epmet-demo/epmet-demo-server/pom.xml index b877c0df18..00a03e4aab 100644 --- a/epmet-module/epmet-demo/epmet-demo-server/pom.xml +++ b/epmet-module/epmet-demo/epmet-demo-server/pom.xml @@ -127,6 +127,8 @@ false + + true @@ -136,7 +138,7 @@ --> 8088 - dev + test @@ -157,6 +159,9 @@ false + + + true diff --git a/epmet-module/epmet-demo/epmet-demo-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java b/epmet-module/epmet-demo/epmet-demo-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..7d9a1fef05 --- /dev/null +++ b/epmet-module/epmet-demo/epmet-demo-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-module/epmet-demo/epmet-demo-server/src/main/resources/bootstrap.yml b/epmet-module/epmet-demo/epmet-demo-server/src/main/resources/bootstrap.yml index b9cf8dd559..59feaf2641 100644 --- a/epmet-module/epmet-demo/epmet-demo-server/src/main/resources/bootstrap.yml +++ b/epmet-module/epmet-demo/epmet-demo-server/src/main/resources/bootstrap.yml @@ -40,6 +40,8 @@ spring: #不把自己注册到注册中心的地址 register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@ diff --git a/epmet-module/epmet-ext/epmet-ext-server/pom.xml b/epmet-module/epmet-ext/epmet-ext-server/pom.xml index dd2c955279..bebc41ab98 100644 --- a/epmet-module/epmet-ext/epmet-ext-server/pom.xml +++ b/epmet-module/epmet-ext/epmet-ext-server/pom.xml @@ -215,6 +215,8 @@ false + + true false @@ -254,6 +256,8 @@ false + + false false @@ -293,6 +297,8 @@ false + + true true @@ -333,6 +339,8 @@ false + + false true diff --git a/epmet-module/epmet-ext/epmet-ext-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java b/epmet-module/epmet-ext/epmet-ext-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..7d9a1fef05 --- /dev/null +++ b/epmet-module/epmet-ext/epmet-ext-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-module/epmet-ext/epmet-ext-server/src/main/resources/bootstrap.yml b/epmet-module/epmet-ext/epmet-ext-server/src/main/resources/bootstrap.yml index 881881db27..bf9a797c20 100644 --- a/epmet-module/epmet-ext/epmet-ext-server/src/main/resources/bootstrap.yml +++ b/epmet-module/epmet-ext/epmet-ext-server/src/main/resources/bootstrap.yml @@ -35,6 +35,8 @@ spring: namespace: @nacos.discovery.namespace@ register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@ diff --git a/epmet-module/epmet-heart/epmet-heart-server/pom.xml b/epmet-module/epmet-heart/epmet-heart-server/pom.xml index ae5b8cc73f..b5b36b0ee1 100644 --- a/epmet-module/epmet-heart/epmet-heart-server/pom.xml +++ b/epmet-module/epmet-heart/epmet-heart-server/pom.xml @@ -133,6 +133,8 @@ false + + true false https://epmet-dev.elinkservice.cn/api/epmetscan/api @@ -174,6 +176,8 @@ false + + false false https://epmet-dev.elinkservice.cn/api/epmetscan/api @@ -215,6 +219,8 @@ false + + true true https://epmet-dev.elinkservice.cn/api/epmetscan/api @@ -256,6 +262,8 @@ false + + false true https://epmet-open.elinkservice.cn/api/epmetscan/api diff --git a/epmet-module/epmet-heart/epmet-heart-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java b/epmet-module/epmet-heart/epmet-heart-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..7d9a1fef05 --- /dev/null +++ b/epmet-module/epmet-heart/epmet-heart-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-module/epmet-heart/epmet-heart-server/src/main/resources/bootstrap.yml b/epmet-module/epmet-heart/epmet-heart-server/src/main/resources/bootstrap.yml index 8fe49b4ea0..fe4ff2e5fa 100644 --- a/epmet-module/epmet-heart/epmet-heart-server/src/main/resources/bootstrap.yml +++ b/epmet-module/epmet-heart/epmet-heart-server/src/main/resources/bootstrap.yml @@ -37,6 +37,8 @@ spring: #不把自己注册到注册中心的地址 register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@ diff --git a/epmet-module/epmet-job/epmet-job-server/pom.xml b/epmet-module/epmet-job/epmet-job-server/pom.xml index 725a7342eb..e29f5759e5 100644 --- a/epmet-module/epmet-job/epmet-job-server/pom.xml +++ b/epmet-module/epmet-job/epmet-job-server/pom.xml @@ -136,6 +136,8 @@ false + + true false @@ -171,6 +173,8 @@ false + + false false @@ -206,6 +210,8 @@ false + + true true @@ -239,6 +245,9 @@ false + + false + true diff --git a/epmet-module/epmet-job/epmet-job-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java b/epmet-module/epmet-job/epmet-job-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..7d9a1fef05 --- /dev/null +++ b/epmet-module/epmet-job/epmet-job-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-module/epmet-job/epmet-job-server/src/main/resources/bootstrap.yml b/epmet-module/epmet-job/epmet-job-server/src/main/resources/bootstrap.yml index e0dc62b7f0..0b590717e1 100644 --- a/epmet-module/epmet-job/epmet-job-server/src/main/resources/bootstrap.yml +++ b/epmet-module/epmet-job/epmet-job-server/src/main/resources/bootstrap.yml @@ -49,6 +49,8 @@ spring: #不把自己注册到注册中心的地址 register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@ diff --git a/epmet-module/epmet-message/epmet-message-server/pom.xml b/epmet-module/epmet-message/epmet-message-server/pom.xml index 6cf052999a..87d40ea5fb 100644 --- a/epmet-module/epmet-message/epmet-message-server/pom.xml +++ b/epmet-module/epmet-message/epmet-message-server/pom.xml @@ -182,6 +182,8 @@ false + + true false @@ -231,6 +233,8 @@ false + + false false @@ -279,6 +283,8 @@ false + + true true @@ -325,6 +331,9 @@ false + + false + true diff --git a/epmet-module/epmet-message/epmet-message-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java b/epmet-module/epmet-message/epmet-message-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..7d9a1fef05 --- /dev/null +++ b/epmet-module/epmet-message/epmet-message-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-module/epmet-message/epmet-message-server/src/main/resources/bootstrap.yml b/epmet-module/epmet-message/epmet-message-server/src/main/resources/bootstrap.yml index 03945c0ebf..f4f88d87ca 100644 --- a/epmet-module/epmet-message/epmet-message-server/src/main/resources/bootstrap.yml +++ b/epmet-module/epmet-message/epmet-message-server/src/main/resources/bootstrap.yml @@ -40,6 +40,8 @@ spring: #不把自己注册到注册中心的地址 register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@ diff --git a/epmet-module/epmet-oss/epmet-oss-server/pom.xml b/epmet-module/epmet-oss/epmet-oss-server/pom.xml index e3178a703e..e8b08e97cc 100644 --- a/epmet-module/epmet-oss/epmet-oss-server/pom.xml +++ b/epmet-module/epmet-oss/epmet-oss-server/pom.xml @@ -141,6 +141,8 @@ false + + true false @@ -180,6 +182,8 @@ false + + false false @@ -218,6 +222,8 @@ false + + true true @@ -253,6 +259,8 @@ false + + false true diff --git a/epmet-module/epmet-oss/epmet-oss-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java b/epmet-module/epmet-oss/epmet-oss-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..7d9a1fef05 --- /dev/null +++ b/epmet-module/epmet-oss/epmet-oss-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-module/epmet-oss/epmet-oss-server/src/main/resources/bootstrap.yml b/epmet-module/epmet-oss/epmet-oss-server/src/main/resources/bootstrap.yml index 3bfe0d68bc..1b939a5edd 100644 --- a/epmet-module/epmet-oss/epmet-oss-server/src/main/resources/bootstrap.yml +++ b/epmet-module/epmet-oss/epmet-oss-server/src/main/resources/bootstrap.yml @@ -54,6 +54,8 @@ spring: #不把自己注册到注册中心的地址 register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@ diff --git a/epmet-module/epmet-point/epmet-point-server/pom.xml b/epmet-module/epmet-point/epmet-point-server/pom.xml index f4349548e3..40d0d1d9e7 100644 --- a/epmet-module/epmet-point/epmet-point-server/pom.xml +++ b/epmet-module/epmet-point/epmet-point-server/pom.xml @@ -154,6 +154,8 @@ false + + true false @@ -195,6 +197,8 @@ false + + true false @@ -236,6 +240,8 @@ false + + true true @@ -277,6 +283,8 @@ false + + false true diff --git a/epmet-module/epmet-point/epmet-point-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java b/epmet-module/epmet-point/epmet-point-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..7d9a1fef05 --- /dev/null +++ b/epmet-module/epmet-point/epmet-point-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-module/epmet-point/epmet-point-server/src/main/resources/bootstrap.yml b/epmet-module/epmet-point/epmet-point-server/src/main/resources/bootstrap.yml index 08cae6a575..af068df15d 100644 --- a/epmet-module/epmet-point/epmet-point-server/src/main/resources/bootstrap.yml +++ b/epmet-module/epmet-point/epmet-point-server/src/main/resources/bootstrap.yml @@ -37,6 +37,9 @@ spring: #不把自己注册到注册中心的地址 register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + # 是否开启服务实例列表变更监听 + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@ @@ -113,6 +116,7 @@ hystrix: ribbon: ReadTimeout: 300000 ConnectTimeout: 300000 +# ServerListRefreshInterval: 5000 #pageHelper分页插件 pagehelper: diff --git a/epmet-module/epmet-third/epmet-third-server/pom.xml b/epmet-module/epmet-third/epmet-third-server/pom.xml index a945beb046..a885af4c93 100644 --- a/epmet-module/epmet-third/epmet-third-server/pom.xml +++ b/epmet-module/epmet-third/epmet-third-server/pom.xml @@ -197,6 +197,8 @@ false + + true false @@ -236,6 +238,8 @@ false + + false false @@ -274,6 +278,8 @@ false + + true true @@ -313,6 +319,8 @@ false + + false true diff --git a/epmet-module/epmet-third/epmet-third-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java b/epmet-module/epmet-third/epmet-third-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..7d9a1fef05 --- /dev/null +++ b/epmet-module/epmet-third/epmet-third-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-module/epmet-third/epmet-third-server/src/main/resources/bootstrap.yml b/epmet-module/epmet-third/epmet-third-server/src/main/resources/bootstrap.yml index 70ea9ad34a..0dc793c264 100644 --- a/epmet-module/epmet-third/epmet-third-server/src/main/resources/bootstrap.yml +++ b/epmet-module/epmet-third/epmet-third-server/src/main/resources/bootstrap.yml @@ -35,6 +35,8 @@ spring: namespace: @nacos.discovery.namespace@ register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@ diff --git a/epmet-module/gov-access/gov-access-server/pom.xml b/epmet-module/gov-access/gov-access-server/pom.xml index 909b706005..28e033fa3b 100644 --- a/epmet-module/gov-access/gov-access-server/pom.xml +++ b/epmet-module/gov-access/gov-access-server/pom.xml @@ -124,6 +124,8 @@ false + + true false @@ -161,6 +163,8 @@ false + + false false @@ -198,6 +202,8 @@ false + + true true @@ -232,6 +238,8 @@ false + + false true diff --git a/epmet-module/gov-access/gov-access-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java b/epmet-module/gov-access/gov-access-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..7d9a1fef05 --- /dev/null +++ b/epmet-module/gov-access/gov-access-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-module/gov-access/gov-access-server/src/main/resources/bootstrap.yml b/epmet-module/gov-access/gov-access-server/src/main/resources/bootstrap.yml index 978f85db31..0531e3885e 100644 --- a/epmet-module/gov-access/gov-access-server/src/main/resources/bootstrap.yml +++ b/epmet-module/gov-access/gov-access-server/src/main/resources/bootstrap.yml @@ -37,6 +37,8 @@ spring: #不把自己注册到注册中心的地址 register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@ diff --git a/epmet-module/gov-grid/gov-grid-server/pom.xml b/epmet-module/gov-grid/gov-grid-server/pom.xml index ad73cacdaa..0e81d0360b 100644 --- a/epmet-module/gov-grid/gov-grid-server/pom.xml +++ b/epmet-module/gov-grid/gov-grid-server/pom.xml @@ -116,6 +116,8 @@ false + + true false @@ -148,6 +150,8 @@ false + + false false @@ -180,6 +184,8 @@ false + + true true @@ -212,6 +218,8 @@ false + + false true diff --git a/epmet-module/gov-grid/gov-grid-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java b/epmet-module/gov-grid/gov-grid-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..7d9a1fef05 --- /dev/null +++ b/epmet-module/gov-grid/gov-grid-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-module/gov-grid/gov-grid-server/src/main/resources/bootstrap.yml b/epmet-module/gov-grid/gov-grid-server/src/main/resources/bootstrap.yml index 4afc1b94e4..d9f0085bd7 100644 --- a/epmet-module/gov-grid/gov-grid-server/src/main/resources/bootstrap.yml +++ b/epmet-module/gov-grid/gov-grid-server/src/main/resources/bootstrap.yml @@ -33,6 +33,8 @@ spring: #不把自己注册到注册中心的地址 register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@ diff --git a/epmet-module/gov-issue/gov-issue-server/pom.xml b/epmet-module/gov-issue/gov-issue-server/pom.xml index 04eb309484..a28e3463a8 100644 --- a/epmet-module/gov-issue/gov-issue-server/pom.xml +++ b/epmet-module/gov-issue/gov-issue-server/pom.xml @@ -166,6 +166,8 @@ false + + true false https://epmet-dev.elinkservice.cn/api/epmetscan/api @@ -207,6 +209,8 @@ false + + false false https://epmet-dev.elinkservice.cn/api/epmetscan/api @@ -248,6 +252,8 @@ false + + true true https://epmet-dev.elinkservice.cn/api/epmetscan/api @@ -285,6 +291,8 @@ false + + false true https://epmet-open.elinkservice.cn/api/epmetscan/api diff --git a/epmet-module/gov-issue/gov-issue-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java b/epmet-module/gov-issue/gov-issue-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..7d9a1fef05 --- /dev/null +++ b/epmet-module/gov-issue/gov-issue-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-module/gov-issue/gov-issue-server/src/main/resources/bootstrap.yml b/epmet-module/gov-issue/gov-issue-server/src/main/resources/bootstrap.yml index 09823befef..b69f6ceec2 100644 --- a/epmet-module/gov-issue/gov-issue-server/src/main/resources/bootstrap.yml +++ b/epmet-module/gov-issue/gov-issue-server/src/main/resources/bootstrap.yml @@ -37,6 +37,8 @@ spring: #不把自己注册到注册中心的地址 register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@ diff --git a/epmet-module/gov-mine/gov-mine-server/pom.xml b/epmet-module/gov-mine/gov-mine-server/pom.xml index 325ef169c4..b8206f3b95 100644 --- a/epmet-module/gov-mine/gov-mine-server/pom.xml +++ b/epmet-module/gov-mine/gov-mine-server/pom.xml @@ -136,6 +136,9 @@ false + + true + https://oapi.dingtalk.com/robot/send?access_token=e894e5690f9d6a527722974c71548ff6c0fe29bd956589a09e21b16442a35ed4 @@ -166,6 +169,9 @@ false + + false + https://oapi.dingtalk.com/robot/send?access_token=e894e5690f9d6a527722974c71548ff6c0fe29bd956589a09e21b16442a35ed4 @@ -196,6 +202,9 @@ false + + true + https://oapi.dingtalk.com/robot/send?access_token=e894e5690f9d6a527722974c71548ff6c0fe29bd956589a09e21b16442a35ed4 @@ -223,6 +232,8 @@ false + + false diff --git a/epmet-module/gov-mine/gov-mine-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java b/epmet-module/gov-mine/gov-mine-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..7d9a1fef05 --- /dev/null +++ b/epmet-module/gov-mine/gov-mine-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-module/gov-mine/gov-mine-server/src/main/resources/bootstrap.yml b/epmet-module/gov-mine/gov-mine-server/src/main/resources/bootstrap.yml index 1cfa2f121e..97052173b9 100644 --- a/epmet-module/gov-mine/gov-mine-server/src/main/resources/bootstrap.yml +++ b/epmet-module/gov-mine/gov-mine-server/src/main/resources/bootstrap.yml @@ -30,6 +30,8 @@ spring: #不把自己注册到注册中心的地址 register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@ diff --git a/epmet-module/gov-org/gov-org-server/pom.xml b/epmet-module/gov-org/gov-org-server/pom.xml index 180922a33d..2bc10abf21 100644 --- a/epmet-module/gov-org/gov-org-server/pom.xml +++ b/epmet-module/gov-org/gov-org-server/pom.xml @@ -160,6 +160,8 @@ false + + true false @@ -203,6 +205,8 @@ false + + false false @@ -246,6 +250,8 @@ false + + true true @@ -287,6 +293,8 @@ false + + false true diff --git a/epmet-module/gov-org/gov-org-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java b/epmet-module/gov-org/gov-org-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..7d9a1fef05 --- /dev/null +++ b/epmet-module/gov-org/gov-org-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-module/gov-org/gov-org-server/src/main/resources/bootstrap.yml b/epmet-module/gov-org/gov-org-server/src/main/resources/bootstrap.yml index 324ed0a5ca..dc1c51b285 100644 --- a/epmet-module/gov-org/gov-org-server/src/main/resources/bootstrap.yml +++ b/epmet-module/gov-org/gov-org-server/src/main/resources/bootstrap.yml @@ -70,6 +70,9 @@ spring: #不把自己注册到注册中心的地址 register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + # 是否开启服务实例列表变更监听 + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@ diff --git a/epmet-module/gov-project/gov-project-server/pom.xml b/epmet-module/gov-project/gov-project-server/pom.xml index 4b1b8389fc..f49d89d16a 100644 --- a/epmet-module/gov-project/gov-project-server/pom.xml +++ b/epmet-module/gov-project/gov-project-server/pom.xml @@ -165,6 +165,8 @@ false + + true false https://epmet-dev.elinkservice.cn/api/epmetscan/api @@ -205,6 +207,8 @@ false + + false false https://epmet-dev.elinkservice.cn/api/epmetscan/api @@ -244,6 +248,8 @@ false + + true true https://epmet-dev.elinkservice.cn/api/epmetscan/api @@ -280,6 +286,8 @@ false + + false true https://epmet-open.elinkservice.cn/api/epmetscan/api diff --git a/epmet-module/gov-project/gov-project-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java b/epmet-module/gov-project/gov-project-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..7d9a1fef05 --- /dev/null +++ b/epmet-module/gov-project/gov-project-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-module/gov-project/gov-project-server/src/main/resources/bootstrap.yml b/epmet-module/gov-project/gov-project-server/src/main/resources/bootstrap.yml index 74f392af96..0e8f0ca47c 100644 --- a/epmet-module/gov-project/gov-project-server/src/main/resources/bootstrap.yml +++ b/epmet-module/gov-project/gov-project-server/src/main/resources/bootstrap.yml @@ -46,6 +46,8 @@ spring: #不把自己注册到注册中心的地址 register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@ diff --git a/epmet-module/gov-voice/gov-voice-server/pom.xml b/epmet-module/gov-voice/gov-voice-server/pom.xml index 8494159692..0403af0463 100644 --- a/epmet-module/gov-voice/gov-voice-server/pom.xml +++ b/epmet-module/gov-voice/gov-voice-server/pom.xml @@ -129,6 +129,8 @@ false + + true false https://epmet-dev.elinkservice.cn/api/epmetscan/api @@ -168,6 +170,8 @@ false + + false false https://epmet-dev.elinkservice.cn/api/epmetscan/api @@ -207,6 +211,8 @@ false + + true true https://epmet-dev.elinkservice.cn/api/epmetscan/api @@ -243,6 +249,8 @@ false + + false true https://epmet-open.elinkservice.cn/api/epmetscan/api diff --git a/epmet-module/gov-voice/gov-voice-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java b/epmet-module/gov-voice/gov-voice-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..7d9a1fef05 --- /dev/null +++ b/epmet-module/gov-voice/gov-voice-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-module/gov-voice/gov-voice-server/src/main/resources/bootstrap.yml b/epmet-module/gov-voice/gov-voice-server/src/main/resources/bootstrap.yml index 8b8c4bdb45..16425361b7 100644 --- a/epmet-module/gov-voice/gov-voice-server/src/main/resources/bootstrap.yml +++ b/epmet-module/gov-voice/gov-voice-server/src/main/resources/bootstrap.yml @@ -46,6 +46,8 @@ spring: #不把自己注册到注册中心的地址 register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@ diff --git a/epmet-module/oper-access/oper-access-server/pom.xml b/epmet-module/oper-access/oper-access-server/pom.xml index 3fd8e4ff1c..5b097299ec 100644 --- a/epmet-module/oper-access/oper-access-server/pom.xml +++ b/epmet-module/oper-access/oper-access-server/pom.xml @@ -117,6 +117,8 @@ false + + true false @@ -155,6 +157,8 @@ false + + false false @@ -193,6 +197,8 @@ false + + true true @@ -228,6 +234,8 @@ false + + false true diff --git a/epmet-module/oper-access/oper-access-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java b/epmet-module/oper-access/oper-access-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..7d9a1fef05 --- /dev/null +++ b/epmet-module/oper-access/oper-access-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-module/oper-access/oper-access-server/src/main/resources/bootstrap.yml b/epmet-module/oper-access/oper-access-server/src/main/resources/bootstrap.yml index f7bad85d13..ba387b80c4 100644 --- a/epmet-module/oper-access/oper-access-server/src/main/resources/bootstrap.yml +++ b/epmet-module/oper-access/oper-access-server/src/main/resources/bootstrap.yml @@ -49,6 +49,8 @@ spring: #不把自己注册到注册中心的地址 register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@ diff --git a/epmet-module/oper-crm/oper-crm-server/pom.xml b/epmet-module/oper-crm/oper-crm-server/pom.xml index ee643299c2..c058e133cb 100644 --- a/epmet-module/oper-crm/oper-crm-server/pom.xml +++ b/epmet-module/oper-crm/oper-crm-server/pom.xml @@ -162,6 +162,8 @@ false + + true false @@ -200,6 +202,8 @@ false + + false false @@ -238,6 +242,8 @@ false + + true true @@ -276,6 +282,8 @@ false + + false true diff --git a/epmet-module/oper-crm/oper-crm-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java b/epmet-module/oper-crm/oper-crm-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..7d9a1fef05 --- /dev/null +++ b/epmet-module/oper-crm/oper-crm-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-module/oper-crm/oper-crm-server/src/main/resources/bootstrap.yml b/epmet-module/oper-crm/oper-crm-server/src/main/resources/bootstrap.yml index 3d0b42162e..dc81eba455 100644 --- a/epmet-module/oper-crm/oper-crm-server/src/main/resources/bootstrap.yml +++ b/epmet-module/oper-crm/oper-crm-server/src/main/resources/bootstrap.yml @@ -49,6 +49,8 @@ spring: #不把自己注册到注册中心的地址 register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@ diff --git a/epmet-module/oper-customize/oper-customize-server/pom.xml b/epmet-module/oper-customize/oper-customize-server/pom.xml index 13ac2c8615..2d04e9a1dd 100644 --- a/epmet-module/oper-customize/oper-customize-server/pom.xml +++ b/epmet-module/oper-customize/oper-customize-server/pom.xml @@ -134,6 +134,8 @@ false + + true false @@ -177,6 +179,8 @@ false + + false false @@ -220,6 +224,8 @@ false + + true true @@ -260,6 +266,8 @@ false + + false true diff --git a/epmet-module/oper-customize/oper-customize-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java b/epmet-module/oper-customize/oper-customize-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..7d9a1fef05 --- /dev/null +++ b/epmet-module/oper-customize/oper-customize-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-module/oper-customize/oper-customize-server/src/main/resources/bootstrap.yml b/epmet-module/oper-customize/oper-customize-server/src/main/resources/bootstrap.yml index 3cb91d990b..c5dec6d251 100644 --- a/epmet-module/oper-customize/oper-customize-server/src/main/resources/bootstrap.yml +++ b/epmet-module/oper-customize/oper-customize-server/src/main/resources/bootstrap.yml @@ -49,6 +49,8 @@ spring: #不把自己注册到注册中心的地址 register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@ diff --git a/epmet-module/resi-group/resi-group-server/pom.xml b/epmet-module/resi-group/resi-group-server/pom.xml index 494b7edfb4..117e26696c 100644 --- a/epmet-module/resi-group/resi-group-server/pom.xml +++ b/epmet-module/resi-group/resi-group-server/pom.xml @@ -181,6 +181,8 @@ false + + true false @@ -238,6 +240,8 @@ false + + false false @@ -293,6 +297,9 @@ false + + true + true @@ -345,6 +352,10 @@ false + + + false + true diff --git a/epmet-module/resi-group/resi-group-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java b/epmet-module/resi-group/resi-group-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..7d9a1fef05 --- /dev/null +++ b/epmet-module/resi-group/resi-group-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-module/resi-group/resi-group-server/src/main/resources/bootstrap.yml b/epmet-module/resi-group/resi-group-server/src/main/resources/bootstrap.yml index b40b72b999..d5bdbb2300 100644 --- a/epmet-module/resi-group/resi-group-server/src/main/resources/bootstrap.yml +++ b/epmet-module/resi-group/resi-group-server/src/main/resources/bootstrap.yml @@ -49,6 +49,8 @@ spring: #不把自己注册到注册中心的地址 register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@ diff --git a/epmet-module/resi-guide/resi-guide-server/pom.xml b/epmet-module/resi-guide/resi-guide-server/pom.xml index ab8cfe3179..94b83622e0 100644 --- a/epmet-module/resi-guide/resi-guide-server/pom.xml +++ b/epmet-module/resi-guide/resi-guide-server/pom.xml @@ -138,6 +138,8 @@ false + + true false @@ -176,6 +178,8 @@ false + + false false @@ -214,6 +218,8 @@ false + + true true @@ -250,6 +256,9 @@ false + + false + true diff --git a/epmet-module/resi-guide/resi-guide-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java b/epmet-module/resi-guide/resi-guide-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..7d9a1fef05 --- /dev/null +++ b/epmet-module/resi-guide/resi-guide-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-module/resi-guide/resi-guide-server/src/main/resources/bootstrap.yml b/epmet-module/resi-guide/resi-guide-server/src/main/resources/bootstrap.yml index a8a75e8d97..8084eeeec5 100644 --- a/epmet-module/resi-guide/resi-guide-server/src/main/resources/bootstrap.yml +++ b/epmet-module/resi-guide/resi-guide-server/src/main/resources/bootstrap.yml @@ -50,6 +50,8 @@ spring: #不把自己注册到注册中心的地址 register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@ diff --git a/epmet-module/resi-hall/resi-hall-server/pom.xml b/epmet-module/resi-hall/resi-hall-server/pom.xml index f3475bf89c..299e457a6d 100644 --- a/epmet-module/resi-hall/resi-hall-server/pom.xml +++ b/epmet-module/resi-hall/resi-hall-server/pom.xml @@ -102,6 +102,8 @@ false + + true false @@ -134,6 +136,8 @@ false + + false false @@ -166,6 +170,8 @@ false + + true true @@ -195,6 +201,8 @@ false + + false true diff --git a/epmet-module/resi-hall/resi-hall-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java b/epmet-module/resi-hall/resi-hall-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..7d9a1fef05 --- /dev/null +++ b/epmet-module/resi-hall/resi-hall-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-module/resi-hall/resi-hall-server/src/main/resources/bootstrap.yml b/epmet-module/resi-hall/resi-hall-server/src/main/resources/bootstrap.yml index 19a4ef53b8..4522364f57 100644 --- a/epmet-module/resi-hall/resi-hall-server/src/main/resources/bootstrap.yml +++ b/epmet-module/resi-hall/resi-hall-server/src/main/resources/bootstrap.yml @@ -33,6 +33,8 @@ spring: #不把自己注册到注册中心的地址 register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@ diff --git a/epmet-module/resi-home/resi-home-server/pom.xml b/epmet-module/resi-home/resi-home-server/pom.xml index 041357ea74..5012828d19 100644 --- a/epmet-module/resi-home/resi-home-server/pom.xml +++ b/epmet-module/resi-home/resi-home-server/pom.xml @@ -126,6 +126,9 @@ false + + true + https://oapi.dingtalk.com/robot/send?access_token=e894e5690f9d6a527722974c71548ff6c0fe29bd956589a09e21b16442a35ed4 @@ -156,6 +159,9 @@ false + + false + https://oapi.dingtalk.com/robot/send?access_token=e894e5690f9d6a527722974c71548ff6c0fe29bd956589a09e21b16442a35ed4 @@ -186,6 +192,9 @@ false + + true + https://oapi.dingtalk.com/robot/send?access_token=e894e5690f9d6a527722974c71548ff6c0fe29bd956589a09e21b16442a35ed4 @@ -213,6 +222,10 @@ false + + + false + https://oapi.dingtalk.com/robot/send?access_token=a5f66c3374b1642fe2142dbf56d5997e280172d4e8f2b546c9423a68c82ece6c diff --git a/epmet-module/resi-home/resi-home-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java b/epmet-module/resi-home/resi-home-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..7d9a1fef05 --- /dev/null +++ b/epmet-module/resi-home/resi-home-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-module/resi-home/resi-home-server/src/main/resources/bootstrap.yml b/epmet-module/resi-home/resi-home-server/src/main/resources/bootstrap.yml index 72ede48632..b0ecb87085 100644 --- a/epmet-module/resi-home/resi-home-server/src/main/resources/bootstrap.yml +++ b/epmet-module/resi-home/resi-home-server/src/main/resources/bootstrap.yml @@ -33,6 +33,8 @@ spring: #不把自己注册到注册中心的地址 register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@ diff --git a/epmet-module/resi-mine/resi-mine-server/pom.xml b/epmet-module/resi-mine/resi-mine-server/pom.xml index 3c90bc99a7..9101e0841f 100644 --- a/epmet-module/resi-mine/resi-mine-server/pom.xml +++ b/epmet-module/resi-mine/resi-mine-server/pom.xml @@ -145,6 +145,9 @@ false + + true + https://oapi.dingtalk.com/robot/send?access_token=e894e5690f9d6a527722974c71548ff6c0fe29bd956589a09e21b16442a35ed4 @@ -174,6 +177,9 @@ false + + false + https://oapi.dingtalk.com/robot/send?access_token=e894e5690f9d6a527722974c71548ff6c0fe29bd956589a09e21b16442a35ed4 @@ -203,6 +209,9 @@ false + + true + https://oapi.dingtalk.com/robot/send?access_token=e894e5690f9d6a527722974c71548ff6c0fe29bd956589a09e21b16442a35ed4 @@ -229,6 +238,10 @@ false + + + false + https://oapi.dingtalk.com/robot/send?access_token=a5f66c3374b1642fe2142dbf56d5997e280172d4e8f2b546c9423a68c82ece6c diff --git a/epmet-module/resi-mine/resi-mine-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java b/epmet-module/resi-mine/resi-mine-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..7d9a1fef05 --- /dev/null +++ b/epmet-module/resi-mine/resi-mine-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-module/resi-mine/resi-mine-server/src/main/resources/bootstrap.yml b/epmet-module/resi-mine/resi-mine-server/src/main/resources/bootstrap.yml index 7257f2ea6f..68fb8924c1 100644 --- a/epmet-module/resi-mine/resi-mine-server/src/main/resources/bootstrap.yml +++ b/epmet-module/resi-mine/resi-mine-server/src/main/resources/bootstrap.yml @@ -33,6 +33,8 @@ spring: #不把自己注册到注册中心的地址 register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@ diff --git a/epmet-module/resi-partymember/resi-partymember-server/pom.xml b/epmet-module/resi-partymember/resi-partymember-server/pom.xml index dcd85a76ce..f52ffd6714 100644 --- a/epmet-module/resi-partymember/resi-partymember-server/pom.xml +++ b/epmet-module/resi-partymember/resi-partymember-server/pom.xml @@ -144,6 +144,8 @@ false + + true false @@ -183,6 +185,8 @@ false + + false false @@ -222,6 +226,8 @@ false + + true true @@ -259,6 +265,9 @@ false + + false + true diff --git a/epmet-module/resi-partymember/resi-partymember-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java b/epmet-module/resi-partymember/resi-partymember-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..7d9a1fef05 --- /dev/null +++ b/epmet-module/resi-partymember/resi-partymember-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-module/resi-partymember/resi-partymember-server/src/main/resources/bootstrap.yml b/epmet-module/resi-partymember/resi-partymember-server/src/main/resources/bootstrap.yml index 0978c177e1..211b221914 100644 --- a/epmet-module/resi-partymember/resi-partymember-server/src/main/resources/bootstrap.yml +++ b/epmet-module/resi-partymember/resi-partymember-server/src/main/resources/bootstrap.yml @@ -49,6 +49,8 @@ spring: #不把自己注册到注册中心的地址 register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@ diff --git a/epmet-module/resi-voice/resi-voice-server/pom.xml b/epmet-module/resi-voice/resi-voice-server/pom.xml index 3ec248aebc..d98c860324 100644 --- a/epmet-module/resi-voice/resi-voice-server/pom.xml +++ b/epmet-module/resi-voice/resi-voice-server/pom.xml @@ -102,6 +102,9 @@ false + + true + @@ -133,6 +136,8 @@ false + + false @@ -164,6 +169,8 @@ false + + true @@ -196,6 +203,9 @@ false + + false + diff --git a/epmet-module/resi-voice/resi-voice-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java b/epmet-module/resi-voice/resi-voice-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..7d9a1fef05 --- /dev/null +++ b/epmet-module/resi-voice/resi-voice-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-module/resi-voice/resi-voice-server/src/main/resources/bootstrap.yml b/epmet-module/resi-voice/resi-voice-server/src/main/resources/bootstrap.yml index 9d398946ba..156a3bf5d7 100644 --- a/epmet-module/resi-voice/resi-voice-server/src/main/resources/bootstrap.yml +++ b/epmet-module/resi-voice/resi-voice-server/src/main/resources/bootstrap.yml @@ -30,6 +30,8 @@ spring: #不把自己注册到注册中心的地址 register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@ diff --git a/epmet-openapi/epmet-openapi-scan/pom.xml b/epmet-openapi/epmet-openapi-scan/pom.xml index f8641772b6..1fbf533003 100644 --- a/epmet-openapi/epmet-openapi-scan/pom.xml +++ b/epmet-openapi/epmet-openapi-scan/pom.xml @@ -104,6 +104,9 @@ false + + true + https://oapi.dingtalk.com/robot/send?access_token=e894e5690f9d6a527722974c71548ff6c0fe29bd956589a09e21b16442a35ed4 @@ -119,7 +122,7 @@ 8107 - dev + local @@ -135,6 +138,10 @@ false + + + false + https://oapi.dingtalk.com/robot/send?access_token=e894e5690f9d6a527722974c71548ff6c0fe29bd956589a09e21b16442a35ed4 @@ -162,6 +169,9 @@ false + + true + https://oapi.dingtalk.com/robot/send?access_token=e894e5690f9d6a527722974c71548ff6c0fe29bd956589a09e21b16442a35ed4 @@ -194,6 +204,9 @@ false + + false + diff --git a/epmet-openapi/epmet-openapi-scan/src/main/java/com/epmet/openapi/scan/config/NacosServiceListListenerRegisterer.java b/epmet-openapi/epmet-openapi-scan/src/main/java/com/epmet/openapi/scan/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..d372fca779 --- /dev/null +++ b/epmet-openapi/epmet-openapi-scan/src/main/java/com/epmet/openapi/scan/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.openapi.scan.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-openapi/epmet-openapi-scan/src/main/resources/bootstrap.yml b/epmet-openapi/epmet-openapi-scan/src/main/resources/bootstrap.yml index 0aa0583c97..07a611bc39 100644 --- a/epmet-openapi/epmet-openapi-scan/src/main/resources/bootstrap.yml +++ b/epmet-openapi/epmet-openapi-scan/src/main/resources/bootstrap.yml @@ -33,6 +33,8 @@ spring: #不把自己注册到注册中心的地址 register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@ diff --git a/epmet-user/epmet-user-server/pom.xml b/epmet-user/epmet-user-server/pom.xml index 0139688d8e..79c3f816af 100644 --- a/epmet-user/epmet-user-server/pom.xml +++ b/epmet-user/epmet-user-server/pom.xml @@ -171,6 +171,8 @@ false + + true false https://epmet-dev.elinkservice.cn/api/epmetscan/api @@ -210,6 +212,8 @@ false + + false false https://epmet-dev.elinkservice.cn/api/epmetscan/api @@ -250,6 +254,8 @@ false + + true true https://epmet-dev.elinkservice.cn/api/epmetscan/api @@ -287,6 +293,9 @@ false + + false + true https://epmet-open.elinkservice.cn/api/epmetscan/api diff --git a/epmet-user/epmet-user-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java b/epmet-user/epmet-user-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java new file mode 100644 index 0000000000..7d9a1fef05 --- /dev/null +++ b/epmet-user/epmet-user-server/src/main/java/com/epmet/config/NacosServiceListListenerRegisterer.java @@ -0,0 +1,161 @@ +package com.epmet.config; + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.Event; +import com.alibaba.nacos.api.naming.listener.EventListener; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.alibaba.nacos.api.naming.pojo.ListView; +import com.epmet.commons.tools.exception.ExceptionUtils; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +/** + * @author wxz + * @Description Nacos服务列表刷新监听注册器 + * @date 2021.09.22 14:33:11 + */ +@Slf4j +@Configuration +@ConditionalOnProperty(prefix = "spring.cloud.nacos.discovery.serviceListChangedListening", name = "enable", havingValue = "true", matchIfMissing = false) +public class NacosServiceListListenerRegisterer { + + public static final String REFRESH_SERVER_LIST_METHOD_NAME = "restOfInit"; + // 服务列表拉取间隔:10s + public static final long SERVICE_LIST_PULLING_DELAY_SECONDS = 10; + + private NamingService namingService; + + private ScheduledExecutorService executor; + + @Autowired + private NacosDiscoveryProperties discoveryProperties; + + @Autowired + private SpringClientFactory springClientFactory; + + // 监听中的服务列表 + private List observingServers = new ArrayList<>(33); + + @PostConstruct + public void init() { + namingService = discoveryProperties.namingServiceInstance(); + // 启动监听 + executor = new ScheduledThreadPoolExecutor(2, new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + thread.setName("NacosServiceListWatchingRegisterer"); + return thread; + } + }); + + // 立即启动,并15s刷新一次服务列表,用于新服务列表的发现 + ScheduledFuture future = executor.scheduleAtFixedRate(new EpmetNacosServiceListListener(), 0, SERVICE_LIST_PULLING_DELAY_SECONDS, TimeUnit.SECONDS); + } + + public class EpmetNacosServiceListListener implements Runnable { + + @Override + public void run() { + doRefreshServerList(); + } + + /** + * @param + * @return + * @description 执行刷新 + * @author wxz + * @date 2021.09.22 16:04:49 + */ + private synchronized void doRefreshServerList() { + ListView serviceListView = null; + try { + serviceListView = namingService.getServicesOfServer(1, 100); + //启动监听 + if (serviceListView == null || serviceListView.getCount() == 0) { + log.info("【Nacos服务列表定时刷新】当前无任何可添加监听的服务"); + return; + } + List serviceList = serviceListView.getData(); + log.info("【Nacos服务列表定时刷新】Nacos服务端服务列表: {}", serviceList); + + for (String service : serviceList) { + try { + + // 如果该服务已经在监听列表中存在了,则不再注册监听。注:不能取消空服务的监听,因为空服务随时可能恢复运行,需要实时监听。 + if (observingServers.contains(service)) { + continue; + } + + namingService.subscribe(service, new EventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + log.info("【Nacos服务列表刷新监听】收到事件:{}:[{}]", namingEvent.getServiceName(), namingEvent.getInstances()); + doRefreshServerList(service); + } + } + }); + + // 将该服务加入到监听列表中 + observingServers.add(service); + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】订阅ApplicationContext的刷新事件失败,错误信息:{}", errorStackTrace); + } + } + + } catch (NacosException e) { + String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + log.error("【Nacos服务列表定时刷新】链接Nacos服务端失败,错误信息:{}", errorStackTrace); + } + } + + /** + * @param serviceName + * @return + * @description 刷新ServerList + * @author wxz + * @date 2021.09.22 09:29:16 + */ + private void doRefreshServerList(String serviceName) { + // 刷新方式1:反射调用DynamicServerListLoadBalancer中的restOfInit()方法。该方法原来只执行一次,此处不推荐用 + //ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + //if (loadBalancer instanceof ZoneAwareLoadBalancer) { + // ZoneAwareLoadBalancer zaLoadBalancer = (ZoneAwareLoadBalancer) loadBalancer; + // IClientConfig clientConfig = springClientFactory.getClientConfig(serviceName); + // try { + // Method restOfInitMethod = zaLoadBalancer.getClass().getSuperclass().getDeclaredMethod(REFRESH_SERVER_LIST_METHOD_NAME, IClientConfig.class); + // restOfInitMethod.setAccessible(true); + // restOfInitMethod.invoke(zaLoadBalancer, clientConfig); + // } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // String errorStackTrace = ExceptionUtils.getErrorStackTrace(e); + // log.error("【LoadBalancer刷新服务列表】失败:{}", errorStackTrace); + // } + //} + + // 刷新方式2:DynamicServerListLoadBalancer#updateListOfServers()该方法为ribbon定时刷新服务列表的时候真正调用的方法,但是加了@VisibleForTesting + // 暂且 1 try + ILoadBalancer loadBalancer = springClientFactory.getLoadBalancer(serviceName); + if (loadBalancer instanceof DynamicServerListLoadBalancer) { + DynamicServerListLoadBalancer dslb = (DynamicServerListLoadBalancer) loadBalancer; + dslb.updateListOfServers(); + } + } + } + +} diff --git a/epmet-user/epmet-user-server/src/main/resources/bootstrap.yml b/epmet-user/epmet-user-server/src/main/resources/bootstrap.yml index 667b9ba7c2..a3e65ad17c 100644 --- a/epmet-user/epmet-user-server/src/main/resources/bootstrap.yml +++ b/epmet-user/epmet-user-server/src/main/resources/bootstrap.yml @@ -70,6 +70,8 @@ spring: #不把自己注册到注册中心的地址 register-enabled: @nacos.register-enabled@ ip: @nacos.ip@ + serviceListChangedListening: + enable: @nacos.service-list-changed-listening.enable@ config: enabled: @nacos.config-enabled@ server-addr: @nacos.server-addr@