mirror of
https://gitee.com/durcframework/SOP.git
synced 2025-08-11 21:57:56 +08:00
新增路由监控功能、优化负载均衡策略、新增拦截器接口
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package com.gitee.sop.gatewaycommon.bean;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.gateway.result.GatewayResultExecutor;
|
||||
import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptor;
|
||||
import com.gitee.sop.gatewaycommon.limit.DefaultLimitManager;
|
||||
import com.gitee.sop.gatewaycommon.limit.LimitManager;
|
||||
import com.gitee.sop.gatewaycommon.loadbalancer.builder.AppIdGrayUserBuilder;
|
||||
@@ -18,6 +19,7 @@ import com.gitee.sop.gatewaycommon.manager.IsvRoutePermissionManager;
|
||||
import com.gitee.sop.gatewaycommon.manager.LimitConfigManager;
|
||||
import com.gitee.sop.gatewaycommon.manager.RouteConfigManager;
|
||||
import com.gitee.sop.gatewaycommon.manager.ServiceErrorManager;
|
||||
import com.gitee.sop.gatewaycommon.monitor.MonitorManager;
|
||||
import com.gitee.sop.gatewaycommon.param.ParameterFormatter;
|
||||
import com.gitee.sop.gatewaycommon.result.DataNameBuilder;
|
||||
import com.gitee.sop.gatewaycommon.result.DefaultDataNameBuilder;
|
||||
@@ -157,6 +159,16 @@ public class ApiConfig {
|
||||
*/
|
||||
private TokenValidator tokenValidator = apiParam -> apiParam != null && StringUtils.isNotBlank(apiParam.fetchAccessToken());
|
||||
|
||||
/**
|
||||
* 路由拦截器
|
||||
*/
|
||||
private List<RouteInterceptor> routeInterceptors = new ArrayList<>(4);
|
||||
|
||||
/**
|
||||
* 监控管理
|
||||
*/
|
||||
private MonitorManager monitorManager = new MonitorManager();
|
||||
|
||||
// -------- fields ---------
|
||||
|
||||
/**
|
||||
|
@@ -0,0 +1,114 @@
|
||||
package com.gitee.sop.gatewaycommon.bean;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptorContext;
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public class DefaultRouteInterceptorContext implements RouteInterceptorContext {
|
||||
|
||||
/** 请求参数 */
|
||||
private ApiParam apiParam;
|
||||
/** 错误信息 */
|
||||
private String serviceErrorMsg;
|
||||
/** 微服务返回状态 */
|
||||
private int responseStatus;
|
||||
/** 开始时间 */
|
||||
private long beginTimeMillis;
|
||||
/** 结束时间 */
|
||||
private long finishTimeMillis;
|
||||
/** 请求上下文 */
|
||||
private Object requestContext;
|
||||
/** 微服务返回结果 */
|
||||
private String serviceResult;
|
||||
/** 请求包大小 */
|
||||
private long requestDataSize;
|
||||
/** 返回内容大小 */
|
||||
private long responseDataSize;
|
||||
|
||||
@Override
|
||||
public ApiParam getApiParam() {
|
||||
return apiParam;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServiceResult() {
|
||||
return serviceResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getResponseStatus() {
|
||||
return responseStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getBeginTimeMillis() {
|
||||
return beginTimeMillis;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getFinishTimeMillis() {
|
||||
return finishTimeMillis;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getRequestContext() {
|
||||
return requestContext;
|
||||
}
|
||||
|
||||
public void setApiParam(ApiParam apiParam) {
|
||||
this.apiParam = apiParam;
|
||||
}
|
||||
|
||||
public void setServiceResult(String serviceResult) {
|
||||
this.serviceResult = serviceResult;
|
||||
}
|
||||
|
||||
public void setResponseStatus(int responseStatus) {
|
||||
this.responseStatus = responseStatus;
|
||||
}
|
||||
|
||||
public void setBeginTimeMillis(long beginTimeMillis) {
|
||||
this.beginTimeMillis = beginTimeMillis;
|
||||
}
|
||||
|
||||
public void setFinishTimeMillis(long finishTimeMillis) {
|
||||
this.finishTimeMillis = finishTimeMillis;
|
||||
}
|
||||
|
||||
public void setRequestContext(Object requestContext) {
|
||||
this.requestContext = requestContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServiceErrorMsg() {
|
||||
return serviceErrorMsg;
|
||||
}
|
||||
|
||||
public void setServiceErrorMsg(String serviceErrorMsg) {
|
||||
this.serviceErrorMsg = serviceErrorMsg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getRequestDataSize() {
|
||||
return requestDataSize;
|
||||
}
|
||||
|
||||
public void setRequestDataSize(long requestDataSize) {
|
||||
// spring cloud gateway get请求contentLength返回-1
|
||||
if (requestDataSize < 0) {
|
||||
requestDataSize = 0;
|
||||
}
|
||||
this.requestDataSize = requestDataSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getResponseDataSize() {
|
||||
return responseDataSize;
|
||||
}
|
||||
|
||||
public void setResponseDataSize(long responseDataSize) {
|
||||
this.responseDataSize = responseDataSize;
|
||||
}
|
||||
}
|
@@ -16,14 +16,6 @@ public class SopConstants {
|
||||
public static final String DEFAULT_SIGN_METHOD = "md5";
|
||||
public static final String EMPTY_JSON = "{}";
|
||||
|
||||
public static final String REDIRECT_METHOD_KEY = "r-method";
|
||||
|
||||
public static final String REDIRECT_VERSION_KEY = "r-version";
|
||||
|
||||
public static final String REDIRECT_PATH_KEY = "r-path";
|
||||
|
||||
public static final String SOP_NOT_MERGE = "sop.not-merge";
|
||||
|
||||
public static final String METADATA_SERVER_CONTEXT_PATH = "server.servlet.context-path";
|
||||
|
||||
public static final String METADATA_SERVER_CONTEXT_PATH_COMPATIBILITY = "context-path";
|
||||
@@ -58,4 +50,6 @@ public class SopConstants {
|
||||
public static final String METADATA_ENV_PRE_VALUE = "pre";
|
||||
public static final String METADATA_ENV_GRAY_VALUE = "gray";
|
||||
|
||||
public static final String CACHE_ROUTE_INTERCEPTOR_CONTEXT = "cacheRouteInterceptorContext";
|
||||
|
||||
}
|
||||
|
@@ -8,8 +8,8 @@ import com.gitee.sop.gatewaycommon.gateway.common.RequestContentDataExtractor;
|
||||
import com.gitee.sop.gatewaycommon.gateway.common.SopServerHttpRequestDecorator;
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||
import com.gitee.sop.gatewaycommon.param.FormHttpOutputMessage;
|
||||
import com.gitee.sop.gatewaycommon.route.ForwardInfo;
|
||||
import com.gitee.sop.gatewaycommon.param.ParamNames;
|
||||
import com.gitee.sop.gatewaycommon.route.ForwardInfo;
|
||||
import com.gitee.sop.gatewaycommon.util.RequestUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@@ -82,15 +82,13 @@ public class ServerWebExchangeUtil {
|
||||
return ServerRequest.create(exchange, messageReaders);
|
||||
}
|
||||
|
||||
public static ServerWebExchange getRestfulExchange(ServerWebExchange exchange, String path) {
|
||||
public static ApiParam getApiParamForRestful(ServerWebExchange exchange, String path) {
|
||||
int index = path.indexOf(REST_PATH);
|
||||
// 取"/rest"的后面部分
|
||||
String newPath = path.substring(index + REST_PATH.length());
|
||||
ApiParam apiParam = new ApiParam();
|
||||
apiParam.setName(newPath);
|
||||
apiParam.setVersion("");
|
||||
ApiParam apiParam = ApiParam.createRestfulApiParam(newPath);
|
||||
setApiParam(exchange, apiParam);
|
||||
return getForwardExchange(exchange, newPath);
|
||||
return apiParam;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,7 +98,7 @@ public class ServerWebExchangeUtil {
|
||||
* @param forwardPath 重定向path
|
||||
* @return 返回新的ServerWebExchange,配合chain.filter(newExchange);使用
|
||||
*/
|
||||
public static ServerWebExchange getForwardExchange(ServerWebExchange exchange, String forwardPath) {
|
||||
private static ServerWebExchange getForwardExchange(ServerWebExchange exchange, String forwardPath) {
|
||||
ServerHttpRequest newRequest = exchange.getRequest()
|
||||
.mutate()
|
||||
.path(forwardPath).build();
|
||||
|
@@ -4,6 +4,7 @@ import com.gitee.sop.gatewaycommon.bean.ApiConfig;
|
||||
import com.gitee.sop.gatewaycommon.gateway.controller.ConfigChannelController;
|
||||
import com.gitee.sop.gatewaycommon.gateway.controller.ErrorLogController;
|
||||
import com.gitee.sop.gatewaycommon.gateway.controller.GatewayController;
|
||||
import com.gitee.sop.gatewaycommon.gateway.controller.GatewayMonitorController;
|
||||
import com.gitee.sop.gatewaycommon.gateway.filter.GatewayModifyResponseGatewayFilter;
|
||||
import com.gitee.sop.gatewaycommon.gateway.filter.IndexFilter;
|
||||
import com.gitee.sop.gatewaycommon.gateway.filter.LimitFilter;
|
||||
@@ -64,6 +65,11 @@ public class BaseGatewayConfiguration extends AbstractConfiguration {
|
||||
return new ErrorLogController();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GatewayMonitorController gatewayMonitorController() {
|
||||
return new GatewayMonitorController();
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义异常处理[@@]注册Bean时依赖的Bean,会从容器中直接获取,所以直接注入即可
|
||||
*
|
||||
|
@@ -0,0 +1,21 @@
|
||||
package com.gitee.sop.gatewaycommon.gateway.controller;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.support.BaseMonitorController;
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@RestController
|
||||
public class GatewayMonitorController extends BaseMonitorController<ServerWebExchange> {
|
||||
|
||||
@Override
|
||||
protected ApiParam getApiParam(ServerWebExchange request) {
|
||||
Map<String, String> params = request.getRequest().getQueryParams().toSingleValueMap();
|
||||
return ApiParam.build(params);
|
||||
}
|
||||
}
|
@@ -1,11 +1,14 @@
|
||||
package com.gitee.sop.gatewaycommon.gateway.filter;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.DefaultRouteInterceptorContext;
|
||||
import com.gitee.sop.gatewaycommon.bean.SopConstants;
|
||||
import com.gitee.sop.gatewaycommon.exception.ApiException;
|
||||
import com.gitee.sop.gatewaycommon.gateway.ServerWebExchangeUtil;
|
||||
import com.gitee.sop.gatewaycommon.gateway.route.GatewayForwardChooser;
|
||||
import com.gitee.sop.gatewaycommon.manager.EnvironmentKeys;
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||
import com.gitee.sop.gatewaycommon.route.ForwardInfo;
|
||||
import com.gitee.sop.gatewaycommon.util.RouteInterceptorUtil;
|
||||
import com.gitee.sop.gatewaycommon.validate.Validator;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -67,7 +70,10 @@ public class IndexFilter implements WebFilter {
|
||||
log.error("尝试调用restful请求,但sop.restful.enable未开启");
|
||||
return ServerWebExchangeUtil.forwardUnknown(exchange, chain);
|
||||
}
|
||||
ServerWebExchange newExchange = ServerWebExchangeUtil.getRestfulExchange(exchange, path);
|
||||
ApiParam apiParam = ServerWebExchangeUtil.getApiParamForRestful(exchange, path);
|
||||
this.doValidate(exchange, apiParam);
|
||||
ForwardInfo forwardInfo = gatewayForwardChooser.getForwardInfo(exchange);
|
||||
ServerWebExchange newExchange = ServerWebExchangeUtil.getForwardExchange(exchange, forwardInfo);
|
||||
return chain.filter(newExchange);
|
||||
}
|
||||
if (Objects.equals(path, indexPath)) {
|
||||
@@ -122,12 +128,21 @@ public class IndexFilter implements WebFilter {
|
||||
private void doValidate(ServerWebExchange exchange, ApiParam apiParam) {
|
||||
try {
|
||||
validator.validate(apiParam);
|
||||
this.afterValidate(exchange, apiParam);
|
||||
} catch (ApiException e) {
|
||||
log.error("验证失败,ip:{}, params:{}, errorMsg:{}", apiParam.fetchIp(), apiParam.toJSONString(), e.getMessage());
|
||||
ServerWebExchangeUtil.setThrowable(exchange, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void afterValidate(ServerWebExchange exchange, ApiParam param) {
|
||||
RouteInterceptorUtil.runPreRoute(exchange, param, context -> {
|
||||
DefaultRouteInterceptorContext defaultRouteInterceptorContext = (DefaultRouteInterceptorContext) context;
|
||||
defaultRouteInterceptorContext.setRequestDataSize(exchange.getRequest().getHeaders().getContentLength());
|
||||
exchange.getAttributes().put(SopConstants.CACHE_ROUTE_INTERCEPTOR_CONTEXT, context);
|
||||
});
|
||||
}
|
||||
|
||||
private ServerHttpRequestDecorator decorate(
|
||||
ServerWebExchange exchange
|
||||
, HttpHeaders headers
|
||||
|
@@ -3,6 +3,7 @@ package com.gitee.sop.gatewaycommon.gateway.loadbalancer;
|
||||
import com.gitee.sop.gatewaycommon.gateway.ServerWebExchangeUtil;
|
||||
import com.gitee.sop.gatewaycommon.loadbalancer.ServerChooserContext;
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||
import com.gitee.sop.gatewaycommon.util.LoadBalanceUtil;
|
||||
import com.netflix.client.config.IClientConfig;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
@@ -14,7 +15,6 @@ import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
/**
|
||||
* 重写负载均衡处理。
|
||||
@@ -58,7 +58,7 @@ public class SopLoadBalancerClient extends RibbonLoadBalancerClient implements S
|
||||
}
|
||||
|
||||
private RibbonServer getRibbonServer(String serviceId, List<Server> servers) {
|
||||
Server server = this.chooseRandomServer(servers);
|
||||
Server server = LoadBalanceUtil.chooseByRoundRobin(serviceId, servers);
|
||||
if (server == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -70,26 +70,6 @@ public class SopLoadBalancerClient extends RibbonLoadBalancerClient implements S
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机选取一台实例
|
||||
*
|
||||
* @param servers 服务列表
|
||||
* @return 返回实例,没有返回null
|
||||
*/
|
||||
private Server chooseRandomServer(List<Server> servers) {
|
||||
if (servers.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
int serverCount = servers.size();
|
||||
// 随机选取一台实例
|
||||
int index = chooseRandomInt(serverCount);
|
||||
return servers.get(index);
|
||||
}
|
||||
|
||||
private int chooseRandomInt(int serverCount) {
|
||||
return ThreadLocalRandom.current().nextInt(serverCount);
|
||||
}
|
||||
|
||||
private ServerIntrospector serverIntrospector(String serviceId) {
|
||||
ServerIntrospector serverIntrospector = this.clientFactory.getInstance(serviceId,
|
||||
ServerIntrospector.class);
|
||||
|
@@ -2,6 +2,7 @@ package com.gitee.sop.gatewaycommon.gateway.result;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptorContext;
|
||||
import com.gitee.sop.gatewaycommon.bean.SopConstants;
|
||||
import com.gitee.sop.gatewaycommon.exception.ApiException;
|
||||
import com.gitee.sop.gatewaycommon.gateway.ServerWebExchangeUtil;
|
||||
@@ -17,7 +18,6 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
@@ -29,7 +29,8 @@ public class GatewayResultExecutor extends BaseExecutorAdapter<ServerWebExchange
|
||||
|
||||
@Override
|
||||
public int getResponseStatus(ServerWebExchange exchange) {
|
||||
int responseStatus = HttpStatus.OK.value();
|
||||
HttpStatus statusCode = exchange.getResponse().getStatusCode();
|
||||
int responseStatus = statusCode.value();
|
||||
List<String> errorCodeList = exchange.getResponse().getHeaders().get(SopConstants.X_SERVICE_ERROR_CODE);
|
||||
if (!CollectionUtils.isEmpty(errorCodeList)) {
|
||||
String errorCode = errorCodeList.get(0);
|
||||
@@ -59,6 +60,11 @@ public class GatewayResultExecutor extends BaseExecutorAdapter<ServerWebExchange
|
||||
return exchange.getLocaleContext().getLocale();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RouteInterceptorContext getRouteInterceptorContext(ServerWebExchange exchange) {
|
||||
return (RouteInterceptorContext) exchange.getAttributes().get(SopConstants.CACHE_ROUTE_INTERCEPTOR_CONTEXT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String buildErrorResult(ServerWebExchange exchange, Throwable ex) {
|
||||
Locale locale = getLocale(exchange);
|
||||
|
@@ -11,8 +11,6 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
*/
|
||||
public class GatewayForwardChooser extends BaseForwardChooser<ServerWebExchange> {
|
||||
|
||||
private static final String VALIDATE_ERROR_PATH = "/sop/validateError";
|
||||
|
||||
@Override
|
||||
public ApiParam getApiParam(ServerWebExchange exchange) {
|
||||
return ServerWebExchangeUtil.getApiParam(exchange);
|
||||
@@ -22,7 +20,7 @@ public class GatewayForwardChooser extends BaseForwardChooser<ServerWebExchange>
|
||||
public ForwardInfo getForwardInfo(ServerWebExchange exchange) {
|
||||
// 如果有异常,直接跳转到异常处理
|
||||
if (ServerWebExchangeUtil.getThrowable(exchange) != null) {
|
||||
return new ForwardInfo(VALIDATE_ERROR_PATH, "");
|
||||
return ForwardInfo.getErrorForwardInfo();
|
||||
}
|
||||
return super.getForwardInfo(exchange);
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ import com.gitee.sop.gatewaycommon.bean.RouteDefinition;
|
||||
import com.gitee.sop.gatewaycommon.manager.RouteRepository;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
|
||||
import org.springframework.cloud.gateway.route.Route;
|
||||
@@ -77,13 +78,27 @@ public class GatewayRouteRepository implements RouteRepository<GatewayTargetRout
|
||||
for (Map.Entry<String, GatewayTargetRoute> entry : routes.entrySet()) {
|
||||
// /food/get/?id?
|
||||
String pattern = entry.getKey();
|
||||
if (StringUtils.containsAny(pattern, "{") && this.pathMatcher.match(pattern, id)) {
|
||||
return entry.getValue();
|
||||
if (this.pathMatcher.match(pattern, id)) {
|
||||
return clone(id, entry.getValue());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private GatewayTargetRoute clone(String path, GatewayTargetRoute gatewayTargetRoute) {
|
||||
String prefix = "/" + gatewayTargetRoute.getServiceRouteInfo().getServiceId();
|
||||
if (path.startsWith(prefix)) {
|
||||
path = path.substring(prefix.length());
|
||||
}
|
||||
RouteDefinition routeDefinition = gatewayTargetRoute.getRouteDefinition();
|
||||
RouteDefinition newRouteDefinition = new RouteDefinition();
|
||||
BeanUtils.copyProperties(routeDefinition, newRouteDefinition);
|
||||
newRouteDefinition.setPath(path);
|
||||
return new GatewayTargetRoute(gatewayTargetRoute.getServiceRouteInfo()
|
||||
, newRouteDefinition
|
||||
, gatewayTargetRoute.getTargetRouteDefinition());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Collection<GatewayTargetRoute> getAll() {
|
||||
|
@@ -0,0 +1,96 @@
|
||||
package com.gitee.sop.gatewaycommon.interceptor;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
|
||||
import com.gitee.sop.gatewaycommon.monitor.MonitorInfo;
|
||||
import com.gitee.sop.gatewaycommon.monitor.MonitorManager;
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* SOP默认的拦截器,用于收集监控数据
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
public class MonitorRouteInterceptor implements RouteInterceptor {
|
||||
|
||||
private ThreadPoolExecutor threadPoolExecutor;
|
||||
|
||||
public MonitorRouteInterceptor(int threadPoolSize) {
|
||||
threadPoolExecutor = new ThreadPoolExecutor(threadPoolSize, threadPoolSize,
|
||||
0L, TimeUnit.MILLISECONDS,
|
||||
new LinkedBlockingQueue<>());
|
||||
}
|
||||
|
||||
public MonitorRouteInterceptor() {
|
||||
this(4);
|
||||
}
|
||||
|
||||
public MonitorRouteInterceptor(ThreadPoolExecutor threadPoolExecutor) {
|
||||
this.threadPoolExecutor = threadPoolExecutor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRoute(RouteInterceptorContext context) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterRoute(RouteInterceptorContext context) {
|
||||
threadPoolExecutor.execute(() -> this.storeRequestInfo(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录接口调用流量,最大时间,最小时间,总时长,平均时长,调用次数,成功次数,失败次数.
|
||||
* 需要考虑并发情况。
|
||||
*/
|
||||
protected void storeRequestInfo(RouteInterceptorContext context) {
|
||||
MonitorManager monitorManager = ApiConfig.getInstance().getMonitorManager();
|
||||
ApiParam apiParam = context.getApiParam();
|
||||
String routeId = apiParam.fetchNameVersion();
|
||||
long spendTime = context.getFinishTimeMillis() - context.getBeginTimeMillis();
|
||||
// 这步操作是线程安全的,底层调用了ConcurrentHashMap.computeIfAbsent
|
||||
MonitorInfo monitorInfo = monitorManager.getMonitorInfo(routeId, (k) -> this.createMonitorInfo(apiParam));
|
||||
|
||||
monitorInfo.storeMaxTime(spendTime);
|
||||
monitorInfo.storeMinTime(spendTime);
|
||||
monitorInfo.getTotalCount().incrementAndGet();
|
||||
monitorInfo.getTotalTime().addAndGet(spendTime);
|
||||
monitorInfo.getTotalRequestDataSize().addAndGet(context.getRequestDataSize());
|
||||
monitorInfo.getTotalResponseDataSize().addAndGet(context.getResponseDataSize());
|
||||
if (context.isSuccessRequest()) {
|
||||
monitorInfo.getSuccessCount().incrementAndGet();
|
||||
} else {
|
||||
monitorInfo.getErrorCount().incrementAndGet();
|
||||
String errorMsg = context.getServiceErrorMsg();
|
||||
monitorInfo.addErrorMsg(errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
private MonitorInfo createMonitorInfo(ApiParam apiParam) {
|
||||
MonitorInfo monitorInfo = new MonitorInfo();
|
||||
monitorInfo.setName(apiParam.fetchName());
|
||||
monitorInfo.setVersion(apiParam.fetchVersion());
|
||||
monitorInfo.setServiceId(apiParam.fetchServiceId());
|
||||
monitorInfo.setTotalRequestDataSize(new AtomicLong());
|
||||
monitorInfo.setTotalResponseDataSize(new AtomicLong());
|
||||
monitorInfo.setTotalTime(new AtomicLong());
|
||||
monitorInfo.setMaxTime(0L);
|
||||
monitorInfo.setMinTime(0L);
|
||||
monitorInfo.setSuccessCount(new AtomicLong());
|
||||
monitorInfo.setTotalCount(new AtomicLong());
|
||||
monitorInfo.setErrorCount(new AtomicLong());
|
||||
monitorInfo.setErrorMsgList(new ArrayList<>(10));
|
||||
return monitorInfo;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return -1000;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
package com.gitee.sop.gatewaycommon.interceptor;
|
||||
|
||||
/**
|
||||
* 路由拦截器
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
public interface RouteInterceptor {
|
||||
|
||||
/**
|
||||
* 在路由转发前执行,签名校验通过后会立即执行此方法
|
||||
*
|
||||
* @param context context
|
||||
*/
|
||||
void preRoute(RouteInterceptorContext context);
|
||||
|
||||
/**
|
||||
* 微服务返回结果后执行,可能返回成功,也可能返回失败结果,
|
||||
* 可通过 {@link RouteInterceptorContext#getResponseStatus()} 来判断是否返回正确结果。
|
||||
*
|
||||
* @param context context
|
||||
*/
|
||||
void afterRoute(RouteInterceptorContext context);
|
||||
|
||||
/**
|
||||
* 拦截器执行顺序,值小优先执行,建议从0开始,小于0留给系统使用
|
||||
*
|
||||
* @return 返回顺序
|
||||
*/
|
||||
int getOrder();
|
||||
|
||||
/**
|
||||
* 是否匹配,返回true执行拦截器,默认true
|
||||
* @param context context
|
||||
* @return 返回true执行拦截器
|
||||
*/
|
||||
default boolean match(RouteInterceptorContext context) {
|
||||
return true;
|
||||
}
|
||||
}
|
@@ -0,0 +1,85 @@
|
||||
package com.gitee.sop.gatewaycommon.interceptor;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.SopConstants;
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
/**
|
||||
* 拦截器参数
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
public interface RouteInterceptorContext {
|
||||
|
||||
/**
|
||||
* 返回ApiParam
|
||||
*
|
||||
* @return 返回ApiParam
|
||||
*/
|
||||
ApiParam getApiParam();
|
||||
|
||||
/**
|
||||
* 获取微服务返回的内容
|
||||
*
|
||||
* @return 微服务返回内容
|
||||
*/
|
||||
String getServiceResult();
|
||||
|
||||
/**
|
||||
* 获取微服务端的错误信息,status为200时,返回null。
|
||||
*
|
||||
* @return 返回错误信息
|
||||
*/
|
||||
String getServiceErrorMsg();
|
||||
|
||||
/**
|
||||
* 获取微服务返回状态码
|
||||
*
|
||||
* @return 返回状态码,正确为200,错误为非200
|
||||
*/
|
||||
int getResponseStatus();
|
||||
|
||||
/**
|
||||
* 获取路由开始时间
|
||||
*
|
||||
* @return 返回开始时间
|
||||
*/
|
||||
long getBeginTimeMillis();
|
||||
|
||||
/**
|
||||
* 获取路由结束时间
|
||||
*
|
||||
* @return 返回结束时间
|
||||
*/
|
||||
long getFinishTimeMillis();
|
||||
|
||||
/**
|
||||
* 获取上下文信息,zuul返回RequestContext,Gateway返回ServerWebExchange
|
||||
*
|
||||
* @return 返回上下文对象
|
||||
*/
|
||||
Object getRequestContext();
|
||||
|
||||
/**
|
||||
* 获取请求内容大小
|
||||
*
|
||||
* @return 返回请求内容大小
|
||||
*/
|
||||
long getRequestDataSize();
|
||||
|
||||
/**
|
||||
* 获取返回结果内容大小
|
||||
* @return 返回返回结果内容大小
|
||||
*/
|
||||
long getResponseDataSize();
|
||||
|
||||
/**
|
||||
* 是否是成功请求,微服务主动抛出的异常也算作成功,JSR303校验失败也算作成功。
|
||||
* 只有微服务返回未知的错误算作失败。
|
||||
* @return true:成功请求
|
||||
*/
|
||||
default boolean isSuccessRequest() {
|
||||
int responseStatus = getResponseStatus();
|
||||
return responseStatus == HttpStatus.OK.value() || responseStatus == SopConstants.BIZ_ERROR_STATUS;
|
||||
}
|
||||
}
|
@@ -28,7 +28,7 @@ public interface ServerChooserContext<T> extends ApiParamAware<T> {
|
||||
|
||||
default boolean isRequestGrayServer(T t) {
|
||||
ApiParam apiParam = getApiParam(t);
|
||||
return apiParam.isGrayRequest();
|
||||
return apiParam.fetchGrayRequest();
|
||||
}
|
||||
|
||||
String getHost(T t);
|
||||
|
@@ -3,8 +3,10 @@ package com.gitee.sop.gatewaycommon.manager;
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiContext;
|
||||
import com.gitee.sop.gatewaycommon.bean.BeanInitializer;
|
||||
import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptor;
|
||||
import com.gitee.sop.gatewaycommon.bean.SpringContext;
|
||||
import com.gitee.sop.gatewaycommon.gateway.loadbalancer.NacosServerIntrospector;
|
||||
import com.gitee.sop.gatewaycommon.interceptor.MonitorRouteInterceptor;
|
||||
import com.gitee.sop.gatewaycommon.limit.LimitManager;
|
||||
import com.gitee.sop.gatewaycommon.loadbalancer.SopPropertiesFactory;
|
||||
import com.gitee.sop.gatewaycommon.message.ErrorFactory;
|
||||
@@ -16,6 +18,7 @@ import com.gitee.sop.gatewaycommon.route.ServiceListener;
|
||||
import com.gitee.sop.gatewaycommon.route.ServiceRouteListener;
|
||||
import com.gitee.sop.gatewaycommon.secret.IsvManager;
|
||||
import com.gitee.sop.gatewaycommon.session.SessionManager;
|
||||
import com.gitee.sop.gatewaycommon.util.RouteInterceptorUtil;
|
||||
import com.gitee.sop.gatewaycommon.validate.SignConfig;
|
||||
import com.gitee.sop.gatewaycommon.validate.Validator;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -40,6 +43,8 @@ import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
@@ -254,6 +259,7 @@ public class AbstractConfiguration implements ApplicationContextAware, Applicati
|
||||
|
||||
initMessage();
|
||||
initBeanInitializer();
|
||||
initRouteInterceptor();
|
||||
doAfter();
|
||||
}
|
||||
|
||||
@@ -262,6 +268,13 @@ public class AbstractConfiguration implements ApplicationContextAware, Applicati
|
||||
beanInitializerMap.values().forEach(BeanInitializer::load);
|
||||
}
|
||||
|
||||
protected void initRouteInterceptor() {
|
||||
Map<String, RouteInterceptor> routeInterceptorMap = applicationContext.getBeansOfType(RouteInterceptor.class);
|
||||
Collection<RouteInterceptor> routeInterceptors = new ArrayList<>(routeInterceptorMap.values());
|
||||
routeInterceptors.add(new MonitorRouteInterceptor());
|
||||
RouteInterceptorUtil.addInterceptors(routeInterceptors);
|
||||
}
|
||||
|
||||
protected void doAfter() {
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,88 @@
|
||||
package com.gitee.sop.gatewaycommon.monitor;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* 每个接口 总调用流量,最大时间,最小时间,总时长,平均时长,调用次数,成功次数,失败次数,错误查看。
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
@Data
|
||||
public class MonitorInfo {
|
||||
|
||||
/**
|
||||
* 接口名
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 版本号
|
||||
*/
|
||||
private String version;
|
||||
/**
|
||||
* serviceId
|
||||
*/
|
||||
private String serviceId;
|
||||
/**
|
||||
* 总请求数据量
|
||||
*/
|
||||
private AtomicLong totalRequestDataSize;
|
||||
/**
|
||||
* 总返回数据量
|
||||
*/
|
||||
private AtomicLong totalResponseDataSize;
|
||||
/**
|
||||
* 请求耗时最长时间
|
||||
*/
|
||||
private Long maxTime;
|
||||
/**
|
||||
* 请求耗时最小时间
|
||||
*/
|
||||
private Long minTime;
|
||||
/**
|
||||
* 总时长
|
||||
*/
|
||||
private AtomicLong totalTime;
|
||||
/**
|
||||
* 总调用次数
|
||||
*/
|
||||
private AtomicLong totalCount;
|
||||
/**
|
||||
* 成功次数
|
||||
*/
|
||||
private AtomicLong successCount;
|
||||
/**
|
||||
* 失败次数(业务主动抛出的异常算作成功,如参数校验,未知的错误算失败)
|
||||
*/
|
||||
private AtomicLong errorCount;
|
||||
/**
|
||||
* 错误信息
|
||||
*/
|
||||
private List<String> errorMsgList;
|
||||
|
||||
public synchronized void storeMaxTime(long spendTime) {
|
||||
if (spendTime > maxTime) {
|
||||
maxTime = spendTime;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void storeMinTime(long spendTime) {
|
||||
if (minTime == 0 || spendTime < minTime) {
|
||||
minTime = spendTime;
|
||||
}
|
||||
}
|
||||
|
||||
public void addErrorMsg(String errorMsg) {
|
||||
if (errorMsg == null || "".equals(errorMsg)) {
|
||||
return;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (errorMsgList != null && errorMsgList.size() < 10) {
|
||||
errorMsgList.add(errorMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
package com.gitee.sop.gatewaycommon.monitor;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public class MonitorManager {
|
||||
|
||||
private static Map<String, MonitorInfo> monitorMap = new ConcurrentHashMap<>(128);
|
||||
|
||||
public Map<String, MonitorInfo> getMonitorData() {
|
||||
return monitorMap;
|
||||
}
|
||||
|
||||
public MonitorInfo getMonitorInfo(String routeId, Function<String, MonitorInfo> createFun) {
|
||||
return monitorMap.computeIfAbsent(routeId, createFun);
|
||||
}
|
||||
|
||||
}
|
@@ -29,12 +29,24 @@ public class ApiParam extends JSONObject implements Param {
|
||||
private String restName;
|
||||
private String restVersion;
|
||||
|
||||
private String serviceId;
|
||||
private String ip;
|
||||
private boolean restful;
|
||||
private boolean mergeResult = true;
|
||||
|
||||
private boolean isGrayRequest;
|
||||
|
||||
private transient UploadContext uploadContext;
|
||||
|
||||
public static ApiParam createRestfulApiParam(String path) {
|
||||
ApiParam apiParam = new ApiParam();
|
||||
apiParam.setName(path);
|
||||
apiParam.setVersion("");
|
||||
apiParam.setRestful(true);
|
||||
apiParam.setMergeResult(false);
|
||||
return apiParam;
|
||||
}
|
||||
|
||||
public void fitNameVersion() {
|
||||
if (restName != null) {
|
||||
this.put(ParamNames.API_NAME, restName);
|
||||
@@ -204,12 +216,7 @@ public class ApiParam extends JSONObject implements Param {
|
||||
|
||||
@Override
|
||||
public String fetchSignMethod() {
|
||||
String signMethod = getString(ParamNames.SIGN_TYPE_NAME);
|
||||
if (signMethod == null) {
|
||||
return SopConstants.DEFAULT_SIGN_METHOD;
|
||||
} else {
|
||||
return signMethod;
|
||||
}
|
||||
return getString(ParamNames.SIGN_TYPE_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -251,11 +258,35 @@ public class ApiParam extends JSONObject implements Param {
|
||||
return ip;
|
||||
}
|
||||
|
||||
public boolean isGrayRequest() {
|
||||
public boolean fetchGrayRequest() {
|
||||
return isGrayRequest;
|
||||
}
|
||||
|
||||
public void setGrayRequest(boolean grayRequest) {
|
||||
isGrayRequest = grayRequest;
|
||||
}
|
||||
|
||||
public String fetchServiceId() {
|
||||
return serviceId;
|
||||
}
|
||||
|
||||
public void setServiceId(String serviceId) {
|
||||
this.serviceId = serviceId;
|
||||
}
|
||||
|
||||
public boolean fetchMergeResult() {
|
||||
return mergeResult;
|
||||
}
|
||||
|
||||
public void setMergeResult(boolean mergeResult) {
|
||||
this.mergeResult = mergeResult;
|
||||
}
|
||||
|
||||
public boolean fetchRestful() {
|
||||
return restful;
|
||||
}
|
||||
|
||||
public void setRestful(boolean restful) {
|
||||
this.restful = restful;
|
||||
}
|
||||
}
|
||||
|
@@ -3,50 +3,47 @@ package com.gitee.sop.gatewaycommon.result;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiContext;
|
||||
import com.gitee.sop.gatewaycommon.bean.ErrorDefinition;
|
||||
import com.gitee.sop.gatewaycommon.bean.DefaultRouteInterceptorContext;
|
||||
import com.gitee.sop.gatewaycommon.bean.Isv;
|
||||
import com.gitee.sop.gatewaycommon.bean.RouteDefinition;
|
||||
import com.gitee.sop.gatewaycommon.bean.ServiceRouteInfo;
|
||||
import com.gitee.sop.gatewaycommon.bean.SopConstants;
|
||||
import com.gitee.sop.gatewaycommon.bean.TargetRoute;
|
||||
import com.gitee.sop.gatewaycommon.manager.RouteRepositoryContext;
|
||||
import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptorContext;
|
||||
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
|
||||
import com.gitee.sop.gatewaycommon.message.ErrorMeta;
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||
import com.gitee.sop.gatewaycommon.param.ParamNames;
|
||||
import com.gitee.sop.gatewaycommon.secret.IsvManager;
|
||||
import com.gitee.sop.gatewaycommon.validate.alipay.AlipayConstants;
|
||||
import com.gitee.sop.gatewaycommon.util.RouteInterceptorUtil;
|
||||
import com.gitee.sop.gatewaycommon.validate.alipay.AlipaySignature;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 处理微服务返回结果
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class BaseExecutorAdapter<T, R> implements ResultExecutor<T, R> {
|
||||
private static final ErrorMeta SUCCESS_META = ErrorEnum.SUCCESS.getErrorMeta();
|
||||
private static final ErrorMeta ISP_UNKNOW_ERROR_META = ErrorEnum.ISP_UNKNOWN_ERROR.getErrorMeta();
|
||||
private static final ErrorMeta ISP_BIZ_ERROR = ErrorEnum.BIZ_ERROR.getErrorMeta();
|
||||
private static final ErrorMeta ISV_MISSING_METHOD_META = ErrorEnum.ISV_MISSING_METHOD.getErrorMeta();
|
||||
|
||||
private static Map<Integer, ErrorEnum> HTTP_STATUS_ERROR_ENUM_MAP = new HashMap<>(8);
|
||||
|
||||
private static final String GATEWAY_CODE_NAME = "code";
|
||||
private static final String GATEWAY_MSG_NAME = "msg";
|
||||
private static final String ARRAY_START = "[";
|
||||
private static final String ARRAY_END = "]";
|
||||
private static final String ROOT_JSON = "{'items':%s}".replace("'", "\"");
|
||||
private static final String ERROR_METHOD = "error";
|
||||
|
||||
static {
|
||||
HTTP_STATUS_ERROR_ENUM_MAP.put(HttpStatus.OK.value(), ErrorEnum.SUCCESS);
|
||||
HTTP_STATUS_ERROR_ENUM_MAP.put(SopConstants.BIZ_ERROR_STATUS, ErrorEnum.BIZ_ERROR);
|
||||
HTTP_STATUS_ERROR_ENUM_MAP.put(HttpStatus.NOT_FOUND.value(), ErrorEnum.ISV_INVALID_METHOD);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@@ -75,65 +72,82 @@ public abstract class BaseExecutorAdapter<T, R> implements ResultExecutor<T, R>
|
||||
|
||||
/**
|
||||
* 获取locale
|
||||
*
|
||||
* @param t request
|
||||
* @return 返回locale
|
||||
*/
|
||||
protected abstract Locale getLocale(T t);
|
||||
|
||||
/**
|
||||
* 返回拦截器上下文
|
||||
*
|
||||
* @param t request
|
||||
* @return 返回拦截器上下文
|
||||
*/
|
||||
protected abstract RouteInterceptorContext getRouteInterceptorContext(T t);
|
||||
|
||||
@Override
|
||||
public String mergeResult(T request, String serviceResult) {
|
||||
serviceResult = formatResult(serviceResult);
|
||||
boolean isMergeResult = this.isMergeResult(request);
|
||||
if (!isMergeResult) {
|
||||
return serviceResult;
|
||||
}
|
||||
serviceResult = wrapResult(serviceResult);
|
||||
int responseStatus = this.getResponseStatus(request);
|
||||
JSONObject responseData;
|
||||
if (responseStatus == HttpStatus.OK.value()) {
|
||||
// 200正常返回
|
||||
responseData = JSON.parseObject(serviceResult);
|
||||
responseData.put(GATEWAY_CODE_NAME, SUCCESS_META.getCode());
|
||||
responseData.put(GATEWAY_MSG_NAME, SUCCESS_META.getError(getLocale(request)).getMsg());
|
||||
} else if (responseStatus == SopConstants.BIZ_ERROR_STATUS) {
|
||||
// 如果是业务出错
|
||||
this.storeError(request, ErrorType.BIZ);
|
||||
responseData = JSON.parseObject(serviceResult);
|
||||
responseData.put(GATEWAY_CODE_NAME, ISP_BIZ_ERROR.getCode());
|
||||
responseData.put(GATEWAY_MSG_NAME, ISP_BIZ_ERROR.getError(getLocale(request)).getMsg());
|
||||
} else if (responseStatus == HttpStatus.NOT_FOUND.value()) {
|
||||
responseData = JSON.parseObject(serviceResult);
|
||||
responseData.put(GATEWAY_CODE_NAME, ISV_MISSING_METHOD_META.getCode());
|
||||
responseData.put(GATEWAY_MSG_NAME, ISV_MISSING_METHOD_META.getError(getLocale(request)).getCode());
|
||||
this.doAfterRoute(serviceResult, responseStatus, request);
|
||||
String finalResult;
|
||||
if (isMergeResult) {
|
||||
JSONObject responseData = this.parseServiceResult(serviceResult, responseStatus, request);
|
||||
finalResult = this.merge(request, responseData);
|
||||
} else {
|
||||
ApiParam params = this.getApiParam(request);
|
||||
log.error("微服务端报错,params:{}, 微服务返回结果:{}", params, serviceResult);
|
||||
this.storeError(request, ErrorType.UNKNOWN);
|
||||
// 微服务端有可能返回500错误
|
||||
// {"path":"/book/getBook3","error":"Internal Server Error","message":"id不能为空","timestamp":"2019-02-13T07:41:00.495+0000","status":500}
|
||||
responseData = new JSONObject();
|
||||
responseData.put(GATEWAY_CODE_NAME, ISP_UNKNOW_ERROR_META.getCode());
|
||||
responseData.put(GATEWAY_MSG_NAME, ISP_UNKNOW_ERROR_META.getError(getLocale(request)).getMsg());
|
||||
finalResult = serviceResult;
|
||||
}
|
||||
return this.merge(request, responseData);
|
||||
return finalResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存错误信息
|
||||
* 执行拦截器after操作
|
||||
*
|
||||
* @param request request
|
||||
* @param serviceResult 微服务结果
|
||||
* @param responseStatus 微服务状态码
|
||||
* @param requestContext 微服务状态码
|
||||
*/
|
||||
protected void storeError(T request, ErrorType errorType) {
|
||||
ApiInfo apiInfo = this.getApiInfo(request);
|
||||
String errorMsg = this.getResponseErrorMessage(request);
|
||||
ErrorDefinition errorDefinition = new ErrorDefinition();
|
||||
BeanUtils.copyProperties(apiInfo, errorDefinition);
|
||||
errorDefinition.setErrorMsg(errorMsg);
|
||||
if (errorType == ErrorType.UNKNOWN) {
|
||||
ApiConfig.getInstance().getServiceErrorManager().saveUnknownError(errorDefinition);
|
||||
private void doAfterRoute(String serviceResult, int responseStatus, T requestContext) {
|
||||
RouteInterceptorContext routeInterceptorContext = getRouteInterceptorContext(requestContext);
|
||||
if (routeInterceptorContext instanceof DefaultRouteInterceptorContext) {
|
||||
DefaultRouteInterceptorContext defaultRouteInterceptorContext = (DefaultRouteInterceptorContext) routeInterceptorContext;
|
||||
defaultRouteInterceptorContext.setResponseStatus(responseStatus);
|
||||
defaultRouteInterceptorContext.setServiceResult(serviceResult);
|
||||
defaultRouteInterceptorContext.setFinishTimeMillis(System.currentTimeMillis());
|
||||
defaultRouteInterceptorContext.setResponseDataSize(serviceResult.length());
|
||||
if (responseStatus != HttpStatus.OK.value() && responseStatus != SopConstants.BIZ_ERROR_STATUS) {
|
||||
String responseErrorMessage = getResponseErrorMessage(requestContext);
|
||||
if (StringUtils.isEmpty(responseErrorMessage)) {
|
||||
responseErrorMessage = serviceResult;
|
||||
}
|
||||
defaultRouteInterceptorContext.setServiceErrorMsg(responseErrorMessage);
|
||||
}
|
||||
}
|
||||
if (errorType == ErrorType.BIZ) {
|
||||
ApiConfig.getInstance().getServiceErrorManager().saveBizError(errorDefinition);
|
||||
RouteInterceptorUtil.runAfterRoute(routeInterceptorContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将微服务的返回结果解析成JSONObject
|
||||
*
|
||||
* @param serviceResult 微服务返回结果
|
||||
* @param responseStatus 返回状态
|
||||
* @param request 请求
|
||||
* @return 返回JSONObject
|
||||
*/
|
||||
protected JSONObject parseServiceResult(String serviceResult, int responseStatus, T request) {
|
||||
ErrorEnum errorEnum = HTTP_STATUS_ERROR_ENUM_MAP.get(responseStatus);
|
||||
if (errorEnum == null) {
|
||||
// 其它异常不应该把异常信息告诉给客户端,将微服务内容设置成空的json
|
||||
serviceResult = SopConstants.EMPTY_JSON;
|
||||
errorEnum = ErrorEnum.ISP_UNKNOWN_ERROR;
|
||||
}
|
||||
ErrorMeta errorMeta = errorEnum.getErrorMeta();
|
||||
JSONObject responseData = JSON.parseObject(serviceResult);
|
||||
responseData.put(GATEWAY_CODE_NAME, errorMeta.getCode());
|
||||
responseData.put(GATEWAY_MSG_NAME, errorMeta.getError(getLocale(request)).getMsg());
|
||||
return responseData;
|
||||
}
|
||||
|
||||
|
||||
@@ -144,53 +158,15 @@ public abstract class BaseExecutorAdapter<T, R> implements ResultExecutor<T, R>
|
||||
* @return true:需要合并
|
||||
*/
|
||||
protected boolean isMergeResult(T request) {
|
||||
// 默认全局设置
|
||||
Boolean defaultSetting = ApiContext.getApiConfig().getMergeResult();
|
||||
if (defaultSetting != null) {
|
||||
return defaultSetting;
|
||||
}
|
||||
ApiParam params = this.getApiParam(request);
|
||||
TargetRoute targetRoute = RouteRepositoryContext.getRouteRepository().get(params.fetchNameVersion());
|
||||
RouteDefinition baseRouteDefinition = Optional.ofNullable(targetRoute)
|
||||
.map(TargetRoute::getRouteDefinition)
|
||||
.orElse(null);
|
||||
return Optional.ofNullable(baseRouteDefinition)
|
||||
.map(routeDefinition -> {
|
||||
int mergeResult = baseRouteDefinition.getMergeResult();
|
||||
return BooleanUtils.toBoolean(mergeResult);
|
||||
})
|
||||
.orElse(true);
|
||||
return params.fetchMergeResult();
|
||||
}
|
||||
|
||||
protected ApiInfo getApiInfo(T request) {
|
||||
ApiParam params = this.getApiParam(request);
|
||||
TargetRoute targetRoute = RouteRepositoryContext.getRouteRepository().get(params.fetchNameVersion());
|
||||
|
||||
String serviceId = Optional.ofNullable(targetRoute)
|
||||
.flatMap(route -> Optional.ofNullable(route.getServiceRouteInfo()))
|
||||
.map(ServiceRouteInfo::getServiceId)
|
||||
.orElse(SopConstants.UNKNOWN_SERVICE);
|
||||
|
||||
RouteDefinition baseRouteDefinition = Optional.ofNullable(targetRoute)
|
||||
.map(TargetRoute::getRouteDefinition)
|
||||
.orElse(null);
|
||||
|
||||
ApiInfo apiInfo = new ApiInfo();
|
||||
apiInfo.name = params.fetchName();
|
||||
apiInfo.version = params.fetchVersion();
|
||||
apiInfo.serviceId = serviceId;
|
||||
apiInfo.gatewayRouteDefinition = baseRouteDefinition;
|
||||
return apiInfo;
|
||||
}
|
||||
|
||||
protected String wrapResult(String serviceResult) {
|
||||
if (serviceResult == null) {
|
||||
serviceResult = "";
|
||||
}
|
||||
serviceResult = serviceResult.trim();
|
||||
if (StringUtils.isEmpty(serviceResult)) {
|
||||
protected String formatResult(String serviceResult) {
|
||||
if (StringUtils.isBlank(serviceResult)) {
|
||||
return SopConstants.EMPTY_JSON;
|
||||
}
|
||||
// 如果直接返回数组,需要进行包装,变成:{"items": [...]}
|
||||
if (serviceResult.startsWith(ARRAY_START) && serviceResult.endsWith(ARRAY_END)) {
|
||||
return String.format(ROOT_JSON, serviceResult);
|
||||
}
|
||||
@@ -200,12 +176,11 @@ public abstract class BaseExecutorAdapter<T, R> implements ResultExecutor<T, R>
|
||||
public String merge(T exchange, JSONObject responseData) {
|
||||
JSONObject finalData = new JSONObject(true);
|
||||
ApiParam params = this.getApiParam(exchange);
|
||||
String name = params.fetchName();
|
||||
ApiConfig apiConfig = ApiConfig.getInstance();
|
||||
// 点换成下划线
|
||||
DataNameBuilder dataNameBuilder = apiConfig.getDataNameBuilder();
|
||||
// alipay_goods_get_response
|
||||
String responseDataNodeName = dataNameBuilder.build(name);
|
||||
String responseDataNodeName = dataNameBuilder.build(params.fetchName());
|
||||
finalData.put(responseDataNodeName, responseData);
|
||||
ResultAppender resultAppender = apiConfig.getResultAppender();
|
||||
// 追加额外的结果
|
||||
@@ -213,19 +188,23 @@ public abstract class BaseExecutorAdapter<T, R> implements ResultExecutor<T, R>
|
||||
resultAppender.append(finalData, params, exchange);
|
||||
}
|
||||
// 添加服务端sign
|
||||
this.addResponseSign(apiConfig, params, finalData, responseDataNodeName);
|
||||
return finalData.toJSONString();
|
||||
}
|
||||
|
||||
private void addResponseSign(ApiConfig apiConfig, ApiParam params, JSONObject finalData, String responseDataNodeName) {
|
||||
if (apiConfig.isShowReturnSign() && !CollectionUtils.isEmpty(params)) {
|
||||
// 添加try...catch,生成sign出错不影响结果正常返回
|
||||
try {
|
||||
String responseSignContent = this.buildResponseSignContent(responseDataNodeName, finalData);
|
||||
String sign = this.createResponseSign(apiConfig, params, responseSignContent);
|
||||
if (StringUtils.hasLength(sign)) {
|
||||
String sign = this.createResponseSign(apiConfig.getIsvManager(), params, responseSignContent);
|
||||
if (StringUtils.isNotBlank(sign)) {
|
||||
finalData.put(ParamNames.RESPONSE_SIGN_NAME, sign);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("生成平台签名失败, params: {}, serviceResult:{}", JSON.toJSONString(params), responseData, e);
|
||||
log.error("生成平台签名失败, params: {}", params.toJSONString(), e);
|
||||
}
|
||||
}
|
||||
return finalData.toJSONString();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -246,26 +225,20 @@ public abstract class BaseExecutorAdapter<T, R> implements ResultExecutor<T, R>
|
||||
return null;
|
||||
}
|
||||
|
||||
protected String getParamValue(Map<String, Object> apiParam, String key, String defaultValue) {
|
||||
return CollectionUtils.isEmpty(apiParam) ? defaultValue : (String) apiParam.getOrDefault(key, defaultValue);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 这里需要使用平台的私钥生成一个sign,需要配置两套公私钥。
|
||||
*
|
||||
* @param apiConfig 配置
|
||||
* @param isvManager isvManager
|
||||
* @param params 请求参数
|
||||
* @param responseSignContent 待签名内容
|
||||
* @return 返回平台生成的签名
|
||||
*/
|
||||
protected String createResponseSign(ApiConfig apiConfig, Map<String, Object> params, String responseSignContent) {
|
||||
protected String createResponseSign(IsvManager isvManager, ApiParam params, String responseSignContent) {
|
||||
if (StringUtils.isEmpty(responseSignContent)) {
|
||||
return null;
|
||||
}
|
||||
IsvManager isvManager = apiConfig.getIsvManager();
|
||||
// 根据appId获取秘钥
|
||||
String appKey = this.getParamValue(params, ParamNames.APP_KEY_NAME, "");
|
||||
String appKey = params.fetchAppKey();
|
||||
if (StringUtils.isEmpty(appKey)) {
|
||||
return null;
|
||||
}
|
||||
@@ -274,30 +247,12 @@ public abstract class BaseExecutorAdapter<T, R> implements ResultExecutor<T, R>
|
||||
if (StringUtils.isEmpty(privateKeyPlatform)) {
|
||||
return null;
|
||||
}
|
||||
String charset = Optional.ofNullable(params.get(ParamNames.CHARSET_NAME))
|
||||
.map(String::valueOf)
|
||||
.orElse(SopConstants.UTF8);
|
||||
String signType = getParamValue(params, ParamNames.SIGN_TYPE_NAME, AlipayConstants.SIGN_TYPE_RSA2);
|
||||
return AlipaySignature.rsaSign(responseSignContent, privateKeyPlatform, charset, signType);
|
||||
return AlipaySignature.rsaSign(
|
||||
responseSignContent
|
||||
, privateKeyPlatform
|
||||
, params.fetchCharset()
|
||||
, params.fetchSignMethod()
|
||||
);
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
protected static class ApiInfo {
|
||||
private String name;
|
||||
private String version;
|
||||
private String serviceId;
|
||||
private RouteDefinition gatewayRouteDefinition;
|
||||
}
|
||||
|
||||
enum ErrorType {
|
||||
/**
|
||||
* 未知错误
|
||||
*/
|
||||
UNKNOWN,
|
||||
/**
|
||||
* 业务错误
|
||||
*/
|
||||
BIZ
|
||||
}
|
||||
}
|
||||
|
@@ -1,18 +1,14 @@
|
||||
package com.gitee.sop.gatewaycommon.route;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
|
||||
import com.gitee.sop.gatewaycommon.bean.RouteDefinition;
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiParamAware;
|
||||
import com.gitee.sop.gatewaycommon.bean.TargetRoute;
|
||||
import com.gitee.sop.gatewaycommon.loadbalancer.builder.GrayUserBuilder;
|
||||
import com.gitee.sop.gatewaycommon.manager.EnvGrayManager;
|
||||
import com.gitee.sop.gatewaycommon.manager.RouteRepositoryContext;
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -28,9 +24,6 @@ public abstract class BaseForwardChooser<T> implements ForwardChooser<T>, ApiPar
|
||||
ApiParam apiParam = getApiParam(t);
|
||||
String nameVersion = apiParam.fetchNameVersion();
|
||||
TargetRoute targetRoute = RouteRepositoryContext.getRouteRepository().get(nameVersion);
|
||||
RouteDefinition routeDefinitionOrig = targetRoute.getRouteDefinition();
|
||||
String path = routeDefinitionOrig.getPath();
|
||||
String version = apiParam.fetchVersion();
|
||||
String serviceId = targetRoute.getServiceRouteInfo().fetchServiceIdLowerCase();
|
||||
// 如果服务在灰度阶段,返回一个灰度版本号
|
||||
String grayVersion = envGrayManager.getVersion(serviceId, nameVersion);
|
||||
@@ -40,16 +33,10 @@ public abstract class BaseForwardChooser<T> implements ForwardChooser<T>, ApiPar
|
||||
TargetRoute targetRouteDest = RouteRepositoryContext.getRouteRepository().get(newNameVersion);
|
||||
if (targetRouteDest != null) {
|
||||
apiParam.setGrayRequest(true);
|
||||
if (BooleanUtils.toBoolean(routeDefinitionOrig.getCompatibleMode())) {
|
||||
version = grayVersion;
|
||||
} else {
|
||||
// 获取灰度接口
|
||||
RouteDefinition routeDefinition = targetRouteDest.getRouteDefinition();
|
||||
path = routeDefinition.getPath();
|
||||
}
|
||||
targetRoute = targetRouteDest;
|
||||
}
|
||||
}
|
||||
return new ForwardInfo(path, version);
|
||||
return new ForwardInfo(targetRoute);
|
||||
}
|
||||
|
||||
protected boolean isGrayUser(String serviceId, ApiParam apiParam) {
|
||||
|
@@ -1,5 +1,6 @@
|
||||
package com.gitee.sop.gatewaycommon.route;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.TargetRoute;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
@@ -7,13 +8,48 @@ import lombok.Data;
|
||||
*/
|
||||
@Data
|
||||
public class ForwardInfo {
|
||||
private String path;
|
||||
private String version;
|
||||
private String domain;
|
||||
|
||||
public ForwardInfo(String path, String version) {
|
||||
this.path = path;
|
||||
this.version = version;
|
||||
private TargetRoute targetRoute;
|
||||
|
||||
public static ForwardInfo getErrorForwardInfo() {
|
||||
return ErrorForwardInfo.errorForwardInfo;
|
||||
}
|
||||
|
||||
public ForwardInfo(TargetRoute targetRoute) {
|
||||
this.targetRoute = targetRoute;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return targetRoute.getRouteDefinition().getPath();
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return targetRoute.getRouteDefinition().getVersion();
|
||||
}
|
||||
|
||||
static class ErrorForwardInfo extends ForwardInfo {
|
||||
|
||||
private static final String VALIDATE_ERROR_PATH = "/sop/validateError";
|
||||
|
||||
public static ErrorForwardInfo errorForwardInfo = new ErrorForwardInfo();
|
||||
|
||||
public ErrorForwardInfo() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public ErrorForwardInfo(TargetRoute targetRoute) {
|
||||
super(targetRoute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return VALIDATE_ERROR_PATH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,21 @@
|
||||
package com.gitee.sop.gatewaycommon.support;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
|
||||
import com.gitee.sop.gatewaycommon.monitor.MonitorManager;
|
||||
import com.gitee.sop.gatewaycommon.result.ApiResult;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
/**
|
||||
* 提供监控数据
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
public abstract class BaseMonitorController<T> extends SopBaseController<T> {
|
||||
|
||||
@GetMapping("/sop/getMonitorData")
|
||||
public ApiResult doExecute(T request) {
|
||||
MonitorManager monitorManager = ApiConfig.getInstance().getMonitorManager();
|
||||
return execute(request, monitorManager::getMonitorData);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
package com.gitee.sop.gatewaycommon.support;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||
import com.gitee.sop.gatewaycommon.result.ApiResult;
|
||||
import com.gitee.sop.gatewaycommon.result.JsonResult;
|
||||
import com.gitee.sop.gatewaycommon.validate.taobao.TaobaoSigner;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public abstract class SopBaseController<T> {
|
||||
|
||||
TaobaoSigner signer = new TaobaoSigner();
|
||||
|
||||
@Value("${sop.secret}")
|
||||
private String secret;
|
||||
|
||||
protected abstract ApiParam getApiParam(T t);
|
||||
|
||||
public ApiResult execute(T request, Supplier<Object> supplier) {
|
||||
try {
|
||||
this.check(request);
|
||||
JsonResult apiResult = new JsonResult();
|
||||
apiResult.setData(supplier.get());
|
||||
return apiResult;
|
||||
} catch (Exception e) {
|
||||
ApiResult apiResult = new ApiResult();
|
||||
apiResult.setCode("505050");
|
||||
apiResult.setMsg(e.getMessage());
|
||||
return apiResult;
|
||||
}
|
||||
}
|
||||
|
||||
protected void check(T request) {
|
||||
ApiParam apiParam = getApiParam(request);
|
||||
boolean right = signer.checkSign(apiParam, secret);
|
||||
if (!right) {
|
||||
throw new RuntimeException("签名校验失败");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,63 @@
|
||||
package com.gitee.sop.gatewaycommon.util;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public class LoadBalanceUtil {
|
||||
|
||||
/**
|
||||
* key:serviceId,value:指示变量i
|
||||
*/
|
||||
private static Map<String, Integer> serviceIdRoundMap = new ConcurrentHashMap<>(8);
|
||||
|
||||
/**
|
||||
* 轮询选择一台机器。<br>
|
||||
* <p>
|
||||
* 假设有N台服务器:S = {S1, S2, …, Sn},一个指示变量i表示上一次选择的服务器ID。变量i被初始化为N-1。
|
||||
* </p>
|
||||
* 参考:https://blog.csdn.net/qq_37469055/article/details/87991327
|
||||
* @param serviceId serviceId,不同的serviceId对应的服务器数量不一样,需要区分开
|
||||
* @param servers 服务器列表
|
||||
* @return 返回一台服务器实例
|
||||
*/
|
||||
public static <T> T chooseByRoundRobin(String serviceId, List<T> servers) {
|
||||
if (servers == null || servers.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
int n = servers.size();
|
||||
int i = serviceIdRoundMap.computeIfAbsent(serviceId, (k) -> n - 1);
|
||||
int j = i;
|
||||
do {
|
||||
j = (j + 1) % n;
|
||||
i = j;
|
||||
serviceIdRoundMap.put(serviceId, i);
|
||||
return servers.get(i);
|
||||
} while (j != i);
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机选取一台实例
|
||||
*
|
||||
* @param servers 服务列表
|
||||
* @return 返回实例,没有返回null
|
||||
*/
|
||||
public static <T> T chooseByRandom(List<T> servers) {
|
||||
if (servers.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
int serverCount = servers.size();
|
||||
// 随机选取一台实例
|
||||
int index = chooseRandomInt(serverCount);
|
||||
return servers.get(index);
|
||||
}
|
||||
|
||||
private static int chooseRandomInt(int serverCount) {
|
||||
return ThreadLocalRandom.current().nextInt(serverCount);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
package com.gitee.sop.gatewaycommon.util;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
|
||||
import com.gitee.sop.gatewaycommon.bean.DefaultRouteInterceptorContext;
|
||||
import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptor;
|
||||
import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptorContext;
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Slf4j
|
||||
public class RouteInterceptorUtil {
|
||||
|
||||
public static void runPreRoute(Object requestContext, ApiParam param, Consumer<RouteInterceptorContext> saveContext) {
|
||||
DefaultRouteInterceptorContext defaultRouteInterceptorContext = new DefaultRouteInterceptorContext();
|
||||
saveContext.accept(defaultRouteInterceptorContext);
|
||||
defaultRouteInterceptorContext.setBeginTimeMillis(System.currentTimeMillis());
|
||||
defaultRouteInterceptorContext.setRequestContext(requestContext);
|
||||
defaultRouteInterceptorContext.setApiParam(param);
|
||||
getRouteInterceptors().forEach(routeInterceptor -> {
|
||||
if (routeInterceptor.match(defaultRouteInterceptorContext)) {
|
||||
routeInterceptor.preRoute(defaultRouteInterceptorContext);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void runAfterRoute(RouteInterceptorContext routeInterceptorContext) {
|
||||
if (routeInterceptorContext == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
getRouteInterceptors().forEach(routeInterceptor -> {
|
||||
if (routeInterceptor.match(routeInterceptorContext)) {
|
||||
routeInterceptor.afterRoute(routeInterceptorContext);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
log.error("执行路由拦截器异常, apiParam:{}", routeInterceptorContext.getApiParam().toJSONString());
|
||||
}
|
||||
}
|
||||
|
||||
public static List<RouteInterceptor> getRouteInterceptors() {
|
||||
return ApiConfig.getInstance().getRouteInterceptors();
|
||||
}
|
||||
|
||||
public static void addInterceptors(Collection<RouteInterceptor> interceptors) {
|
||||
List<RouteInterceptor> routeInterceptors = getRouteInterceptors();
|
||||
routeInterceptors.addAll(interceptors);
|
||||
routeInterceptors.sort(Comparator.comparing(RouteInterceptor::getOrder));
|
||||
}
|
||||
}
|
@@ -69,6 +69,7 @@ public class ApiValidator implements Validator {
|
||||
public void validate(ApiParam param) {
|
||||
checkIP(param);
|
||||
TargetRoute targetRoute = checkEnable(param);
|
||||
initFields(targetRoute, param);
|
||||
ApiConfig apiConfig = ApiContext.getApiConfig();
|
||||
if (apiConfig.isIgnoreValidate()
|
||||
|| BooleanUtils.toBoolean(targetRoute.getRouteDefinition().getIgnoreValidate())) {
|
||||
@@ -127,6 +128,19 @@ public class ApiValidator implements Validator {
|
||||
return targetRoute;
|
||||
}
|
||||
|
||||
private void initFields(TargetRoute targetRoute, ApiParam apiParam) {
|
||||
apiParam.setServiceId(targetRoute.getServiceRouteInfo().getServiceId());
|
||||
boolean mergeResult;
|
||||
Boolean defaultSetting = ApiContext.getApiConfig().getMergeResult();
|
||||
if (defaultSetting != null) {
|
||||
mergeResult = defaultSetting;
|
||||
} else {
|
||||
RouteDefinition routeDefinition = targetRoute.getRouteDefinition();
|
||||
mergeResult = routeDefinition == null || BooleanUtils.toBoolean(routeDefinition.getMergeResult());
|
||||
}
|
||||
apiParam.setMergeResult(mergeResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验上传文件内容
|
||||
*
|
||||
|
@@ -1,18 +1,17 @@
|
||||
package com.gitee.sop.gatewaycommon.zuul;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
|
||||
import com.gitee.sop.gatewaycommon.bean.DefaultRouteInterceptorContext;
|
||||
import com.gitee.sop.gatewaycommon.bean.SopConstants;
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||
import com.gitee.sop.gatewaycommon.param.ParamBuilder;
|
||||
import com.gitee.sop.gatewaycommon.util.RequestUtil;
|
||||
import com.gitee.sop.gatewaycommon.util.ResponseUtil;
|
||||
import com.gitee.sop.gatewaycommon.util.RouteInterceptorUtil;
|
||||
import com.gitee.sop.gatewaycommon.validate.Validator;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* 负责签名校验
|
||||
* @author tanghc
|
||||
@@ -29,14 +28,10 @@ public class ValidateService {
|
||||
/**
|
||||
* 校验操作
|
||||
*
|
||||
* @param request request
|
||||
* @param response response
|
||||
* @param currentContext currentContext
|
||||
* @param callback 校验后操作
|
||||
*/
|
||||
public void validate(HttpServletRequest request, HttpServletResponse response, ValidateCallback callback) {
|
||||
RequestContext currentContext = RequestContext.getCurrentContext();
|
||||
currentContext.setRequest(RequestUtil.wrapRequest(request));
|
||||
currentContext.setResponse(response);
|
||||
public void validate(RequestContext currentContext, ValidateCallback callback) {
|
||||
// 解析参数
|
||||
ApiParam param = ZuulContext.getApiParam();
|
||||
if (param == null) {
|
||||
@@ -56,18 +51,27 @@ public class ValidateService {
|
||||
// 验证操作,这里有负责验证签名参数
|
||||
try {
|
||||
validator.validate(param);
|
||||
this.afterValidate(currentContext, param);
|
||||
} catch (Exception e) {
|
||||
error = e;
|
||||
}
|
||||
param.fitNameVersion();
|
||||
if (error == null) {
|
||||
callback.onSuccess(currentContext);
|
||||
} else {
|
||||
callback.onError(currentContext, param, error);
|
||||
if (callback != null) {
|
||||
if (error == null) {
|
||||
callback.onSuccess(currentContext);
|
||||
} else {
|
||||
callback.onError(currentContext, param, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void afterValidate(RequestContext currentContext, ApiParam param) {
|
||||
RouteInterceptorUtil.runPreRoute(currentContext, param, context -> {
|
||||
DefaultRouteInterceptorContext defaultRouteInterceptorContext = (DefaultRouteInterceptorContext) context;
|
||||
defaultRouteInterceptorContext.setRequestDataSize(currentContext.getRequest().getContentLengthLong());
|
||||
currentContext.set(SopConstants.CACHE_ROUTE_INTERCEPTOR_CONTEXT, context);
|
||||
});
|
||||
}
|
||||
|
||||
public interface ValidateCallback {
|
||||
/**
|
||||
|
@@ -10,13 +10,13 @@ import com.gitee.sop.gatewaycommon.zuul.controller.ConfigChannelController;
|
||||
import com.gitee.sop.gatewaycommon.zuul.controller.ErrorLogController;
|
||||
import com.gitee.sop.gatewaycommon.zuul.controller.ZuulErrorController;
|
||||
import com.gitee.sop.gatewaycommon.zuul.controller.ZuulIndexController;
|
||||
import com.gitee.sop.gatewaycommon.zuul.controller.ZuulMonitorController;
|
||||
import com.gitee.sop.gatewaycommon.zuul.filter.ErrorFilter;
|
||||
import com.gitee.sop.gatewaycommon.zuul.filter.FormBodyWrapperFilterExt;
|
||||
import com.gitee.sop.gatewaycommon.zuul.filter.PostResultFilter;
|
||||
import com.gitee.sop.gatewaycommon.zuul.filter.PreHttpServletRequestWrapperFilter;
|
||||
import com.gitee.sop.gatewaycommon.zuul.filter.PreLimitFilter;
|
||||
import com.gitee.sop.gatewaycommon.zuul.filter.PreParameterFormatterFilter;
|
||||
import com.gitee.sop.gatewaycommon.zuul.filter.PreValidateFilter;
|
||||
import com.gitee.sop.gatewaycommon.zuul.filter.Servlet30WrapperFilterExt;
|
||||
import com.gitee.sop.gatewaycommon.zuul.route.SopRouteLocator;
|
||||
import com.gitee.sop.gatewaycommon.zuul.route.ZuulForwardChooser;
|
||||
@@ -57,6 +57,11 @@ public class BaseZuulConfiguration extends AbstractConfiguration {
|
||||
return new ZuulIndexController();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ZuulMonitorController zuulMonitorController() {
|
||||
return new ZuulMonitorController();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
ParamBuilder<RequestContext> paramBuilder() {
|
||||
@@ -120,14 +125,6 @@ public class BaseZuulConfiguration extends AbstractConfiguration {
|
||||
return new ZuulRouteCache(zuulRouteRepository);
|
||||
}
|
||||
|
||||
/**
|
||||
* 前置校验
|
||||
*/
|
||||
@Bean
|
||||
PreValidateFilter preValidateFilter() {
|
||||
return new PreValidateFilter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
ValidateService validateService() {
|
||||
return new ValidateService();
|
||||
|
@@ -1,17 +1,18 @@
|
||||
package com.gitee.sop.gatewaycommon.zuul.controller;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.SopConstants;
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||
import com.gitee.sop.gatewaycommon.util.RequestUtil;
|
||||
import com.gitee.sop.gatewaycommon.zuul.ValidateService;
|
||||
import com.gitee.sop.gatewaycommon.zuul.ZuulContext;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* zuul网关入口
|
||||
@@ -52,26 +53,32 @@ public class ZuulIndexController {
|
||||
*/
|
||||
@RequestMapping("/")
|
||||
public void index(HttpServletRequest request, HttpServletResponse response) {
|
||||
validateService.validate(request, response, callback);
|
||||
RequestContext currentContext = RequestContext.getCurrentContext();
|
||||
currentContext.setRequest(RequestUtil.wrapRequest(request));
|
||||
currentContext.setResponse(response);
|
||||
validateService.validate(currentContext, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* restful入口
|
||||
* @param request
|
||||
* @param response
|
||||
* @throws ServletException
|
||||
* @throws IOException
|
||||
*
|
||||
* @param request request
|
||||
* @param response response
|
||||
*/
|
||||
@RequestMapping("/rest/**")
|
||||
public void rest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
public void rest(HttpServletRequest request, HttpServletResponse response) {
|
||||
RequestContext currentContext = RequestContext.getCurrentContext();
|
||||
currentContext.setRequest(RequestUtil.wrapRequest(request));
|
||||
currentContext.setResponse(response);
|
||||
|
||||
String url = request.getRequestURL().toString();
|
||||
int index = url.indexOf(restPath);
|
||||
// 取/rest的后面部分
|
||||
String path = url.substring(index + restPath.length());
|
||||
request.setAttribute(SopConstants.REDIRECT_METHOD_KEY, path);
|
||||
request.setAttribute(SopConstants.REDIRECT_VERSION_KEY, EMPTY_VERSION);
|
||||
request.setAttribute(SopConstants.SOP_NOT_MERGE, true);
|
||||
request.getRequestDispatcher(this.path).forward(request, response);
|
||||
ApiParam apiParam = ApiParam.createRestfulApiParam(path);
|
||||
ZuulContext.setApiParam(apiParam);
|
||||
|
||||
validateService.validate(currentContext, callback);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,21 @@
|
||||
package com.gitee.sop.gatewaycommon.zuul.controller;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.support.BaseMonitorController;
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||
import com.gitee.sop.gatewaycommon.util.RequestUtil;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@RestController
|
||||
public class ZuulMonitorController extends BaseMonitorController<HttpServletRequest> {
|
||||
@Override
|
||||
protected ApiParam getApiParam(HttpServletRequest request) {
|
||||
Map<String, String> params = RequestUtil.convertRequestParamsToMap(request);
|
||||
return ApiParam.build(params);
|
||||
}
|
||||
}
|
@@ -23,17 +23,14 @@ public abstract class BaseZuulFilter extends ZuulFilter {
|
||||
/** 签名验证过滤 */
|
||||
public static final int PRE_VALIDATE_FILTER_ORDER = -1000;
|
||||
|
||||
/** 参数格式化过滤器 */
|
||||
public static final int PRE_PARAMETER_FORMATTER_FILTER_ORDER = PRE_VALIDATE_FILTER_ORDER + 1;
|
||||
|
||||
/** 权限验证过滤 */
|
||||
public static final int PRE_ROUTE_PERMISSION_FILTER_ORDER = PRE_VALIDATE_FILTER_ORDER + 100;
|
||||
|
||||
/** 限流过滤 */
|
||||
public static final int PRE_LIMIT_FILTER_ORDER = PRE_ROUTE_PERMISSION_FILTER_ORDER + 100;
|
||||
public static final int PRE_LIMIT_FILTER_ORDER = -990;
|
||||
|
||||
/** 参数格式化过滤器 */
|
||||
public static final int PRE_PARAMETER_FORMATTER_FILTER_ORDER = -980;
|
||||
|
||||
/** 灰度发布过滤器 */
|
||||
public static final int PRE_ENV_GRAY_FILTER_ORDER = PRE_LIMIT_FILTER_ORDER + 100;
|
||||
public static final int PRE_ENV_GRAY_FILTER_ORDER = -970;
|
||||
|
||||
private Integer filterOrder;
|
||||
|
||||
|
@@ -1,23 +1,17 @@
|
||||
package com.gitee.sop.gatewaycommon.zuul.filter;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||
import com.gitee.sop.gatewaycommon.param.ParamBuilder;
|
||||
import com.gitee.sop.gatewaycommon.zuul.ZuulContext;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* 校验工作转移到了 com.gitee.sop.gateway.controller.RedirectController
|
||||
*
|
||||
* <p>
|
||||
* 将校验工作提前,如果在zuul过滤器中校验,抛出异常将会打印非常多的日志,并且无法实现自定义返回结果。
|
||||
*
|
||||
* @deprecated see {@link com.gitee.sop.gatewaycommon.zuul.ValidateService}
|
||||
* @author tanghc
|
||||
*/
|
||||
@Deprecated
|
||||
public class PreValidateFilter extends BaseZuulFilter {
|
||||
|
||||
@Autowired
|
||||
private ParamBuilder<RequestContext> paramBuilder;
|
||||
|
||||
@Override
|
||||
protected FilterType getFilterType() {
|
||||
return FilterType.PRE;
|
||||
@@ -30,11 +24,6 @@ public class PreValidateFilter extends BaseZuulFilter {
|
||||
|
||||
@Override
|
||||
protected Object doRun(RequestContext requestContext) {
|
||||
ApiParam param = ZuulContext.getApiParam();
|
||||
if (param == null) {
|
||||
param = paramBuilder.build(requestContext);
|
||||
ZuulContext.setApiParam(param);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,5 @@
|
||||
package com.gitee.sop.gatewaycommon.zuul.param;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.SopConstants;
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||
import com.gitee.sop.gatewaycommon.param.BaseParamBuilder;
|
||||
import com.gitee.sop.gatewaycommon.util.RequestUtil;
|
||||
@@ -63,11 +62,6 @@ public class ZuulParamBuilder extends BaseParamBuilder<RequestContext> {
|
||||
|
||||
@Override
|
||||
protected void processApiParam(ApiParam apiParam, RequestContext ctx) {
|
||||
HttpServletRequest request = ctx.getRequest();
|
||||
String method = (String) request.getAttribute(SopConstants.REDIRECT_METHOD_KEY);
|
||||
String version = (String) request.getAttribute(SopConstants.REDIRECT_VERSION_KEY);
|
||||
apiParam.setRestName(method);
|
||||
apiParam.setRestVersion(version);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ package com.gitee.sop.gatewaycommon.zuul.result;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptorContext;
|
||||
import com.gitee.sop.gatewaycommon.bean.SopConstants;
|
||||
import com.gitee.sop.gatewaycommon.exception.ApiException;
|
||||
import com.gitee.sop.gatewaycommon.message.Error;
|
||||
@@ -17,6 +18,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
@@ -24,15 +26,6 @@ import java.util.Locale;
|
||||
@Slf4j
|
||||
public class ZuulResultExecutor extends BaseExecutorAdapter<RequestContext, String> implements ResultExecutorForZuul {
|
||||
|
||||
@Override
|
||||
protected boolean isMergeResult(RequestContext request) {
|
||||
Object notMerge = request.getRequest().getAttribute(SopConstants.SOP_NOT_MERGE);
|
||||
if (notMerge != null) {
|
||||
return false;
|
||||
}
|
||||
return super.isMergeResult(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getResponseStatus(RequestContext requestContext) {
|
||||
List<Pair<String, String>> bizHeaders = requestContext.getZuulResponseHeaders();
|
||||
@@ -46,21 +39,11 @@ public class ZuulResultExecutor extends BaseExecutorAdapter<RequestContext, Stri
|
||||
|
||||
@Override
|
||||
public String getResponseErrorMessage(RequestContext requestContext) {
|
||||
List<Pair<String, String>> bizHeaders = requestContext.getZuulResponseHeaders();
|
||||
int index = -1;
|
||||
String errorMsg = null;
|
||||
for (int i = 0; i < bizHeaders.size(); i++) {
|
||||
Pair<String, String> header = bizHeaders.get(i);
|
||||
if (SopConstants.X_SERVICE_ERROR_MESSAGE.equals(header.first())) {
|
||||
errorMsg = header.second();
|
||||
index = i;
|
||||
break;
|
||||
return getHeader(requestContext, SopConstants.X_SERVICE_ERROR_MESSAGE, (index)->{
|
||||
if (index > -1) {
|
||||
requestContext.getZuulResponseHeaders().remove(index);
|
||||
}
|
||||
}
|
||||
if (index > -1) {
|
||||
requestContext.getZuulResponseHeaders().remove(index);
|
||||
}
|
||||
return errorMsg;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -73,6 +56,11 @@ public class ZuulResultExecutor extends BaseExecutorAdapter<RequestContext, Stri
|
||||
return requestContext.getRequest().getLocale();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RouteInterceptorContext getRouteInterceptorContext(RequestContext requestContext) {
|
||||
return (RouteInterceptorContext) requestContext.get(SopConstants.CACHE_ROUTE_INTERCEPTOR_CONTEXT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String buildErrorResult(RequestContext requestContext, Throwable throwable) {
|
||||
Locale locale = getLocale(requestContext);
|
||||
@@ -99,4 +87,22 @@ public class ZuulResultExecutor extends BaseExecutorAdapter<RequestContext, Stri
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
private String getHeader(RequestContext requestContext, String name, Consumer<Integer> after) {
|
||||
List<Pair<String, String>> bizHeaders = requestContext.getZuulResponseHeaders();
|
||||
int index = -1;
|
||||
String value = null;
|
||||
for (int i = 0; i < bizHeaders.size(); i++) {
|
||||
Pair<String, String> header = bizHeaders.get(i);
|
||||
if (name.equals(header.first())) {
|
||||
value = header.second();
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (after != null) {
|
||||
after.accept(index);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
@@ -45,24 +45,15 @@ public class SopRouteLocator implements RouteLocator, Ordered {
|
||||
/**
|
||||
* 这里决定使用哪个路由
|
||||
*
|
||||
* @param path
|
||||
* @param path 当前请求路径
|
||||
* @return 返回跳转的路由
|
||||
*/
|
||||
@Override
|
||||
public Route getMatchingRoute(String path) {
|
||||
ApiParam param = ZuulContext.getApiParam();
|
||||
String nameVersion = param.fetchNameVersion();
|
||||
ZuulTargetRoute zuulTargetRoute = zuulRouteRepository.get(nameVersion);
|
||||
if (zuulTargetRoute == null) {
|
||||
return null;
|
||||
}
|
||||
Route targetRouteDefinition = zuulTargetRoute.getTargetRouteDefinition();
|
||||
ForwardInfo forwardInfo = zuulForwardChooser.getForwardInfo(RequestContext.getCurrentContext());
|
||||
String forwardPath = forwardInfo.getPath();
|
||||
targetRouteDefinition.setPath(forwardPath);
|
||||
String versionInHead = forwardInfo.getVersion();
|
||||
RequestContext.getCurrentContext().addZuulRequestHeader(ParamNames.HEADER_VERSION_NAME, versionInHead);
|
||||
return targetRouteDefinition;
|
||||
String version = forwardInfo.getVersion();
|
||||
RequestContext.getCurrentContext().addZuulRequestHeader(ParamNames.HEADER_VERSION_NAME, version);
|
||||
return (Route)forwardInfo.getTargetRoute().getTargetRouteDefinition();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -0,0 +1,62 @@
|
||||
package com.gitee.sop.gatewaycommon;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.util.LoadBalanceUtil;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 轮询选择一台机器。
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
public class RoundRobinTest extends TestCase {
|
||||
|
||||
public void testDo() {
|
||||
String serviceId = "order-service";
|
||||
List<String> serverList = new ArrayList<>(Arrays.asList("server1", "server2", "server3"));
|
||||
System.out.println(chooseRoundRobinServer(serviceId, serverList));
|
||||
System.out.println(chooseRoundRobinServer(serviceId, serverList));
|
||||
System.out.println(chooseRoundRobinServer(serviceId, serverList));
|
||||
System.out.println(chooseRoundRobinServer(serviceId, serverList));
|
||||
System.out.println(chooseRoundRobinServer(serviceId, serverList));
|
||||
}
|
||||
|
||||
public void testAdd() {
|
||||
String serviceId = "order-service";
|
||||
List<String> serverList = new ArrayList<>(Arrays.asList("server1", "server2", "server3"));
|
||||
System.out.println(chooseRoundRobinServer(serviceId, serverList));
|
||||
System.out.println(chooseRoundRobinServer(serviceId, serverList));
|
||||
// 中途添加一个服务器
|
||||
serverList.add("server4");
|
||||
System.out.println(chooseRoundRobinServer(serviceId, serverList));
|
||||
System.out.println(chooseRoundRobinServer(serviceId, serverList));
|
||||
System.out.println(chooseRoundRobinServer(serviceId, serverList));
|
||||
}
|
||||
|
||||
public void testRemove() {
|
||||
String serviceId = "order-service";
|
||||
List<String> serverList = new ArrayList<>(Arrays.asList("server1", "server2", "server3"));
|
||||
System.out.println(chooseRoundRobinServer(serviceId, serverList));
|
||||
System.out.println(chooseRoundRobinServer(serviceId, serverList));
|
||||
// 中途减少一台服务器
|
||||
serverList.remove(2);
|
||||
System.out.println(chooseRoundRobinServer(serviceId, serverList));
|
||||
System.out.println(chooseRoundRobinServer(serviceId, serverList));
|
||||
System.out.println(chooseRoundRobinServer(serviceId, serverList));
|
||||
}
|
||||
|
||||
/**
|
||||
* 轮询选择一台机器。
|
||||
* 假设有N台服务器:S = {S1, S2, …, Sn},一个指示变量i表示上一次选择的服务器ID。变量i被初始化为N-1。
|
||||
*
|
||||
* @param serviceId serviceId
|
||||
* @param servers 服务器列表
|
||||
* @return 返回一台服务器实例
|
||||
*/
|
||||
private <T> T chooseRoundRobinServer(String serviceId, List<T> servers) {
|
||||
return LoadBalanceUtil.chooseByRoundRobin(serviceId, servers);
|
||||
}
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
package com.gitee.sop.gateway.interceptor;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptor;
|
||||
import com.gitee.sop.gatewaycommon.interceptor.RouteInterceptorContext;
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 演示拦截器
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
@Component
|
||||
public class MyRouteInterceptor implements RouteInterceptor {
|
||||
|
||||
@Override
|
||||
public void preRoute(RouteInterceptorContext context) {
|
||||
ApiParam apiParam = context.getApiParam();
|
||||
System.out.println("请求接口:" + apiParam.fetchNameVersion());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterRoute(RouteInterceptorContext context) {
|
||||
System.out.println("请求成功,微服务返回结果:" + context.getServiceResult());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
package com.gitee.sop.gateway.manager;
|
||||
|
||||
import com.gitee.fastmybatis.core.query.Query;
|
||||
import com.gitee.sop.gateway.entity.ConfigLimit;
|
||||
import com.gitee.sop.gateway.mapper.ConfigLimitMapper;
|
||||
import com.gitee.sop.gatewaycommon.bean.ChannelMsg;
|
||||
import com.gitee.sop.gatewaycommon.bean.ConfigLimitDto;
|
||||
@@ -37,7 +38,7 @@ public class DbLimitConfigManager extends DefaultLimitConfigManager {
|
||||
|
||||
}
|
||||
|
||||
protected void putVal(Object object) {
|
||||
protected void putVal(ConfigLimit object) {
|
||||
ConfigLimitDto configLimitDto = new ConfigLimitDto();
|
||||
MyBeanUtil.copyPropertiesIgnoreNull(object, configLimitDto);
|
||||
this.update(configLimitDto);
|
||||
|
@@ -42,7 +42,7 @@ public class DbRouteConfigManager extends DefaultRouteConfigManager {
|
||||
|
||||
@Override
|
||||
public void process(ChannelMsg channelMsg) {
|
||||
final RouteConfig routeConfig = channelMsg.toObject( RouteConfig.class);
|
||||
final RouteConfig routeConfig = channelMsg.toObject(RouteConfig.class);
|
||||
switch (channelMsg.getOperation()) {
|
||||
case "reload":
|
||||
log.info("重新加载路由配置信息,routeConfigDto:{}", routeConfig);
|
||||
|
Reference in New Issue
Block a user