parent
3e506b27ef
commit
c0fb37255a
@ -0,0 +1,68 @@ |
||||
package com.gitee.sop.gatewaycommon.route; |
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.InstanceDefinition; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.concurrent.ConcurrentHashMap; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
@Slf4j |
||||
public abstract class BaseRegistryListener implements RegistryListener { |
||||
|
||||
private static final int FIVE_SECONDS = 1000 * 5; |
||||
|
||||
private Map<String, Long> updateTimeMap = new ConcurrentHashMap<>(16); |
||||
|
||||
public static List<String> EXCLUDE_SERVICE_ID_LIST = new ArrayList<>(8); |
||||
|
||||
static { |
||||
EXCLUDE_SERVICE_ID_LIST.add("api-gateway"); |
||||
EXCLUDE_SERVICE_ID_LIST.add("website-server"); |
||||
} |
||||
|
||||
@Autowired |
||||
private ServiceListener serviceListener; |
||||
|
||||
/** |
||||
* 移除路由信息 |
||||
* |
||||
* @param serviceId serviceId |
||||
*/ |
||||
public void removeRoutes(String serviceId) { |
||||
serviceListener.onRemoveService(serviceId.toLowerCase()); |
||||
} |
||||
|
||||
/** |
||||
* 拉取路由信息 |
||||
* |
||||
* @param instance 服务实例 |
||||
*/ |
||||
public void pullRoutes(InstanceDefinition instance) { |
||||
// serviceId统一小写
|
||||
instance.setServiceId(instance.getServiceId().toLowerCase()); |
||||
serviceListener.onAddInstance(instance); |
||||
} |
||||
|
||||
protected boolean canOperator(String serviceId) { |
||||
for (String excludeServiceId : EXCLUDE_SERVICE_ID_LIST) { |
||||
if (excludeServiceId.equalsIgnoreCase(serviceId)) { |
||||
return false; |
||||
} |
||||
} |
||||
// 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; |
||||
} |
||||
} |
@ -1,154 +0,0 @@ |
||||
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,41 @@ |
||||
package com.gitee.sop.gatewaycommon.route; |
||||
|
||||
import org.springframework.http.HttpStatus; |
||||
import org.springframework.util.DigestUtils; |
||||
import org.springframework.web.client.DefaultResponseErrorHandler; |
||||
import org.springframework.web.client.RestTemplate; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
public abstract class BaseServiceListener implements ServiceListener { |
||||
|
||||
private static final String SECRET = "a3d9sf!1@odl90zd>fkASwq"; |
||||
|
||||
private static RestTemplate restTemplate = new RestTemplate(); |
||||
|
||||
static { |
||||
// 解决statusCode不等于200,就抛异常问题
|
||||
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() { |
||||
@Override |
||||
protected boolean hasError(HttpStatus statusCode) { |
||||
return statusCode == null; |
||||
} |
||||
}); |
||||
} |
||||
|
||||
protected 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; |
||||
} |
||||
|
||||
protected static String buildQuery() { |
||||
return buildQuery(SECRET); |
||||
} |
||||
|
||||
public static RestTemplate getRestTemplate() { |
||||
return restTemplate; |
||||
} |
||||
} |
@ -0,0 +1,71 @@ |
||||
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 NacosRegistryListener extends BaseRegistryListener { |
||||
|
||||
@Autowired |
||||
private NacosDiscoveryProperties nacosDiscoveryProperties; |
||||
|
||||
@NacosInjected |
||||
private ConfigService configService; |
||||
|
||||
@Override |
||||
public void onEvent(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; |
||||
} |
||||
subscribes.stream() |
||||
.filter(serviceInfo -> this.canOperator(serviceInfo.getName())) |
||||
.forEach(serviceInfo -> { |
||||
String serviceName = serviceInfo.getName(); |
||||
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); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
} |
@ -1,75 +0,0 @@ |
||||
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 onEvent(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); |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,12 @@ |
||||
package com.gitee.sop.gatewaycommon.route; |
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.InstanceDefinition; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
public interface ServiceListener { |
||||
void onRemoveService(String serviceId); |
||||
|
||||
void onAddInstance(InstanceDefinition instance); |
||||
} |
@ -0,0 +1,92 @@ |
||||
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 java.util.Map; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
@Slf4j |
||||
public class ServiceRouteListener extends BaseServiceListener { |
||||
|
||||
private static final String SOP_ROUTES_PATH = "/sop/routes"; |
||||
|
||||
private static final String METADATA_SERVER_CONTEXT_PATH = "server.servlet.context-path"; |
||||
|
||||
private static final String METADATA_SOP_ROUTES_PATH = "sop.routes.path"; |
||||
|
||||
@Autowired |
||||
private BaseRouteCache<?> baseRouteCache; |
||||
|
||||
@Autowired |
||||
private RoutesProcessor routesProcessor; |
||||
|
||||
@Override |
||||
public void onRemoveService(String serviceId) { |
||||
log.info("服务下线,删除路由配置,serviceId: {}", serviceId); |
||||
baseRouteCache.remove(serviceId); |
||||
routesProcessor.removeAllRoutes(serviceId); |
||||
} |
||||
|
||||
@Override |
||||
public void onAddInstance(InstanceDefinition instance) { |
||||
String serviceName = instance.getServiceId(); |
||||
String url = getRouteRequestUrl(instance); |
||||
log.info("拉取路由配置,serviceId: {}, url: {}", serviceName, url); |
||||
ResponseEntity<String> responseEntity = getRestTemplate().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()); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 拉取路由请求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(); |
||||
return homeUrl + servletPath + query; |
||||
} |
||||
|
||||
private static String getHomeUrl(InstanceDefinition instance) { |
||||
return "http://" + instance.getIp() + ":" + instance.getPort(); |
||||
} |
||||
} |
@ -0,0 +1,38 @@ |
||||
package com.gitee.sop.gatewaycommon.validate; |
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.SopConstants; |
||||
|
||||
import java.io.UnsupportedEncodingException; |
||||
import java.net.URLEncoder; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
public class SignConfig { |
||||
private static volatile Wrapper wrapper = new Wrapper() {}; |
||||
|
||||
public static void enableUrlencodeMode() { |
||||
wrapper = new Wrapper() { |
||||
@Override |
||||
public String wrapVal(Object val) { |
||||
String valStr = String.valueOf(val); |
||||
try { |
||||
return URLEncoder.encode(valStr, SopConstants.UTF8); |
||||
} catch (UnsupportedEncodingException e) { |
||||
return valStr; |
||||
} |
||||
} |
||||
}; |
||||
} |
||||
|
||||
public static String wrapVal(Object val) { |
||||
return wrapper.wrapVal(val); |
||||
} |
||||
|
||||
interface Wrapper { |
||||
default String wrapVal(Object val) { |
||||
return String.valueOf(val); |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,50 @@ |
||||
package com.gitee.sop.websiteserver.listener; |
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.InstanceDefinition; |
||||
import com.gitee.sop.gatewaycommon.route.BaseServiceListener; |
||||
import com.gitee.sop.websiteserver.manager.DocManager; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.http.HttpStatus; |
||||
import org.springframework.http.ResponseEntity; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
@Slf4j |
||||
public class ServiceDocListener extends BaseServiceListener { |
||||
|
||||
private static final String SECRET = "b749a2ec000f4f29"; |
||||
|
||||
@Autowired |
||||
private DocManager docManager; |
||||
|
||||
@Override |
||||
public void onRemoveService(String serviceId) { |
||||
docManager.remove(serviceId); |
||||
} |
||||
|
||||
@Override |
||||
public void onAddInstance(InstanceDefinition instance) { |
||||
String serviceId = instance.getServiceId(); |
||||
String url = getRouteRequestUrl(instance); |
||||
ResponseEntity<String> responseEntity = getRestTemplate().getForEntity(url, String.class); |
||||
if (responseEntity.getStatusCode() == HttpStatus.OK) { |
||||
String body = responseEntity.getBody(); |
||||
docManager.addDocInfo( |
||||
serviceId |
||||
, body |
||||
, callback -> log.info("加载服务文档,serviceId={}, 机器={}" |
||||
, serviceId |
||||
, instance.getIp() + ":" + instance.getPort()) |
||||
); |
||||
} else { |
||||
log.error("加载文档失败, status:{}, body:{}", responseEntity.getStatusCodeValue(), responseEntity.getBody()); |
||||
} |
||||
} |
||||
|
||||
private static String getRouteRequestUrl(InstanceDefinition instance) { |
||||
String query = buildQuery(SECRET); |
||||
return "http://" + instance.getIp() + ":" + instance.getPort() + "/v2/api-docs" + query; |
||||
} |
||||
} |
@ -1,116 +0,0 @@ |
||||
package com.gitee.sop.websiteserver.manager; |
||||
|
||||
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 lombok.extern.slf4j.Slf4j; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.cloud.alibaba.nacos.NacosDiscoveryProperties; |
||||
import org.springframework.http.HttpStatus; |
||||
import org.springframework.http.ResponseEntity; |
||||
import org.springframework.stereotype.Service; |
||||
import org.springframework.util.CollectionUtils; |
||||
import org.springframework.util.DigestUtils; |
||||
import org.springframework.web.client.DefaultResponseErrorHandler; |
||||
import org.springframework.web.client.RestTemplate; |
||||
|
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.Objects; |
||||
|
||||
/** |
||||
* @author tanghc |
||||
*/ |
||||
@Slf4j |
||||
@Service |
||||
public class DocDiscovery { |
||||
|
||||
private static final String SECRET = "b749a2ec000f4f29"; |
||||
|
||||
private static final int FIVE_SECONDS = 1000 * 5; |
||||
|
||||
@Autowired |
||||
private NacosDiscoveryProperties nacosDiscoveryProperties; |
||||
|
||||
private RestTemplate restTemplate = new RestTemplate(); |
||||
|
||||
private Map<String, Long> updateTimeMap = new HashMap<>(16); |
||||
|
||||
public DocDiscovery() { |
||||
// 解决statusCode不等于200,就抛异常问题
|
||||
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() { |
||||
@Override |
||||
protected boolean hasError(HttpStatus statusCode) { |
||||
return statusCode == null; |
||||
} |
||||
}); |
||||
} |
||||
|
||||
public synchronized void refresh(DocManager docManager) { |
||||
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 serviceId = serviceInfo.getName(); |
||||
// 如果是本机服务,跳过
|
||||
if (Objects.equals(thisServiceId, serviceId) || "api-gateway".equalsIgnoreCase(serviceId)) { |
||||
continue; |
||||
} |
||||
// nacos会不停的触发事件,这里做了一层拦截
|
||||
// 同一个serviceId5秒内允许访问一次
|
||||
Long lastUpdateTime = updateTimeMap.getOrDefault(serviceId, 0L); |
||||
long now = System.currentTimeMillis(); |
||||
if (now - lastUpdateTime < FIVE_SECONDS) { |
||||
continue; |
||||
} |
||||
updateTimeMap.put(serviceId, now); |
||||
try { |
||||
List<Instance> allInstances = namingService.getAllInstances(serviceId); |
||||
if (CollectionUtils.isEmpty(allInstances)) { |
||||
// 如果没有服务列表,则删除所有路由信息
|
||||
docManager.remove(serviceId); |
||||
} else { |
||||
for (Instance instance : allInstances) { |
||||
String url = getRouteRequestUrl(instance); |
||||
ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class); |
||||
if (responseEntity.getStatusCode() == HttpStatus.OK) { |
||||
String body = responseEntity.getBody(); |
||||
docManager.addDocInfo( |
||||
serviceId |
||||
, body |
||||
, callback -> log.info("加载服务文档,serviceId={}, 机器={}" |
||||
, serviceId, instance.getIp() + ":" + instance.getPort()) |
||||
); |
||||
} |
||||
} |
||||
} |
||||
} catch (NacosException e) { |
||||
log.error("选择服务实例失败,serviceId:{}", serviceId, e); |
||||
} |
||||
} |
||||
} |
||||
|
||||
private static String getRouteRequestUrl(Instance instance) { |
||||
String query = buildQuery(SECRET); |
||||
return "http://" + instance.getIp() + ":" + instance.getPort() + "/v2/api-docs" + query; |
||||
} |
||||
|
||||
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; |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue