文档分组显示,代码优化

1.x
tanghc 6 years ago
parent e1e7b2c738
commit 5bd787b7e3
  1. 178
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/BaseRouteManager.java
  2. 151
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/ZookeeperContext.java
  3. 7
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/ZuulErrorController.java
  4. 27
      sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/EnvironmentContext.java
  5. 11
      sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/ServiceConstants.java
  6. 99
      sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/ZookeeperTool.java
  7. 54
      sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/EasyopenDocSupportController.java
  8. 2
      sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/SpringMvcServiceConfiguration.java
  9. 93
      sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/manager/ServiceZookeeperApiMetaManager.java
  10. 2
      sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ApiArgumentResolver.java
  11. 16
      sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/swagger/ApiListingScannerExt.java
  12. 36
      sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/swagger/SwaggerSecurityFilter.java
  13. 10
      sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/swagger/SwaggerSupport.java
  14. 53
      sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/swagger/SwaggerValidator.java
  15. 28
      sop-example/sop-easyopen/src/main/java/com/gitee/easyopen/server/config/BaseSopDocController.java
  16. 3
      sop-example/sop-easyopen/src/main/java/com/gitee/easyopen/server/config/SopConfig.java
  17. 14
      sop-gateway/src/test/java/com/gitee/sop/gateway/CuratorTest.java
  18. 17
      sop-registry/src/main/resources/application-dev.yml
  19. 12
      sop-registry/src/main/resources/application.properties
  20. 3
      sop-registry/src/main/resources/application.yml
  21. 12
      sop-website/website-front/pages/doc/doc.html
  22. 11
      sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/bean/WebsiteConstants.java
  23. 80
      sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/bean/ZookeeperContext.java
  24. 9
      sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/controller/DocController.java
  25. 108
      sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/manager/DocManagerImpl.java

@ -5,15 +5,10 @@ import com.gitee.sop.gatewaycommon.bean.BaseRouteDefinition;
import com.gitee.sop.gatewaycommon.bean.BaseServiceRouteInfo; import com.gitee.sop.gatewaycommon.bean.BaseServiceRouteInfo;
import com.gitee.sop.gatewaycommon.bean.TargetRoute; import com.gitee.sop.gatewaycommon.bean.TargetRoute;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.ChildData; import org.apache.curator.framework.recipes.cache.ChildData;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import java.util.List;
/** /**
* 路由管理采用zookeeper实现监听路由的增删改并适时更新到本地路由的存储格式为 * 路由管理采用zookeeper实现监听路由的增删改并适时更新到本地路由的存储格式为
@ -64,8 +59,10 @@ public abstract class BaseRouteManager<R extends BaseServiceRouteInfo<E>, E exte
public BaseRouteManager(Environment environment, RouteRepository<T> routeRepository) { public BaseRouteManager(Environment environment, RouteRepository<T> routeRepository) {
this.environment = environment; this.environment = environment;
ZookeeperContext.setEnvironment(environment);
this.routeRepository = routeRepository; this.routeRepository = routeRepository;
this.routeRootPath = ZookeeperContext.getRouteRootPath(); this.routeRootPath = ZookeeperContext.getRouteRootPath();
this.createRouteRootPath(this.routeRootPath);
} }
@Override @Override
@ -75,73 +72,64 @@ public abstract class BaseRouteManager<R extends BaseServiceRouteInfo<E>, E exte
protected void refreshRouteInfo() { protected void refreshRouteInfo() {
try { try {
ZookeeperContext.createOrUpdateData(routeRootPath, ""); this.watchServiceChange(routeRootPath);
CuratorFramework client = ZookeeperContext.getClient();
this.watchServiceChange(client, routeRootPath);
} catch (Exception e) { } catch (Exception e) {
log.error("刷新路由配置失败", e); log.error("刷新路由配置失败", e);
throw new IllegalStateException("刷新路由配置失败"); throw new IllegalStateException("刷新路由配置失败");
} }
} }
protected void createRouteRootPath(String routeRootPath) {
try {
ZookeeperContext.createPath(routeRootPath, "");
} catch (Exception e) {
log.error("创建路由根节点失败", e);
throw new IllegalStateException("创建路由根节点失败");
}
}
/** /**
* 监听微服务更改 * 监听微服务更改
* *
* @param client
* @param rootPath * @param rootPath
* @throws Exception * @throws Exception
*/ */
protected void watchServiceChange(CuratorFramework client, String rootPath) throws Exception { protected void watchServiceChange(String rootPath) throws Exception {
// 为子节点添加watcher ZookeeperContext.getChildrenAndListen(rootPath, childDataList -> {
// PathChildrenCache: 监听数据节点的增删改,可以设置触发的事件 log.info("========== 加载路由信息 ==========");
PathChildrenCache childrenCache = new PathChildrenCache(client, rootPath, true); log.info("{} # 路由根节点", rootPath);
for (ChildData childData : childDataList) {
/** String serviceNodeData = new String(childData.getData());
* StartMode: 初始化方式 R serviceRouteInfo = JSON.parseObject(serviceNodeData, getServiceRouteInfoClass());
* POST_INITIALIZED_EVENT异步初始化初始化之后会触发事件 String servicePath = childData.getPath();
* NORMAL异步初始化 log.info("\t{} # service节点,节点数据:{}", servicePath, serviceNodeData);
* BUILD_INITIAL_CACHE同步初始化 try {
*/ this.loadServiceRouteItem(serviceRouteInfo, servicePath);
childrenCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE); } catch (Exception e) {
log.error("加载路由信息失败,servicePath:{}", servicePath, e);
// 列出子节点数据列表,需要使用BUILD_INITIAL_CACHE同步初始化模式才能获得,异步是获取不到的 }
List<ChildData> childDataList = childrenCache.getCurrentData(); }
log.info("========== 加载路由信息 =========="); }, (client, event) -> {
log.info("{} # 根节点", rootPath); PathChildrenCacheEvent.Type type = event.getType();
for (ChildData childData : childDataList) { synchronized (type) {
String serviceNodeData = new String(childData.getData()); // 通过判断event type的方式来实现不同事件的触发
R serviceRouteInfo = JSON.parseObject(serviceNodeData, getServiceRouteInfoClass()); if (PathChildrenCacheEvent.Type.CHILD_ADDED.equals(type)) {
String servicePath = childData.getPath(); String serviceNodeData = new String(event.getData().getData());
log.info("\t{} # service节点,节点数据:{}", servicePath, serviceNodeData); R serviceRouteInfo = JSON.parseObject(serviceNodeData, getServiceRouteInfoClass());
this.loadServiceRouteItem(client, serviceRouteInfo, servicePath); // 添加子节点时触发
} String servicePath = event.getData().getPath();
log.info("监听服务节点增删改,rootPath:{}", rootPath); log.info("新增serviceId节点:{},节点数据:{}", servicePath, serviceNodeData);
// 监听根节点下面的子节点 this.watchRouteItems(serviceRouteInfo, servicePath);
childrenCache.getListenable().addListener(new PathChildrenCacheListener() { } else if (PathChildrenCacheEvent.Type.CHILD_UPDATED.equals(type)) {
@Override // 修改子节点数据时触发,暂时没有什么操作
public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { String nodeData = new String(event.getData().getData());
PathChildrenCacheEvent.Type type = event.getType(); log.info("修改serviceId节点:{},节点数据:{}", event.getData().getPath(), nodeData);
synchronized (type) { } else if (PathChildrenCacheEvent.Type.CHILD_REMOVED.equals(type)) {
// 通过判断event type的方式来实现不同事件的触发 // 删除service节点
if (PathChildrenCacheEvent.Type.CHILD_ADDED.equals(type)) { String nodeData = new String(event.getData().getData());
String serviceNodeData = new String(event.getData().getData()); log.info("删除serviceId节点:{},节点数据:{}", event.getData().getPath(), nodeData);
R serviceRouteInfo = JSON.parseObject(serviceNodeData, getServiceRouteInfoClass()); R serviceRouteInfo = JSON.parseObject(nodeData, getServiceRouteInfoClass());
// 添加子节点时触发 routeRepository.deleteAll(serviceRouteInfo.getServiceId());
String servicePath = event.getData().getPath();
log.info("新增serviceId节点:{},节点数据:{}", servicePath, serviceNodeData);
loadServiceRouteItem(client, serviceRouteInfo, servicePath);
} else if (PathChildrenCacheEvent.Type.CHILD_UPDATED.equals(type)) {
// 修改子节点数据时触发,暂时没有什么操作
String nodeData = new String(event.getData().getData());
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("删除serviceId节点:{},节点数据:{}", event.getData().getPath(), nodeData);
R serviceRouteInfo = JSON.parseObject(nodeData, getServiceRouteInfoClass());
routeRepository.deleteAll(serviceRouteInfo.getServiceId());
}
} }
} }
}); });
@ -152,54 +140,48 @@ public abstract class BaseRouteManager<R extends BaseServiceRouteInfo<E>, E exte
* *
* @param servicePath * @param servicePath
*/ */
protected void loadServiceRouteItem(CuratorFramework client, R serviceRouteInfo, String servicePath) throws Exception { protected void loadServiceRouteItem(R serviceRouteInfo, String servicePath) throws Exception {
// 获取service节点下所有的路由节点,里面保存的是路由名称,前面没有斜杠"/" ZookeeperContext.getChildrenData(servicePath, childDataList -> {
List<String> pathNameList = client.getChildren().forPath(servicePath); for (ChildData childData : childDataList) {
for (String pathName : pathNameList) { String routeItemPath = childData.getPath();
// 完整的路径 byte[] routeItemData = childData.getData();
String routeItemPath = servicePath + "/" + pathName; String routeDataJson = buildZookeeperData(routeItemData);
byte[] routeItemData = client.getData().forPath(routeItemPath); log.info("\t\t{} # 路由节点,节点数据:{}", routeItemPath, routeDataJson);
String routeDataJson = buildZookeeperData(routeItemData); this.saveRouteItem(serviceRouteInfo, routeDataJson);
log.info("\t\t{} # 路由节点,节点数据:{}", routeItemPath, routeDataJson); }
this.saveRouteItem(serviceRouteInfo, routeDataJson); });
} this.watchRouteItems(serviceRouteInfo, servicePath);
this.watchRouteItems(client, serviceRouteInfo, servicePath);
} }
/** /**
* 监听serviceId目录下面的子节点 * 监听serviceId目录下面的子节点
* *
* @param client
* @param serviceRouteInfo * @param serviceRouteInfo
* @param servicePath serviceId节点 * @param servicePath serviceId节点
*/ */
protected void watchRouteItems(CuratorFramework client, R serviceRouteInfo, String servicePath) throws Exception { protected void watchRouteItems(R serviceRouteInfo, String servicePath) throws Exception {
log.info("监听路由节点增删改,servicePath:{}", servicePath); log.info("监听{}下子节点增删改", servicePath);
PathChildrenCache childrenCache = new PathChildrenCache(client, servicePath, true);
childrenCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
// 添加事件监听器 // 添加事件监听器
childrenCache.getListenable().addListener(new PathChildrenCacheListener() { ZookeeperContext.listenChildren(servicePath, (client, event) -> {
@Override PathChildrenCacheEvent.Type type = event.getType();
public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { synchronized (type) {
PathChildrenCacheEvent.Type type = event.getType(); // 通过判断event type的方式来实现不同事件的触发
synchronized (type) { if (PathChildrenCacheEvent.Type.CHILD_ADDED.equals(type)) {
// 通过判断event type的方式来实现不同事件的触发 // 新增单个路由
if (PathChildrenCacheEvent.Type.CHILD_ADDED.equals(type)) { String routeDataJson = buildZookeeperData(event.getData().getData());
// 新增单个路由 log.info("新增单个路由,serviceId:{}, 路由数据:{}", serviceRouteInfo.getServiceId(), routeDataJson);
String routeDataJson = buildZookeeperData(event.getData().getData()); saveRouteItem(serviceRouteInfo, routeDataJson);
log.info("新增单个路由,serviceId:{}, 路由数据:{}", serviceRouteInfo.getServiceId(), routeDataJson); } else if (PathChildrenCacheEvent.Type.CHILD_UPDATED.equals(type)) {
saveRouteItem(serviceRouteInfo, routeDataJson); // 修改单个路由
} else if (PathChildrenCacheEvent.Type.CHILD_UPDATED.equals(type)) { String routeDataJson = buildZookeeperData(event.getData().getData());
// 修改单个路由 log.info("修改单个路由,serviceId:{}, 路由数据:{}", serviceRouteInfo.getServiceId(), routeDataJson);
String routeDataJson = buildZookeeperData(event.getData().getData()); updateRouteItem(serviceRouteInfo, routeDataJson);
log.info("修改单个路由,serviceId:{}, 路由数据:{}", serviceRouteInfo.getServiceId(), routeDataJson); } else if (PathChildrenCacheEvent.Type.CHILD_REMOVED.equals(type)) {
updateRouteItem(serviceRouteInfo, routeDataJson); // 删除单个路由
} else if (PathChildrenCacheEvent.Type.CHILD_REMOVED.equals(type)) { String routeDataJson = buildZookeeperData(event.getData().getData());
// 删除单个路由 log.info("删除单个路由,serviceId:{}, 路由数据:{}", serviceRouteInfo.getServiceId(), routeDataJson);
String routeDataJson = buildZookeeperData(event.getData().getData()); deleteRouteItem(serviceRouteInfo, routeDataJson);
log.info("删除单个路由,serviceId:{}, 路由数据:{}", serviceRouteInfo.getServiceId(), routeDataJson);
deleteRouteItem(serviceRouteInfo, routeDataJson);
}
} }
} }
}); });

@ -9,19 +9,17 @@ import org.apache.curator.framework.recipes.cache.ChildData;
import org.apache.curator.framework.recipes.cache.NodeCache; import org.apache.curator.framework.recipes.cache.NodeCache;
import org.apache.curator.framework.recipes.cache.NodeCacheListener; import org.apache.curator.framework.recipes.cache.NodeCacheListener;
import org.apache.curator.framework.recipes.cache.PathChildrenCache; import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.data.Stat;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function;
import static com.gitee.sop.gatewaycommon.bean.SopConstants.SOP_MSG_CHANNEL_PATH; import static com.gitee.sop.gatewaycommon.bean.SopConstants.SOP_MSG_CHANNEL_PATH;
import static com.gitee.sop.gatewaycommon.bean.SopConstants.SOP_SERVICE_ROUTE_PATH;
import static com.gitee.sop.gatewaycommon.bean.SopConstants.SOP_ROUTE_PERMISSION_PATH; import static com.gitee.sop.gatewaycommon.bean.SopConstants.SOP_ROUTE_PERMISSION_PATH;
import static com.gitee.sop.gatewaycommon.bean.SopConstants.SOP_SERVICE_ROUTE_PATH;
/** /**
* @author tanghc * @author tanghc
@ -86,62 +84,52 @@ public class ZookeeperContext {
ZookeeperContext.client = client; ZookeeperContext.client = client;
} }
public static boolean isPathExist(String path) {
try {
return client.checkExists().forPath(path) != null;
} catch (Exception e) {
return false;
}
}
/** /**
* 对已存在的path赋值如果path不存在抛异常 * 新建或保存节点
* *
* @param path 已存在的 * @param path
* @param data * @param data
* @return 返回Stat * @return 返回path
* @throws Exception * @throws Exception
*/ */
public static Stat updatePathData(String path, String data) throws Exception { public static String createOrUpdateData(String path, String data) throws Exception {
if (!isPathExist(path)) { return getClient().create()
throw new IllegalStateException("path " + path + " 不存在"); // 如果节点存在则Curator将会使用给出的数据设置这个节点的值
} .orSetData()
return getClient().setData().forPath(path, data.getBytes());
}
/**
* 创建新的path并赋值如果path已存在抛异常
* @param path 待创建的path
* @param data
*/
public static String createNewData(String path, String data) throws Exception {
if (isPathExist(path)) {
throw new IllegalStateException("path " + path + " 已存在");
}
return getClient().create()
// 如果指定节点的父节点不存在,则Curator将会自动级联创建父节点 // 如果指定节点的父节点不存在,则Curator将会自动级联创建父节点
.creatingParentContainersIfNeeded() .creatingParentContainersIfNeeded()
.forPath(path, data.getBytes()); .forPath(path, data.getBytes());
} }
/** /**
* 新建或保存节点 * 创建path如果path存在不报错静默返回path名称
*
* @param path * @param path
* @param data * @param data
* @return 返回path * @return
* @throws Exception * @throws Exception
*/ */
public static String createOrUpdateData(String path, String data) throws Exception { public static String createPath(String path, String data) throws Exception {
if (isPathExist(path)) {
return path;
}
return getClient().create() return getClient().create()
// 如果节点存在则Curator将会使用给出的数据设置这个节点的值
.orSetData()
// 如果指定节点的父节点不存在,则Curator将会自动级联创建父节点 // 如果指定节点的父节点不存在,则Curator将会自动级联创建父节点
.creatingParentContainersIfNeeded() .creatingParentContainersIfNeeded()
.forPath(path, data.getBytes()); .forPath(path, data.getBytes());
} }
public static boolean isPathExist(String path) {
try {
return client.checkExists().forPath(path) != null;
} catch (Exception e) {
return false;
}
}
/** /**
* 监听一个节点 * 监听一个节点
*
* @param path * @param path
* @param onChange 节点修改后触发 * @param onChange 节点修改后触发
* @return 返回path * @return 返回path
@ -160,39 +148,82 @@ public class ZookeeperContext {
return ret; return ret;
} }
public static String getData(String path) throws Exception { /**
if (!isPathExist(path)) { * 获取子节点信息并监听子节点
return null; *
} * @param parentPath 父节点路径
byte[] data = getClient().getData().forPath(path); * @param listConsumer 子节点数据
return new String(data); * @param listener 监听事件
* @throws Exception
*/
public static void getChildrenAndListen(String parentPath, Consumer<List<ChildData>> listConsumer, PathChildrenCacheListener listener) throws Exception {
// 为子节点添加watcher
// PathChildrenCache: 监听数据节点的增删改,可以设置触发的事件
PathChildrenCache childrenCache = new PathChildrenCache(client, parentPath, true);
/**
* StartMode: 初始化方式
* POST_INITIALIZED_EVENT异步初始化初始化之后会触发事件
* NORMAL异步初始化
* BUILD_INITIAL_CACHE同步初始化
*/
childrenCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
// 列出子节点数据列表,需要使用BUILD_INITIAL_CACHE同步初始化模式才能获得,异步是获取不到的
List<ChildData> childDataList = childrenCache.getCurrentData();
listConsumer.accept(childDataList);
log.info("监听子节点增删改,监听路径:{}", parentPath);
// 监听根节点下面的子节点
childrenCache.getListenable().addListener(listener);
} }
/** /**
* 获取子节点数据 * 获取子节点信息
* *
* @param parentPath 父节点 * @param parentPath 父节点路径
* @return 返回子节点数据 * @param listConsumer 子节点数据
* @throws Exception * @throws Exception
*/ */
public static List<ChildData> getChildrenData(String parentPath) throws Exception { public static void getChildrenData(String parentPath, Consumer<List<ChildData>> listConsumer) throws Exception {
PathChildrenCache pathChildrenCache = buildPathChildrenCache(parentPath); // 为子节点添加watcher
if (pathChildrenCache == null) { // PathChildrenCache: 监听数据节点的增删改,可以设置触发的事件
return Collections.emptyList(); PathChildrenCache childrenCache = new PathChildrenCache(client, parentPath, true);
}
return pathChildrenCache.getCurrentData(); /**
* StartMode: 初始化方式
* POST_INITIALIZED_EVENT异步初始化初始化之后会触发事件
* NORMAL异步初始化
* BUILD_INITIAL_CACHE同步初始化
*/
childrenCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
// 列出子节点数据列表,需要使用BUILD_INITIAL_CACHE同步初始化模式才能获得,异步是获取不到的
List<ChildData> childDataList = childrenCache.getCurrentData();
listConsumer.accept(childDataList);
childrenCache.close();
} }
public static PathChildrenCache buildPathChildrenCache(String path) throws Exception { /**
if (!isPathExist(path)) { * 监听子节点的增删改
return null; *
} * @param parentPath 父节点路径
* @param listener
* @throws Exception
*/
public static void listenChildren(String parentPath, PathChildrenCacheListener listener) throws Exception {
// 为子节点添加watcher
// PathChildrenCache: 监听数据节点的增删改,可以设置触发的事件 // PathChildrenCache: 监听数据节点的增删改,可以设置触发的事件
// 且第三个参数要设置为true,不然ChildData对象中的getData返回null PathChildrenCache childrenCache = new PathChildrenCache(client, parentPath, true);
PathChildrenCache childrenCache = new PathChildrenCache(client, path, true);
// 列出子节点数据列表,需要使用BUILD_INITIAL_CACHE同步初始化模式才能获得,异步是获取不到的 /**
* StartMode: 初始化方式
* POST_INITIALIZED_EVENT异步初始化初始化之后会触发事件
* NORMAL异步初始化
* BUILD_INITIAL_CACHE同步初始化
*/
childrenCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE); childrenCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
return childrenCache; // 监听根节点下面的子节点
childrenCache.getListenable().addListener(listener);
} }
} }

@ -10,6 +10,8 @@ import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletResponse;
/** /**
* 处理网关自身异常 * 处理网关自身异常
* *
@ -26,8 +28,11 @@ public class ZuulErrorController implements ErrorController {
*/ */
@RequestMapping(ERROR_PATH) @RequestMapping(ERROR_PATH)
@ResponseBody @ResponseBody
public Object error() { public Object error(HttpServletResponse response) {
RequestContext ctx = RequestContext.getCurrentContext(); RequestContext ctx = RequestContext.getCurrentContext();
if (ctx.getResponse() == null) {
ctx.setResponse(response);
}
ctx.setResponseStatusCode(HttpStatus.OK.value()); ctx.setResponseStatusCode(HttpStatus.OK.value());
Throwable throwable = ctx.getThrowable(); Throwable throwable = ctx.getThrowable();
return this.buildResult(throwable); return this.buildResult(throwable);

@ -1,27 +0,0 @@
package com.gitee.sop.servercommon.bean;
import org.springframework.core.env.Environment;
/**
* @author thc
*/
public class EnvironmentContext {
private static Environment environment;
public static Environment getEnvironment() {
return environment;
}
public static void setEnvironment(Environment environment) {
EnvironmentContext.environment = environment;
}
public static String getProfile(Environment env) {
return env.getProperty("spring.profiles.active", "default");
}
public static String getProfile() {
return getProfile(environment);
}
}

@ -0,0 +1,11 @@
package com.gitee.sop.servercommon.bean;
/**
* @author tanghc
*/
public class ServiceConstants {
/**
* zookeeper存放接口路由信息的根目录
*/
public static final String SOP_SERVICE_ROUTE_PATH = "/com.gitee.sop.route";
}

@ -0,0 +1,99 @@
package com.gitee.sop.servercommon.bean;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.springframework.core.env.Environment;
import java.io.Closeable;
import java.io.IOException;
/**
* @author tanghc
*/
@Slf4j
@Getter
public class ZookeeperTool implements Closeable {
private CuratorFramework client;
private Environment environment;
public ZookeeperTool(Environment environment) {
this.environment = environment;
initZookeeperClient(environment);
}
public void initZookeeperClient(Environment environment) {
String zookeeperServerAddr = environment.getProperty("spring.cloud.zookeeper.connect-string");
if (StringUtils.isBlank(zookeeperServerAddr)) {
throw new RuntimeException("未指定spring.cloud.zookeeper.connect-string参数");
}
String baseSleepTimeMs = environment.getProperty("spring.cloud.zookeeper.baseSleepTimeMs");
String maxRetries = environment.getProperty("spring.cloud.zookeeper.maxRetries");
log.info("初始化zookeeper客户端,zookeeperServerAddr:{}, baseSleepTimeMs:{}, maxRetries:{}",
zookeeperServerAddr, baseSleepTimeMs, maxRetries);
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString(zookeeperServerAddr)
.retryPolicy(new ExponentialBackoffRetry(NumberUtils.toInt(baseSleepTimeMs, 3000), NumberUtils.toInt(maxRetries, 3)))
.build();
client.start();
this.client = client;
}
public boolean isPathExist(String path) {
try {
return client.checkExists().forPath(path) != null;
} catch (Exception e) {
return false;
}
}
/**
* 创建path如果path存在不报错静默返回path名称
*
* @param path
* @param data
* @return
* @throws Exception
*/
public String createPath(String path, String data) throws Exception {
if (isPathExist(path)) {
return path;
}
return getClient().create()
// 如果指定节点的父节点不存在,则Curator将会自动级联创建父节点
.creatingParentContainersIfNeeded()
.forPath(path, data.getBytes());
}
/**
* 新建或保存节点
*
* @param path
* @param data
* @return 返回path
* @throws Exception
*/
public String createOrUpdateData(String path, String data) throws Exception {
return getClient().create()
// 如果节点存在则Curator将会使用给出的数据设置这个节点的值
.orSetData()
// 如果指定节点的父节点不存在,则Curator将会自动级联创建父节点
.creatingParentContainersIfNeeded()
.forPath(path, data.getBytes());
}
@Override
public void close() throws IOException {
if (this.client != null) {
this.client.close();
}
}
}

@ -0,0 +1,54 @@
package com.gitee.sop.servercommon.configuration;
import com.gitee.easyopen.doc.ApiDocHolder;
import com.gitee.sop.servercommon.swagger.SwaggerValidator;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* 文档支持
* @author thc
*/
public abstract class EasyopenDocSupportController {
private SwaggerValidator swaggerValidator;
public abstract String getDocTitle();
public EasyopenDocSupportController() {
swaggerValidator = new SwaggerValidator(this.swaggerAccessProtected());
}
@RequestMapping("/v2/api-docs")
@ResponseBody
public Map<String, Object> getDocInfo(HttpServletRequest request, HttpServletResponse response) throws IOException {
if (swaggerValidator.swaggerAccessProtected() && !swaggerValidator.validate(request)) {
swaggerValidator.writeForbidden(response);
return null;
}
Map<String, Object> context = this.getContext();
context.put("easyopen", "1.16.3");
context.put("apiModules", ApiDocHolder.getApiDocBuilder().getApiModules());
context.put("title", getDocTitle());
return context;
}
public Map<String, Object> getContext() {
return new HashMap<>(8);
}
/**
* swagger访问是否加密保护
* @return
*/
protected boolean swaggerAccessProtected() {
return true;
}
}

@ -1,6 +1,5 @@
package com.gitee.sop.servercommon.configuration; package com.gitee.sop.servercommon.configuration;
import com.gitee.sop.servercommon.bean.EnvironmentContext;
import com.gitee.sop.servercommon.bean.ServiceConfig; import com.gitee.sop.servercommon.bean.ServiceConfig;
import com.gitee.sop.servercommon.manager.ApiMetaManager; import com.gitee.sop.servercommon.manager.ApiMetaManager;
import com.gitee.sop.servercommon.manager.DefaultRequestMappingEvent; import com.gitee.sop.servercommon.manager.DefaultRequestMappingEvent;
@ -55,7 +54,6 @@ public class SpringMvcServiceConfiguration {
@PostConstruct @PostConstruct
public final void after() { public final void after() {
log.info("-----spring容器加载完毕-----"); log.info("-----spring容器加载完毕-----");
EnvironmentContext.setEnvironment(environment);
Executors.newSingleThreadExecutor().execute(()->{ Executors.newSingleThreadExecutor().execute(()->{
uploadRouteToZookeeper(); uploadRouteToZookeeper();
}); });

@ -2,15 +2,15 @@ package com.gitee.sop.servercommon.manager;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.gitee.sop.servercommon.bean.ServiceApiInfo; import com.gitee.sop.servercommon.bean.ServiceApiInfo;
import com.gitee.sop.servercommon.bean.ServiceConstants;
import com.gitee.sop.servercommon.bean.ZookeeperTool;
import com.gitee.sop.servercommon.route.GatewayPredicateDefinition; import com.gitee.sop.servercommon.route.GatewayPredicateDefinition;
import com.gitee.sop.servercommon.route.GatewayRouteDefinition; import com.gitee.sop.servercommon.route.GatewayRouteDefinition;
import com.gitee.sop.servercommon.route.ServiceRouteInfo; import com.gitee.sop.servercommon.route.ServiceRouteInfo;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework; import org.apache.commons.io.IOUtils;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -33,7 +33,7 @@ public class ServiceZookeeperApiMetaManager implements ApiMetaManager {
/** /**
* zookeeper存放接口路由信息的根目录 * zookeeper存放接口路由信息的根目录
*/ */
public static final String SOP_SERVICE_ROUTE_PATH = "/com.gitee.sop.route"; public static final String SOP_SERVICE_ROUTE_PATH = ServiceConstants.SOP_SERVICE_ROUTE_PATH;
public static final String PATH_START_CHAR = "/"; public static final String PATH_START_CHAR = "/";
/** /**
@ -44,24 +44,56 @@ public class ServiceZookeeperApiMetaManager implements ApiMetaManager {
private static ServiceApiInfo.ApiMeta FIRST_API_META = new ServiceApiInfo.ApiMeta("_first.route_", "/", "v_000"); private static ServiceApiInfo.ApiMeta FIRST_API_META = new ServiceApiInfo.ApiMeta("_first.route_", "/", "v_000");
private final String routeRootPath; private final String routeRootPath = SOP_SERVICE_ROUTE_PATH;
private final String zookeeperServerAddr; private final String zookeeperServerAddr;
private Environment environment; private Environment environment;
private ZookeeperTool zookeeperTool;
private String serviceId;
public ServiceZookeeperApiMetaManager(Environment environment) { public ServiceZookeeperApiMetaManager(Environment environment) {
this.environment = environment; this.environment = environment;
this.routeRootPath = SOP_SERVICE_ROUTE_PATH; serviceId = environment.getProperty("spring.application.name");
if (StringUtils.isEmpty(serviceId)) {
throw new IllegalArgumentException("请在application.properties中指定spring.application.name属性");
}
zookeeperServerAddr = environment.getProperty("spring.cloud.zookeeper.connect-string"); zookeeperServerAddr = environment.getProperty("spring.cloud.zookeeper.connect-string");
if (StringUtils.isEmpty(zookeeperServerAddr)) { if (StringUtils.isEmpty(zookeeperServerAddr)) {
throw new IllegalArgumentException("未指定spring.cloud.zookeeper.connect-string参数"); throw new IllegalArgumentException("未指定spring.cloud.zookeeper.connect-string参数");
} }
this.zookeeperTool = new ZookeeperTool(environment);
this.uploadServiceId(environment);
}
/**
* 上传serviceId目录
*
* @param environment
*/
protected void uploadServiceId(Environment environment) {
try {
ServiceRouteInfo serviceRouteInfo = this.buildServiceRouteInfo();
// 保存路径
String savePath = routeRootPath + "/" + serviceId;
String nodeData = JSON.toJSONString(serviceRouteInfo);
log.info("zookeeper创建serviceId路径,path:{}, nodeData:{}", savePath, nodeData);
this.zookeeperTool.createPath(savePath, nodeData);
} catch (Exception e) {
throw new IllegalStateException("zookeeper操作失败");
}
} }
@Override @Override
public void uploadApi(ServiceApiInfo serviceApiInfo) { public void uploadApi(ServiceApiInfo serviceApiInfo) {
ServiceRouteInfo serviceRouteInfo = this.buildServiceGatewayInfo(serviceApiInfo); try {
this.uploadServiceRouteInfoToZookeeper(serviceRouteInfo); ServiceRouteInfo serviceRouteInfo = this.buildServiceGatewayInfo(serviceApiInfo);
this.uploadServiceRouteInfoToZookeeper(serviceRouteInfo);
} finally {
IOUtils.closeQuietly(zookeeperTool);
}
} }
/** /**
@ -78,11 +110,16 @@ public class ServiceZookeeperApiMetaManager implements ApiMetaManager {
GatewayRouteDefinition gatewayRouteDefinition = this.buildGatewayRouteDefinition(serviceApiInfo, apiMeta); GatewayRouteDefinition gatewayRouteDefinition = this.buildGatewayRouteDefinition(serviceApiInfo, apiMeta);
routeDefinitionList.add(gatewayRouteDefinition); routeDefinitionList.add(gatewayRouteDefinition);
} }
ServiceRouteInfo serviceRouteInfo = this.buildServiceRouteInfo();
serviceRouteInfo.setRouteDefinitionList(routeDefinitionList);
return serviceRouteInfo;
}
protected ServiceRouteInfo buildServiceRouteInfo() {
ServiceRouteInfo serviceRouteInfo = new ServiceRouteInfo(); ServiceRouteInfo serviceRouteInfo = new ServiceRouteInfo();
serviceRouteInfo.setServiceId(serviceApiInfo.getServiceId()); serviceRouteInfo.setServiceId(serviceId);
String description = environment.getProperty("spring.application.description"); String description = environment.getProperty("spring.application.description");
serviceRouteInfo.setDescription(description); serviceRouteInfo.setDescription(description);
serviceRouteInfo.setRouteDefinitionList(routeDefinitionList);
return serviceRouteInfo; return serviceRouteInfo;
} }
@ -144,75 +181,51 @@ public class ServiceZookeeperApiMetaManager implements ApiMetaManager {
* @param serviceRouteInfo * @param serviceRouteInfo
*/ */
protected void uploadServiceRouteInfoToZookeeper(ServiceRouteInfo serviceRouteInfo) { protected void uploadServiceRouteInfoToZookeeper(ServiceRouteInfo serviceRouteInfo) {
CuratorFramework client = null;
try { try {
// 保存路径 // 保存路径
String savePath = routeRootPath + "/" + serviceRouteInfo.getServiceId(); String savePath = routeRootPath + "/" + serviceRouteInfo.getServiceId();
client = CuratorFrameworkFactory.builder()
.connectString(zookeeperServerAddr)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.build();
client.start();
log.info("上传接口信息到zookeeper,path:{}, serviceId:{}, 接口数量:{}", log.info("上传接口信息到zookeeper,path:{}, serviceId:{}, 接口数量:{}",
savePath, savePath,
serviceRouteInfo.getServiceId(), serviceRouteInfo.getServiceId(),
serviceRouteInfo.getRouteDefinitionList().size()); serviceRouteInfo.getRouteDefinitionList().size());
String parentPath = this.uploadFolder(client, serviceRouteInfo); String parentPath = this.uploadFolder(serviceRouteInfo);
this.uploadRouteItems(client, serviceRouteInfo, parentPath); this.uploadRouteItems(serviceRouteInfo, parentPath);
} catch (Exception e) { } catch (Exception e) {
log.error("更新接口信息到zookeeper失败, serviceId:{}", serviceRouteInfo.getServiceId(), e); log.error("更新接口信息到zookeeper失败, serviceId:{}", serviceRouteInfo.getServiceId(), e);
} finally {
if (client != null) {
client.close();
}
} }
} }
/** /**
* 上传文件夹内容 * 上传文件夹内容
* *
* @param client
* @param serviceRouteInfo * @param serviceRouteInfo
* @return 返回文件夹路径 * @return 返回文件夹路径
*/ */
protected String uploadFolder(CuratorFramework client, ServiceRouteInfo serviceRouteInfo) throws Exception { protected String uploadFolder(ServiceRouteInfo serviceRouteInfo) throws Exception {
// 保存路径 // 保存路径
String savePath = routeRootPath + "/" + serviceRouteInfo.getServiceId(); String savePath = routeRootPath + "/" + serviceRouteInfo.getServiceId();
String serviceRouteInfoJson = JSON.toJSONString(serviceRouteInfo); String serviceRouteInfoJson = JSON.toJSONString(serviceRouteInfo);
log.info("上传service目录到zookeeper,路径:{},内容:{}", savePath, serviceRouteInfoJson); log.info("上传service目录到zookeeper,路径:{},内容:{}", savePath, serviceRouteInfoJson);
this.saveNode(client, savePath, serviceRouteInfoJson.getBytes()); this.zookeeperTool.createOrUpdateData(savePath, serviceRouteInfoJson);
return savePath; return savePath;
} }
/** /**
* 上传路由信息 * 上传路由信息
* *
* @param client
* @param serviceRouteInfo * @param serviceRouteInfo
* @throws Exception * @throws Exception
*/ */
protected void uploadRouteItems(CuratorFramework client, ServiceRouteInfo serviceRouteInfo, String parentPath) throws Exception { protected void uploadRouteItems(ServiceRouteInfo serviceRouteInfo, String parentPath) throws Exception {
List<GatewayRouteDefinition> routeDefinitionList = serviceRouteInfo.getRouteDefinitionList(); List<GatewayRouteDefinition> routeDefinitionList = serviceRouteInfo.getRouteDefinitionList();
for (GatewayRouteDefinition routeDefinition : routeDefinitionList) { for (GatewayRouteDefinition routeDefinition : routeDefinitionList) {
// 父目录/子目录 // 父目录/子目录
String savePath = parentPath + PATH_START_CHAR + routeDefinition.getId(); String savePath = parentPath + PATH_START_CHAR + routeDefinition.getId();
String routeDefinitionJson = JSON.toJSONString(routeDefinition); String routeDefinitionJson = JSON.toJSONString(routeDefinition);
log.info("上传路由配置到zookeeper,路径:{},路由数据:{}", savePath, routeDefinitionJson); log.info("上传路由配置到zookeeper,路径:{},路由数据:{}", savePath, routeDefinitionJson);
this.saveNode(client, savePath, routeDefinitionJson.getBytes()); this.zookeeperTool.createOrUpdateData(savePath, routeDefinitionJson);
} }
} }
protected void saveNode(CuratorFramework client, String path, byte[] data) throws Exception {
client.create()
// 如果节点存在则Curator将会使用给出的数据设置这个节点的值
.orSetData()
// 如果指定节点的父节点不存在,则Curator将会自动级联创建父节点
.creatingParentContainersIfNeeded()
.forPath(path, data);
}
} }

@ -10,7 +10,7 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.method.support.ModelAndViewContainer;
/** /**
* 解析request参数中的业务参数隐射到方法参数上 * 解析request参数中的业务参数绑定到方法参数上
* *
* @author tanghc * @author tanghc
*/ */

@ -1,16 +0,0 @@
package com.gitee.sop.servercommon.swagger;
import springfox.documentation.spring.web.plugins.DocumentationPluginsManager;
import springfox.documentation.spring.web.scanners.ApiDescriptionReader;
import springfox.documentation.spring.web.scanners.ApiListingScanner;
import springfox.documentation.spring.web.scanners.ApiModelReader;
/**
* @author tanghc
*/
public class ApiListingScannerExt extends ApiListingScanner {
public ApiListingScannerExt(ApiDescriptionReader apiDescriptionReader, ApiModelReader apiModelReader, DocumentationPluginsManager pluginsManager) {
super(apiDescriptionReader, apiModelReader, pluginsManager);
}
}

@ -1,8 +1,6 @@
package com.gitee.sop.servercommon.swagger; package com.gitee.sop.servercommon.swagger;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.DigestUtils;
import javax.servlet.Filter; import javax.servlet.Filter;
import javax.servlet.FilterChain; import javax.servlet.FilterChain;
@ -13,7 +11,6 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -25,9 +22,7 @@ public class SwaggerSecurityFilter implements Filter {
protected List<String> urlFilters = new ArrayList<>(); protected List<String> urlFilters = new ArrayList<>();
private String secret = "b749a2ec000f4f29"; {
public SwaggerSecurityFilter() {
urlFilters.add(".*?/doc\\.html.*"); urlFilters.add(".*?/doc\\.html.*");
urlFilters.add(".*?/v2/api-docs.*"); urlFilters.add(".*?/v2/api-docs.*");
urlFilters.add(".*?/v2/api-docs-ext.*"); urlFilters.add(".*?/v2/api-docs-ext.*");
@ -37,6 +32,12 @@ public class SwaggerSecurityFilter implements Filter {
urlFilters.add(".*?/swagger-resources/configuration/security.*"); urlFilters.add(".*?/swagger-resources/configuration/security.*");
} }
private SwaggerValidator swaggerValidator;
public SwaggerSecurityFilter(boolean swaggerAccessProtected) {
this.swaggerValidator = new SwaggerValidator(swaggerAccessProtected);
}
protected boolean match(String uri) { protected boolean match(String uri) {
boolean match = false; boolean match = false;
if (uri != null) { if (uri != null) {
@ -50,6 +51,7 @@ public class SwaggerSecurityFilter implements Filter {
return match; return match;
} }
@Override @Override
public void init(FilterConfig filterConfig) throws ServletException { public void init(FilterConfig filterConfig) throws ServletException {
@ -57,6 +59,10 @@ public class SwaggerSecurityFilter implements Filter {
@Override @Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
if (!swaggerValidator.swaggerAccessProtected()) {
filterChain.doFilter(servletRequest, servletResponse);
return;
}
HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse; HttpServletResponse response = (HttpServletResponse) servletResponse;
String uri = request.getRequestURI(); String uri = request.getRequestURI();
@ -64,29 +70,15 @@ public class SwaggerSecurityFilter implements Filter {
if (!match(uri)) { if (!match(uri)) {
filterChain.doFilter(servletRequest, servletResponse); filterChain.doFilter(servletRequest, servletResponse);
} else { } else {
if (validate(request)) { if (swaggerValidator.validate(request)) {
filterChain.doFilter(servletRequest, servletResponse); filterChain.doFilter(servletRequest, servletResponse);
} else { } else {
response.setContentType("text/palin;charset=UTF-8"); swaggerValidator.writeForbidden(response);
response.setStatus(403);
PrintWriter printWriter = response.getWriter();
printWriter.write("access forbidden");
printWriter.flush();
} }
} }
} }
protected boolean validate(HttpServletRequest request) {
String time = request.getParameter("time");
String sign = request.getParameter("sign");
if (StringUtils.isAnyBlank(time, sign)) {
return false;
}
String source = secret + time + secret;
String serverSign = DigestUtils.md5DigestAsHex(source.getBytes());
return serverSign.equals(sign);
}
@Override @Override
public void destroy() { public void destroy() {

@ -32,7 +32,15 @@ public abstract class SwaggerSupport {
@Bean @Bean
public SwaggerSecurityFilter swaggerSecurityFilter() { public SwaggerSecurityFilter swaggerSecurityFilter() {
return new SwaggerSecurityFilter(); return new SwaggerSecurityFilter(swaggerAccessProtected());
}
/**
* swagger访问是否加密保护
* @return
*/
protected boolean swaggerAccessProtected() {
return true;
} }
protected ApiInfo apiInfo() { protected ApiInfo apiInfo() {

@ -0,0 +1,53 @@
package com.gitee.sop.servercommon.swagger;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.DigestUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @author tanghc
*/
public class SwaggerValidator {
private String secret = "b749a2ec000f4f29";
private boolean swaggerAccessProtected = true;
public SwaggerValidator(boolean swaggerAccessProtected) {
this.swaggerAccessProtected = swaggerAccessProtected;
}
public SwaggerValidator() {
}
/**
* swagger访问是否加密保护
* @return
*/
public boolean swaggerAccessProtected() {
return swaggerAccessProtected;
}
public boolean validate(HttpServletRequest request) {
String time = request.getParameter("time");
String sign = request.getParameter("sign");
if (StringUtils.isAnyBlank(time, sign)) {
return false;
}
String source = secret + time + secret;
String serverSign = DigestUtils.md5DigestAsHex(source.getBytes());
return serverSign.equals(sign);
}
public void writeForbidden(HttpServletResponse response) throws IOException {
response.setContentType("text/palin;charset=UTF-8");
response.setStatus(403);
PrintWriter printWriter = response.getWriter();
printWriter.write("access forbidden");
printWriter.flush();
}
}

@ -1,28 +0,0 @@
package com.gitee.easyopen.server.config;
import com.gitee.easyopen.doc.ApiDocHolder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
import java.util.Map;
public abstract class BaseSopDocController {
public abstract String getDocTitle();
@RequestMapping("/v2/api-docs")
@ResponseBody
public Map<String, Object> getDocInfo() {
Map<String, Object> context = this.getContext();
context.put("easyopen", "1.16.3");
context.put("apiModules", ApiDocHolder.getApiDocBuilder().getApiModules());
context.put("title", getDocTitle());
return context;
}
public Map<String, Object> getContext() {
return new HashMap<>(8);
}
}

@ -1,5 +1,6 @@
package com.gitee.easyopen.server.config; package com.gitee.easyopen.server.config;
import com.gitee.sop.servercommon.configuration.EasyopenDocSupportController;
import com.gitee.sop.servercommon.configuration.EasyopenServiceConfiguration; import com.gitee.sop.servercommon.configuration.EasyopenServiceConfiguration;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
@ -11,7 +12,7 @@ import org.springframework.stereotype.Controller;
public class SopConfig extends EasyopenServiceConfiguration { public class SopConfig extends EasyopenServiceConfiguration {
@Controller @Controller
public static class SopDocController extends BaseSopDocController { public static class SopDocController extends EasyopenDocSupportController {
@Override @Override
public String getDocTitle() { public String getDocTitle() {
return "商品API"; return "商品API";

@ -11,7 +11,7 @@ import org.apache.curator.retry.ExponentialBackoffRetry;
*/ */
public class CuratorTest extends TestCase { public class CuratorTest extends TestCase {
private String zookeeperServerAddr = "127.0.0.1:2181"; private String zookeeperServerAddr = "localhost:2181";
/** /**
* 递归删除节点只能在测试环境用 * 递归删除节点只能在测试环境用
@ -30,17 +30,5 @@ public class CuratorTest extends TestCase {
client.delete().deletingChildrenIfNeeded().forPath(SopConstants.SOP_SERVICE_ROUTE_PATH); client.delete().deletingChildrenIfNeeded().forPath(SopConstants.SOP_SERVICE_ROUTE_PATH);
} catch (Exception e) { } catch (Exception e) {
} }
try {
client.delete().deletingChildrenIfNeeded().forPath(SopConstants.SOP_SERVICE_ROUTE_PATH + "-default");
} catch (Exception e) {
}
try {
client.delete().deletingChildrenIfNeeded().forPath(SopConstants.SOP_SERVICE_ROUTE_PATH + "-dev");
} catch (Exception e) {
}
try {
client.delete().deletingChildrenIfNeeded().forPath(SopConstants.SOP_SERVICE_ROUTE_PATH + "-test");
} catch (Exception e) {
}
} }
} }

@ -0,0 +1,17 @@
eureka:
client:
fetch-registry: false
# 不注册自己
register-with-eureka: false
serviceUrl:
defaultZone: http://${eureka.host}:${eureka.port}/eureka/
# 注册中心地址
host: localhost
port: 1111
server:
port: 1111
spring:
application:
name: sop-registry

@ -1,12 +0,0 @@
spring.application.name=sop-registry
server.port=1111
# ---- eureka注册中心 ----
# 不注册自己
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
# 注册中心地址
eureka.host=localhost
eureka.port=1111
eureka.client.serviceUrl.defaultZone=http://${eureka.host}:${eureka.port}/eureka/

@ -0,0 +1,3 @@
spring:
profiles:
active: dev

@ -82,12 +82,6 @@
<blockquote class="layui-elem-quote layui-text sop-description"> <blockquote class="layui-elem-quote layui-text sop-description">
</blockquote> </blockquote>
<div class="site-title">
<fieldset class="layui-elem-field layui-field-title site-title">
<legend><a name="use">公共参数</a></legend>
</fieldset>
</div>
<div class="site-text"> <div class="site-text">
<h3>请求地址</h3> <h3>请求地址</h3>
<table class="layui-table"> <table class="layui-table">
@ -106,6 +100,12 @@
</table> </table>
</div> </div>
<div class="site-title">
<fieldset class="layui-elem-field layui-field-title site-title">
<legend><a name="use">公共参数</a></legend>
</fieldset>
</div>
<div class="site-text"> <div class="site-text">
<h3>公共请求参数</h3> <h3>公共请求参数</h3>
<table class="layui-table"> <table class="layui-table">

@ -0,0 +1,11 @@
package com.gitee.sop.websiteserver.bean;
/**
* @author tanghc
*/
public class WebsiteConstants {
/**
* zookeeper存放接口路由信息的根目录
*/
public static final String SOP_SERVICE_ROUTE_PATH = "/com.gitee.sop.route";
}

@ -0,0 +1,80 @@
package com.gitee.sop.websiteserver.bean;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.TreeCache;
import org.apache.curator.framework.recipes.cache.TreeCacheListener;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.springframework.core.env.Environment;
import org.springframework.util.Assert;
/**
* @author tanghc
*/
@Slf4j
public class ZookeeperContext {
private static CuratorFramework client;
public static void setEnvironment(Environment environment) {
Assert.notNull(environment, "environment不能为null");
initZookeeperClient(environment);
}
public synchronized static void initZookeeperClient(Environment environment) {
if (client != null) {
return;
}
String zookeeperServerAddr = environment.getProperty("spring.cloud.zookeeper.connect-string");
if (StringUtils.isBlank(zookeeperServerAddr)) {
throw new RuntimeException("未指定spring.cloud.zookeeper.connect-string参数");
}
String baseSleepTimeMs = environment.getProperty("spring.cloud.zookeeper.baseSleepTimeMs");
String maxRetries = environment.getProperty("spring.cloud.zookeeper.maxRetries");
log.info("初始化zookeeper客户端,zookeeperServerAddr:{}, baseSleepTimeMs:{}, maxRetries:{}",
zookeeperServerAddr, baseSleepTimeMs, maxRetries);
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString(zookeeperServerAddr)
.retryPolicy(new ExponentialBackoffRetry(NumberUtils.toInt(baseSleepTimeMs, 3000), NumberUtils.toInt(maxRetries, 3)))
.build();
client.start();
setClient(client);
}
public static String getRouteRootPath() {
return WebsiteConstants.SOP_SERVICE_ROUTE_PATH;
}
public static CuratorFramework getClient() {
return client;
}
public static void setClient(CuratorFramework client) {
ZookeeperContext.client = client;
}
/**
* 监听子节点可以自定义层级
* @param parentPath 父节点路径
* @param maxDepth 层级从1开始比如当前监听节点/t1目录最深为/t1/t2/t3/t4,则maxDepth=3,说明下面3级子目录全
* @param listener
* @throws Exception
*/
public static void listenChildren(String parentPath, int maxDepth, TreeCacheListener listener) throws Exception {
final TreeCache treeCache = TreeCache
.newBuilder(client, parentPath)
.setCacheData(true)
.setMaxDepth(maxDepth)
.build();
treeCache.getListenable().addListener(listener);
//没有开启模式作为入参的方法
treeCache.start();
}
}

@ -65,10 +65,13 @@ public class DocController {
} }
@GetMapping("/doc/reload") // 后门地址,可手动更新文档内容,一般情况下用不到
public void reload(String pwd) { @GetMapping("/reload")
if (StringUtils.equals(this.pwd, pwd)) { public String reload(String pwd) {
boolean correct = StringUtils.equals(this.pwd, pwd);
if (correct) {
docManager.load(); docManager.load();
} }
return String.valueOf(correct);
} }
} }

@ -8,12 +8,14 @@ import com.gitee.sop.websiteserver.bean.EurekaApplication;
import com.gitee.sop.websiteserver.bean.EurekaApps; import com.gitee.sop.websiteserver.bean.EurekaApps;
import com.gitee.sop.websiteserver.bean.EurekaInstance; import com.gitee.sop.websiteserver.bean.EurekaInstance;
import com.gitee.sop.websiteserver.bean.EurekaUri; import com.gitee.sop.websiteserver.bean.EurekaUri;
import com.gitee.sop.websiteserver.bean.ZookeeperContext;
import com.gitee.sop.websiteserver.vo.ServiceInfoVO; import com.gitee.sop.websiteserver.vo.ServiceInfoVO;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.curator.framework.recipes.cache.TreeCacheEvent;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
@ -29,6 +31,11 @@ import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -53,13 +60,19 @@ public class DocManagerImpl implements DocManager {
DocParser easyopenDocParser = new EasyopenDocParser(); DocParser easyopenDocParser = new EasyopenDocParser();
private String secret = "b749a2ec000f4f29p"; ExecutorService executorService = Executors.newSingleThreadExecutor();
DelayQueue<Msg> queue = new DelayQueue<>();
private String secret = "b749a2ec000f4f29";
@Autowired @Autowired
private Environment environment; private Environment environment;
private String eurekaUrl; private String eurekaUrl;
private volatile boolean listenInited;
@Override @Override
public void load() { public void load() {
try { try {
@ -70,7 +83,7 @@ public class DocManagerImpl implements DocManager {
ServiceInfoVO serviceInfoVo = entry.getValue().get(0); ServiceInfoVO serviceInfoVo = entry.getValue().get(0);
loadDocInfo(serviceInfoVo); loadDocInfo(serviceInfoVo);
} }
} catch (IOException e) { } catch (Exception e) {
log.error("加载失败", e); log.error("加载失败", e);
} }
} }
@ -136,11 +149,13 @@ public class DocManagerImpl implements DocManager {
.forEach(eurekaApplication -> { .forEach(eurekaApplication -> {
List<EurekaInstance> instanceList = eurekaApplication.getInstance(); List<EurekaInstance> instanceList = eurekaApplication.getInstance();
for (EurekaInstance instance : instanceList) { for (EurekaInstance instance : instanceList) {
ServiceInfoVO vo = new ServiceInfoVO(); if ("UP".equals(instance.getStatus())) {
vo.setName(eurekaApplication.getName()); ServiceInfoVO vo = new ServiceInfoVO();
vo.setIpAddr(instance.getIpAddr()); vo.setName(eurekaApplication.getName());
vo.setServerPort(instance.fetchPort()); vo.setIpAddr(instance.getIpAddr());
serviceInfoVoList.add(vo); vo.setServerPort(instance.fetchPort());
serviceInfoVoList.add(vo);
}
} }
}); });
@ -162,7 +177,7 @@ public class DocManagerImpl implements DocManager {
} }
@PostConstruct @PostConstruct
protected void after() { protected void after() throws Exception {
String eurekaUrls = environment.getProperty("eureka.client.serviceUrl.defaultZone"); String eurekaUrls = environment.getProperty("eureka.client.serviceUrl.defaultZone");
if (StringUtils.isBlank(eurekaUrls)) { if (StringUtils.isBlank(eurekaUrls)) {
throw new IllegalArgumentException("未指定eureka.client.serviceUrl.defaultZone参数"); throw new IllegalArgumentException("未指定eureka.client.serviceUrl.defaultZone参数");
@ -172,5 +187,82 @@ public class DocManagerImpl implements DocManager {
url = eurekaUrls.substring(0, eurekaUrls.length() - 1); url = eurekaUrls.substring(0, eurekaUrls.length() - 1);
} }
this.eurekaUrl = url; this.eurekaUrl = url;
this.listenServiceId();
}
/**
* 监听serviceId更改
*
* @throws Exception
*/
protected void listenServiceId() throws Exception {
executorService.execute(new Consumer(queue, this));
ZookeeperContext.setEnvironment(environment);
String routeRootPath = ZookeeperContext.getRouteRootPath();
// 如果节点内容有变化则自动更新文档
ZookeeperContext.listenChildren(routeRootPath, 1, (client, event) -> {
if (listenInited) {
long id = System.currentTimeMillis();
// 延迟20秒执行
queue.offer(new Msg(id, 1000 * 20));
}
TreeCacheEvent.Type type = event.getType();
if (type == TreeCacheEvent.Type.INITIALIZED) {
listenInited = true;
}
});
}
static class Msg implements Delayed {
private long id;
private long delay;
// 自定义实现比较方法返回 1 0 -1三个参数
public Msg(long id, long delay) {
this.id = id;
this.delay = delay + System.currentTimeMillis();
}
@Override
public int compareTo(Delayed delayed) {
Msg msg = (Msg) delayed;
return Long.valueOf(this.id) > Long.valueOf(msg.id) ? 1
: (Long.valueOf(this.id) < Long.valueOf(msg.id) ? -1 : 0);
}
// 延迟任务是否到时就是按照这个方法判断如果返回的是负数则说明到期否则还没到期
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(this.delay - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
}
@Slf4j
static class Consumer implements Runnable {
private DelayQueue<Msg> queue;
private DocManager docManager;
public Consumer(DelayQueue<Msg> queue, DocManager docManager) {
this.queue = queue;
this.docManager = docManager;
}
@Override
public void run() {
while (true) {
try {
queue.take();
log.info("延迟队列触发--重新加载文档信息");
docManager.load();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} }
} }

Loading…
Cancel
Save