diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/IndexFilter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/IndexFilter.java index df32dc52..0d9801b9 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/IndexFilter.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/IndexFilter.java @@ -32,6 +32,8 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.net.URI; +import java.util.Arrays; +import java.util.List; import java.util.Objects; /** @@ -44,7 +46,11 @@ import java.util.Objects; public class IndexFilter implements WebFilter { private static final String REST_PATH_PREFIX = "/rest"; - private static final String SOP_PATH_PREFIX = "/sop"; + + /** 路径白名单 */ + private static List PATH_WHITE_LIST = Arrays.asList( + "/sop", "/actuator" + ); @Value("${sop.gateway-index-path:/}") private String indexPath; @@ -59,8 +65,8 @@ public class IndexFilter implements WebFilter { public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); String path = request.getURI().getPath(); - // SOP路径,直接放行 - if (path.startsWith(SOP_PATH_PREFIX)) { + // 路径是否在白名单中,直接放行 + if (this.isPathInWhiteList(path)) { return chain.filter(exchange); } // 如果是restful请求,直接转发 @@ -125,6 +131,15 @@ public class IndexFilter implements WebFilter { } } + private boolean isPathInWhiteList(String path) { + for (String whitePath : PATH_WHITE_LIST) { + if (path.startsWith(whitePath)) { + return true; + } + } + return false; + } + private void doValidate(ServerWebExchange exchange, ApiParam apiParam) { try { validator.validate(apiParam); diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/EurekaRegistryListener.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/EurekaRegistryListener.java index 0e481a40..61cc436e 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/EurekaRegistryListener.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/EurekaRegistryListener.java @@ -5,6 +5,7 @@ import com.netflix.appinfo.InstanceInfo; import com.netflix.discovery.shared.Application; import com.netflix.discovery.shared.Applications; import org.apache.commons.collections.CollectionUtils; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.netflix.eureka.CloudEurekaClient; import org.springframework.context.ApplicationEvent; @@ -23,6 +24,9 @@ public class EurekaRegistryListener extends BaseRegistryListener { private Set cacheServices = new HashSet<>(); + @Autowired(required = false) + private List registryEventList; + @Override public void onEvent(ApplicationEvent applicationEvent) { Object source = applicationEvent.getSource(); @@ -57,10 +61,8 @@ public class EurekaRegistryListener extends BaseRegistryListener { } Set removedServiceIdList = getRemovedServiceId(serviceList); - // 如果有服务删除 - if (removedServiceIdList.size() > 0) { - this.doRemove(removedServiceIdList); - } + // 如果有服务下线 + this.doRemove(removedServiceIdList); cacheServices = new HashSet<>(serviceList); } @@ -106,12 +108,23 @@ public class EurekaRegistryListener extends BaseRegistryListener { instanceDefinition.setPort(instanceInfo.getPort()); instanceDefinition.setMetadata(instanceInfo.getMetadata()); pullRoutes(instanceDefinition); + if (registryEventList != null) { + registryEventList.forEach(registryEvent -> registryEvent.onRegistry(instanceDefinition)); + } } }); } private void doRemove(Set deletedServices) { - deletedServices.forEach(this::removeRoutes); + if (deletedServices == null) { + return; + } + deletedServices.forEach(serviceId -> { + this.removeRoutes(serviceId); + if (registryEventList != null) { + registryEventList.forEach(registryEvent -> registryEvent.onRemove(serviceId)); + } + }); } } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/NacosRegistryListener.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/NacosRegistryListener.java index a514f1b1..75519137 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/NacosRegistryListener.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/NacosRegistryListener.java @@ -6,16 +6,15 @@ import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ListView; import com.gitee.sop.gatewaycommon.bean.InstanceDefinition; -import lombok.AllArgsConstructor; -import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEvent; import org.springframework.util.CollectionUtils; +import java.util.Collections; +import java.util.Comparator; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @@ -28,7 +27,9 @@ import java.util.stream.Collectors; @Slf4j public class NacosRegistryListener extends BaseRegistryListener { - private volatile Set cacheServices = new HashSet<>(); + private static final String METADATA_KEY_TIME_STARTUP = "time.startup"; + + private volatile Set cacheServices = new HashSet<>(); @Autowired private NacosDiscoveryProperties nacosDiscoveryProperties; @@ -38,53 +39,18 @@ public class NacosRegistryListener extends BaseRegistryListener { @Override public synchronized void onEvent(ApplicationEvent applicationEvent) { - NamingService namingService = nacosDiscoveryProperties.namingServiceInstance(); - ListView servicesOfServer = null; - try { - servicesOfServer = namingService.getServicesOfServer(1, Integer.MAX_VALUE); - } catch (NacosException e) { - log.error("namingService.getServicesOfServer()错误", e); - } - if (servicesOfServer == null || CollectionUtils.isEmpty(servicesOfServer.getData())) { - return; - } + List serviceList = this.getServiceList(); + final Set currentServices = new HashSet<>(serviceList); - Map instanceMap = servicesOfServer - .getData() - .stream() - .filter(this::canOperator) - .map(serviceName -> { - try { - List allInstances = namingService.getAllInstances(serviceName); - if (CollectionUtils.isEmpty(allInstances)) { - return null; - } - Instance instance = allInstances.stream() - .filter(Instance::isHealthy) - .findFirst() - .orElse(null); - return instance == null ? null : new InstanceInfo(serviceName, instance); - } catch (NacosException e) { - log.error("namingService.getAllInstances(serviceName)错误,serviceName:{}", serviceName, e); - return null; - } - }) - .filter(Objects::nonNull) - .collect(Collectors.toMap(InstanceInfo::getServiceName, InstanceInfo::getInstance)); - - - Set serviceNames = instanceMap.keySet(); - - final Set currentServices = new HashSet<>(serviceNames); // 删除现有的,剩下的就是新服务 currentServices.removeAll(cacheServices); // 如果有新的服务注册进来 if (currentServices.size() > 0) { - currentServices.forEach(serviceName -> { - Instance instance = instanceMap.get(serviceName); + currentServices.forEach(nacosServiceHolder -> { + Instance instance = nacosServiceHolder.getInstance(); InstanceDefinition instanceDefinition = new InstanceDefinition(); instanceDefinition.setInstanceId(instance.getInstanceId()); - instanceDefinition.setServiceId(serviceName); + instanceDefinition.setServiceId(nacosServiceHolder.getServiceId()); instanceDefinition.setIp(instance.getIp()); instanceDefinition.setPort(instance.getPort()); instanceDefinition.setMetadata(instance.getMetadata()); @@ -95,18 +61,63 @@ public class NacosRegistryListener extends BaseRegistryListener { }); } - // 如果有服务删除 - Set removedServiceIdList = getRemovedServiceId(serviceNames); - if (removedServiceIdList.size() > 0) { - removedServiceIdList.forEach(serviceId->{ - this.removeRoutes(serviceId); - if (registryEventList != null) { - registryEventList.forEach(registryEvent -> registryEvent.onRemove(serviceId)); - } - }); - } + // 如果有服务下线 + Set removedServiceIdList = getRemovedServiceId(serviceList); + // 移除 + this.doRemove(removedServiceIdList); - cacheServices = new HashSet<>(serviceNames); + cacheServices = new HashSet<>(serviceList); + } + + /** + * 获取建康的服务实例 + * @return 没有返回空的list + */ + private List getServiceList() { + NamingService namingService = nacosDiscoveryProperties.namingServiceInstance(); + ListView servicesOfServer = null; + try { + servicesOfServer = namingService.getServicesOfServer(1, Integer.MAX_VALUE); + } catch (NacosException e) { + log.error("namingService.getServicesOfServer()错误", e); + } + if (servicesOfServer == null || CollectionUtils.isEmpty(servicesOfServer.getData())) { + return Collections.emptyList(); + } + return servicesOfServer + .getData() + .stream() + .filter(this::canOperator) + .map(serviceName -> { + try { + // 获取服务实例 + List allInstances = namingService.getAllInstances(serviceName); + if (CollectionUtils.isEmpty(allInstances)) { + return null; + } + Instance instance = allInstances.stream() + // 只获取建康实例 + .filter(Instance::isHealthy) + // 根据启动时间倒叙,找到最新的服务器 + .max(Comparator.comparing(ins -> { + String startupTime = ins.getMetadata().getOrDefault(METADATA_KEY_TIME_STARTUP, "0"); + return Long.valueOf(startupTime); + })) + .orElse(null); + if (instance == null) { + return null; + } else { + String startupTime = instance.getMetadata().getOrDefault("time.startup", "0"); + long time = Long.parseLong(startupTime); + return new NacosServiceHolder(serviceName, time, instance); + } + } catch (NacosException e) { + log.error("namingService.getAllInstances(serviceName)错误,serviceName:{}", serviceName, e); + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); } /** @@ -115,18 +126,29 @@ public class NacosRegistryListener extends BaseRegistryListener { * @param serviceList 最新的serviceId集合 * @return 返回已下线的serviceId */ - private Set getRemovedServiceId(Set serviceList) { - Set cache = cacheServices; - // 删除最新的,剩下就是已经删除的 - cache.removeAll(serviceList); + private Set getRemovedServiceId(List serviceList) { + Set cache = cacheServices.stream() + .map(NacosServiceHolder::getServiceId) + .collect(Collectors.toSet()); + + Set newList = serviceList.stream() + .map(NacosServiceHolder::getServiceId) + .collect(Collectors.toSet()); + + cache.removeAll(newList); return cache; } - @Data - @AllArgsConstructor - private static class InstanceInfo { - private String serviceName; - private Instance instance; + private void doRemove(Set deletedServices) { + if (deletedServices == null) { + return; + } + deletedServices.forEach(serviceId -> { + this.removeRoutes(serviceId); + if (registryEventList != null) { + registryEventList.forEach(registryEvent -> registryEvent.onRemove(serviceId)); + } + }); } } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/NacosServiceHolder.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/NacosServiceHolder.java new file mode 100644 index 00000000..0b022f62 --- /dev/null +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/NacosServiceHolder.java @@ -0,0 +1,20 @@ +package com.gitee.sop.gatewaycommon.route; + +import com.alibaba.nacos.api.naming.pojo.Instance; + +/** + * @author tanghc + */ +public class NacosServiceHolder extends ServiceHolder { + + private Instance instance; + + public NacosServiceHolder(String serviceId, long lastUpdatedTimestamp, Instance instance) { + super(serviceId, lastUpdatedTimestamp); + this.instance = instance; + } + + public Instance getInstance() { + return instance; + } +} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/ServiceHolder.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/ServiceHolder.java index 1febf746..a5641f08 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/ServiceHolder.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/ServiceHolder.java @@ -2,10 +2,29 @@ package com.gitee.sop.gatewaycommon.route; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.Getter; +import lombok.Setter; -@Data +import java.util.Objects; + +@Getter +@Setter @AllArgsConstructor public class ServiceHolder { private String serviceId; private long lastUpdatedTimestamp; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ServiceHolder that = (ServiceHolder) o; + return lastUpdatedTimestamp == that.lastUpdatedTimestamp && + serviceId.equals(that.serviceId); + } + + @Override + public int hashCode() { + return Objects.hash(serviceId, lastUpdatedTimestamp); + } } \ No newline at end of file diff --git a/sop-common/sop-service-common/pom.xml b/sop-common/sop-service-common/pom.xml index e32c138e..a9c89991 100644 --- a/sop-common/sop-service-common/pom.xml +++ b/sop-common/sop-service-common/pom.xml @@ -42,6 +42,11 @@ + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + true + org.springframework spring-webmvc @@ -97,6 +102,7 @@ javax.servlet-api provided + diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/BaseServiceConfiguration.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/BaseServiceConfiguration.java index 84534a9d..87c26fde 100644 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/BaseServiceConfiguration.java +++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/BaseServiceConfiguration.java @@ -1,5 +1,7 @@ package com.gitee.sop.servercommon.configuration; +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.cloud.nacos.discovery.NacosWatch; import com.gitee.sop.servercommon.bean.ServiceConfig; import com.gitee.sop.servercommon.interceptor.ServiceContextInterceptor; import com.gitee.sop.servercommon.manager.EnvironmentContext; @@ -7,13 +9,16 @@ import com.gitee.sop.servercommon.manager.ServiceRouteController; import com.gitee.sop.servercommon.mapping.ApiMappingHandlerMapping; import com.gitee.sop.servercommon.message.ServiceErrorFactory; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations; import org.springframework.context.annotation.Bean; import org.springframework.core.env.Environment; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; +import org.springframework.scheduling.TaskScheduler; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @@ -21,6 +26,7 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl import javax.annotation.PostConstruct; import java.nio.charset.StandardCharsets; +import java.text.SimpleDateFormat; import java.util.List; /** @@ -84,6 +90,15 @@ public class BaseServiceConfiguration implements WebMvcConfigurer, WebMvcRegistr return new ServiceRouteController(); } + @Bean + @ConditionalOnMissingBean + @ConditionalOnProperty("spring.cloud.nacos.discovery.server-addr") + public NacosWatch nacosWatch(NacosDiscoveryProperties nacosDiscoveryProperties, ObjectProvider taskScheduler) { + // 在元数据中新增启动时间,不能修改这个值,不然网关拉取接口会有问题 + nacosDiscoveryProperties.getMetadata().put("time.startup", String.valueOf(System.currentTimeMillis())); + return new NacosWatch(nacosDiscoveryProperties, taskScheduler); + } + @PostConstruct public final void after() { log.info("-----spring容器加载完毕-----");