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