parent
51ca5ba319
commit
be0adc0a8e
@ -0,0 +1,22 @@ |
|||||||
|
package com.gitee.sop.adminserver.bean; |
||||||
|
|
||||||
|
import lombok.Data; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
@Data |
||||||
|
public class GatewayPushDTO { |
||||||
|
private String dataId; |
||||||
|
private String groupId; |
||||||
|
private ChannelMsg channelMsg; |
||||||
|
|
||||||
|
public GatewayPushDTO() { |
||||||
|
} |
||||||
|
|
||||||
|
public GatewayPushDTO(String dataId, String groupId, ChannelMsg channelMsg) { |
||||||
|
this.dataId = dataId; |
||||||
|
this.groupId = groupId; |
||||||
|
this.channelMsg = channelMsg; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,69 @@ |
|||||||
|
package com.gitee.sop.adminserver.entity; |
||||||
|
|
||||||
|
import lombok.Data; |
||||||
|
|
||||||
|
import javax.persistence.Column; |
||||||
|
import javax.persistence.GeneratedValue; |
||||||
|
import javax.persistence.GenerationType; |
||||||
|
import javax.persistence.Id; |
||||||
|
import javax.persistence.Table; |
||||||
|
import java.util.Date; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 表名:config_service_route |
||||||
|
* 备注:路由配置 |
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
@Table(name = "config_service_route") |
||||||
|
@Data |
||||||
|
public class ConfigServiceRoute { |
||||||
|
@Id |
||||||
|
@Column(name = "id") |
||||||
|
@GeneratedValue(strategy = GenerationType.AUTO) |
||||||
|
/** 数据库字段:id */ |
||||||
|
private String id; |
||||||
|
|
||||||
|
/** 数据库字段:service_id */ |
||||||
|
private String serviceId; |
||||||
|
|
||||||
|
/** 接口名, 数据库字段:name */ |
||||||
|
private String name; |
||||||
|
|
||||||
|
/** 版本号, 数据库字段:version */ |
||||||
|
private String version; |
||||||
|
|
||||||
|
/** 路由断言(SpringCloudGateway专用), 数据库字段:predicates */ |
||||||
|
private String predicates; |
||||||
|
|
||||||
|
/** 路由过滤器(SpringCloudGateway专用), 数据库字段:filters */ |
||||||
|
private String filters; |
||||||
|
|
||||||
|
/** 路由规则转发的目标uri, 数据库字段:uri */ |
||||||
|
private String uri; |
||||||
|
|
||||||
|
/** uri后面跟的path, 数据库字段:path */ |
||||||
|
private String path; |
||||||
|
|
||||||
|
/** 路由执行的顺序, 数据库字段:order */ |
||||||
|
private Integer order; |
||||||
|
|
||||||
|
/** 是否忽略验证,业务参数验证除外, 数据库字段:ignore_validate */ |
||||||
|
private Byte ignoreValidate; |
||||||
|
|
||||||
|
/** 状态,0:待审核,1:启用,2:禁用, 数据库字段:status */ |
||||||
|
private Byte status; |
||||||
|
|
||||||
|
/** 是否合并结果, 数据库字段:merge_result */ |
||||||
|
private Byte mergeResult; |
||||||
|
|
||||||
|
/** 是否需要授权才能访问, 数据库字段:permission */ |
||||||
|
private Byte permission; |
||||||
|
|
||||||
|
/** 数据库字段:gmt_create */ |
||||||
|
private Date gmtCreate; |
||||||
|
|
||||||
|
/** 数据库字段:gmt_modified */ |
||||||
|
private Date gmtModified; |
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
package com.gitee.sop.adminserver.entity; |
||||||
|
|
||||||
|
import lombok.Data; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
@Data |
||||||
|
public class RouteRoleDTO { |
||||||
|
/** routeId, 数据库字段:route_id */ |
||||||
|
private String routeId; |
||||||
|
|
||||||
|
/** 角色代码, 数据库字段:role_code */ |
||||||
|
private String roleCode; |
||||||
|
|
||||||
|
/** 角色描述, 数据库字段:description */ |
||||||
|
private String description; |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
package com.gitee.sop.adminserver.mapper; |
||||||
|
|
||||||
|
import com.gitee.fastmybatis.core.mapper.CrudMapper; |
||||||
|
import com.gitee.sop.adminserver.entity.ConfigServiceRoute; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public interface ConfigServiceRouteMapper extends CrudMapper<ConfigServiceRoute, Long> { |
||||||
|
} |
@ -1,52 +0,0 @@ |
|||||||
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.RouteDefinition; |
|
||||||
import com.gitee.sop.adminserver.bean.NacosConfigs; |
|
||||||
import com.gitee.sop.adminserver.bean.ServiceRouteInfo; |
|
||||||
import org.apache.commons.lang.StringUtils; |
|
||||||
import org.springframework.stereotype.Service; |
|
||||||
|
|
||||||
import java.util.Collections; |
|
||||||
import java.util.List; |
|
||||||
|
|
||||||
import static java.util.stream.Collectors.toList; |
|
||||||
|
|
||||||
/** |
|
||||||
* @author tanghc |
|
||||||
*/ |
|
||||||
@Service |
|
||||||
public class RouteService { |
|
||||||
|
|
||||||
@NacosInjected |
|
||||||
private ConfigService configService; |
|
||||||
|
|
||||||
public List<RouteDefinition> getRouteDefinitionList(RouteSearchParam param) throws Exception { |
|
||||||
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); |
|
||||||
|
|
||||||
return serviceRouteInfo.getRouteDefinitionList() |
|
||||||
.stream() |
|
||||||
.filter(gatewayRouteDefinition -> { |
|
||||||
boolean isRoute = gatewayRouteDefinition.getOrder() != Integer.MIN_VALUE; |
|
||||||
String id = param.getId(); |
|
||||||
if (StringUtils.isBlank(id)) { |
|
||||||
return isRoute; |
|
||||||
} else { |
|
||||||
return isRoute && gatewayRouteDefinition.getId().contains(id); |
|
||||||
} |
|
||||||
}) |
|
||||||
.collect(toList()); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -0,0 +1,19 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
||||||
|
<!-- 注意:文件名必须跟Dao类名字一致,因为是根据文件名做关联。 --> |
||||||
|
<mapper namespace="com.gitee.sop.adminserver.mapper.PermRolePermissionMapper"> |
||||||
|
|
||||||
|
<select id="listRouteRole" resultType="com.gitee.sop.adminserver.entity.RouteRoleDTO"> |
||||||
|
SELECT |
||||||
|
t.route_id as routeId, |
||||||
|
t.role_code as roleCode, |
||||||
|
t2.description |
||||||
|
FROM perm_role_permission t |
||||||
|
INNER JOIN perm_role t2 ON t.role_code=t2.role_code |
||||||
|
WHERE t.route_id in |
||||||
|
<foreach collection="routeIdList" item="routeId" open="(" separator="," close=")"> |
||||||
|
#{routeId} |
||||||
|
</foreach> |
||||||
|
</select> |
||||||
|
|
||||||
|
</mapper> |
@ -0,0 +1,22 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.bean; |
||||||
|
|
||||||
|
import lombok.Data; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
@Data |
||||||
|
public class GatewayPushDTO { |
||||||
|
private String dataId; |
||||||
|
private String groupId; |
||||||
|
private ChannelMsg channelMsg; |
||||||
|
|
||||||
|
public GatewayPushDTO() { |
||||||
|
} |
||||||
|
|
||||||
|
public GatewayPushDTO(String dataId, String groupId, ChannelMsg channelMsg) { |
||||||
|
this.dataId = dataId; |
||||||
|
this.groupId = groupId; |
||||||
|
this.channelMsg = channelMsg; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,154 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.route; |
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON; |
||||||
|
import com.gitee.sop.gatewaycommon.bean.InstanceDefinition; |
||||||
|
import com.gitee.sop.gatewaycommon.bean.ServiceRouteInfo; |
||||||
|
import com.gitee.sop.gatewaycommon.manager.BaseRouteCache; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.apache.commons.lang.StringUtils; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.http.HttpStatus; |
||||||
|
import org.springframework.http.ResponseEntity; |
||||||
|
import org.springframework.util.DigestUtils; |
||||||
|
import org.springframework.web.client.DefaultResponseErrorHandler; |
||||||
|
import org.springframework.web.client.RestTemplate; |
||||||
|
|
||||||
|
import java.util.Map; |
||||||
|
import java.util.concurrent.ConcurrentHashMap; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
public abstract class BaseRoutesListener implements RegistryListener { |
||||||
|
|
||||||
|
private static final String SOP_ROUTES_PATH = "/sop/routes"; |
||||||
|
|
||||||
|
private static final String SECRET = "a3d9sf!1@odl90zd>fkASwq"; |
||||||
|
|
||||||
|
private static final int FIVE_SECONDS = 1000 * 5; |
||||||
|
|
||||||
|
private static final String METADATA_SERVER_CONTEXT_PATH = "server.servlet.context-path"; |
||||||
|
|
||||||
|
private static final String METADATA_SOP_ROUTES_PATH = "sop.routes.path"; |
||||||
|
|
||||||
|
private static RestTemplate restTemplate = new RestTemplate(); |
||||||
|
|
||||||
|
static { |
||||||
|
// 解决statusCode不等于200,就抛异常问题
|
||||||
|
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() { |
||||||
|
@Override |
||||||
|
protected boolean hasError(HttpStatus statusCode) { |
||||||
|
return statusCode == null; |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
private Map<String, Long> updateTimeMap = new ConcurrentHashMap<>(16); |
||||||
|
|
||||||
|
@Autowired |
||||||
|
private BaseRouteCache<?> baseRouteCache; |
||||||
|
|
||||||
|
@Autowired |
||||||
|
private RoutesProcessor routesProcessor; |
||||||
|
|
||||||
|
/** |
||||||
|
* 移除路由信息 |
||||||
|
* |
||||||
|
* @param serviceId serviceId |
||||||
|
*/ |
||||||
|
public void removeRoutes(String serviceId) { |
||||||
|
doOperator(serviceId, () -> { |
||||||
|
log.info("服务下线,删除路由配置,serviceId: {}", serviceId); |
||||||
|
baseRouteCache.remove(serviceId); |
||||||
|
routesProcessor.removeAllRoutes(serviceId); |
||||||
|
}); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 拉取路由信息 |
||||||
|
* |
||||||
|
* @param instance 服务实例 |
||||||
|
*/ |
||||||
|
public void pullRoutes(InstanceDefinition instance) { |
||||||
|
String serviceName = instance.getServiceId(); |
||||||
|
doOperator(serviceName, () -> { |
||||||
|
String url = getRouteRequestUrl(instance); |
||||||
|
log.info("拉取路由配置,serviceId: {}, url: {}", serviceName, url); |
||||||
|
ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class); |
||||||
|
if (responseEntity.getStatusCode() == HttpStatus.OK) { |
||||||
|
String body = responseEntity.getBody(); |
||||||
|
ServiceRouteInfo serviceRouteInfo = JSON.parseObject(body, ServiceRouteInfo.class); |
||||||
|
baseRouteCache.load(serviceRouteInfo, callback -> routesProcessor.saveRoutes(serviceRouteInfo, instance)); |
||||||
|
} else { |
||||||
|
log.error("拉取路由配置异常,url: {}, status: {}, body: {}", url, responseEntity.getStatusCodeValue(), responseEntity.getBody()); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private void doOperator(String serviceId, Runnable runnable) { |
||||||
|
if (canOperator(serviceId)) { |
||||||
|
runnable.run(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private boolean canOperator(String serviceId) { |
||||||
|
// nacos会不停的触发事件,这里做了一层拦截
|
||||||
|
// 同一个serviceId5秒内允许访问一次
|
||||||
|
Long lastUpdateTime = updateTimeMap.getOrDefault(serviceId, 0L); |
||||||
|
long now = System.currentTimeMillis(); |
||||||
|
boolean can = now - lastUpdateTime > FIVE_SECONDS; |
||||||
|
if (can) { |
||||||
|
updateTimeMap.put(serviceId, now); |
||||||
|
} |
||||||
|
return can; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 拉取路由请求url |
||||||
|
* |
||||||
|
* @param instance 服务实例 |
||||||
|
* @return 返回最终url |
||||||
|
*/ |
||||||
|
private static String getRouteRequestUrl(InstanceDefinition instance) { |
||||||
|
Map<String, String> metadata = instance.getMetadata(); |
||||||
|
String customPath = metadata.get(METADATA_SOP_ROUTES_PATH); |
||||||
|
String homeUrl; |
||||||
|
String servletPath; |
||||||
|
// 如果metadata中指定了获取路由的url
|
||||||
|
if (StringUtils.isNotBlank(customPath)) { |
||||||
|
// 自定义完整的url
|
||||||
|
if (customPath.startsWith("http")) { |
||||||
|
homeUrl = customPath; |
||||||
|
servletPath = ""; |
||||||
|
} else { |
||||||
|
homeUrl = getHomeUrl(instance); |
||||||
|
servletPath = customPath; |
||||||
|
} |
||||||
|
} else { |
||||||
|
// 默认处理
|
||||||
|
homeUrl = getHomeUrl(instance); |
||||||
|
String contextPath = metadata.getOrDefault(METADATA_SERVER_CONTEXT_PATH, ""); |
||||||
|
servletPath = contextPath + SOP_ROUTES_PATH; |
||||||
|
} |
||||||
|
if (StringUtils.isNotBlank(servletPath) && !servletPath.startsWith("/")) { |
||||||
|
servletPath = '/' + servletPath; |
||||||
|
} |
||||||
|
String query = buildQuery(SECRET); |
||||||
|
return homeUrl + servletPath + query; |
||||||
|
} |
||||||
|
|
||||||
|
private static String getHomeUrl(InstanceDefinition instance) { |
||||||
|
return "http://" + instance.getIp() + ":" + instance.getPort(); |
||||||
|
} |
||||||
|
|
||||||
|
private static String buildQuery(String secret) { |
||||||
|
String time = String.valueOf(System.currentTimeMillis()); |
||||||
|
String source = secret + time + secret; |
||||||
|
String sign = DigestUtils.md5DigestAsHex(source.getBytes()); |
||||||
|
return "?time=" + time + "&sign=" + sign; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,34 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.route; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.bean.InstanceDefinition; |
||||||
|
import com.netflix.appinfo.InstanceInfo; |
||||||
|
import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceCanceledEvent; |
||||||
|
import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceRegisteredEvent; |
||||||
|
import org.springframework.context.ApplicationEvent; |
||||||
|
|
||||||
|
/** |
||||||
|
* 加载服务路由,eureka实现 |
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class EurekaRoutesListener extends BaseRoutesListener { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onRegister(ApplicationEvent applicationEvent) { |
||||||
|
EurekaInstanceRegisteredEvent event = (EurekaInstanceRegisteredEvent)applicationEvent; |
||||||
|
InstanceInfo instanceInfo = event.getInstanceInfo(); |
||||||
|
InstanceDefinition instanceDefinition = new InstanceDefinition(); |
||||||
|
instanceDefinition.setInstanceId(instanceInfo.getInstanceId()); |
||||||
|
instanceDefinition.setServiceId(instanceInfo.getAppName()); |
||||||
|
instanceDefinition.setIp(instanceInfo.getIPAddr()); |
||||||
|
instanceDefinition.setPort(instanceInfo.getPort()); |
||||||
|
instanceDefinition.setMetadata(instanceInfo.getMetadata()); |
||||||
|
pullRoutes(instanceDefinition); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onDeregister(ApplicationEvent applicationEvent) { |
||||||
|
EurekaInstanceCanceledEvent event = (EurekaInstanceCanceledEvent)applicationEvent; |
||||||
|
removeRoutes(event.getServerId()); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,80 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.route; |
||||||
|
|
||||||
|
import com.alibaba.nacos.api.annotation.NacosInjected; |
||||||
|
import com.alibaba.nacos.api.config.ConfigService; |
||||||
|
import com.alibaba.nacos.api.exception.NacosException; |
||||||
|
import com.alibaba.nacos.api.naming.NamingService; |
||||||
|
import com.alibaba.nacos.api.naming.pojo.Instance; |
||||||
|
import com.alibaba.nacos.api.naming.pojo.ServiceInfo; |
||||||
|
import com.gitee.sop.gatewaycommon.bean.InstanceDefinition; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.cloud.alibaba.nacos.NacosDiscoveryProperties; |
||||||
|
import org.springframework.context.ApplicationEvent; |
||||||
|
import org.springframework.util.CollectionUtils; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
import java.util.Objects; |
||||||
|
|
||||||
|
/** |
||||||
|
* 加载服务路由,nacos实现 |
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
public class NacosRoutesListener extends BaseRoutesListener { |
||||||
|
|
||||||
|
@Autowired |
||||||
|
private NacosDiscoveryProperties nacosDiscoveryProperties; |
||||||
|
|
||||||
|
@NacosInjected |
||||||
|
private ConfigService configService; |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onRegister(ApplicationEvent applicationEvent) { |
||||||
|
NamingService namingService = nacosDiscoveryProperties.namingServiceInstance(); |
||||||
|
List<ServiceInfo> subscribes = null; |
||||||
|
try { |
||||||
|
subscribes = namingService.getSubscribeServices(); |
||||||
|
} catch (NacosException e) { |
||||||
|
log.error("namingService.getSubscribeServices()错误", e); |
||||||
|
} |
||||||
|
if (CollectionUtils.isEmpty(subscribes)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
// subscribe
|
||||||
|
String thisServiceId = nacosDiscoveryProperties.getService(); |
||||||
|
for (ServiceInfo serviceInfo : subscribes) { |
||||||
|
String serviceName = serviceInfo.getName(); |
||||||
|
// 如果是本机服务,跳过
|
||||||
|
if (Objects.equals(thisServiceId, serviceName)) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
try { |
||||||
|
List<Instance> allInstances = namingService.getAllInstances(serviceName); |
||||||
|
if (CollectionUtils.isEmpty(allInstances)) { |
||||||
|
// 如果没有服务列表,则删除所有路由信息
|
||||||
|
removeRoutes(serviceName); |
||||||
|
} else { |
||||||
|
for (Instance instance : allInstances) { |
||||||
|
InstanceDefinition instanceDefinition = new InstanceDefinition(); |
||||||
|
instanceDefinition.setInstanceId(instance.getInstanceId()); |
||||||
|
instanceDefinition.setServiceId(serviceName); |
||||||
|
instanceDefinition.setIp(instance.getIp()); |
||||||
|
instanceDefinition.setPort(instance.getPort()); |
||||||
|
instanceDefinition.setMetadata(instance.getMetadata()); |
||||||
|
pullRoutes(instanceDefinition); |
||||||
|
} |
||||||
|
} |
||||||
|
} catch (Exception e) { |
||||||
|
log.error("选择服务实例失败,serviceName: {}", serviceName, e); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onDeregister(ApplicationEvent applicationEvent) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.route; |
||||||
|
|
||||||
|
import org.springframework.context.ApplicationEvent; |
||||||
|
|
||||||
|
/** |
||||||
|
* 发现新服务,更新路由信息 |
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public interface RegistryListener { |
||||||
|
|
||||||
|
/** |
||||||
|
* 触发新服务注册 |
||||||
|
* |
||||||
|
* @param applicationEvent 事件,可能是nacos事件,也可能是eureka事件 |
||||||
|
*/ |
||||||
|
void onRegister(ApplicationEvent applicationEvent); |
||||||
|
|
||||||
|
/** |
||||||
|
* 注销服务 |
||||||
|
* |
||||||
|
* @param applicationEvent |
||||||
|
*/ |
||||||
|
void onDeregister(ApplicationEvent applicationEvent); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.route; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.bean.InstanceDefinition; |
||||||
|
import com.gitee.sop.gatewaycommon.bean.ServiceRouteInfo; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public interface RoutesProcessor { |
||||||
|
/** |
||||||
|
* 删除serviceId下所有路由 |
||||||
|
* |
||||||
|
* @param serviceId serviceId |
||||||
|
*/ |
||||||
|
void removeAllRoutes(String serviceId); |
||||||
|
|
||||||
|
/** |
||||||
|
* 保存路由 |
||||||
|
* |
||||||
|
* @param serviceRouteInfo 路由信息 |
||||||
|
* @param instance 服务实例 |
||||||
|
*/ |
||||||
|
void saveRoutes(ServiceRouteInfo serviceRouteInfo, InstanceDefinition instance); |
||||||
|
} |
@ -0,0 +1,43 @@ |
|||||||
|
package com.gitee.sop.gatewaycommon.support; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.route.EurekaRoutesListener; |
||||||
|
import com.gitee.sop.gatewaycommon.route.RegistryListener; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceCanceledEvent; |
||||||
|
import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceRegisteredEvent; |
||||||
|
import org.springframework.context.annotation.Bean; |
||||||
|
import org.springframework.context.event.EventListener; |
||||||
|
|
||||||
|
/** |
||||||
|
* eureka注册中心事件监听 |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public class EurekaListenerConfiguration { |
||||||
|
|
||||||
|
@Autowired |
||||||
|
private RegistryListener registryListener; |
||||||
|
|
||||||
|
/** |
||||||
|
* 客户端注册触发 |
||||||
|
* @param event |
||||||
|
*/ |
||||||
|
@EventListener |
||||||
|
public void onEurekaInstanceRegisteredEvent(EurekaInstanceRegisteredEvent event) { |
||||||
|
registryListener.onRegister(event); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 客户端下线触发 |
||||||
|
* @param event |
||||||
|
*/ |
||||||
|
@EventListener |
||||||
|
public void onEurekaInstanceCanceledEvent(EurekaInstanceCanceledEvent event) { |
||||||
|
registryListener.onDeregister(event); |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
RegistryListener registryListener() { |
||||||
|
return new EurekaRoutesListener(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,68 @@ |
|||||||
|
package com.gitee.sop.gateway.controller; |
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON; |
||||||
|
import com.gitee.sop.gateway.manager.ChannelMsgProcessor; |
||||||
|
import com.gitee.sop.gateway.manager.DbEnvGrayManager; |
||||||
|
import com.gitee.sop.gateway.manager.DbIPBlacklistManager; |
||||||
|
import com.gitee.sop.gateway.manager.DbIsvManager; |
||||||
|
import com.gitee.sop.gateway.manager.DbIsvRoutePermissionManager; |
||||||
|
import com.gitee.sop.gateway.manager.DbLimitConfigManager; |
||||||
|
import com.gitee.sop.gateway.manager.DbRouteConfigManager; |
||||||
|
import com.gitee.sop.gatewaycommon.bean.GatewayPushDTO; |
||||||
|
import com.gitee.sop.gatewaycommon.bean.NacosConfigs; |
||||||
|
import com.gitee.sop.gatewaycommon.bean.SpringContext; |
||||||
|
import com.gitee.sop.gatewaycommon.util.RequestUtil; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.springframework.beans.factory.annotation.Value; |
||||||
|
import org.springframework.web.bind.annotation.PostMapping; |
||||||
|
import org.springframework.web.bind.annotation.RestController; |
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
import java.io.IOException; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
@RestController |
||||||
|
public class ConfigChannelController { |
||||||
|
|
||||||
|
private static Map<String, Class<? extends ChannelMsgProcessor>> processorMap = new HashMap<>(16); |
||||||
|
|
||||||
|
static { |
||||||
|
processorMap.put(NacosConfigs.GROUP_CHANNEL + NacosConfigs.DATA_ID_GRAY, DbEnvGrayManager.class); |
||||||
|
processorMap.put(NacosConfigs.GROUP_CHANNEL + NacosConfigs.DATA_ID_IP_BLACKLIST, DbIPBlacklistManager.class); |
||||||
|
processorMap.put(NacosConfigs.GROUP_CHANNEL + NacosConfigs.DATA_ID_ISV, DbIsvManager.class); |
||||||
|
processorMap.put(NacosConfigs.GROUP_CHANNEL + NacosConfigs.DATA_ID_ROUTE_PERMISSION, DbIsvRoutePermissionManager.class); |
||||||
|
processorMap.put(NacosConfigs.GROUP_CHANNEL + NacosConfigs.DATA_ID_LIMIT_CONFIG, DbLimitConfigManager.class); |
||||||
|
processorMap.put(NacosConfigs.GROUP_CHANNEL + NacosConfigs.DATA_ID_ROUTE_CONFIG, DbRouteConfigManager.class); |
||||||
|
} |
||||||
|
|
||||||
|
@Value("${zuul.secret}") |
||||||
|
private String secret; |
||||||
|
|
||||||
|
@PostMapping("/configChannelMsg") |
||||||
|
public String configChannel(HttpServletRequest request) throws IOException { |
||||||
|
String requestJson = RequestUtil.getText(request); |
||||||
|
String sign = request.getHeader("sign"); |
||||||
|
try { |
||||||
|
RequestUtil.checkResponseBody(requestJson, sign, secret); |
||||||
|
} catch (Exception e) { |
||||||
|
log.error("configChannelMsg错误", e); |
||||||
|
return e.getMessage(); |
||||||
|
} |
||||||
|
GatewayPushDTO gatewayPushDTO = JSON.parseObject(requestJson, GatewayPushDTO.class); |
||||||
|
ChannelMsgProcessor channelMsgProcessor = getChannelMsgProcessor(gatewayPushDTO); |
||||||
|
channelMsgProcessor.process(gatewayPushDTO.getChannelMsg()); |
||||||
|
return "ok"; |
||||||
|
} |
||||||
|
|
||||||
|
private ChannelMsgProcessor getChannelMsgProcessor(GatewayPushDTO gatewayPushDTO) { |
||||||
|
String key = gatewayPushDTO.getGroupId() + gatewayPushDTO.getDataId(); |
||||||
|
Class<? extends ChannelMsgProcessor> aClass = processorMap.get(key); |
||||||
|
return SpringContext.getBean(aClass); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,71 @@ |
|||||||
|
package com.gitee.sop.gateway.entity; |
||||||
|
|
||||||
|
import com.gitee.fastmybatis.core.annotation.LogicDelete; |
||||||
|
import lombok.Data; |
||||||
|
|
||||||
|
import java.util.Date; |
||||||
|
|
||||||
|
import javax.persistence.Column; |
||||||
|
import javax.persistence.GeneratedValue; |
||||||
|
import javax.persistence.GenerationType; |
||||||
|
import javax.persistence.Id; |
||||||
|
import javax.persistence.Table; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 表名:config_service_route |
||||||
|
* 备注:路由配置 |
||||||
|
* |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
@Table(name = "config_service_route") |
||||||
|
@Data |
||||||
|
public class ConfigServiceRoute { |
||||||
|
@Id |
||||||
|
@Column(name = "id") |
||||||
|
@GeneratedValue(strategy = GenerationType.AUTO) |
||||||
|
/** 数据库字段:id */ |
||||||
|
private String id; |
||||||
|
|
||||||
|
/** 数据库字段:service_id */ |
||||||
|
private String serviceId; |
||||||
|
|
||||||
|
/** 接口名, 数据库字段:name */ |
||||||
|
private String name; |
||||||
|
|
||||||
|
/** 版本号, 数据库字段:version */ |
||||||
|
private String version; |
||||||
|
|
||||||
|
/** 路由断言(SpringCloudGateway专用), 数据库字段:predicates */ |
||||||
|
private String predicates; |
||||||
|
|
||||||
|
/** 路由过滤器(SpringCloudGateway专用), 数据库字段:filters */ |
||||||
|
private String filters; |
||||||
|
|
||||||
|
/** 路由规则转发的目标uri, 数据库字段:uri */ |
||||||
|
private String uri; |
||||||
|
|
||||||
|
/** uri后面跟的path, 数据库字段:path */ |
||||||
|
private String path; |
||||||
|
|
||||||
|
/** 路由执行的顺序, 数据库字段:order */ |
||||||
|
private Integer order; |
||||||
|
|
||||||
|
/** 是否忽略验证,业务参数验证除外, 数据库字段:ignore_validate */ |
||||||
|
private Byte ignoreValidate; |
||||||
|
|
||||||
|
/** 状态,0:待审核,1:启用,2:禁用, 数据库字段:status */ |
||||||
|
private Byte status; |
||||||
|
|
||||||
|
/** 是否合并结果, 数据库字段:merge_result */ |
||||||
|
private Byte mergeResult; |
||||||
|
|
||||||
|
/** 是否需要授权才能访问, 数据库字段:permission */ |
||||||
|
private Byte permission; |
||||||
|
|
||||||
|
/** 数据库字段:gmt_create */ |
||||||
|
private Date gmtCreate; |
||||||
|
|
||||||
|
/** 数据库字段:gmt_modified */ |
||||||
|
private Date gmtModified; |
||||||
|
} |
@ -0,0 +1,10 @@ |
|||||||
|
package com.gitee.sop.gateway.manager; |
||||||
|
|
||||||
|
import com.gitee.sop.gatewaycommon.bean.ChannelMsg; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public interface ChannelMsgProcessor { |
||||||
|
void process(ChannelMsg channelMsg); |
||||||
|
} |
@ -0,0 +1,68 @@ |
|||||||
|
package com.gitee.sop.gateway.manager; |
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON; |
||||||
|
import com.gitee.fastmybatis.core.query.Query; |
||||||
|
import com.gitee.sop.gateway.entity.ConfigServiceRoute; |
||||||
|
import com.gitee.sop.gateway.mapper.ConfigServiceRouteMapper; |
||||||
|
import com.gitee.sop.gatewaycommon.bean.InstanceDefinition; |
||||||
|
import com.gitee.sop.gatewaycommon.bean.ServiceRouteInfo; |
||||||
|
import com.gitee.sop.gatewaycommon.route.RoutesProcessor; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.stereotype.Component; |
||||||
|
import org.springframework.transaction.annotation.Transactional; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
@Component |
||||||
|
public class DbRoutesProcessor implements RoutesProcessor { |
||||||
|
|
||||||
|
@Autowired |
||||||
|
private ConfigServiceRouteMapper configServiceRouteMapper; |
||||||
|
|
||||||
|
@Override |
||||||
|
public void removeAllRoutes(String serviceId) { |
||||||
|
// 删除serviceId下所有的路由
|
||||||
|
Query delServiceQuery = new Query().eq("service_id", serviceId); |
||||||
|
configServiceRouteMapper.deleteByQuery(delServiceQuery); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
@Transactional |
||||||
|
public void saveRoutes(ServiceRouteInfo serviceRouteInfo, InstanceDefinition instance) { |
||||||
|
log.info("保存路由信息到数据库,instance: {}", instance); |
||||||
|
String serviceId = serviceRouteInfo.getServiceId(); |
||||||
|
List<ConfigServiceRoute> configServiceRoutes = serviceRouteInfo |
||||||
|
.getRouteDefinitionList() |
||||||
|
.parallelStream() |
||||||
|
.map(routeDefinition -> { |
||||||
|
ConfigServiceRoute configServiceRoute = new ConfigServiceRoute(); |
||||||
|
configServiceRoute.setId(routeDefinition.getId()); |
||||||
|
configServiceRoute.setName(routeDefinition.getName()); |
||||||
|
configServiceRoute.setVersion(routeDefinition.getVersion()); |
||||||
|
configServiceRoute.setUri(routeDefinition.getUri()); |
||||||
|
configServiceRoute.setPath(routeDefinition.getPath()); |
||||||
|
configServiceRoute.setFilters(JSON.toJSONString(routeDefinition.getFilters())); |
||||||
|
configServiceRoute.setPredicates(JSON.toJSONString(routeDefinition.getPredicates())); |
||||||
|
configServiceRoute.setIgnoreValidate((byte) routeDefinition.getIgnoreValidate()); |
||||||
|
configServiceRoute.setMergeResult((byte) routeDefinition.getMergeResult()); |
||||||
|
configServiceRoute.setStatus((byte) routeDefinition.getStatus()); |
||||||
|
configServiceRoute.setPermission((byte) routeDefinition.getPermission()); |
||||||
|
configServiceRoute.setOrder(routeDefinition.getOrder()); |
||||||
|
configServiceRoute.setServiceId(serviceId); |
||||||
|
return configServiceRoute; |
||||||
|
}) |
||||||
|
.collect(Collectors.toList()); |
||||||
|
|
||||||
|
// 删除serviceId下所有的路由
|
||||||
|
this.removeAllRoutes(serviceId); |
||||||
|
|
||||||
|
// 批量保存
|
||||||
|
configServiceRouteMapper.saveBatch(configServiceRoutes); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
package com.gitee.sop.gateway.mapper; |
||||||
|
|
||||||
|
import com.gitee.fastmybatis.core.mapper.CrudMapper; |
||||||
|
import com.gitee.sop.gateway.entity.ConfigServiceRoute; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @author tanghc |
||||||
|
*/ |
||||||
|
public interface ConfigServiceRouteMapper extends CrudMapper<ConfigServiceRoute, Long> { |
||||||
|
} |
Loading…
Reference in new issue