路由管理优化

1.x
tanghc 6 years ago
parent d1f33d4e19
commit 2138ff6fda
  1. 32
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/AbstractTargetRoute.java
  2. 6
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/BaseServiceRouteInfo.java
  3. 29
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ServiceApiInfo.java
  4. 38
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ServiceRouteRepository.java
  5. 12
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/TargetRoute.java
  6. 5
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/configuration/BaseGatewayConfiguration.java
  7. 31
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayContext.java
  8. 87
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayRouteRepository.java
  9. 13
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayTargetRoute.java
  10. 13
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayZookeeperRouteManager.java
  11. 180
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/BaseRouteManager.java
  12. 27
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteDefinitionItemContext.java
  13. 2
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteManager.java
  14. 32
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteRepository.java
  15. 19
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteRepositoryContext.java
  16. 15
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ApiParamFactory.java
  17. 8
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/BaseZuulConfiguration.java
  18. 4
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/BaseZuulFilter.java
  19. 5
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/PreValidateFilter.java
  20. 12
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/route/SopRouteLocator.java
  21. 45
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/route/ZuulRouteRepository.java
  22. 1
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/route/ZuulServiceRouteInfo.java
  23. 16
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/route/ZuulTargetRoute.java
  24. 15
      sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/route/ZuulZookeeperRouteManager.java
  25. 31
      sop-gateway/src/test/java/com/gitee/sop/gateway/CuratorTest.java
  26. 127
      sop-gateway/src/test/java/com/gitee/sop/gateway/PostTest.java
  27. 121
      sop-gateway/src/test/java/com/gitee/sop/gateway/SopPostTest.java
  28. 102
      sop-gateway/src/test/java/com/gitee/sop/gateway/TestBase.java
  29. 4
      sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/ServiceApiInfo.java
  30. 14
      sop-service-common/src/main/java/com/gitee/sop/servercommon/manager/DefaultRequestMappingEvent.java
  31. 4
      sop-service-common/src/main/java/com/gitee/sop/servercommon/manager/RedisApiMetaManager.java
  32. 76
      sop-service-common/src/main/java/com/gitee/sop/servercommon/manager/ServiceZookeeperApiMetaManager.java
  33. 4
      sop-service-common/src/main/java/com/gitee/sop/servercommon/route/GatewayFilterDefinition.java
  34. 6
      sop-service-common/src/main/java/com/gitee/sop/servercommon/route/ServiceRouteInfo.java
  35. 4
      sop-test/src/test/java/com/gitee/sop/AlipayClientPostTest.java

@ -0,0 +1,32 @@
package com.gitee.sop.gatewaycommon.bean;
/**
* @author tanghc
*/
public abstract class AbstractTargetRoute<R extends BaseServiceRouteInfo, E extends BaseRouteDefinition, T> implements TargetRoute<R,E, T> {
private R serviceRouteInfo;
private E routeDefinition;
private T targetRoute;
public AbstractTargetRoute(R serviceRouteInfo, E routeDefinition, T targetRoute) {
this.serviceRouteInfo = serviceRouteInfo;
this.routeDefinition = routeDefinition;
this.targetRoute = targetRoute;
}
@Override
public R getServiceRouteInfo() {
return serviceRouteInfo;
}
@Override
public E getRouteDefinition() {
return routeDefinition;
}
@Override
public T getTargetRouteDefinition() {
return targetRoute;
}
}

@ -2,6 +2,7 @@ package com.gitee.sop.gatewaycommon.bean;
import lombok.Data;
import java.util.Collections;
import java.util.List;
/**
@ -9,7 +10,6 @@ import java.util.List;
*/
@Data
public class BaseServiceRouteInfo<T extends BaseRouteDefinition> {
private String appName;
private String md5;
private List<T> routeDefinitionList;
private String serviceId;
private List<T> routeDefinitionList = Collections.emptyList();
}

@ -1,29 +0,0 @@
package com.gitee.sop.gatewaycommon.bean;
import lombok.Data;
import java.util.List;
/**
* @author tanghc
*/
@Data
public class ServiceApiInfo {
private String md5;
private String appName;
private List<ApiMeta> apis;
@Data
public static class ApiMeta {
/** 接口名 */
private String name;
/** 请求path */
private String path;
/** 版本号 */
private String version;
public String fetchNameVersion() {
return name + version;
}
}
}

@ -1,38 +0,0 @@
package com.gitee.sop.gatewaycommon.bean;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
/**
* @author tanghc
*/
public abstract class ServiceRouteRepository<R, E> {
/**
* key:serviceId
*/
private Map<String, Set<E>> serviceRouteMap = new ConcurrentHashMap<>();
public abstract String getServiceId(R r);
public void saveRouteDefinition(R serviceRouteInfo, E definition) {
String serverId = getServiceId(serviceRouteInfo);
Set<E> routeDefinitionSet = serviceRouteMap.putIfAbsent(serverId, new HashSet<>(16));
if (routeDefinitionSet == null) {
routeDefinitionSet = serviceRouteMap.get(serverId);
}
routeDefinitionSet.add(definition);
}
public synchronized void deleteAll(R serviceRouteInfo, Consumer<E> consumer) {
String serverId = getServiceId(serviceRouteInfo);
Set<E> definitionSet = serviceRouteMap.getOrDefault(serverId, Collections.emptySet());
for (E routeDefinition : definitionSet) {
consumer.accept(routeDefinition);
}
definitionSet.clear();
}
}

@ -0,0 +1,12 @@
package com.gitee.sop.gatewaycommon.bean;
/**
* @author tanghc
*/
public interface TargetRoute<R extends BaseServiceRouteInfo, E extends BaseRouteDefinition, T> {
R getServiceRouteInfo();
E getRouteDefinition();
T getTargetRouteDefinition();
}

@ -9,6 +9,7 @@ import com.gitee.sop.gatewaycommon.gateway.route.GatewayRouteRepository;
import com.gitee.sop.gatewaycommon.gateway.route.NameVersionRoutePredicateFactory;
import com.gitee.sop.gatewaycommon.gateway.route.ReadBodyRoutePredicateFactory;
import com.gitee.sop.gatewaycommon.gateway.route.GatewayZookeeperRouteManager;
import com.gitee.sop.gatewaycommon.manager.RouteRepositoryContext;
import com.gitee.sop.gatewaycommon.message.ErrorFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
@ -97,7 +98,9 @@ public class BaseGatewayConfiguration {
@Bean
GatewayRouteRepository gatewayRouteRepository() {
return new GatewayRouteRepository();
GatewayRouteRepository gatewayRouteRepository = new GatewayRouteRepository();
RouteRepositoryContext.setRouteRepository(gatewayRouteRepository);
return gatewayRouteRepository;
}

@ -1,31 +0,0 @@
package com.gitee.sop.gatewaycommon.gateway.route;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
/**
* @author tanghc
*/
@Getter
@Setter
@ToString
public class GatewayContext {
public static final String CACHE_GATEWAY_CONTEXT = "cacheGatewayContext";
/**
* cache json body
*/
private String cacheBody;
/**
* cache formdata
*/
private MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
/**
* cache reqeust path
*/
private String path;
}

@ -1,6 +1,5 @@
package com.gitee.sop.gatewaycommon.gateway.route;
import com.gitee.sop.gatewaycommon.bean.ServiceRouteRepository;
import com.gitee.sop.gatewaycommon.manager.RouteRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
@ -13,19 +12,23 @@ import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory;
import org.springframework.cloud.gateway.route.InMemoryRouteDefinitionRepository;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.cloud.gateway.support.ConfigurationUtils;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.core.convert.ConversionService;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.validation.Validator;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import static java.util.Collections.synchronizedMap;
/**
* 路由存储管理负责动态更新路由
@ -33,19 +36,14 @@ import java.util.concurrent.ConcurrentHashMap;
* @author thc
*/
@Slf4j
public class GatewayRouteRepository extends InMemoryRouteDefinitionRepository
implements ApplicationEventPublisherAware,
public class GatewayRouteRepository implements ApplicationEventPublisherAware,
RouteDefinitionRepository,
BeanFactoryAware,
RouteRepository<GatewayServiceRouteInfo, RouteDefinition> {
RouteRepository<GatewayTargetRoute> {
private final SpelExpressionParser parser = new SpelExpressionParser();
private final Map<String, GatewayTargetRoute> routes = synchronizedMap(new LinkedHashMap<>());
private ServiceRouteRepository<GatewayServiceRouteInfo, RouteDefinition> serviceRouteRepository = new ServiceRouteRepository<GatewayServiceRouteInfo, RouteDefinition>() {
@Override
public String getServiceId(GatewayServiceRouteInfo serviceRouteInfo) {
return serviceRouteInfo.getAppName();
}
};
private final SpelExpressionParser parser = new SpelExpressionParser();
@Autowired
private ConversionService conversionService;
@ -57,31 +55,54 @@ public class GatewayRouteRepository extends InMemoryRouteDefinitionRepository
private BeanFactory beanFactory;
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
List<RouteDefinition> list = routes.values().parallelStream()
.map(targetRoute -> targetRoute.getTargetRouteDefinition())
.collect(Collectors.toList());
return Flux.fromIterable(list);
}
@Override
public Mono<Void> save(Mono<RouteDefinition> route) {
return null;
}
@Override
public Mono<Void> delete(Mono<String> routeId) {
return null;
}
/**
* 根据ID获取路由
*/
@Override
public RouteDefinition get(String id) {
return getRouteDefinitions()
.filter(routeDefinition -> {
return routeDefinition.getId().equals(id);
}).blockFirst();
public GatewayTargetRoute get(String id) {
return routes.get(id);
}
/**
* 增加路由
*/
@Override
public String add(GatewayServiceRouteInfo serviceRouteInfo, RouteDefinition definition) {
super.save(Mono.just(definition)).subscribe();
serviceRouteRepository.saveRouteDefinition(serviceRouteInfo, definition);
this.initPredicateDefinition(definition);
public String add(GatewayTargetRoute targetRoute) {
GatewayRouteDefinition baseRouteDefinition = targetRoute.getRouteDefinition();
routes.put(baseRouteDefinition.getId(), targetRoute);
this.initPredicateDefinition(targetRoute);
this.publisher.publishEvent(new RefreshRoutesEvent(this));
return "success";
}
protected void initPredicateDefinition(RouteDefinition definition) {
for (PredicateDefinition predicate : definition.getPredicates()) {
@Override
public void update(GatewayTargetRoute targetRoute) {
GatewayRouteDefinition baseRouteDefinition = targetRoute.getRouteDefinition();
routes.put(baseRouteDefinition.getId(), targetRoute);
}
protected void initPredicateDefinition(GatewayTargetRoute targetRoute) {
GatewayRouteDefinition routeDefinition = targetRoute.getRouteDefinition();
RouteDefinition targetRouteDefinition = targetRoute.getTargetRouteDefinition();
for (PredicateDefinition predicate : targetRouteDefinition.getPredicates()) {
Map<String, String> args = predicate.getArgs();
if (!args.isEmpty()) {
RoutePredicateFactory<NameVersionRoutePredicateFactory.Config> factory = new NameVersionRoutePredicateFactory();
@ -89,7 +110,7 @@ public class GatewayRouteRepository extends InMemoryRouteDefinitionRepository
Object config = factory.newConfig();
ConfigurationUtils.bind(config, properties, factory.shortcutFieldPrefix(), predicate.getName(),
validator, conversionService);
this.publisher.publishEvent(new PredicateArgsEvent(this, definition.getId(), properties));
this.publisher.publishEvent(new PredicateArgsEvent(this, routeDefinition.getId(), properties));
}
}
@ -100,15 +121,19 @@ public class GatewayRouteRepository extends InMemoryRouteDefinitionRepository
*/
@Override
public void delete(String id) {
super.delete(Mono.just(id));
routes.remove(id);
this.publisher.publishEvent(new PredicateArgsEvent(this, id, Collections.emptyMap()));
}
@Override
public void deleteAll(GatewayServiceRouteInfo serviceRouteInfo) {
serviceRouteRepository.deleteAll(serviceRouteInfo, routeDefinition -> {
this.delete(routeDefinition.getId());
});
public void deleteAll(String serviceId) {
List<String> idList = this.routes.values().stream()
.map(zuulTargetRoute -> zuulTargetRoute.getRouteDefinition().getId())
.collect(Collectors.toList());
for (String id : idList) {
this.delete(id);
}
}
@Override

@ -0,0 +1,13 @@
package com.gitee.sop.gatewaycommon.gateway.route;
import com.gitee.sop.gatewaycommon.bean.AbstractTargetRoute;
import org.springframework.cloud.gateway.route.RouteDefinition;
/**
* @author tanghc
*/
public class GatewayTargetRoute extends AbstractTargetRoute<GatewayServiceRouteInfo, GatewayRouteDefinition, RouteDefinition> {
public GatewayTargetRoute(GatewayServiceRouteInfo baseServiceRouteInfo, GatewayRouteDefinition baseRouteDefinition, RouteDefinition targetRoute) {
super(baseServiceRouteInfo, baseRouteDefinition, targetRoute);
}
}

@ -21,9 +21,9 @@ import java.util.List;
*/
@Getter
@Slf4j
public class GatewayZookeeperRouteManager extends BaseRouteManager<GatewayServiceRouteInfo, GatewayRouteDefinition, RouteDefinition> {
public class GatewayZookeeperRouteManager extends BaseRouteManager<GatewayServiceRouteInfo, GatewayRouteDefinition, GatewayTargetRoute> {
public GatewayZookeeperRouteManager(Environment environment, RouteRepository<GatewayServiceRouteInfo, RouteDefinition> routeRepository) {
public GatewayZookeeperRouteManager(Environment environment, RouteRepository<GatewayTargetRoute> routeRepository) {
super(environment, routeRepository);
}
@ -33,7 +33,12 @@ public class GatewayZookeeperRouteManager extends BaseRouteManager<GatewayServic
}
@Override
protected RouteDefinition buildRouteDefinition(GatewayServiceRouteInfo serviceRouteInfo,GatewayRouteDefinition gatewayRouteDefinition) {
protected Class<GatewayRouteDefinition> getRouteDefinitionClass() {
return GatewayRouteDefinition.class;
}
@Override
protected GatewayTargetRoute buildRouteDefinition(GatewayServiceRouteInfo serviceRouteInfo, GatewayRouteDefinition gatewayRouteDefinition) {
RouteDefinition routeDefinition = new RouteDefinition();
routeDefinition.setId(gatewayRouteDefinition.getId());
routeDefinition.setUri(URI.create(gatewayRouteDefinition.getUri()));
@ -54,7 +59,7 @@ public class GatewayZookeeperRouteManager extends BaseRouteManager<GatewayServic
routeDefinition.setFilters(filterDefinitionList);
routeDefinition.setPredicates(predicateDefinitionList);
return routeDefinition;
return new GatewayTargetRoute(serviceRouteInfo, gatewayRouteDefinition, routeDefinition);
}
}

@ -3,6 +3,7 @@ package com.gitee.sop.gatewaycommon.manager;
import com.alibaba.fastjson.JSON;
import com.gitee.sop.gatewaycommon.bean.BaseRouteDefinition;
import com.gitee.sop.gatewaycommon.bean.BaseServiceRouteInfo;
import com.gitee.sop.gatewaycommon.bean.TargetRoute;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
@ -20,7 +21,14 @@ import static com.gitee.sop.gatewaycommon.bean.SopConstants.SOP_SERVICE_ROUTE_PA
/**
* 路由管理
* 路由管理采用zookeeper实现监听路由的增删改并适时更新到本地路由的存储格式为
* <pre>
* /sop-service-route 根节点
* /serviceId 服务节点名字为服务名
* /route1 路由节点名字为name+version存放路由信息
* /route2
* /...
* </pre>
*
* @param <R> 路由根对象可以理解为最外面的大json{....,routeDefinitionList:[]}
* @param <E> 路由Item对象对应大json里面的具体路由信息routeDefinitionList:[]
@ -28,19 +36,38 @@ import static com.gitee.sop.gatewaycommon.bean.SopConstants.SOP_SERVICE_ROUTE_PA
* @author tanghc
*/
@Slf4j
public abstract class BaseRouteManager<R extends BaseServiceRouteInfo<E>, E extends BaseRouteDefinition, T> implements RouteManager {
public abstract class BaseRouteManager<R extends BaseServiceRouteInfo<E>, E extends BaseRouteDefinition, T extends TargetRoute> implements RouteManager {
protected String sopServiceApiPath = SOP_SERVICE_ROUTE_PATH;
protected String sopRouteRootPath = SOP_SERVICE_ROUTE_PATH;
protected Environment environment;
protected RouteRepository<R, T> routeRepository;
protected RouteRepository<T> routeRepository;
/**
* 返回路由根对象class
*
* @return
*/
protected abstract Class<R> getServiceRouteInfoClass();
/**
* 返回路由Item对象class
*
* @return
*/
protected abstract Class<E> getRouteDefinitionClass();
/**
* 构建目标路由对象zuul和gateway定义的路由对象
*
* @param serviceRouteInfo
* @param routeDefinition
* @return
*/
protected abstract T buildRouteDefinition(R serviceRouteInfo, E routeDefinition);
public BaseRouteManager(Environment environment, RouteRepository<R, T> routeRepository) {
public BaseRouteManager(Environment environment, RouteRepository<T> routeRepository) {
this.environment = environment;
this.routeRepository = routeRepository;
}
@ -65,18 +92,25 @@ public abstract class BaseRouteManager<R extends BaseServiceRouteInfo<E>, E exte
.orSetData()
// 如果指定节点的父节点不存在,则Curator将会自动级联创建父节点
.creatingParentContainersIfNeeded()
.forPath(sopServiceApiPath, "".getBytes());
.forPath(sopRouteRootPath, "".getBytes());
this.watchChildren(client, sopServiceApiPath);
this.watchServiceChange(client, sopRouteRootPath);
} catch (Exception e) {
e.printStackTrace();
}
}
protected void watchChildren(CuratorFramework client, String sopServiceApiPath) throws Exception {
/**
* 监听微服务更改
*
* @param client
* @param sopServiceApiPath
* @throws Exception
*/
protected void watchServiceChange(CuratorFramework client, String sopServiceApiPath) throws Exception {
// 为子节点添加watcher
// PathChildrenCache: 监听数据节点的增删改,可以设置触发的事件
final PathChildrenCache childrenCache = new PathChildrenCache(client, sopServiceApiPath, true);
PathChildrenCache childrenCache = new PathChildrenCache(client, sopServiceApiPath, true);
/**
* StartMode: 初始化方式
@ -88,17 +122,19 @@ public abstract class BaseRouteManager<R extends BaseServiceRouteInfo<E>, E exte
// 列出子节点数据列表,需要使用BUILD_INITIAL_CACHE同步初始化模式才能获得,异步是获取不到的
List<ChildData> childDataList = childrenCache.getCurrentData();
log.info("微服务API详细数据列表:");
log.info("获取service列表:");
for (ChildData childData : childDataList) {
String nodeData = new String(childData.getData());
log.info("\t* 子节点路径:" + childData.getPath() + ",该节点的数据为:" + nodeData);
R serviceRouteInfo = JSON.parseObject(nodeData, getServiceRouteInfoClass());
saveRouteDefinitionList(serviceRouteInfo);
String serviceNodeData = new String(childData.getData());
R serviceRouteInfo = JSON.parseObject(serviceNodeData, getServiceRouteInfoClass());
String servicePath = childData.getPath();
log.info("\t* service节点路径:" + servicePath + ",该节点的数据为:" + serviceNodeData);
this.loadServiceRouteItem(client, serviceRouteInfo, servicePath);
}
// 添加事件监听器
log.info("监听服务节点增删改,rootPath:{}", sopRouteRootPath);
// 监听根节点下面的子节点
childrenCache.getListenable().addListener(new PathChildrenCacheListener() {
@Override
public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent event) throws Exception {
public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
PathChildrenCacheEvent.Type type = event.getType();
synchronized (type) {
// 通过判断event type的方式来实现不同事件的触发
@ -106,41 +142,111 @@ public abstract class BaseRouteManager<R extends BaseServiceRouteInfo<E>, E exte
String nodeData = new String(event.getData().getData());
R serviceRouteInfo = JSON.parseObject(nodeData, getServiceRouteInfoClass());
// 添加子节点时触发
log.info("子节点:{}添加,数据为:{}", event.getData().getPath(), nodeData);
saveRouteDefinitionList(serviceRouteInfo);
String servicePath = event.getData().getPath();
log.info("新增serviceId节点:{},数据为:{}", servicePath, nodeData);
loadServiceRouteItem(client, serviceRouteInfo, servicePath);
} else if (PathChildrenCacheEvent.Type.CHILD_UPDATED.equals(type)) {
// 修改子节点数据时触发,暂时没有什么操作
String nodeData = new String(event.getData().getData());
R serviceRouteInfo = JSON.parseObject(nodeData, getServiceRouteInfoClass());
// 修改子节点数据时触发
log.info("子节点:{}修改,数据为:{}", event.getData().getPath(), nodeData);
// 删除下面所有节点
routeRepository.deleteAll(serviceRouteInfo);
// 添加新节点
saveRouteDefinitionList(serviceRouteInfo);
log.info("修改serviceId节点:{},数据为:{}", event.getData().getPath(), nodeData);
} else if (PathChildrenCacheEvent.Type.CHILD_REMOVED.equals(type)) {
// 删除service节点
String nodeData = new String(event.getData().getData());
log.info("子节点:{}删除,数据为:{}", event.getData().getPath(), nodeData);
log.info("删除serviceId节点:{},数据为:{}", event.getData().getPath(), nodeData);
R serviceRouteInfo = JSON.parseObject(nodeData, getServiceRouteInfoClass());
deleteRouteDefinitionList(serviceRouteInfo);
routeRepository.deleteAll(serviceRouteInfo.getServiceId());
}
}
}
});
}
protected void saveRouteDefinitionList(R serviceRouteInfo) {
for (E routeDefinitionItem : serviceRouteInfo.getRouteDefinitionList()) {
RouteDefinitionItemContext.add(routeDefinitionItem);
T routeDefinition = buildRouteDefinition(serviceRouteInfo, routeDefinitionItem);
routeRepository.add(serviceRouteInfo, routeDefinition);
/**
* 加载service下的路由信息
*
* @param servicePath
*/
protected void loadServiceRouteItem(CuratorFramework client, R serviceRouteInfo, String servicePath) throws Exception {
// 获取service节点下所有的路由节点,里面保存的是路由名称,前面没有斜杠"/"
List<String> pathNameList = client.getChildren().forPath(servicePath);
for (String pathName : pathNameList) {
// 完整的路径
String routeItemPath = servicePath + "/" + pathName;
byte[] routeItemData = client.getData().forPath(routeItemPath);
String routeDataJson = buildZookeeperData(routeItemData);
log.info("加载路由,path:{}, 数据为:{}", routeItemPath, routeDataJson);
this.saveRouteItem(serviceRouteInfo, routeDataJson);
}
this.watchRouteItems(client, serviceRouteInfo, servicePath);
}
protected void deleteRouteDefinitionList(R serviceRouteInfo) {
for (E routeDefinitionItem : serviceRouteInfo.getRouteDefinitionList()) {
RouteDefinitionItemContext.delete(routeDefinitionItem);
}
routeRepository.deleteAll(serviceRouteInfo);
/**
* 监听serviceId目录下面的子节点
*
* @param client
* @param serviceRouteInfo
* @param servicePath serviceId节点
*/
protected void watchRouteItems(CuratorFramework client, R serviceRouteInfo, String servicePath) throws Exception {
log.info("监听路由节点增删改,servicePath:{}", servicePath);
PathChildrenCache childrenCache = new PathChildrenCache(client, servicePath, true);
childrenCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
// 添加事件监听器
childrenCache.getListenable().addListener(new PathChildrenCacheListener() {
@Override
public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
PathChildrenCacheEvent.Type type = event.getType();
synchronized (type) {
// 通过判断event type的方式来实现不同事件的触发
if (PathChildrenCacheEvent.Type.CHILD_ADDED.equals(type)) {
// 新增单个路由
String routeDataJson = buildZookeeperData(event.getData().getData());
log.info("新增单个路由,serviceId:{}, route:{}", serviceRouteInfo.getServiceId(), routeDataJson);
saveRouteItem(serviceRouteInfo, routeDataJson);
} else if (PathChildrenCacheEvent.Type.CHILD_UPDATED.equals(type)) {
// 修改单个路由
String routeDataJson = buildZookeeperData(event.getData().getData());
log.info("修改单个路由,serviceId:{}, route:{}", serviceRouteInfo.getServiceId(), routeDataJson);
updateRouteItem(serviceRouteInfo, routeDataJson);
} else if (PathChildrenCacheEvent.Type.CHILD_REMOVED.equals(type)) {
// 删除单个路由
String routeDataJson = buildZookeeperData(event.getData().getData());
log.info("删除单个路由,serviceId:{}, route:{}", serviceRouteInfo.getServiceId(), routeDataJson);
deleteRouteItem(serviceRouteInfo, routeDataJson);
}
}
}
});
}
protected void saveRouteItem(R serviceRouteInfo, String nodeDataJson) {
T routeDefinition = getRouteDefinition(serviceRouteInfo, nodeDataJson);
routeRepository.add(routeDefinition);
}
protected void updateRouteItem(R serviceRouteInfo, String nodeDataJson) {
T routeDefinition = getRouteDefinition(serviceRouteInfo, nodeDataJson);
routeRepository.update(routeDefinition);
}
protected void deleteRouteItem(R serviceRouteInfo, String nodeDataJson) {
E routeDefinitionItem = getRouteDefinitionItem(nodeDataJson);
routeRepository.delete(routeDefinitionItem.getId());
}
protected T getRouteDefinition(R serviceRouteInfo, String nodeDataJson) {
E routeDefinitionItem = getRouteDefinitionItem(nodeDataJson);
T routeDefinition = buildRouteDefinition(serviceRouteInfo, routeDefinitionItem);
return routeDefinition;
}
protected E getRouteDefinitionItem(String nodeDataJson) {
return JSON.parseObject(nodeDataJson, getRouteDefinitionClass());
}
protected String buildZookeeperData(byte[] data) {
return new String(data);
}
}

@ -1,27 +0,0 @@
package com.gitee.sop.gatewaycommon.manager;
import com.gitee.sop.gatewaycommon.bean.BaseRouteDefinition;
import java.util.HashMap;
import java.util.Map;
/**
* @author tanghc
*/
public class RouteDefinitionItemContext {
// key:id
private static Map<String, BaseRouteDefinition> routeDefinitionMap = new HashMap<>(64);
public static void add(BaseRouteDefinition routeDefinition) {
routeDefinitionMap.put(routeDefinition.getId(), routeDefinition);
}
public static BaseRouteDefinition getRouteDefinition(String id) {
return routeDefinitionMap.get(id);
}
public static void delete(BaseRouteDefinition routeDefinition) {
routeDefinitionMap.remove(routeDefinition.getId());
}
}

@ -6,8 +6,6 @@ package com.gitee.sop.gatewaycommon.manager;
*/
public interface RouteManager {
String API_STORE_KEY = "com.gitee.sop.api";
/**
* 刷新素有的微服务接口信息
*/

@ -1,14 +1,40 @@
package com.gitee.sop.gatewaycommon.manager;
import com.gitee.sop.gatewaycommon.bean.TargetRoute;
/**
* @author tanghc
*/
public interface RouteRepository<R, T> {
public interface RouteRepository<T extends TargetRoute> {
/**
* 获取路由信息
* @param id 路由id
* @return
*/
T get(String id);
String add(R serviceRouteInfo, T route);
/**
* 添加路由
* @param targetRoute
* @return
*/
String add(T targetRoute);
/**
* 更新路由
* @param targetRoute
*/
void update(T targetRoute);
/**
* 删除路由
* @param id 路由id
*/
void delete(String id);
void deleteAll(R serviceRouteInfo);
/**
* 删除service下的所有路由
* @param serviceId
*/
void deleteAll(String serviceId);
}

@ -0,0 +1,19 @@
package com.gitee.sop.gatewaycommon.manager;
import com.gitee.sop.gatewaycommon.bean.TargetRoute;
/**
* @author tanghc
*/
public class RouteRepositoryContext {
private static RouteRepository<? extends TargetRoute> routeRepository;
public static RouteRepository<? extends TargetRoute> getRouteRepository() {
return routeRepository;
}
public static <T extends TargetRoute> void setRouteRepository(RouteRepository<T> routeRepository) {
RouteRepositoryContext.routeRepository = routeRepository;
}
}

@ -1,21 +1,32 @@
package com.gitee.sop.gatewaycommon.param;
import com.gitee.sop.gatewaycommon.bean.BaseRouteDefinition;
import com.gitee.sop.gatewaycommon.manager.RouteDefinitionItemContext;
import com.gitee.sop.gatewaycommon.bean.TargetRoute;
import com.gitee.sop.gatewaycommon.manager.RouteRepository;
import com.gitee.sop.gatewaycommon.manager.RouteRepositoryContext;
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
/**
* @author tanghc
*/
@Slf4j
public class ApiParamFactory {
public static ApiParam build(Map<String, ?> params) {
ApiParam apiParam = new ApiParam();
for (Map.Entry<String, ?> entry : params.entrySet()) {
apiParam.put(entry.getKey(), entry.getValue());
}
BaseRouteDefinition routeDefinition = RouteDefinitionItemContext.getRouteDefinition(apiParam.fetchNameVersion());
RouteRepository<? extends TargetRoute> routeRepository = RouteRepositoryContext.getRouteRepository();
if (routeRepository == null) {
log.error("RouteRepositoryContext.setRouteRepository()方法未使用");
throw ErrorEnum.AOP_UNKNOW_ERROR.getErrorMeta().getException();
}
TargetRoute targetRoute = routeRepository.get(apiParam.fetchNameVersion());
BaseRouteDefinition routeDefinition = targetRoute.getRouteDefinition();
if (routeDefinition == null) {
throw ErrorEnum.ISV_INVALID_METHOD.getErrorMeta().getException();
}

@ -1,13 +1,17 @@
package com.gitee.sop.gatewaycommon.zuul.configuration;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.bean.TargetRoute;
import com.gitee.sop.gatewaycommon.manager.RouteManager;
import com.gitee.sop.gatewaycommon.manager.RouteRepository;
import com.gitee.sop.gatewaycommon.manager.RouteRepositoryContext;
import com.gitee.sop.gatewaycommon.message.ErrorFactory;
import com.gitee.sop.gatewaycommon.zuul.filter.ErrorFilter;
import com.gitee.sop.gatewaycommon.zuul.filter.PostResultFilter;
import com.gitee.sop.gatewaycommon.zuul.filter.PreValidateFilter;
import com.gitee.sop.gatewaycommon.zuul.route.SopRouteLocator;
import com.gitee.sop.gatewaycommon.zuul.route.ZuulRouteRepository;
import com.gitee.sop.gatewaycommon.zuul.route.ZuulTargetRoute;
import com.gitee.sop.gatewaycommon.zuul.route.ZuulZookeeperRouteManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.ServerProperties;
@ -42,7 +46,9 @@ public class BaseZuulConfiguration {
*/
@Bean
ZuulRouteRepository zuulRouteRepository() {
return new ZuulRouteRepository();
ZuulRouteRepository zuulRouteRepository = new ZuulRouteRepository();
RouteRepositoryContext.setRouteRepository(zuulRouteRepository);
return zuulRouteRepository;
}
/**

@ -87,7 +87,7 @@ public abstract class BaseZuulFilter extends ZuulFilter {
/**
* to classify a filter by type. Standard types in Zuul are "pre" for pre-routing filtering,
* "route" for routing to an origin, "post" for post-routing filters, "error" for error handling.
* "routeDefinition" for routing to an origin, "post" for post-routing filters, "error" for error handling.
* We also support a "static" type for static responses see StaticResponseFilter.
* Any filterType made be created or added and doRun by calling FilterProcessor.runFilters(type)
*/
@ -95,7 +95,7 @@ public abstract class BaseZuulFilter extends ZuulFilter {
/** zuul过滤器pre类型 */
PRE("pre"),
/** zuul过滤器route类型 */
ROUTE("route"),
ROUTE("routeDefinition"),
/** zuul过滤器post类型 */
POST("post"),
/** zuul过滤器error类型 */

@ -2,19 +2,14 @@ package com.gitee.sop.gatewaycommon.zuul.filter;
import com.gitee.sop.gatewaycommon.bean.ApiContext;
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
import com.gitee.sop.gatewaycommon.bean.BaseRouteDefinition;
import com.gitee.sop.gatewaycommon.exception.ApiException;
import com.gitee.sop.gatewaycommon.manager.RouteDefinitionItemContext;
import com.gitee.sop.gatewaycommon.param.ApiParam;
import com.gitee.sop.gatewaycommon.validate.Validator;
import com.gitee.sop.gatewaycommon.zuul.ZuulContext;
import com.gitee.sop.gatewaycommon.zuul.route.ZuulRouteDefinition;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import javax.servlet.http.HttpServletRequest;
/**
* 前置校验
* @author tanghc

@ -9,6 +9,7 @@ import org.springframework.core.Ordered;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
* 路由定位
@ -29,7 +30,10 @@ public class SopRouteLocator implements RouteLocator, Ordered {
@Override
public List<Route> getRoutes() {
return zuulRouteRepository.listAll();
return zuulRouteRepository.listAll()
.parallelStream()
.map(zuulTargetRoute -> zuulTargetRoute.getTargetRouteDefinition())
.collect(Collectors.toList());
}
/**
@ -41,7 +45,11 @@ public class SopRouteLocator implements RouteLocator, Ordered {
public Route getMatchingRoute(String path) {
ApiParam param = ZuulContext.getApiParam();
String nameVersion = param.fetchNameVersion();
return zuulRouteRepository.get(nameVersion);
ZuulTargetRoute zuulTargetRoute = zuulRouteRepository.get(nameVersion);
if (zuulTargetRoute == null) {
return null;
}
return zuulTargetRoute.getTargetRouteDefinition();
}
@Override

@ -1,39 +1,33 @@
package com.gitee.sop.gatewaycommon.zuul.route;
import com.gitee.sop.gatewaycommon.bean.ServiceRouteRepository;
import com.gitee.sop.gatewaycommon.manager.RouteRepository;
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
import org.springframework.cloud.netflix.zuul.filters.Route;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
* 本地存放路由内容的地方
*
* @author tanghc
*/
public class ZuulRouteRepository implements RouteRepository<ZuulServiceRouteInfo, Route> {
public class ZuulRouteRepository implements RouteRepository<ZuulTargetRoute> {
/**
* keynameVersion
*/
private Map<String, Route> nameVersionServiceIdMap = new ConcurrentHashMap<>(128);
private Map<String, ZuulTargetRoute> nameVersionServiceIdMap = new ConcurrentHashMap<>(128);
private ServiceRouteRepository<ZuulServiceRouteInfo, Route> serviceRouteRepository = new ServiceRouteRepository<ZuulServiceRouteInfo, Route>() {
@Override
public String getServiceId(ZuulServiceRouteInfo serviceRouteInfo) {
return serviceRouteInfo.getAppName();
}
};
public List<Route> listAll() {
public List<ZuulTargetRoute> listAll() {
return new ArrayList<>(nameVersionServiceIdMap.values());
}
@Override
public Route get(String id) {
Route route = nameVersionServiceIdMap.get(id);
public ZuulTargetRoute get(String id) {
ZuulTargetRoute route = nameVersionServiceIdMap.get(id);
if (route == null) {
throw ErrorEnum.ISV_INVALID_METHOD.getErrorMeta().getException();
}
@ -41,17 +35,26 @@ public class ZuulRouteRepository implements RouteRepository<ZuulServiceRouteInfo
}
@Override
public String add(ZuulServiceRouteInfo serviceRouteInfo, Route route) {
nameVersionServiceIdMap.put(route.getId(), route);
serviceRouteRepository.saveRouteDefinition(serviceRouteInfo, route);
public String add(ZuulTargetRoute targetRoute) {
nameVersionServiceIdMap.put(targetRoute.getRouteDefinition().getId(), targetRoute);
return null;
}
@Override
public void deleteAll(ZuulServiceRouteInfo serviceRouteInfo) {
serviceRouteRepository.deleteAll(serviceRouteInfo, route -> {
this.delete(route.getId());
});
public void update(ZuulTargetRoute targetRoute) {
nameVersionServiceIdMap.put(targetRoute.getRouteDefinition().getId(), targetRoute);
}
@Override
public void deleteAll(String serviceId) {
Collection<ZuulTargetRoute> values = nameVersionServiceIdMap.values();
List<String> idList = values.stream()
.map(zuulTargetRoute -> zuulTargetRoute.getRouteDefinition().getId())
.collect(Collectors.toList());
for (String id : idList) {
this.delete(id);
}
}
@Override

@ -7,6 +7,5 @@ import lombok.Data;
/**
* @author thc
*/
@Data
public class ZuulServiceRouteInfo extends BaseServiceRouteInfo<ZuulRouteDefinition> {
}

@ -0,0 +1,16 @@
package com.gitee.sop.gatewaycommon.zuul.route;
import com.gitee.sop.gatewaycommon.bean.AbstractTargetRoute;
import lombok.Getter;
import org.springframework.cloud.netflix.zuul.filters.Route;
/**
* @author tanghc
*/
@Getter
public class ZuulTargetRoute extends AbstractTargetRoute<ZuulServiceRouteInfo, ZuulRouteDefinition, Route> {
public ZuulTargetRoute(ZuulServiceRouteInfo baseServiceRouteInfo, ZuulRouteDefinition baseRouteDefinition, Route targetRoute) {
super(baseServiceRouteInfo, baseRouteDefinition, targetRoute);
}
}

@ -9,12 +9,13 @@ import org.springframework.core.env.Environment;
/**
* 路由内容管理新增活修改路由
*
* @author tanghc
*/
@Slf4j
public class ZuulZookeeperRouteManager extends BaseRouteManager<ZuulServiceRouteInfo, ZuulRouteDefinition, Route> {
public class ZuulZookeeperRouteManager extends BaseRouteManager<ZuulServiceRouteInfo, ZuulRouteDefinition, ZuulTargetRoute> {
public ZuulZookeeperRouteManager(Environment environment, RouteRepository<ZuulServiceRouteInfo, Route> routeRepository) {
public ZuulZookeeperRouteManager(Environment environment, RouteRepository<ZuulTargetRoute> routeRepository) {
super(environment, routeRepository);
}
@ -24,7 +25,13 @@ public class ZuulZookeeperRouteManager extends BaseRouteManager<ZuulServiceRoute
}
@Override
protected Route buildRouteDefinition(ZuulServiceRouteInfo serviceRouteInfo, ZuulRouteDefinition routeDefinition) {
return new Route(routeDefinition.getId(), RoutePathUtil.findPath(routeDefinition.getUri()), serviceRouteInfo.getAppName(), null, false, null);
protected Class<ZuulRouteDefinition> getRouteDefinitionClass() {
return ZuulRouteDefinition.class;
}
@Override
protected ZuulTargetRoute buildRouteDefinition(ZuulServiceRouteInfo serviceRouteInfo, ZuulRouteDefinition routeDefinition) {
Route route = new Route(routeDefinition.getId(), RoutePathUtil.findPath(routeDefinition.getUri()), serviceRouteInfo.getServiceId(), null, false, null);
return new ZuulTargetRoute(serviceRouteInfo, routeDefinition, route);
}
}

@ -0,0 +1,31 @@
package com.gitee.sop.gateway;
import com.gitee.sop.gatewaycommon.bean.SopConstants;
import junit.framework.TestCase;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
/**
* @author tanghc
*/
public class CuratorTest extends TestCase {
private String zookeeperServerAddr = "127.0.0.1:2181";
/**
* 递归删除节点只能在测试环境用
* @throws Exception
*/
public void testDel() throws Exception {
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString(zookeeperServerAddr)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.build();
client.start();
client.delete().deletingChildrenIfNeeded().forPath(SopConstants.SOP_SERVICE_ROUTE_PATH);
}
}

@ -1,127 +0,0 @@
package com.gitee.sop.gateway;
import com.alibaba.fastjson.JSON;
import org.junit.Test;
import java.io.IOException;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class PostTest extends TestBase {
String url = "http://localhost:8081/zuul";
String appKey = "test";
String secret = "123456";
@Test
public void testPost() throws Exception {
// 业务参数
Map<String, String> jsonMap = new HashMap<String, String>();
jsonMap.put("id", "1");
jsonMap.put("id", "葫芦娃");
String json = JSON.toJSONString(jsonMap);
json = URLEncoder.encode(json, "utf-8");
// 系统参数
Map<String, Object> param = new HashMap<String, Object>();
param.put("name", "story.get");
param.put("app_key", appKey);
param.put("data", json);
param.put("timestamp", getTime());
param.put("version", "2.0");
String sign = buildSign(param, secret);
param.put("sign", sign);
System.out.println("=====请求数据=====");
String postJson = JSON.toJSONString(param);
System.out.println(postJson);
post(url, param); // 发送请求
}
/**
* 构建签名
*
* @param paramsMap
* 参数
* @param secret
* 密钥
* @return
* @throws IOException
*/
public static String buildSign(Map<String, ?> paramsMap, String secret) throws IOException {
Set<String> keySet = paramsMap.keySet();
List<String> paramNames = new ArrayList<String>(keySet);
Collections.sort(paramNames);
StringBuilder paramNameValue = new StringBuilder();
for (String paramName : paramNames) {
paramNameValue.append(paramName).append(paramsMap.get(paramName));
}
String source = secret + paramNameValue.toString() + secret;
return md5(source);
}
/**
* 生成md5,全部大写
*
* @param message
* @return
*/
public static String md5(String message) {
try {
// 1 创建一个提供信息摘要算法的对象,初始化为md5算法对象
MessageDigest md = MessageDigest.getInstance("MD5");
// 2 将消息变成byte数组
byte[] input = message.getBytes();
// 3 计算后获得字节数组,这就是那128位了
byte[] buff = md.digest(input);
// 4 把数组每一字节(一个字节占八位)换成16进制连成md5字符串
return byte2hex(buff);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 二进制转十六进制字符串
*
* @param bytes
* @return
*/
private static String byte2hex(byte[] bytes) {
StringBuilder sign = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(bytes[i] & 0xFF);
if (hex.length() == 1) {
sign.append("0");
}
sign.append(hex.toUpperCase());
}
return sign.toString();
}
public String getTime() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
}
}

@ -1,121 +0,0 @@
package com.gitee.sop.gateway;
import com.alibaba.fastjson.JSON;
import org.junit.Test;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class SopPostTest extends TestBase {
String url = "http://localhost:8081/zuul";
String appKey = "test";
String secret = "123456";
@Test
public void testPost() throws Exception {
// 系统参数
Map<String, Object> param = new HashMap<String, Object>();
param.put("method", "story.get");
param.put("app_key", appKey);
param.put("timestamp", getTime());
param.put("version", "2.0");
// 业务参数
param.put("id", "1");
param.put("name", "葫芦娃");
String sign = buildSign(param, secret);
param.put("sign", sign);
System.out.println("=====请求数据=====");
String postJson = JSON.toJSONString(param);
System.out.println(postJson);
post(url, param); // 发送请求
}
/**
* 构建签名
*
* @param paramsMap 参数
* @param secret 密钥
* @return
* @throws IOException
*/
public static String buildSign(Map<String, ?> paramsMap, String secret) throws IOException {
Set<String> keySet = paramsMap.keySet();
List<String> paramNames = new ArrayList<String>(keySet);
Collections.sort(paramNames);
StringBuilder paramNameValue = new StringBuilder();
for (String paramName : paramNames) {
paramNameValue.append(paramName).append(paramsMap.get(paramName));
}
String source = secret + paramNameValue.toString() + secret;
return md5(source);
}
/**
* 生成md5,全部大写
*
* @param message
* @return
*/
public static String md5(String message) {
try {
// 1 创建一个提供信息摘要算法的对象,初始化为md5算法对象
MessageDigest md = MessageDigest.getInstance("MD5");
// 2 将消息变成byte数组
byte[] input = message.getBytes(StandardCharsets.UTF_8);
// 3 计算后获得字节数组,这就是那128位了
byte[] buff = md.digest(input);
// 4 把数组每一字节(一个字节占八位)换成16进制连成md5字符串
return byte2hex(buff);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 二进制转十六进制字符串
*
* @param bytes
* @return
*/
private static String byte2hex(byte[] bytes) {
StringBuilder sign = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(bytes[i] & 0xFF);
if (hex.length() == 1) {
sign.append("0");
}
sign.append(hex.toUpperCase());
}
return sign.toString();
}
public String getTime() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
}
}

@ -1,102 +0,0 @@
package com.gitee.sop.gateway;
import junit.framework.TestCase;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author tanghc
*/
public class TestBase extends TestCase {
public void post(String url, String postJson) throws IOException {
HttpClient httpClient = HttpClientBuilder.create().build();
HttpPost post = new HttpPost(url);
// 构造消息头
post.setHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
// 构建消息实体
StringEntity entity = new StringEntity(postJson, Charset.forName("UTF-8"));
entity.setContentEncoding("UTF-8");
// 发送Json格式的数据请求
entity.setContentType("application/json");
post.setEntity(entity);
HttpResponse response = httpClient.execute(post);
HttpEntity responseEntity = response.getEntity();
String content = IOUtils.toString(responseEntity.getContent(), "UTF-8");
System.out.println(content);
}
/**
* 发送POST请求
* @param url
* @return JSON或者字符串
* @throws Exception
*/
public static Object post(String url, Map<String, Object> params) throws Exception{
CloseableHttpClient client = null;
CloseableHttpResponse response = null;
try{
/**
* 创建一个httpclient对象
*/
client = HttpClients.createDefault();
/**
* 创建一个post对象
*/
HttpPost post = new HttpPost(url);
List<NameValuePair> nameValuePairs = params.entrySet().stream().map(entry -> {
return new BasicNameValuePair(entry.getKey(), String.valueOf(entry.getValue()));
}).collect(Collectors.toList());
/**
* 包装成一个Entity对象
*/
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(nameValuePairs, "UTF-8");
/**
* 设置请求的内容
*/
post.setEntity(entity);
/**
* 设置请求的报文头部的编码
*/
post.setHeader(new BasicHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8"));
/**
* 执行post请求
*/
response = client.execute(post);
/**
* 通过EntityUitls获取返回内容
*/
String result = EntityUtils.toString(response.getEntity(),"UTF-8");
System.out.println(result);
return result;
}catch (Exception e){
e.printStackTrace();
}finally {
IOUtils.closeQuietly(client);
IOUtils.closeQuietly(response);
}
return null;
}
}

@ -1,6 +1,5 @@
package com.gitee.sop.servercommon.bean;
import com.alibaba.fastjson.annotation.JSONField;
import com.gitee.sop.servercommon.route.GatewayRouteDefinition;
import lombok.Data;
@ -11,8 +10,7 @@ import java.util.List;
*/
@Data
public class ServiceApiInfo {
private String md5;
private String appName;
private String serviceId;
private List<ApiMeta> apis;
private List<GatewayRouteDefinition> routeDefinitionList;

@ -35,8 +35,8 @@ public class DefaultRequestMappingEvent implements RequestMappingEvent {
@Override
public void onRegisterSuccess(ApiMappingHandlerMapping apiMappingHandlerMapping) {
String appName = environment.getProperty("spring.application.name");
if (appName == null) {
String serviceId = environment.getProperty("spring.application.name");
if (serviceId == null) {
throw new RuntimeException("请在application.properties中指定spring.application.name属性");
}
List<ServiceApiInfo.ApiMeta> apis = this.buildApiMetaList(apiMappingHandlerMapping);
@ -49,9 +49,8 @@ public class DefaultRequestMappingEvent implements RequestMappingEvent {
});
ServiceApiInfo serviceApiInfo = new ServiceApiInfo();
serviceApiInfo.setAppName(appName);
serviceApiInfo.setServiceId(serviceId);
serviceApiInfo.setApis(apis);
serviceApiInfo.setMd5(getMd5(apis));
apiMetaManager.uploadApi(serviceApiInfo);
}
@ -106,11 +105,4 @@ public class DefaultRequestMappingEvent implements RequestMappingEvent {
return path;
}
protected String getMd5(List<ServiceApiInfo.ApiMeta> apis) {
StringBuilder sb = new StringBuilder();
for (ServiceApiInfo.ApiMeta api : apis) {
sb.append(api.fetchNameVersion());
}
return DigestUtils.md5DigestAsHex(sb.toString().getBytes());
}
}

@ -20,9 +20,9 @@ public class RedisApiMetaManager implements ApiMetaManager {
@Override
public void uploadApi(ServiceApiInfo serviceApiInfo) {
log.info("上传接口信息到Redis,appName:{}, md5:{}, 接口数量:{}", serviceApiInfo.getAppName(), serviceApiInfo.getMd5(), serviceApiInfo.getApis().size());
log.info("上传接口信息到Redis,serviceId:{}, 接口数量:{}", serviceApiInfo.getServiceId(), serviceApiInfo.getApis().size());
String serviceApiInfoJson = JSON.toJSONString(serviceApiInfo);
redisTemplate.opsForHash().put(API_STORE_KEY, serviceApiInfo.getAppName(), serviceApiInfoJson);
redisTemplate.opsForHash().put(API_STORE_KEY, serviceApiInfo.getServiceId(), serviceApiInfoJson);
// 发送订阅事件
redisTemplate.convertAndSend(API_CHANGE_CHANNEL, serviceApiInfoJson);
}

@ -20,6 +20,7 @@ import java.util.Collections;
import java.util.List;
/**
* 上传路由到zookeeper
* @author tanghc
*/
@Slf4j
@ -35,11 +36,11 @@ public class ServiceZookeeperApiMetaManager implements ApiMetaManager {
/**
* NameVersion=alipay.story.get1.0
* see com.gitee.sop.gatewaycommon.route.NameVersionRoutePredicateFactory
* see com.gitee.sop.gatewaycommon.routeDefinition.NameVersionRoutePredicateFactory
*/
private static String QUERY_PREDICATE_DEFINITION_TPL = "NameVersion=%s";
private static ServiceApiInfo.ApiMeta FIRST_API_META = new ServiceApiInfo.ApiMeta("_" + System.currentTimeMillis() + "_", "/", "0.0");
private static ServiceApiInfo.ApiMeta FIRST_API_META = new ServiceApiInfo.ApiMeta("_first.route_", "/", "v_000");
private Environment environment;
@ -68,27 +69,27 @@ public class ServiceZookeeperApiMetaManager implements ApiMetaManager {
routeDefinitionList.add(gatewayRouteDefinition);
}
ServiceRouteInfo serviceRouteInfo = new ServiceRouteInfo();
serviceRouteInfo.setAppName(serviceApiInfo.getAppName());
serviceRouteInfo.setServiceId(serviceApiInfo.getServiceId());
serviceRouteInfo.setRouteDefinitionList(routeDefinitionList);
serviceRouteInfo.setMd5(serviceApiInfo.getMd5());
return serviceRouteInfo;
}
/**
* 添加com.gitee.sop.gatewaycommon.route.ReadBodyRoutePredicateFactory,解决form表单获取不到问题
* 添加com.gitee.sop.gatewaycommon.routeDefinition.ReadBodyRoutePredicateFactory,解决form表单获取不到问题
*
* @return
*/
protected GatewayRouteDefinition buildReadBodyRouteDefinition(ServiceApiInfo serviceApiInfo) {
GatewayRouteDefinition gatewayRouteDefinition = this.buildGatewayRouteDefinition(serviceApiInfo, FIRST_API_META);
GatewayRouteDefinition readBodyRouteDefinition = this.buildGatewayRouteDefinition(serviceApiInfo, FIRST_API_META);
readBodyRouteDefinition.setOrder(Integer.MIN_VALUE);
GatewayPredicateDefinition gatewayPredicateDefinition = new GatewayPredicateDefinition();
gatewayPredicateDefinition.setName("ReadBody");
GatewayPredicateDefinition readerBodyPredicateDefinition = this.buildNameVersionPredicateDefinition(FIRST_API_META);
List<GatewayPredicateDefinition> predicates = Arrays.asList(gatewayPredicateDefinition, readerBodyPredicateDefinition);
gatewayRouteDefinition.setPredicates(predicates);
readBodyRouteDefinition.setPredicates(predicates);
return gatewayRouteDefinition;
return readBodyRouteDefinition;
}
protected GatewayRouteDefinition buildGatewayRouteDefinition(ServiceApiInfo serviceApiInfo, ServiceApiInfo.ApiMeta apiMeta) {
@ -114,7 +115,7 @@ public class ServiceZookeeperApiMetaManager implements ApiMetaManager {
if (!servletPath.startsWith(PATH_START_CHAR)) {
servletPath = PATH_START_CHAR + servletPath;
}
return "lb://" + serviceApiInfo.getAppName() + "#" + servletPath;
return "lb://" + serviceApiInfo.getServiceId() + "#" + servletPath;
}
protected String getServletPath(ServiceApiInfo serviceApiInfo, ServiceApiInfo.ApiMeta apiMeta) {
@ -135,11 +136,10 @@ public class ServiceZookeeperApiMetaManager implements ApiMetaManager {
if (StringUtils.isEmpty(zookeeperServerAddr)) {
throw new RuntimeException("未指定spring.cloud.zookeeper.connect-string参数");
}
String serviceRouteInfoJson = JSON.toJSONString(serviceRouteInfo);
CuratorFramework client = null;
try {
// 保存路径
String savePath = SOP_SERVICE_ROUTE_PATH + "/" + serviceRouteInfo.getAppName();
String savePath = SOP_SERVICE_ROUTE_PATH + "/" + serviceRouteInfo.getServiceId();
client = CuratorFrameworkFactory.builder()
.connectString(zookeeperServerAddr)
@ -148,20 +148,15 @@ public class ServiceZookeeperApiMetaManager implements ApiMetaManager {
client.start();
log.info("上传接口信息到zookeeper,path:{}, appName:{}, md5:{}, 接口数量:{}",
log.info("上传接口信息到zookeeper,path:{}, serviceId:{}, 接口数量:{}",
savePath,
serviceRouteInfo.getAppName(),
serviceRouteInfo.getMd5(),
serviceRouteInfo.getServiceId(),
serviceRouteInfo.getRouteDefinitionList().size());
client.create()
// 如果节点存在则Curator将会使用给出的数据设置这个节点的值
.orSetData()
// 如果指定节点的父节点不存在,则Curator将会自动级联创建父节点
.creatingParentContainersIfNeeded()
.forPath(savePath, serviceRouteInfoJson.getBytes());
String parentPath = this.uploadFolder(client, serviceRouteInfo);
this.uploadRouteItems(client, serviceRouteInfo, parentPath);
} catch (Exception e) {
log.error("更新接口信息到zookeeper失败, appName:{}", serviceRouteInfo.getAppName(), e);
log.error("更新接口信息到zookeeper失败, serviceId:{}", serviceRouteInfo.getServiceId(), e);
} finally {
if (client != null) {
client.close();
@ -169,4 +164,43 @@ public class ServiceZookeeperApiMetaManager implements ApiMetaManager {
}
}
/**
* 上传文件夹内容
* @param client
* @param serviceRouteInfo
* @return 返回文件夹路径
*/
protected String uploadFolder(CuratorFramework client, ServiceRouteInfo serviceRouteInfo) throws Exception {
// 保存路径
String savePath = SOP_SERVICE_ROUTE_PATH + "/" + serviceRouteInfo.getServiceId();
String serviceRouteInfoJson = JSON.toJSONString(serviceRouteInfo);
this.saveNode(client, savePath, serviceRouteInfoJson.getBytes());
return savePath;
}
/**
* 上传路由信息
* @param client
* @param serviceRouteInfo
* @throws Exception
*/
protected void uploadRouteItems(CuratorFramework client, ServiceRouteInfo serviceRouteInfo, String parentPath) throws Exception {
List<GatewayRouteDefinition> routeDefinitionList = serviceRouteInfo.getRouteDefinitionList();
for (GatewayRouteDefinition routeDefinition : routeDefinitionList) {
// 父目录/子目录
String savePath = parentPath + PATH_START_CHAR + routeDefinition.getId();
String routeDefinitionJson = JSON.toJSONString(routeDefinition);
this.saveNode(client, savePath, routeDefinitionJson.getBytes());
}
}
protected void saveNode(CuratorFramework client, String path, byte[] data) throws Exception {
client.create()
// 如果节点存在则Curator将会使用给出的数据设置这个节点的值
.orSetData()
// 如果指定节点的父节点不存在,则Curator将会自动级联创建父节点
.creatingParentContainersIfNeeded()
.forPath(path, data);
}
}

@ -10,8 +10,8 @@ import java.util.Map;
*/
@Data
public class GatewayFilterDefinition {
//Filter Name */
/** Filter Name */
private String name;
//对应的路由规则 */
/** 对应的路由规则 */
private Map<String, String> args = new LinkedHashMap<>();
}

@ -1,5 +1,6 @@
package com.gitee.sop.servercommon.route;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import java.util.List;
@ -9,7 +10,8 @@ import java.util.List;
*/
@Data
public class ServiceRouteInfo {
private String appName;
private String md5;
/** 服务名称,对应spring.application.name */
private String serviceId;
@JSONField(serialize = false)
private List<GatewayRouteDefinition> routeDefinitionList;
}

@ -21,8 +21,8 @@ import java.util.Set;
*/
public class AlipayClientPostTest extends TestBase {
String url = "http://localhost:8081/api";
String url = "http://localhost:8081/api"; // zuul
// String url = "http://localhost:8081"; // spring cloud gateway
String appId = "alipay_test";
// 支付宝私钥
String privateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXJv1pQFqWNA/++OYEV7WYXwexZK/J8LY1OWlP9X0T6wHFOvxNKRvMkJ5544SbgsJpVcvRDPrcxmhPbi/sAhdO4x2PiPKIz9Yni2OtYCCeaiE056B+e1O2jXoLeXbfi9fPivJZkxH/tb4xfLkH3bA8ZAQnQsoXA0SguykMRZntF0TndUfvDrLqwhlR8r5iRdZLB6F8o8qXH6UPDfNEnf/K8wX5T4EB1b8x8QJ7Ua4GcIUqeUxGHdQpzNbJdaQvoi06lgccmL+PHzminkFYON7alj1CjDN833j7QMHdPtS9l7B67fOU/p2LAAkPMtoVBfxQt9aFj7B8rEhGCz02iJIBAgMBAAECggEARqOuIpY0v6WtJBfmR3lGIOOokLrhfJrGTLF8CiZMQha+SRJ7/wOLPlsH9SbjPlopyViTXCuYwbzn2tdABigkBHYXxpDV6CJZjzmRZ+FY3S/0POlTFElGojYUJ3CooWiVfyUMhdg5vSuOq0oCny53woFrf32zPHYGiKdvU5Djku1onbDU0Lw8w+5tguuEZ76kZ/lUcccGy5978FFmYpzY/65RHCpvLiLqYyWTtaNT1aQ/9pw4jX9HO9NfdJ9gYFK8r/2f36ZE4hxluAfeOXQfRC/WhPmiw/ReUhxPznG/WgKaa/OaRtAx3inbQ+JuCND7uuKeRe4osP2jLPHPP6AUwQKBgQDUNu3BkLoKaimjGOjCTAwtp71g1oo+k5/uEInAo7lyEwpV0EuUMwLA/HCqUgR4K9pyYV+Oyb8d6f0+Hz0BMD92I2pqlXrD7xV2WzDvyXM3s63NvorRooKcyfd9i6ccMjAyTR2qfLkxv0hlbBbsPHz4BbU63xhTJp3Ghi0/ey/1HQKBgQC2VsgqC6ykfSidZUNLmQZe3J0p/Qf9VLkfrQ+xaHapOs6AzDU2H2osuysqXTLJHsGfrwVaTs00ER2z8ljTJPBUtNtOLrwNRlvgdnzyVAKHfOgDBGwJgiwpeE9voB1oAV/mXqSaUWNnuwlOIhvQEBwekqNyWvhLqC7nCAIhj3yvNQKBgQCqYbeec56LAhWP903Zwcj9VvG7sESqXUhIkUqoOkuIBTWFFIm54QLTA1tJxDQGb98heoCIWf5x/A3xNI98RsqNBX5JON6qNWjb7/dobitti3t99v/ptDp9u8JTMC7penoryLKK0Ty3bkan95Kn9SC42YxaSghzqkt+uvfVQgiNGQKBgGxU6P2aDAt6VNwWosHSe+d2WWXt8IZBhO9d6dn0f7ORvcjmCqNKTNGgrkewMZEuVcliueJquR47IROdY8qmwqcBAN7Vg2K7r7CPlTKAWTRYMJxCT1Hi5gwJb+CZF3+IeYqsJk2NF2s0w5WJTE70k1BSvQsfIzAIDz2yE1oPHvwVAoGAA6e+xQkVH4fMEph55RJIZ5goI4Y76BSvt2N5OKZKd4HtaV+eIhM3SDsVYRLIm9ZquJHMiZQGyUGnsvrKL6AAVNK7eQZCRDk9KQz+0GKOGqku0nOZjUbAu6A2/vtXAaAuFSFx1rUQVVjFulLexkXR3KcztL1Qu2k5pB6Si0K/uwQ=";

Loading…
Cancel
Save