pull/2/MERGE
tanghc 5 years ago
parent 3118f07bbc
commit 2d10d58935
  1. 21
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/IndexFilter.java
  2. 23
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/EurekaRegistryListener.java
  3. 132
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/NacosRegistryListener.java
  4. 20
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/NacosServiceHolder.java
  5. 21
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/route/ServiceHolder.java
  6. 6
      sop-common/sop-service-common/pom.xml
  7. 15
      sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/BaseServiceConfiguration.java

@ -32,6 +32,8 @@ import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import java.net.URI; import java.net.URI;
import java.util.Arrays;
import java.util.List;
import java.util.Objects; import java.util.Objects;
/** /**
@ -44,7 +46,11 @@ import java.util.Objects;
public class IndexFilter implements WebFilter { public class IndexFilter implements WebFilter {
private static final String REST_PATH_PREFIX = "/rest"; private static final String REST_PATH_PREFIX = "/rest";
private static final String SOP_PATH_PREFIX = "/sop";
/** 路径白名单 */
private static List<String> PATH_WHITE_LIST = Arrays.asList(
"/sop", "/actuator"
);
@Value("${sop.gateway-index-path:/}") @Value("${sop.gateway-index-path:/}")
private String indexPath; private String indexPath;
@ -59,8 +65,8 @@ public class IndexFilter implements WebFilter {
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
ServerHttpRequest request = exchange.getRequest(); ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath(); String path = request.getURI().getPath();
// SOP路径,直接放行 // 路径是否在白名单中,直接放行
if (path.startsWith(SOP_PATH_PREFIX)) { if (this.isPathInWhiteList(path)) {
return chain.filter(exchange); return chain.filter(exchange);
} }
// 如果是restful请求,直接转发 // 如果是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) { private void doValidate(ServerWebExchange exchange, ApiParam apiParam) {
try { try {
validator.validate(apiParam); validator.validate(apiParam);

@ -5,6 +5,7 @@ import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.shared.Application; import com.netflix.discovery.shared.Application;
import com.netflix.discovery.shared.Applications; import com.netflix.discovery.shared.Applications;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.eureka.CloudEurekaClient; import org.springframework.cloud.netflix.eureka.CloudEurekaClient;
import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEvent;
@ -23,6 +24,9 @@ public class EurekaRegistryListener extends BaseRegistryListener {
private Set<ServiceHolder> cacheServices = new HashSet<>(); private Set<ServiceHolder> cacheServices = new HashSet<>();
@Autowired(required = false)
private List<RegistryEvent> registryEventList;
@Override @Override
public void onEvent(ApplicationEvent applicationEvent) { public void onEvent(ApplicationEvent applicationEvent) {
Object source = applicationEvent.getSource(); Object source = applicationEvent.getSource();
@ -57,10 +61,8 @@ public class EurekaRegistryListener extends BaseRegistryListener {
} }
Set<String> removedServiceIdList = getRemovedServiceId(serviceList); Set<String> removedServiceIdList = getRemovedServiceId(serviceList);
// 如果有服务删除 // 如果有服务下线
if (removedServiceIdList.size() > 0) { this.doRemove(removedServiceIdList);
this.doRemove(removedServiceIdList);
}
cacheServices = new HashSet<>(serviceList); cacheServices = new HashSet<>(serviceList);
} }
@ -106,12 +108,23 @@ public class EurekaRegistryListener extends BaseRegistryListener {
instanceDefinition.setPort(instanceInfo.getPort()); instanceDefinition.setPort(instanceInfo.getPort());
instanceDefinition.setMetadata(instanceInfo.getMetadata()); instanceDefinition.setMetadata(instanceInfo.getMetadata());
pullRoutes(instanceDefinition); pullRoutes(instanceDefinition);
if (registryEventList != null) {
registryEventList.forEach(registryEvent -> registryEvent.onRegistry(instanceDefinition));
}
} }
}); });
} }
private void doRemove(Set<String> deletedServices) { private void doRemove(Set<String> deletedServices) {
deletedServices.forEach(this::removeRoutes); if (deletedServices == null) {
return;
}
deletedServices.forEach(serviceId -> {
this.removeRoutes(serviceId);
if (registryEventList != null) {
registryEventList.forEach(registryEvent -> registryEvent.onRemove(serviceId));
}
});
} }
} }

@ -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.Instance;
import com.alibaba.nacos.api.naming.pojo.ListView; import com.alibaba.nacos.api.naming.pojo.ListView;
import com.gitee.sop.gatewaycommon.bean.InstanceDefinition; import com.gitee.sop.gatewaycommon.bean.InstanceDefinition;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEvent;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -28,7 +27,9 @@ import java.util.stream.Collectors;
@Slf4j @Slf4j
public class NacosRegistryListener extends BaseRegistryListener { public class NacosRegistryListener extends BaseRegistryListener {
private volatile Set<String> cacheServices = new HashSet<>(); private static final String METADATA_KEY_TIME_STARTUP = "time.startup";
private volatile Set<NacosServiceHolder> cacheServices = new HashSet<>();
@Autowired @Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties; private NacosDiscoveryProperties nacosDiscoveryProperties;
@ -38,6 +39,41 @@ public class NacosRegistryListener extends BaseRegistryListener {
@Override @Override
public synchronized void onEvent(ApplicationEvent applicationEvent) { public synchronized void onEvent(ApplicationEvent applicationEvent) {
List<NacosServiceHolder> serviceList = this.getServiceList();
final Set<NacosServiceHolder> currentServices = new HashSet<>(serviceList);
// 删除现有的,剩下的就是新服务
currentServices.removeAll(cacheServices);
// 如果有新的服务注册进来
if (currentServices.size() > 0) {
currentServices.forEach(nacosServiceHolder -> {
Instance instance = nacosServiceHolder.getInstance();
InstanceDefinition instanceDefinition = new InstanceDefinition();
instanceDefinition.setInstanceId(instance.getInstanceId());
instanceDefinition.setServiceId(nacosServiceHolder.getServiceId());
instanceDefinition.setIp(instance.getIp());
instanceDefinition.setPort(instance.getPort());
instanceDefinition.setMetadata(instance.getMetadata());
pullRoutes(instanceDefinition);
if (registryEventList != null) {
registryEventList.forEach(registryEvent -> registryEvent.onRegistry(instanceDefinition));
}
});
}
// 如果有服务下线
Set<String> removedServiceIdList = getRemovedServiceId(serviceList);
// 移除
this.doRemove(removedServiceIdList);
cacheServices = new HashSet<>(serviceList);
}
/**
* 获取建康的服务实例
* @return 没有返回空的list
*/
private List<NacosServiceHolder> getServiceList() {
NamingService namingService = nacosDiscoveryProperties.namingServiceInstance(); NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();
ListView<String> servicesOfServer = null; ListView<String> servicesOfServer = null;
try { try {
@ -46,67 +82,42 @@ public class NacosRegistryListener extends BaseRegistryListener {
log.error("namingService.getServicesOfServer()错误", e); log.error("namingService.getServicesOfServer()错误", e);
} }
if (servicesOfServer == null || CollectionUtils.isEmpty(servicesOfServer.getData())) { if (servicesOfServer == null || CollectionUtils.isEmpty(servicesOfServer.getData())) {
return; return Collections.emptyList();
} }
return servicesOfServer
Map<String, Instance> instanceMap = servicesOfServer
.getData() .getData()
.stream() .stream()
.filter(this::canOperator) .filter(this::canOperator)
.map(serviceName -> { .map(serviceName -> {
try { try {
// 获取服务实例
List<Instance> allInstances = namingService.getAllInstances(serviceName); List<Instance> allInstances = namingService.getAllInstances(serviceName);
if (CollectionUtils.isEmpty(allInstances)) { if (CollectionUtils.isEmpty(allInstances)) {
return null; return null;
} }
Instance instance = allInstances.stream() Instance instance = allInstances.stream()
// 只获取建康实例
.filter(Instance::isHealthy) .filter(Instance::isHealthy)
.findFirst() // 根据启动时间倒叙,找到最新的服务器
.max(Comparator.comparing(ins -> {
String startupTime = ins.getMetadata().getOrDefault(METADATA_KEY_TIME_STARTUP, "0");
return Long.valueOf(startupTime);
}))
.orElse(null); .orElse(null);
return instance == null ? null : new InstanceInfo(serviceName, instance); 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) { } catch (NacosException e) {
log.error("namingService.getAllInstances(serviceName)错误,serviceName:{}", serviceName, e); log.error("namingService.getAllInstances(serviceName)错误,serviceName:{}", serviceName, e);
return null; return null;
} }
}) })
.filter(Objects::nonNull) .filter(Objects::nonNull)
.collect(Collectors.toMap(InstanceInfo::getServiceName, InstanceInfo::getInstance)); .collect(Collectors.toList());
Set<String> serviceNames = instanceMap.keySet();
final Set<String> currentServices = new HashSet<>(serviceNames);
// 删除现有的,剩下的就是新服务
currentServices.removeAll(cacheServices);
// 如果有新的服务注册进来
if (currentServices.size() > 0) {
currentServices.forEach(serviceName -> {
Instance instance = instanceMap.get(serviceName);
InstanceDefinition instanceDefinition = new InstanceDefinition();
instanceDefinition.setInstanceId(instance.getInstanceId());
instanceDefinition.setServiceId(serviceName);
instanceDefinition.setIp(instance.getIp());
instanceDefinition.setPort(instance.getPort());
instanceDefinition.setMetadata(instance.getMetadata());
pullRoutes(instanceDefinition);
if (registryEventList != null) {
registryEventList.forEach(registryEvent -> registryEvent.onRegistry(instanceDefinition));
}
});
}
// 如果有服务删除
Set<String> removedServiceIdList = getRemovedServiceId(serviceNames);
if (removedServiceIdList.size() > 0) {
removedServiceIdList.forEach(serviceId->{
this.removeRoutes(serviceId);
if (registryEventList != null) {
registryEventList.forEach(registryEvent -> registryEvent.onRemove(serviceId));
}
});
}
cacheServices = new HashSet<>(serviceNames);
} }
/** /**
@ -115,18 +126,29 @@ public class NacosRegistryListener extends BaseRegistryListener {
* @param serviceList 最新的serviceId集合 * @param serviceList 最新的serviceId集合
* @return 返回已下线的serviceId * @return 返回已下线的serviceId
*/ */
private Set<String> getRemovedServiceId(Set<String> serviceList) { private Set<String> getRemovedServiceId(List<NacosServiceHolder> serviceList) {
Set<String> cache = cacheServices; Set<String> cache = cacheServices.stream()
// 删除最新的,剩下就是已经删除的 .map(NacosServiceHolder::getServiceId)
cache.removeAll(serviceList); .collect(Collectors.toSet());
Set<String> newList = serviceList.stream()
.map(NacosServiceHolder::getServiceId)
.collect(Collectors.toSet());
cache.removeAll(newList);
return cache; return cache;
} }
@Data private void doRemove(Set<String> deletedServices) {
@AllArgsConstructor if (deletedServices == null) {
private static class InstanceInfo { return;
private String serviceName; }
private Instance instance; deletedServices.forEach(serviceId -> {
this.removeRoutes(serviceId);
if (registryEventList != null) {
registryEventList.forEach(registryEvent -> registryEvent.onRemove(serviceId));
}
});
} }
} }

@ -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;
}
}

@ -2,10 +2,29 @@ package com.gitee.sop.gatewaycommon.route;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.Getter;
import lombok.Setter;
@Data import java.util.Objects;
@Getter
@Setter
@AllArgsConstructor @AllArgsConstructor
public class ServiceHolder { public class ServiceHolder {
private String serviceId; private String serviceId;
private long lastUpdatedTimestamp; 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);
}
} }

@ -42,6 +42,11 @@
</dependency> </dependency>
<!-- optional --> <!-- optional -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<optional>true</optional>
</dependency>
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId> <artifactId>spring-webmvc</artifactId>
@ -97,6 +102,7 @@
<artifactId>javax.servlet-api</artifactId> <artifactId>javax.servlet-api</artifactId>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
</dependencies> </dependencies>

@ -1,5 +1,7 @@
package com.gitee.sop.servercommon.configuration; 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.bean.ServiceConfig;
import com.gitee.sop.servercommon.interceptor.ServiceContextInterceptor; import com.gitee.sop.servercommon.interceptor.ServiceContextInterceptor;
import com.gitee.sop.servercommon.manager.EnvironmentContext; 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.mapping.ApiMappingHandlerMapping;
import com.gitee.sop.servercommon.message.ServiceErrorFactory; import com.gitee.sop.servercommon.message.ServiceErrorFactory;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 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.boot.autoconfigure.web.servlet.WebMvcRegistrations;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter; 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.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 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 javax.annotation.PostConstruct;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.List; import java.util.List;
/** /**
@ -84,6 +90,15 @@ public class BaseServiceConfiguration implements WebMvcConfigurer, WebMvcRegistr
return new ServiceRouteController(); return new ServiceRouteController();
} }
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty("spring.cloud.nacos.discovery.server-addr")
public NacosWatch nacosWatch(NacosDiscoveryProperties nacosDiscoveryProperties, ObjectProvider<TaskScheduler> taskScheduler) {
// 在元数据中新增启动时间,不能修改这个值,不然网关拉取接口会有问题
nacosDiscoveryProperties.getMetadata().put("time.startup", String.valueOf(System.currentTimeMillis()));
return new NacosWatch(nacosDiscoveryProperties, taskScheduler);
}
@PostConstruct @PostConstruct
public final void after() { public final void after() {
log.info("-----spring容器加载完毕-----"); log.info("-----spring容器加载完毕-----");

Loading…
Cancel
Save