全面使用nacos,舍弃zookeeper

This commit is contained in:
tanghc
2019-08-20 19:17:23 +08:00
parent 55e5a7e795
commit 219f944162
91 changed files with 1775 additions and 2555 deletions

View File

@@ -27,6 +27,17 @@
</properties>
<dependencies>
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>nacos-config-spring-boot-starter</artifactId>
<version>0.2.2</version>
</dependency>
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>nacos-discovery-spring-boot-starter</artifactId>
<version>0.2.2</version>
</dependency>
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-registry-api</artifactId>
@@ -45,39 +56,13 @@
<artifactId>fastmybatis-spring-boot-starter</artifactId>
<version>1.8.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- zookeeper客户端针对zookeeper-3.4.x
如果zookeeper使用3.5.x可以直接使用curator-recipes最高版本
详情http://curator.apache.org/zk-compatibility.html
-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>${curator-recipes.version}</version>
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>${zookeeper.version}</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
@@ -122,5 +107,12 @@
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
</project>

View File

@@ -1,6 +1,5 @@
package com.gitee.sop.adminserver.api.isv;
import com.alibaba.fastjson.JSON;
import com.gitee.easyopen.annotation.Api;
import com.gitee.easyopen.annotation.ApiService;
import com.gitee.easyopen.doc.DataType;
@@ -24,12 +23,11 @@ import com.gitee.sop.adminserver.api.isv.result.IsvKeysGenVO;
import com.gitee.sop.adminserver.api.isv.result.IsvKeysVO;
import com.gitee.sop.adminserver.api.isv.result.RoleVO;
import com.gitee.sop.adminserver.bean.ChannelMsg;
import com.gitee.sop.adminserver.bean.ZookeeperContext;
import com.gitee.sop.adminserver.bean.NacosConfigs;
import com.gitee.sop.adminserver.common.BizException;
import com.gitee.sop.adminserver.common.ChannelOperation;
import com.gitee.sop.adminserver.common.IdGen;
import com.gitee.sop.adminserver.common.RSATool;
import com.gitee.sop.adminserver.common.ZookeeperPathNotExistException;
import com.gitee.sop.adminserver.entity.IsvInfo;
import com.gitee.sop.adminserver.entity.IsvKeys;
import com.gitee.sop.adminserver.entity.PermIsvRole;
@@ -38,6 +36,7 @@ import com.gitee.sop.adminserver.mapper.IsvInfoMapper;
import com.gitee.sop.adminserver.mapper.IsvKeysMapper;
import com.gitee.sop.adminserver.mapper.PermIsvRoleMapper;
import com.gitee.sop.adminserver.mapper.PermRoleMapper;
import com.gitee.sop.adminserver.service.ConfigPushService;
import com.gitee.sop.adminserver.service.RoutePermissionService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
@@ -86,6 +85,9 @@ public class IsvApi {
@Autowired
RoutePermissionService routePermissionService;
@Autowired
private ConfigPushService configPushService;
@Value("${sop.sign-type}")
private String sopSignType;
@@ -231,15 +233,7 @@ public class IsvApi {
return;
}
ChannelMsg channelMsg = new ChannelMsg(ChannelOperation.ISV_INFO_UPDATE, isvDetail);
String path = ZookeeperContext.getIsvInfoChannelPath();
String data = JSON.toJSONString(channelMsg);
try {
log.info("消息推送--ISV信息(update), path:{}, data:{}", path, data);
ZookeeperContext.updatePathData(path, data);
} catch (ZookeeperPathNotExistException e) {
log.error("发送isvChannelMsg失败, path:{}, msg:{}", path, data, e);
throw new BizException("路径不存在");
}
configPushService.publishConfig(NacosConfigs.DATA_ID_ISV, NacosConfigs.GROUP_CHANNEL, channelMsg);
}
private IsvKeysGenVO createIsvKeys() throws Exception {

View File

@@ -1,6 +1,5 @@
package com.gitee.sop.adminserver.api.service;
import com.alibaba.fastjson.JSON;
import com.gitee.easyopen.annotation.Api;
import com.gitee.easyopen.annotation.ApiService;
import com.gitee.easyopen.doc.annotation.ApiDoc;
@@ -14,11 +13,12 @@ import com.gitee.sop.adminserver.api.service.param.ConfigIpBlackForm;
import com.gitee.sop.adminserver.api.service.param.ConfigIpBlacklistPageParam;
import com.gitee.sop.adminserver.api.service.result.ConfigIpBlacklistVO;
import com.gitee.sop.adminserver.bean.ChannelMsg;
import com.gitee.sop.adminserver.bean.ZookeeperContext;
import com.gitee.sop.adminserver.bean.NacosConfigs;
import com.gitee.sop.adminserver.common.BizException;
import com.gitee.sop.adminserver.common.ChannelOperation;
import com.gitee.sop.adminserver.entity.ConfigIpBlacklist;
import com.gitee.sop.adminserver.mapper.ConfigIpBlacklistMapper;
import com.gitee.sop.adminserver.service.ConfigPushService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@@ -33,6 +33,9 @@ public class IPBlacklistApi {
@Autowired
ConfigIpBlacklistMapper configIpBlacklistMapper;
@Autowired
private ConfigPushService configPushService;
@ApiDocMethod(description = "获取IP黑名单分页")
@Api(name = "ip.blacklist.page")
PageEasyui<ConfigIpBlacklistVO> page(ConfigIpBlacklistPageParam form) {
@@ -85,20 +88,7 @@ public class IPBlacklistApi {
public void sendIpBlacklistMsg(ConfigIpBlacklist configIpBlacklist, ChannelOperation channelOperation) throws Exception {
ChannelMsg channelMsg = new ChannelMsg(channelOperation, configIpBlacklist);
String jsonData = JSON.toJSONString(channelMsg);
String path = ZookeeperContext.getIpBlacklistChannelPath();
log.info("消息推送--IP黑名单设置({}), path:{}, data:{}",channelOperation.getOperation(), path, jsonData);
ZookeeperContext.createOrUpdateData(path, jsonData);
configPushService.publishConfig(NacosConfigs.DATA_ID_IP_BLACKLIST, NacosConfigs.GROUP_CHANNEL, channelMsg);
}
enum BlacklistMsgType {
/**
* 黑名单消息类型:添加
*/
ADD,
/**
* 黑名单消息类型:删除
*/
DELETE
}
}

View File

@@ -1,6 +1,5 @@
package com.gitee.sop.adminserver.api.service;
import com.alibaba.fastjson.JSON;
import com.gitee.easyopen.annotation.Api;
import com.gitee.easyopen.annotation.ApiService;
import com.gitee.easyopen.doc.annotation.ApiDoc;
@@ -15,10 +14,7 @@ import com.gitee.sop.adminserver.api.service.param.RouteUpdateParam;
import com.gitee.sop.adminserver.api.service.result.RouteVO;
import com.gitee.sop.adminserver.bean.GatewayRouteDefinition;
import com.gitee.sop.adminserver.bean.RouteConfigDto;
import com.gitee.sop.adminserver.bean.ZookeeperContext;
import com.gitee.sop.adminserver.common.BizException;
import com.gitee.sop.adminserver.common.ZookeeperPathExistException;
import com.gitee.sop.adminserver.common.ZookeeperPathNotExistException;
import com.gitee.sop.adminserver.entity.ConfigRouteBase;
import com.gitee.sop.adminserver.entity.PermRole;
import com.gitee.sop.adminserver.mapper.ConfigRouteBaseMapper;
@@ -28,7 +24,6 @@ import com.gitee.sop.adminserver.service.RouteConfigService;
import com.gitee.sop.adminserver.service.RoutePermissionService;
import com.gitee.sop.adminserver.service.RouteService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -71,6 +66,10 @@ public class RouteApi {
RouteVO vo = new RouteVO();
BeanUtils.copyProperties(gatewayRouteDefinition, vo);
vo.setRoles(this.getRouteRole(gatewayRouteDefinition.getId()));
ConfigRouteBase configRouteBase = configRouteBaseMapper.getByColumn("route_id", gatewayRouteDefinition.getId());
if (configRouteBase != null) {
vo.setStatus(configRouteBase.getStatus());
}
return vo;
})
.collect(Collectors.toList());
@@ -96,54 +95,48 @@ public class RouteApi {
@Api(name = "route.add")
@ApiDocMethod(description = "新增路由")
void addRoute(RouteAddParam param) {
// TODO:新增路由
String id = param.getName() + param.getVersion();
String routePath = ZookeeperContext.buildRoutePath(param.getServiceId(), id);
// String routePath = ZookeeperContext.buildRoutePath(param.getServiceId(), id);
GatewayRouteDefinition routeDefinition = new GatewayRouteDefinition();
CopyUtil.copyPropertiesIgnoreNull(param, routeDefinition);
routeDefinition.setId(id);
routeDefinition.setCustom(1);
try {
ZookeeperContext.addPath(routePath, JSON.toJSONString(routeDefinition));
} catch (ZookeeperPathExistException e) {
throw new BizException("路由已存在");
}
// try {
// ZookeeperContext.addPath(routePath, JSON.toJSONString(routeDefinition));
// } catch (ZookeeperPathExistException e) {
// throw new BizException("路由已存在");
// }
this.updateRouteConfig(routeDefinition);
}
@Api(name = "route.update")
@ApiDocMethod(description = "修改路由")
void updateRoute(RouteUpdateParam param) {
String routePath = ZookeeperContext.buildRoutePath(param.getServiceId(), param.getId());
GatewayRouteDefinition routeDefinition = this.getGatewayRouteDefinition(routePath);
CopyUtil.copyPropertiesIgnoreNull(param, routeDefinition);
try {
ZookeeperContext.updatePathData(routePath, JSON.toJSONString(routeDefinition));
} catch (ZookeeperPathNotExistException e) {
throw new BizException("路由不存在");
}
this.updateRouteConfig(routeDefinition);
// TODO:修改路由
// String routePath = ZookeeperContext.buildRoutePath(param.getServiceId(), param.getId());
// GatewayRouteDefinition routeDefinition = this.getGatewayRouteDefinition(routePath);
// CopyUtil.copyPropertiesIgnoreNull(param, routeDefinition);
// try {
// ZookeeperContext.updatePathData(routePath, JSON.toJSONString(routeDefinition));
// } catch (ZookeeperPathNotExistException e) {
// throw new BizException("路由不存在");
// }
// this.updateRouteConfig(routeDefinition);
}
@Api(name = "route.del")
@ApiDocMethod(description = "删除路由")
void delRoute(RouteDeleteParam param) {
String routePath = ZookeeperContext.buildRoutePath(param.getServiceId(), param.getId());
// TODO:删除路由
/*String routePath = ZookeeperContext.buildRoutePath(param.getServiceId(), param.getId());
GatewayRouteDefinition routeDefinition = this.getGatewayRouteDefinition(routePath);
if (!BooleanUtils.toBoolean(routeDefinition.getCustom())) {
throw new BizException("非自定义路由,无法删除");
}
ZookeeperContext.deletePathDeep(routePath);
ZookeeperContext.deletePathDeep(routePath);*/
}
private GatewayRouteDefinition getGatewayRouteDefinition(String zookeeperRoutePath) {
String data = null;
try {
data = ZookeeperContext.getData(zookeeperRoutePath);
} catch (ZookeeperPathNotExistException e) {
throw new BizException("路由不存在");
}
return JSON.parseObject(data, GatewayRouteDefinition.class);
}
private void updateRouteConfig(GatewayRouteDefinition routeDefinition) {
try {

View File

@@ -1,6 +1,8 @@
package com.gitee.sop.adminserver.api.service;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.annotation.NacosInjected;
import com.alibaba.nacos.api.naming.NamingService;
import com.gitee.easyopen.annotation.Api;
import com.gitee.easyopen.annotation.ApiService;
import com.gitee.easyopen.doc.annotation.ApiDoc;
@@ -15,27 +17,26 @@ import com.gitee.sop.adminserver.api.service.result.ServiceInfoVo;
import com.gitee.sop.adminserver.api.service.result.ServiceInstanceVO;
import com.gitee.sop.adminserver.bean.ChannelMsg;
import com.gitee.sop.adminserver.bean.MetadataEnum;
import com.gitee.sop.adminserver.bean.NacosConfigs;
import com.gitee.sop.adminserver.bean.ServiceGrayDefinition;
import com.gitee.sop.adminserver.bean.ServiceRouteInfo;
import com.gitee.sop.adminserver.bean.ZookeeperContext;
import com.gitee.sop.adminserver.common.BizException;
import com.gitee.sop.adminserver.common.ChannelOperation;
import com.gitee.sop.adminserver.common.StatusEnum;
import com.gitee.sop.adminserver.common.ZookeeperPathExistException;
import com.gitee.sop.adminserver.common.ZookeeperPathNotExistException;
import com.gitee.sop.adminserver.entity.ConfigGray;
import com.gitee.sop.adminserver.entity.ConfigGrayInstance;
import com.gitee.sop.adminserver.mapper.ConfigGrayInstanceMapper;
import com.gitee.sop.adminserver.mapper.ConfigGrayMapper;
import com.gitee.sop.adminserver.service.ConfigPushService;
import com.gitee.sop.registryapi.bean.ServiceInfo;
import com.gitee.sop.registryapi.bean.ServiceInstance;
import com.gitee.sop.registryapi.service.RegistryService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.curator.framework.recipes.cache.ChildData;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import java.io.IOException;
import java.util.ArrayList;
@@ -63,35 +64,54 @@ public class ServiceApi {
@Autowired
private ConfigGrayInstanceMapper configGrayInstanceMapper;
@Autowired
private ConfigPushService configPushService;
@NacosInjected
private NamingService namingService;
@Api(name = "zookeeper.service.list")
@ApiDocMethod(description = "zk中的服务列表", elementClass = RouteServiceInfo.class)
List<RouteServiceInfo> listServiceInfo(ServiceSearchParam param) {
String routeRootPath = ZookeeperContext.getSopRouteRootPath();
List<ChildData> childDataList = ZookeeperContext.getChildrenData(routeRootPath);
List<RouteServiceInfo> serviceInfoList = childDataList.stream()
.filter(childData -> childData.getData() != null && childData.getData().length > 0)
.map(childData -> {
String serviceNodeData = new String(childData.getData());
RouteServiceInfo serviceInfo = JSON.parseObject(serviceNodeData, RouteServiceInfo.class);
return serviceInfo;
})
List<ServiceInfo> servicesOfServer = null;
try {
servicesOfServer = registryService.listAllService(1, Integer.MAX_VALUE);
} catch (Exception e) {
log.error("nacos获取服务列表失败", e);
throw new BizException("获取服务列表失败");
}
return servicesOfServer
.stream()
.filter(serviceInfo -> {
String serviceId = serviceInfo.getServiceId();
if ("api-gateway".equalsIgnoreCase(serviceId)) {
return false;
}
// 隐藏空服务
if (CollectionUtils.isEmpty(serviceInfo.getInstances())) {
return false;
}
if (StringUtils.isBlank(param.getServiceId())) {
return true;
} else {
return serviceInfo.getServiceId().contains(param.getServiceId());
return serviceId.contains(param.getServiceId());
}
})
.map(serviceInfo -> {
RouteServiceInfo routeServiceInfo = new RouteServiceInfo();
routeServiceInfo.setServiceId(serviceInfo.getServiceId());
return routeServiceInfo;
})
.collect(Collectors.toList());
return serviceInfoList;
}
@Api(name = "service.custom.add")
@ApiDocMethod(description = "添加服务")
void addService(ServiceAddParam param) {
// TODO:添加服务
String serviceId = param.getServiceId();
String servicePath = ZookeeperContext.buildServiceIdPath(serviceId);
// String servicePath = ZookeeperContext.buildServiceIdPath(serviceId);
ServiceRouteInfo serviceRouteInfo = new ServiceRouteInfo();
Date now = new Date();
serviceRouteInfo.setServiceId(serviceId);
@@ -100,17 +120,19 @@ public class ServiceApi {
serviceRouteInfo.setUpdateTime(now);
serviceRouteInfo.setCustom(BooleanUtils.toInteger(true));
String serviceData = JSON.toJSONString(serviceRouteInfo);
try {
/* try {
ZookeeperContext.addPath(servicePath, serviceData);
} catch (ZookeeperPathExistException e) {
throw new BizException("服务已存在");
}
}*/
}
@Api(name = "service.custom.del")
@ApiDocMethod(description = "删除自定义服务")
void delService(ServiceSearchParam param) {
String serviceId = param.getServiceId();
// TODO:删除自定义服务
/*String serviceId = param.getServiceId();
String servicePath = ZookeeperContext.buildServiceIdPath(serviceId);
String data = null;
try {
@@ -126,7 +148,7 @@ public class ServiceApi {
if (!BooleanUtils.toBoolean(custom)) {
throw new BizException("非自定义服务,无法删除");
}
ZookeeperContext.deletePathDeep(servicePath);
ZookeeperContext.deletePathDeep(servicePath);*/
}
@Api(name = "service.instance.list")
@@ -134,7 +156,7 @@ public class ServiceApi {
List<ServiceInstanceVO> listService(ServiceSearchParam param) {
List<ServiceInfo> serviceInfos;
try {
serviceInfos = registryService.listAllService(1, 99999);
serviceInfos = registryService.listAllService(1, Integer.MAX_VALUE);
} catch (Exception e) {
log.error("获取服务实例失败", e);
return Collections.emptyList();
@@ -293,10 +315,7 @@ public class ServiceApi {
serviceGrayDefinition.setInstanceId(instanceId);
serviceGrayDefinition.setServiceId(serviceId);
ChannelMsg channelMsg = new ChannelMsg(channelOperation, serviceGrayDefinition);
String jsonData = JSON.toJSONString(channelMsg);
String path = ZookeeperContext.getServiceGrayChannelPath();
log.info("消息推送--灰度发布({}), path:{}, data:{}", channelOperation.getOperation(), path, jsonData);
ZookeeperContext.createOrUpdateData(path, jsonData);
configPushService.publishConfig(NacosConfigs.DATA_ID_GRAY, NacosConfigs.GROUP_CHANNEL, channelMsg);
}
private ConfigGray getConfigGray(String serviceId) {

View File

@@ -0,0 +1,29 @@
package com.gitee.sop.adminserver.bean;
/**
* @author tanghc
*/
public class NacosConfigs {
public static final String GROUP_CHANNEL = "sop:channel";
public static final String GROUP_ROUTE = "sop:route";
public static final String DATA_ID_GRAY = "com.gitee.sop.channel.gray";
public static final String DATA_ID_IP_BLACKLIST = "com.gitee.sop.channel.ipblacklist";
public static final String DATA_ID_ISV = "com.gitee.sop.channel.isv";
public static final String DATA_ID_ROUTE_PERMISSION = "com.gitee.sop.channel.routepermission";
public static final String DATA_ID_LIMIT_CONFIG = "com.gitee.sop.channel.limitconfig";
public static final String DATA_ID_ROUTE_CONFIG = "com.gitee.sop.channel.routeconfig";
private static final String DATA_ID_TPL = "com.gitee.sop.route.%s";
public static String getRouteDataId(String serviceId) {
return String.format(DATA_ID_TPL, serviceId);
}
}

View File

@@ -1,344 +0,0 @@
package com.gitee.sop.adminserver.bean;
import com.gitee.sop.adminserver.common.ZookeeperOperationException;
import com.gitee.sop.adminserver.common.ZookeeperPathExistException;
import com.gitee.sop.adminserver.common.ZookeeperPathNotExistException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
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.ChildData;
import org.apache.curator.framework.recipes.cache.NodeCache;
import org.apache.curator.framework.recipes.cache.NodeCacheListener;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;
import org.springframework.core.env.Environment;
import org.springframework.util.Assert;
import java.io.Closeable;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executors;
import static com.gitee.sop.adminserver.bean.SopAdminConstants.SOP_MSG_CHANNEL_PATH;
/**
* @author tanghc
*/
@Slf4j
public class ZookeeperContext {
private static CuratorFramework client;
private static Environment environment;
public static void setEnvironment(Environment environment) {
Assert.notNull(environment, "environment不能为null");
ZookeeperContext.environment = environment;
initZookeeperClient();
}
public synchronized static void initZookeeperClient() {
if (client != null) {
return;
}
setClient(createClient());
}
public static CuratorFramework createClient() {
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();
return client;
}
public static String getSopRouteRootPath() {
return SopAdminConstants.SOP_SERVICE_ROUTE_PATH;
}
public static String buildServiceIdPath(String serviceId) {
if (StringUtils.isBlank(serviceId)) {
throw new NullPointerException("serviceId不能为空");
}
return getSopRouteRootPath() + "/" + serviceId;
}
public static String buildRoutePath(String serviceId, String routeId) {
if (StringUtils.isBlank(serviceId)) {
throw new NullPointerException("serviceId不能为空");
}
if (StringUtils.isBlank(routeId)) {
throw new NullPointerException("routeId不能为空");
}
String serviceIdPath = getSopRouteRootPath() + "/" + serviceId;
return serviceIdPath + "/" + routeId;
}
public static String getServiceGrayChannelPath() {
return SOP_MSG_CHANNEL_PATH + "/gray";
}
public static String getIsvInfoChannelPath() {
return SOP_MSG_CHANNEL_PATH + "/isvinfo";
}
public static String getIsvRoutePermissionChannelPath() {
return SOP_MSG_CHANNEL_PATH + "/isv-route-permission";
}
public static String getRouteConfigChannelPath() {
return SOP_MSG_CHANNEL_PATH + "/route-conf";
}
public static String getLimitConfigChannelPath() {
return SOP_MSG_CHANNEL_PATH + "/limit-conf";
}
public static String getIpBlacklistChannelPath() {
return SOP_MSG_CHANNEL_PATH + "/ipblacklist-conf";
}
public static CuratorFramework getClient() {
return client;
}
public static void setClient(CuratorFramework 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 data 数据
* @return
* @throws ZookeeperPathNotExistException
*/
public static Stat updatePathData(String path, String data) throws ZookeeperPathNotExistException {
if (!isPathExist(path)) {
throw new ZookeeperPathNotExistException("path " + path + " 不存在");
}
try {
return getClient().setData().forPath(path, data.getBytes());
} catch (Exception e) {
throw new ZookeeperOperationException("updatePathData error, path=" + path, e);
}
}
/**
* 创建新的path并赋值。如果path已存在抛异常
*
* @param path 待创建的path
* @param data 值
* @throws ZookeeperPathExistException
*/
public static String addPath(String path, String data) throws ZookeeperPathExistException {
if (isPathExist(path)) {
throw new ZookeeperPathExistException("path " + path + " 已存在");
}
try {
return addPath(path, CreateMode.PERSISTENT, data);
} catch (Exception e) {
throw new ZookeeperOperationException("addPath error path=" + path, e);
}
}
/**
* 添加节点
*
* @param path 待创建的path
* @param createMode 节点类型
* @param data 节点数据
* @return
*/
public static String addPath(String path, CreateMode createMode, String data) {
try {
return getClient().create()
// 如果指定节点的父节点不存在则Curator将会自动级联创建父节点
.creatingParentContainersIfNeeded()
.withMode(createMode)
.forPath(path, data.getBytes());
} catch (Exception e) {
throw new ZookeeperOperationException("addPath error path=" + path, e);
}
}
/**
* 删除节点及子节点
*
* @param path
*/
public static void deletePathDeep(String path) {
try {
getClient().delete()
.deletingChildrenIfNeeded()
.forPath(path);
} catch (Exception e) {
}
}
/**
* 创建新的path并赋值。如果path已存在则不创建
*
* @param path 待创建的path
* @param data 值
*/
public static String addPathQuietly(String path, String data) {
if (isPathExist(path)) {
return path;
}
try {
return addPath(path, data);
} catch (Exception e) {
throw new ZookeeperOperationException("addPathQuietly error path=" + path, e);
}
}
/**
* 新建或保存节点
*
* @param path
* @param data
* @return
*/
public static String createOrUpdateData(String path, String data) {
try {
return getClient().create()
// 如果节点存在则Curator将会使用给出的数据设置这个节点的值
.orSetData()
// 如果指定节点的父节点不存在则Curator将会自动级联创建父节点
.creatingParentContainersIfNeeded()
.forPath(path, data.getBytes());
} catch (Exception e) {
throw new ZookeeperOperationException("createOrUpdateData error path=" + path, e);
}
}
/**
* 获取节点内容
*
* @param path
* @return
* @throws ZookeeperPathNotExistException
*/
public static String getData(String path) throws ZookeeperPathNotExistException {
if (!isPathExist(path)) {
throw new ZookeeperPathNotExistException("path 不存在, path=" + path);
}
try {
byte[] data = getClient().getData().forPath(path);
return new String(data);
} catch (Exception e) {
throw new ZookeeperOperationException("getData error path=" + path, e);
}
}
/**
* 获取子节点数据
*
* @param parentPath 父节点
* @return
*/
public static List<ChildData> getChildrenData(String parentPath) {
PathChildrenCache pathChildrenCache = buildPathChildrenCache(parentPath);
if (pathChildrenCache == null) {
return Collections.emptyList();
}
return pathChildrenCache.getCurrentData();
}
public static PathChildrenCache buildPathChildrenCache(String path) {
if (!isPathExist(path)) {
return null;
}
// PathChildrenCache: 监听数据节点的增删改,可以设置触发的事件
// 且第三个参数要设置为true不然ChildData对象中的getData返回null
PathChildrenCache childrenCache = new PathChildrenCache(client, path, true);
// 列出子节点数据列表需要使用BUILD_INITIAL_CACHE同步初始化模式才能获得异步是获取不到的
try {
childrenCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
} catch (Exception e) {
throw new ZookeeperOperationException("buildPathChildrenCache error path=" + path, e);
}
return childrenCache;
}
/**
* 监听一个临时节点
*
* @param path
* @param listenCallback 回调
* @return 返回path
* @throws Exception
*/
public static void listenTempPath(String path, ListenCallback listenCallback) throws Exception {
String initData = "{}";
CuratorFramework client = createClient();
client.create()
// 如果指定节点的父节点不存在则Curator将会自动级联创建父节点
.creatingParentContainersIfNeeded()
.withMode(CreateMode.EPHEMERAL)
.forPath(path, initData.getBytes());
final NodeCache cache = new NodeCache(client, path, false);
cache.getListenable().addListener(new NodeCacheListener() {
@Override
public void nodeChanged() throws Exception {
byte[] nodeData = cache.getCurrentData().getData();
String data = new String(nodeData);
if (StringUtils.isNotBlank(data) && !initData.equals(data)) {
listenCallback.onError(data);
Executors.newSingleThreadExecutor().execute(() -> new ZKClose(cache, client));
}
}
});
cache.start();
}
public interface ListenCallback {
void onError(String errorMsg);
}
static class ZKClose implements Runnable {
Closeable[] closes;
public ZKClose(Closeable ...closes) {
this.closes = closes;
}
@Override
public void run() {
try {
Thread.sleep(2000);
IOUtils.closeQuietly(closes);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

View File

@@ -1,26 +0,0 @@
package com.gitee.sop.adminserver.common;
/**
* @author tanghc
*/
public class ZookeeperOperationException extends RuntimeException {
public ZookeeperOperationException() {
}
public ZookeeperOperationException(String message) {
super(message);
}
public ZookeeperOperationException(String message, Throwable cause) {
super(message, cause);
}
public ZookeeperOperationException(Throwable cause) {
super(cause);
}
public ZookeeperOperationException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

View File

@@ -1,10 +0,0 @@
package com.gitee.sop.adminserver.common;
/**
* @author tanghc
*/
public class ZookeeperPathExistException extends Exception {
public ZookeeperPathExistException(String message) {
super(message);
}
}

View File

@@ -1,10 +0,0 @@
package com.gitee.sop.adminserver.common;
/**
* @author tanghc
*/
public class ZookeeperPathNotExistException extends Exception {
public ZookeeperPathNotExistException(String message) {
super(message);
}
}

View File

@@ -8,7 +8,6 @@ import com.gitee.easyopen.ApiParamParser;
import com.gitee.easyopen.ParamNames;
import com.gitee.easyopen.interceptor.ApiInterceptor;
import com.gitee.easyopen.session.ApiSessionManager;
import com.gitee.sop.adminserver.bean.ZookeeperContext;
import com.gitee.sop.adminserver.interceptor.LoginInterceptor;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
@@ -68,7 +67,6 @@ public class WebConfig {
@PostConstruct
public void after() {
ZookeeperContext.setEnvironment(environment);
}
}

View File

@@ -0,0 +1,32 @@
package com.gitee.sop.adminserver.service;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.annotation.NacosInjected;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException;
import com.gitee.sop.adminserver.bean.ChannelMsg;
import com.gitee.sop.adminserver.common.BizException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* @author tanghc
*/
@Slf4j
@Service
public class ConfigPushService {
@NacosInjected
private ConfigService configService;
public void publishConfig(String dataId, String groupId, ChannelMsg channelMsg) {
try {
log.info("nacos配置, dataId:{}, groupId:{}, operation:{}", dataId, groupId, channelMsg.getOperation());
configService.publishConfig(dataId, groupId, JSON.toJSONString(channelMsg));
} catch (NacosException e) {
log.error("nacos配置失败, dataId:{}, groupId:{}, operation:{}", dataId, groupId, channelMsg.getOperation(), e);
throw new BizException("nacos配置失败");
}
}
}

View File

@@ -1,12 +1,12 @@
package com.gitee.sop.adminserver.service;
import com.alibaba.fastjson.JSON;
import com.gitee.sop.adminserver.bean.ChannelMsg;
import com.gitee.sop.adminserver.bean.ConfigLimitDto;
import com.gitee.sop.adminserver.bean.NacosConfigs;
import com.gitee.sop.adminserver.bean.RouteConfigDto;
import com.gitee.sop.adminserver.bean.ZookeeperContext;
import com.gitee.sop.adminserver.common.ChannelOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
@@ -16,6 +16,9 @@ import org.springframework.stereotype.Service;
@Slf4j
public class RouteConfigService {
@Autowired
private ConfigPushService configPushService;
/**
* 发送路由配置消息
* @param routeConfigDto
@@ -23,10 +26,7 @@ public class RouteConfigService {
*/
public void sendRouteConfigMsg(RouteConfigDto routeConfigDto) {
ChannelMsg channelMsg = new ChannelMsg(ChannelOperation.ROUTE_CONFIG_UPDATE, routeConfigDto);
String jsonData = JSON.toJSONString(channelMsg);
String path = ZookeeperContext.getRouteConfigChannelPath();
log.info("消息推送--路由配置(update), path:{}, data:{}", path, jsonData);
ZookeeperContext.createOrUpdateData(path, jsonData);
configPushService.publishConfig(NacosConfigs.DATA_ID_ROUTE_CONFIG, NacosConfigs.GROUP_CHANNEL, channelMsg);
}
/**
@@ -36,9 +36,6 @@ public class RouteConfigService {
*/
public void sendLimitConfigMsg(ConfigLimitDto routeConfigDto) throws Exception {
ChannelMsg channelMsg = new ChannelMsg(ChannelOperation.LIMIT_CONFIG_UPDATE, routeConfigDto);
String jsonData = JSON.toJSONString(channelMsg);
String path = ZookeeperContext.getLimitConfigChannelPath();
log.info("消息推送--限流配置(update), path:{}, data:{}", path, jsonData);
ZookeeperContext.createOrUpdateData(path, jsonData);
configPushService.publishConfig(NacosConfigs.DATA_ID_LIMIT_CONFIG, NacosConfigs.GROUP_CHANNEL, channelMsg);
}
}

View File

@@ -5,8 +5,7 @@ import com.gitee.fastmybatis.core.query.Query;
import com.gitee.sop.adminserver.api.service.param.RoutePermissionParam;
import com.gitee.sop.adminserver.bean.ChannelMsg;
import com.gitee.sop.adminserver.bean.IsvRoutePermission;
import com.gitee.sop.adminserver.bean.SopAdminConstants;
import com.gitee.sop.adminserver.bean.ZookeeperContext;
import com.gitee.sop.adminserver.bean.NacosConfigs;
import com.gitee.sop.adminserver.common.ChannelOperation;
import com.gitee.sop.adminserver.entity.PermIsvRole;
import com.gitee.sop.adminserver.entity.PermRolePermission;
@@ -42,6 +41,9 @@ public class RoutePermissionService {
@Autowired
PermRolePermissionMapper permRolePermissionMapper;
@Autowired
private ConfigPushService configPushService;
/**
* 获取客户端角色码列表
*
@@ -70,10 +72,8 @@ public class RoutePermissionService {
isvRoutePermission.setRouteIdList(routeIdList);
isvRoutePermission.setRouteIdListMd5(roleCodeListMd5);
ChannelMsg channelMsg = new ChannelMsg(ChannelOperation.ROUTE_PERMISSION_UPDATE, isvRoutePermission);
String jsonData = JSON.toJSONString(channelMsg);
String path = ZookeeperContext.getIsvRoutePermissionChannelPath();
log.info("消息推送--路由权限(update), path:{}, data:{}", path, jsonData);
ZookeeperContext.createOrUpdateData(path, jsonData);
configPushService.publishConfig(NacosConfigs.DATA_ID_ROUTE_PERMISSION, NacosConfigs.GROUP_CHANNEL, channelMsg);
}
/**
@@ -98,19 +98,9 @@ public class RoutePermissionService {
* 推送所有路由权限到zookeeper
*/
public void sendRoutePermissionReloadMsg(RoutePermissionParam oldRoutePermission) throws Exception {
String listenPath = SopAdminConstants.RELOAD_ROUTE_PERMISSION_PATH + "/" + System.currentTimeMillis();
ZookeeperContext.listenTempPath(listenPath, errorMsg -> {
log.error("推送所有路由权限到zookeeper失败进行回滚errorMsg: {}oldRoutePermission{}", errorMsg, JSON.toJSONString(oldRoutePermission));
// 回滚
updateRoutePermission(oldRoutePermission);
});
IsvRoutePermission isvRoutePermission = new IsvRoutePermission();
isvRoutePermission.setListenPath(listenPath);
ChannelMsg channelMsg = new ChannelMsg(ChannelOperation.ROUTE_PERMISSION_RELOAD, isvRoutePermission);
String jsonData = JSON.toJSONString(channelMsg);
String path = ZookeeperContext.getIsvRoutePermissionChannelPath();
log.info("消息推送--路由权限(reload), path:{}, data:{}", path, jsonData);
ZookeeperContext.createOrUpdateData(path, jsonData);
configPushService.publishConfig(NacosConfigs.DATA_ID_ROUTE_PERMISSION, NacosConfigs.GROUP_CHANNEL, channelMsg);
}
/**

View File

@@ -1,11 +1,13 @@
package com.gitee.sop.adminserver.service;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.annotation.NacosInjected;
import com.alibaba.nacos.api.config.ConfigService;
import com.gitee.sop.adminserver.api.service.param.RouteSearchParam;
import com.gitee.sop.adminserver.bean.GatewayRouteDefinition;
import com.gitee.sop.adminserver.bean.ZookeeperContext;
import com.gitee.sop.adminserver.bean.NacosConfigs;
import com.gitee.sop.adminserver.bean.ServiceRouteInfo;
import org.apache.commons.lang.StringUtils;
import org.apache.curator.framework.recipes.cache.ChildData;
import org.springframework.stereotype.Service;
import java.util.Collections;
@@ -19,21 +21,22 @@ import static java.util.stream.Collectors.toList;
@Service
public class RouteService {
@NacosInjected
private ConfigService configService;
public List<GatewayRouteDefinition> getRouteDefinitionList(RouteSearchParam param) throws Exception {
if (StringUtils.isBlank(param.getServiceId())) {
String serviceId = param.getServiceId();
if (StringUtils.isBlank(serviceId)) {
return Collections.emptyList();
}
String configData = configService.getConfig(NacosConfigs.getRouteDataId(serviceId), NacosConfigs.GROUP_ROUTE, 3000);
if (StringUtils.isBlank(configData)) {
return Collections.emptyList();
}
ServiceRouteInfo serviceRouteInfo = JSON.parseObject(configData, ServiceRouteInfo.class);
String searchPath = ZookeeperContext.getSopRouteRootPath() + "/" + param.getServiceId();
List<ChildData> childDataList = ZookeeperContext.getChildrenData(searchPath);
List<GatewayRouteDefinition> routeDefinitionStream = childDataList.stream()
.map(childData -> {
String serviceNodeData = new String(childData.getData());
GatewayRouteDefinition routeDefinition = JSON.parseObject(serviceNodeData, GatewayRouteDefinition.class);
return routeDefinition;
})
return serviceRouteInfo.getRouteDefinitionList()
.stream()
.filter(gatewayRouteDefinition -> {
boolean isRoute = gatewayRouteDefinition.getOrder() != Integer.MIN_VALUE;
String id = param.getId();
@@ -44,8 +47,6 @@ public class RouteService {
}
})
.collect(toList());
return routeDefinitionStream;
}
}

View File

@@ -6,12 +6,9 @@ spring.application.name=sop-admin
mysql.username=root
mysql.password=root
# eureka注册中心地址
eureka.url=http://localhost:1111/eureka/
# nacos注册中心地址
nacos.url=127.0.0.1:8848
# zookeeper地址
zookeeper.url=localhost:2181
# zipkin服务监控地址没有开启不用改
zipkin.url=http://127.0.0.1:9411/
# ------- 需要改的配置end -------
@@ -21,10 +18,9 @@ admin.access-token.timeout-minutes=30
# 签名方式rsa支付宝开放平台签名方式md5淘宝开放平台签名方式
sop.sign-type=rsa
# zookeeper配置
spring.cloud.zookeeper.connect-string=${zookeeper.url}
spring.cloud.zookeeper.baseSleepTimeMs=3000
spring.cloud.zookeeper.maxRetries=3
# nacos配置
nacos.config.server-addr=${nacos.url}
nacos.discovery.server-addr=${nacos.url}
# 数据库配置
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
@@ -36,10 +32,7 @@ spring.datasource.password=${mysql.password}
easyopen.show-doc=false
easyopen.ignore-validate=true
# 注册中心地址,根据实际情况改,这里只是参数,并不会去注册
registry.eureka-server-addr=${eureka.url}
registry.nacos-server-addr=${nacos.url}
# 使用eurekaeureka使用nacos填nacos
# 固定不用改
registry.name=nacos
logging.level.com.gitee=debug

View File

@@ -1,45 +0,0 @@
package com.gitee.sop.adminserver;
import com.gitee.sop.adminserver.bean.SopAdminConstants;
import junit.framework.TestCase;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.zookeeper.data.Stat;
public class CuratorTest extends TestCase {
private static String zookeeperServerAddr = "localhost:2181";
static CuratorFramework client;
public CuratorTest() {
client = CuratorFrameworkFactory.builder()
.connectString(zookeeperServerAddr)
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.build();
client.start();
}
/**
* 递归删除节点,只能在测试环境用。
*
* @throws Exception
*/
public void testDel() {
try {
client.delete()
.deletingChildrenIfNeeded()
.forPath(SopAdminConstants.RELOAD_ROUTE_PERMISSION_PATH);
} catch (Exception e) {
}
}
public void testCheck() throws Exception {
String path = SopAdminConstants.RELOAD_ROUTE_PERMISSION_PATH + "/1562231019332";
Stat stat = client.checkExists().forPath(path);
System.out.println(path + (stat == null ? "不存在" : "存在"));
}
}