mirror of
https://gitee.com/durcframework/SOP.git
synced 2025-08-11 21:57:56 +08:00
新增限流功能
This commit is contained in:
@@ -57,8 +57,8 @@
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">限流策略</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="radio" name="type" value="1" title="漏桶策略" lay-filter="limitTypeFilter">
|
||||
<input type="radio" name="type" value="2" title="令牌桶策略" lay-filter="limitTypeFilter">
|
||||
<input type="radio" name="limitType" value="1" title="漏桶策略" lay-filter="limitTypeFilter">
|
||||
<input type="radio" name="limitType" value="2" title="令牌桶策略" lay-filter="limitTypeFilter">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item limit-type type1">
|
||||
|
@@ -51,7 +51,7 @@ lib.importJs('../../assets/js/routerole.js')
|
||||
});
|
||||
|
||||
function checkUpdateForm(formData) {
|
||||
var type = formData.type;
|
||||
var type = formData.limitType;
|
||||
if (type == 1) {
|
||||
if (!/^[1-9]\d*$/.test(formData.execCountPerSecond)) {
|
||||
layer.alert('每秒可处理请求数必须大于0')
|
||||
@@ -118,6 +118,8 @@ lib.importJs('../../assets/js/routerole.js')
|
||||
}
|
||||
}
|
||||
|
||||
var tipType = '<a href="#" onclick="showLimitTypeTip()" title="说明"><i class="layui-icon layui-icon-help"></i></a>'
|
||||
|
||||
function renderTable(postData) {
|
||||
var limitTable = table.render({
|
||||
elem: '#limitTable'
|
||||
@@ -129,8 +131,8 @@ lib.importJs('../../assets/js/routerole.js')
|
||||
{field: 'name', title: '接口名', width: 200}
|
||||
, {field: 'version', title: '版本号', width: 80}
|
||||
, {
|
||||
field: 'type', title: '限流策略', width: 80, templet: function (row) {
|
||||
return LIMIT_TYPE[row.type + ''];
|
||||
field: 'limitType', title: '限流策略 ' + tipType, width: 100, templet: function (row) {
|
||||
return LIMIT_TYPE[row.limitType + ''];
|
||||
}
|
||||
}
|
||||
, {
|
||||
@@ -139,11 +141,11 @@ lib.importJs('../../assets/js/routerole.js')
|
||||
return '--'
|
||||
}
|
||||
var html = [];
|
||||
if (row.type == 1) {
|
||||
if (row.limitType == 1) {
|
||||
html.push('每秒可处理请求数:' + row.execCountPerSecond);
|
||||
html.push('subCode:' + row.limitCode);
|
||||
html.push('subMsg:' + row.limitMsg);
|
||||
} else if(row.type == 2) {
|
||||
} else if(row.limitType == 2) {
|
||||
html.push('令牌桶容量:' + row.tokenBucketCount);
|
||||
}
|
||||
return html.join(',');
|
||||
@@ -173,7 +175,7 @@ lib.importJs('../../assets/js/routerole.js')
|
||||
|
||||
updateForm.setData(data);
|
||||
$('.limit-type').hide();
|
||||
$('.type' + data.type).show();
|
||||
$('.type' + data.limitType).show();
|
||||
|
||||
layer.open({
|
||||
type: 1
|
||||
@@ -191,4 +193,20 @@ lib.importJs('../../assets/js/routerole.js')
|
||||
|
||||
RouteRole.loadAllRole(form, 'roleArea');
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
function showLimitTypeTip() {
|
||||
var leakyRemark = '漏桶策略:每秒处理固定数量的请求,超出请求返回错误信息。';
|
||||
var tokenRemark = '令牌桶策略:每秒放置固定数量的令牌数,不足的令牌数做等待处理,直到拿到令牌为止。';
|
||||
var content = '<div style="font-size: 14px;">'
|
||||
+ leakyRemark
|
||||
+ '<br>'
|
||||
+ tokenRemark
|
||||
+ '</div>';
|
||||
|
||||
layer.open({
|
||||
title: '限流策略说明'
|
||||
,area: ['600px', 'auto']
|
||||
,content: content
|
||||
});
|
||||
}
|
@@ -88,7 +88,7 @@ public class LimitApi {
|
||||
|
||||
private ConfigRouteLimit getDefaultLimit() {
|
||||
ConfigRouteLimit configRouteLimit = new ConfigRouteLimit();
|
||||
configRouteLimit.setType(LimitEnum.TYPE_LEAKY_BUCKET.getVal());
|
||||
configRouteLimit.setLimitType(LimitEnum.TYPE_LEAKY_BUCKET.getVal());
|
||||
configRouteLimit.setLimitStatus(LimitEnum.STATUS_CLOSE.getVal());
|
||||
return configRouteLimit;
|
||||
}
|
||||
|
@@ -21,10 +21,10 @@ public class LimitParam {
|
||||
@NotBlank(message = "serviceId can not null")
|
||||
private String serviceId;
|
||||
|
||||
/** 限流策略,1:漏桶策略,2:令牌桶策略, 数据库字段:type */
|
||||
/** 限流策略,1:漏桶策略,2:令牌桶策略, 数据库字段:limit_type */
|
||||
@ApiDocField(description = "限流策略,1:漏桶策略,2:令牌桶策略")
|
||||
@NotNull
|
||||
private Byte type;
|
||||
private Byte limitType;
|
||||
|
||||
/** 每秒可处理请求数, 数据库字段:exec_count_per_second */
|
||||
@ApiDocField(description = "每秒可处理请求数")
|
||||
|
@@ -11,7 +11,7 @@ import lombok.Data;
|
||||
@Data
|
||||
public class LimitVO {
|
||||
@ApiDocField(description = "是否存在记录")
|
||||
private int hasRecord;
|
||||
private Integer hasRecord;
|
||||
|
||||
@ApiDocField(description = "路由id")
|
||||
private String routeId;
|
||||
@@ -32,10 +32,10 @@ public class LimitVO {
|
||||
private String serviceId;
|
||||
|
||||
/**
|
||||
* 限流策略,1:漏桶策略,2:令牌桶策略, 数据库字段:type
|
||||
* 限流策略,1:漏桶策略,2:令牌桶策略, 数据库字段:limit_type
|
||||
*/
|
||||
@ApiDocField(description = "限流策略,1:漏桶策略,2:令牌桶策略")
|
||||
private Byte type;
|
||||
private Byte limitType;
|
||||
|
||||
/**
|
||||
* 每秒可处理请求数, 数据库字段:exec_count_per_second
|
||||
|
@@ -10,8 +10,8 @@ public class RouteConfigDto {
|
||||
|
||||
private String routeId;
|
||||
|
||||
/** 限流策略,1:漏桶策略,2:令牌桶策略, 数据库字段:type */
|
||||
private Byte type;
|
||||
/** 限流策略,1:漏桶策略,2:令牌桶策略, 数据库字段:limit_type */
|
||||
private Byte limitType;
|
||||
|
||||
/** 每秒可处理请求数, 数据库字段:exec_count_per_second */
|
||||
private Integer execCountPerSecond;
|
||||
|
@@ -32,8 +32,8 @@ public class ConfigRouteLimit {
|
||||
/** serviceId, 数据库字段:service_id */
|
||||
private String serviceId;
|
||||
|
||||
/** 限流策略,1:漏桶策略,2:令牌桶策略, 数据库字段:type */
|
||||
private Byte type;
|
||||
/** 限流策略,1:漏桶策略,2:令牌桶策略, 数据库字段:limit_type */
|
||||
private Byte limitType;
|
||||
|
||||
/** 每秒可处理请求数, 数据库字段:exec_count_per_second */
|
||||
private Integer execCountPerSecond;
|
||||
|
@@ -138,6 +138,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.12.4</version>
|
||||
<configuration>
|
||||
<skipTests>true</skipTests>
|
||||
</configuration>
|
||||
@@ -145,6 +146,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.1</version>
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
@@ -164,24 +166,6 @@
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.9</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
<!-- JDK8必须使用下面的配置 -->
|
||||
<configuration>
|
||||
<additionalparam>-Xdoclint:none</additionalparam>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
@@ -8,9 +8,8 @@
|
||||
<version>1.2.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<groupId>com.gitee.sop</groupId>
|
||||
<artifactId>sop-gateway-common</artifactId>
|
||||
<version>${parent.version}</version>
|
||||
<version>1.2.0-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>sop-gateway-common</name>
|
||||
@@ -21,6 +20,12 @@
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>27.1-jre</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-fileupload</groupId>
|
||||
<artifactId>commons-fileupload</artifactId>
|
||||
|
@@ -3,6 +3,8 @@ package com.gitee.sop.gatewaycommon.bean;
|
||||
import com.gitee.sop.gatewaycommon.gateway.param.GatewayParamBuilder;
|
||||
import com.gitee.sop.gatewaycommon.gateway.result.GatewayResult;
|
||||
import com.gitee.sop.gatewaycommon.gateway.result.GatewayResultExecutor;
|
||||
import com.gitee.sop.gatewaycommon.limit.DefaultLimitManager;
|
||||
import com.gitee.sop.gatewaycommon.limit.LimitManager;
|
||||
import com.gitee.sop.gatewaycommon.manager.DefaultIsvRoutePermissionManager;
|
||||
import com.gitee.sop.gatewaycommon.manager.DefaultRouteConfigManager;
|
||||
import com.gitee.sop.gatewaycommon.manager.IsvRoutePermissionManager;
|
||||
@@ -101,6 +103,11 @@ public class ApiConfig {
|
||||
*/
|
||||
private RouteConfigManager routeConfigManager = new DefaultRouteConfigManager();
|
||||
|
||||
/**
|
||||
* 限流管理
|
||||
*/
|
||||
private LimitManager limitManager = new DefaultLimitManager();
|
||||
|
||||
// -------- fields ---------
|
||||
|
||||
/**
|
||||
@@ -125,6 +132,11 @@ public class ApiConfig {
|
||||
*/
|
||||
private int timeoutSeconds = 60 * 5;
|
||||
|
||||
/**
|
||||
* 是否开启限流功能
|
||||
*/
|
||||
private boolean openLimit = true;
|
||||
|
||||
public void addAppSecret(Map<String, String> appSecretPair) {
|
||||
for (Map.Entry<String, String> entry : appSecretPair.entrySet()) {
|
||||
this.isvManager.update(new IsvDefinition(entry.getKey(), entry.getValue()));
|
||||
|
@@ -7,19 +7,19 @@ public interface Isv {
|
||||
|
||||
/**
|
||||
* appKey
|
||||
* @return
|
||||
* @return 返回appKey
|
||||
*/
|
||||
String getAppKey();
|
||||
|
||||
/**
|
||||
* 秘钥
|
||||
* @return
|
||||
* @return 返回秘钥
|
||||
*/
|
||||
String getSecretInfo();
|
||||
|
||||
/**
|
||||
* 0启用,1禁用
|
||||
* @return
|
||||
* @return 返回状态
|
||||
*/
|
||||
Byte getStatus();
|
||||
}
|
||||
|
@@ -1,6 +1,16 @@
|
||||
package com.gitee.sop.gatewaycommon.bean;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.util.concurrent.RateLimiter;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
@@ -13,33 +23,71 @@ public class RouteConfig {
|
||||
|
||||
private String routeId;
|
||||
|
||||
/** 限流策略,1:漏桶策略,2:令牌桶策略, 数据库字段:type */
|
||||
private Byte type;
|
||||
/** 限流策略,1:漏桶策略,2:令牌桶策略, 数据库字段:limit_type */
|
||||
private Byte limitType = 1;
|
||||
|
||||
/** 每秒可处理请求数, 数据库字段:exec_count_per_second */
|
||||
private Integer execCountPerSecond;
|
||||
private Integer execCountPerSecond = 10;
|
||||
|
||||
/** 返回的错误码, 数据库字段:limit_code */
|
||||
private String limitCode;
|
||||
private String limitCode = "isp.service-busy";
|
||||
|
||||
/** 返回的错误信息, 数据库字段:limit_msg */
|
||||
private String limitMsg;
|
||||
private String limitMsg = "服务繁忙,请稍后再试";
|
||||
|
||||
/** 令牌桶容量, 数据库字段:token_bucket_count */
|
||||
private Integer tokenBucketCount;
|
||||
private Integer tokenBucketCount = 10;
|
||||
|
||||
/** 限流开启状态,1:开启,0关闭, 数据库字段:limit_status */
|
||||
private Byte limitStatus = LIMIT_STATUS_CLOSE;
|
||||
|
||||
/**
|
||||
* 状态,0:待审核,1:启用,2:禁用
|
||||
* 状态,0:待审核,1:启用,2:禁用。默认启用
|
||||
*/
|
||||
private Byte status = STATUS_ENABLE;
|
||||
|
||||
/**
|
||||
* 是否启用
|
||||
* 漏桶计数器
|
||||
*/
|
||||
private LoadingCache<Long, AtomicLong> counter = CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(2, TimeUnit.SECONDS)
|
||||
.build(new CacheLoader<Long, AtomicLong>() {
|
||||
@Override
|
||||
public AtomicLong load(Long seconds) throws Exception {
|
||||
return new AtomicLong(0);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 令牌桶
|
||||
*/
|
||||
@Getter(AccessLevel.PRIVATE)
|
||||
@Setter(AccessLevel.PRIVATE)
|
||||
private volatile RateLimiter rateLimiter;
|
||||
|
||||
public synchronized void initRateLimiter() {
|
||||
rateLimiter = RateLimiter.create(tokenBucketCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取令牌桶
|
||||
* @return
|
||||
*/
|
||||
public synchronized RateLimiter fetchRateLimiter() {
|
||||
if (rateLimiter == null) {
|
||||
synchronized (this.routeId) {
|
||||
if (rateLimiter == null) {
|
||||
rateLimiter = RateLimiter.create(tokenBucketCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
return rateLimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否启用
|
||||
* @return true:启用
|
||||
*/
|
||||
public boolean enable() {
|
||||
return status == STATUS_ENABLE;
|
||||
}
|
||||
|
@@ -10,8 +10,8 @@ public class RouteConfigDto {
|
||||
|
||||
private String routeId;
|
||||
|
||||
/** 限流策略,1:漏桶策略,2:令牌桶策略, 数据库字段:type */
|
||||
private Byte type;
|
||||
/** 限流策略,1:漏桶策略,2:令牌桶策略, 数据库字段:limit_type */
|
||||
private Byte limitType;
|
||||
|
||||
/** 每秒可处理请求数, 数据库字段:exec_count_per_second */
|
||||
private Integer execCountPerSecond;
|
||||
|
@@ -33,9 +33,8 @@ public class BaseGatewayConfiguration extends AbstractConfiguration {
|
||||
/**
|
||||
* 自定义异常处理[@@]注册Bean时依赖的Bean,会从容器中直接获取,所以直接注入即可
|
||||
*
|
||||
* @param viewResolversProvider
|
||||
* @param serverCodecConfigurer
|
||||
* @return
|
||||
* @param viewResolversProvider viewResolversProvider
|
||||
* @param serverCodecConfigurer serverCodecConfigurer
|
||||
*/
|
||||
@Primary
|
||||
@Bean
|
||||
@@ -52,8 +51,6 @@ public class BaseGatewayConfiguration extends AbstractConfiguration {
|
||||
|
||||
/**
|
||||
* 处理返回结果
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
GatewayModifyResponseGatewayFilter gatewayModifyResponseGatewayFilter() {
|
||||
@@ -62,8 +59,6 @@ public class BaseGatewayConfiguration extends AbstractConfiguration {
|
||||
|
||||
/**
|
||||
* 读取post请求参数
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
ReadBodyRoutePredicateFactory readBodyRoutePredicateFactory() {
|
||||
|
@@ -0,0 +1,67 @@
|
||||
package com.gitee.sop.gatewaycommon.gateway.filter;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiContext;
|
||||
import com.gitee.sop.gatewaycommon.bean.RouteConfig;
|
||||
import com.gitee.sop.gatewaycommon.bean.SopConstants;
|
||||
import com.gitee.sop.gatewaycommon.exception.ApiException;
|
||||
import com.gitee.sop.gatewaycommon.limit.LimitManager;
|
||||
import com.gitee.sop.gatewaycommon.limit.LimitType;
|
||||
import com.gitee.sop.gatewaycommon.manager.RouteConfigManager;
|
||||
import com.gitee.sop.gatewaycommon.message.ErrorImpl;
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||
import com.gitee.sop.gatewaycommon.param.ParamNames;
|
||||
import com.gitee.sop.gatewaycommon.util.RouteUtil;
|
||||
import com.gitee.sop.gatewaycommon.validate.Validator;
|
||||
import com.gitee.sop.gatewaycommon.zuul.ZuulContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Slf4j
|
||||
public class LimitFilter implements GlobalFilter, Ordered {
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
ApiConfig apiConfig = ApiConfig.getInstance();
|
||||
// 限流功能未开启,直接返回
|
||||
if (!apiConfig.isOpenLimit()) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
Map<String, ?> apiParam = exchange.getAttribute(SopConstants.CACHE_API_PARAM);
|
||||
String routeId = apiParam.get(ParamNames.API_NAME).toString() + apiParam.get(ParamNames.VERSION_NAME);
|
||||
RouteConfigManager routeConfigManager = apiConfig.getRouteConfigManager();
|
||||
RouteConfig routeConfig = routeConfigManager.get(routeId);
|
||||
if (routeConfig == null) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
// 某个路由限流功能未开启
|
||||
if (routeConfig.getLimitStatus() == RouteConfig.LIMIT_STATUS_CLOSE) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
byte limitType = routeConfig.getLimitType().byteValue();
|
||||
LimitManager limitManager = ApiConfig.getInstance().getLimitManager();
|
||||
if (limitType == LimitType.LEAKY_BUCKET.getType()) {
|
||||
boolean acquire = limitManager.acquire(routeConfig);
|
||||
if (!acquire) {
|
||||
throw new ApiException(new ErrorImpl(routeConfig.getLimitCode(), routeConfig.getLimitMsg()));
|
||||
}
|
||||
} else if (limitType == LimitType.TOKEN_BUCKET.getType()) {
|
||||
limitManager.acquireToken(routeConfig);
|
||||
}
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Orders.LIMIT_ORDER;
|
||||
}
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
package com.gitee.sop.gatewaycommon.gateway.filter;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public class Orders {
|
||||
/** 验证拦截器order */
|
||||
public static final int VALIDATE_ORDER = Ordered.HIGHEST_PRECEDENCE + 1000;
|
||||
|
||||
/** 验证拦截器order */
|
||||
public static final int LIMIT_ORDER = VALIDATE_ORDER + 1;
|
||||
}
|
@@ -2,9 +2,13 @@ package com.gitee.sop.gatewaycommon.gateway.filter;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiContext;
|
||||
import com.gitee.sop.gatewaycommon.bean.RouteConfig;
|
||||
import com.gitee.sop.gatewaycommon.bean.SopConstants;
|
||||
import com.gitee.sop.gatewaycommon.exception.ApiException;
|
||||
import com.gitee.sop.gatewaycommon.manager.RouteConfigManager;
|
||||
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||
import com.gitee.sop.gatewaycommon.util.RouteUtil;
|
||||
import com.gitee.sop.gatewaycommon.validate.Validator;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||
@@ -25,6 +29,7 @@ public class ValidateFilter implements GlobalFilter, Ordered {
|
||||
// 解析参数
|
||||
ApiParam param = apiConfig.getGatewayParamBuilder().build(exchange);
|
||||
exchange.getAttributes().put(SopConstants.CACHE_API_PARAM, param);
|
||||
RouteUtil.checkEnable(param);
|
||||
// 验证操作,这里有负责验证签名参数
|
||||
Validator validator = apiConfig.getValidator();
|
||||
try {
|
||||
@@ -39,6 +44,6 @@ public class ValidateFilter implements GlobalFilter, Ordered {
|
||||
@Override
|
||||
public int getOrder() {
|
||||
// 最优先执行
|
||||
return Ordered.HIGHEST_PRECEDENCE + 1000;
|
||||
return Orders.VALIDATE_ORDER;
|
||||
}
|
||||
}
|
||||
|
@@ -36,21 +36,15 @@ public class GatewayExceptionHandler implements ErrorWebExceptionHandler {
|
||||
ResultExecutor<ServerWebExchange, GatewayResult> resultExecutor = ApiContext.getApiConfig().getGatewayResultExecutor();
|
||||
GatewayResult errorResult = resultExecutor.buildErrorResult(exchange, ex);
|
||||
|
||||
/**
|
||||
* 错误记录
|
||||
*/
|
||||
// 错误记录
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
log.error("[全局异常处理]异常请求路径:{}, msg:{}", request.getPath(), ex.getMessage(), ex);
|
||||
/**
|
||||
* 参考AbstractErrorWebExceptionHandler
|
||||
*/
|
||||
// 参考AbstractErrorWebExceptionHandler
|
||||
if (exchange.getResponse().isCommitted()) {
|
||||
return Mono.error(ex);
|
||||
}
|
||||
ServerRequest newRequest = ServerRequest.create(exchange, this.messageReaders);
|
||||
return RouterFunctions.route(RequestPredicates.all(), (serverRequest) -> {
|
||||
return this.renderErrorResponse(errorResult);
|
||||
}).route(newRequest)
|
||||
return RouterFunctions.route(RequestPredicates.all(), (serverRequest) -> this.renderErrorResponse(errorResult)).route(newRequest)
|
||||
.switchIfEmpty(Mono.error(ex))
|
||||
.flatMap((handler) -> handler.handle(newRequest))
|
||||
.flatMap((response) -> write(exchange, response));
|
||||
@@ -75,7 +69,7 @@ public class GatewayExceptionHandler implements ErrorWebExceptionHandler {
|
||||
/**
|
||||
* 参考AbstractErrorWebExceptionHandler
|
||||
*
|
||||
* @param messageReaders
|
||||
* @param messageReaders messageReaders
|
||||
*/
|
||||
public void setMessageReaders(List<HttpMessageReader<?>> messageReaders) {
|
||||
Assert.notNull(messageReaders, "'messageReaders' must not be null");
|
||||
@@ -85,7 +79,7 @@ public class GatewayExceptionHandler implements ErrorWebExceptionHandler {
|
||||
/**
|
||||
* 参考AbstractErrorWebExceptionHandler
|
||||
*
|
||||
* @param viewResolvers
|
||||
* @param viewResolvers viewResolvers
|
||||
*/
|
||||
public void setViewResolvers(List<ViewResolver> viewResolvers) {
|
||||
this.viewResolvers = viewResolvers;
|
||||
@@ -94,7 +88,7 @@ public class GatewayExceptionHandler implements ErrorWebExceptionHandler {
|
||||
/**
|
||||
* 参考AbstractErrorWebExceptionHandler
|
||||
*
|
||||
* @param messageWriters
|
||||
* @param messageWriters messageWriters
|
||||
*/
|
||||
public void setMessageWriters(List<HttpMessageWriter<?>> messageWriters) {
|
||||
Assert.notNull(messageWriters, "'messageWriters' must not be null");
|
||||
@@ -105,8 +99,8 @@ public class GatewayExceptionHandler implements ErrorWebExceptionHandler {
|
||||
/**
|
||||
* 参考DefaultErrorWebExceptionHandler
|
||||
*
|
||||
* @param result
|
||||
* @return
|
||||
* @param result 返回结果
|
||||
* @return 返回mono
|
||||
*/
|
||||
protected Mono<ServerResponse> renderErrorResponse(GatewayResult result) {
|
||||
return ServerResponse
|
||||
@@ -118,9 +112,9 @@ public class GatewayExceptionHandler implements ErrorWebExceptionHandler {
|
||||
/**
|
||||
* 参考AbstractErrorWebExceptionHandler
|
||||
*
|
||||
* @param exchange
|
||||
* @param response
|
||||
* @return
|
||||
* @param exchange exchange
|
||||
* @param response response
|
||||
* @return 返回Mono
|
||||
*/
|
||||
private Mono<? extends Void> write(ServerWebExchange exchange,
|
||||
ServerResponse response) {
|
||||
|
@@ -1,5 +1,6 @@
|
||||
package com.gitee.sop.gatewaycommon.gateway.route;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.TargetRoute;
|
||||
import com.gitee.sop.gatewaycommon.manager.RouteRepository;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeansException;
|
||||
@@ -22,6 +23,7 @@ import org.springframework.validation.Validator;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@@ -58,7 +60,7 @@ public class GatewayRouteRepository implements ApplicationEventPublisherAware,
|
||||
@Override
|
||||
public Flux<RouteDefinition> getRouteDefinitions() {
|
||||
List<RouteDefinition> list = routes.values().parallelStream()
|
||||
.map(targetRoute -> targetRoute.getTargetRouteDefinition())
|
||||
.map(TargetRoute::getTargetRouteDefinition)
|
||||
.collect(Collectors.toList());
|
||||
return Flux.fromIterable(list);
|
||||
}
|
||||
@@ -81,6 +83,11 @@ public class GatewayRouteRepository implements ApplicationEventPublisherAware,
|
||||
return routes.get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<GatewayTargetRoute> getAll() {
|
||||
return routes.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加路由
|
||||
*/
|
||||
|
@@ -1,11 +1,6 @@
|
||||
package com.gitee.sop.gatewaycommon.gateway.route;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
|
||||
import com.gitee.sop.gatewaycommon.bean.RouteConfig;
|
||||
import com.gitee.sop.gatewaycommon.bean.SopConstants;
|
||||
import com.gitee.sop.gatewaycommon.bean.TargetRoute;
|
||||
import com.gitee.sop.gatewaycommon.manager.RouteConfigManager;
|
||||
import com.gitee.sop.gatewaycommon.manager.RouteRepositoryContext;
|
||||
import com.gitee.sop.gatewaycommon.param.ParamNames;
|
||||
import com.gitee.sop.gatewaycommon.util.RequestUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -45,7 +40,7 @@ public class NameVersionRoutePredicateFactory extends AbstractRoutePredicateFact
|
||||
* config.param为nameVersion
|
||||
*
|
||||
* @param config
|
||||
* @return
|
||||
* @return 返回断言
|
||||
*/
|
||||
@Override
|
||||
public Predicate<ServerWebExchange> apply(Config config) {
|
||||
@@ -65,14 +60,6 @@ public class NameVersionRoutePredicateFactory extends AbstractRoutePredicateFact
|
||||
String name = params.getOrDefault(ParamNames.API_NAME, String.valueOf(System.currentTimeMillis()));
|
||||
String version = params.getOrDefault(ParamNames.VERSION_NAME, "");
|
||||
boolean match = (name + version).equals(nameVersion);
|
||||
if (match) {
|
||||
TargetRoute targetRoute = RouteRepositoryContext.getRouteRepository().get(nameVersion);
|
||||
RouteConfigManager routeConfigManager = ApiConfig.getInstance().getRouteConfigManager();
|
||||
RouteConfig routeConfig = routeConfigManager.get(nameVersion);
|
||||
if (targetRoute != null && !routeConfig.enable()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return match;
|
||||
};
|
||||
}
|
||||
|
@@ -6,7 +6,7 @@ import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 获取form表单插件,使用方式:
|
||||
* @Bean
|
||||
* @Bean
|
||||
* ReadBodyRoutePredicateFactory readBodyRoutePredicateFactory() {
|
||||
* return new ReadBodyRoutePredicateFactory();
|
||||
* }
|
||||
|
@@ -0,0 +1,52 @@
|
||||
package com.gitee.sop.gatewaycommon.limit;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.RouteConfig;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Slf4j
|
||||
public class DefaultLimitManager implements LimitManager {
|
||||
|
||||
@Override
|
||||
public double acquireToken(RouteConfig routeConfig) {
|
||||
if (routeConfig.getLimitStatus() == RouteConfig.LIMIT_STATUS_CLOSE) {
|
||||
return 0;
|
||||
}
|
||||
if (LimitType.LEAKY_BUCKET.getType() == routeConfig.getLimitType().byteValue()) {
|
||||
throw new IllegalStateException("漏桶策略无法调用此方法");
|
||||
}
|
||||
return routeConfig.fetchRateLimiter().acquire();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean acquire(RouteConfig routeConfig) {
|
||||
if (routeConfig.getLimitStatus() == RouteConfig.LIMIT_STATUS_CLOSE) {
|
||||
return true;
|
||||
}
|
||||
if (LimitType.TOKEN_BUCKET.getType() == routeConfig.getLimitType().byteValue()) {
|
||||
throw new IllegalStateException("令牌桶策略无法调用此方法");
|
||||
}
|
||||
int execCountPerSecond = routeConfig.getExecCountPerSecond();
|
||||
long currentSeconds = System.currentTimeMillis() / 1000;
|
||||
try {
|
||||
LoadingCache<Long, AtomicLong> counter = routeConfig.getCounter();
|
||||
// 被限流了
|
||||
if (counter.get(currentSeconds).incrementAndGet() > execCountPerSecond) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} catch (ExecutionException e) {
|
||||
log.error("漏桶限流出错,routeConfig", routeConfig, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
package com.gitee.sop.gatewaycommon.limit;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.RouteConfig;
|
||||
|
||||
/**
|
||||
* 限流管理
|
||||
* @author tanghc
|
||||
*/
|
||||
public interface LimitManager {
|
||||
|
||||
/**
|
||||
* 从令牌桶中获取令牌,如果使用{@link LimitType#TOKEN_BUCKET
|
||||
* RateType.TOKEN_BUCKET}限流策略,则该方法生效
|
||||
*
|
||||
* @param routeConfig 路由配置
|
||||
* @return 返回耗时时间,秒
|
||||
*/
|
||||
double acquireToken(RouteConfig routeConfig);
|
||||
|
||||
/**
|
||||
* 是否需要限流,如果使用{@link LimitType#LEAKY_BUCKET
|
||||
* RateType.LIMIT}限流策略,则该方法生效
|
||||
*
|
||||
* @param routeConfig 路由配置
|
||||
* @return 如果返回true,表示可以执行业务代码,返回false则需要限流
|
||||
*/
|
||||
boolean acquire(RouteConfig routeConfig);
|
||||
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
package com.gitee.sop.gatewaycommon.limit;
|
||||
|
||||
/**
|
||||
* 限流策略
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
public enum LimitType {
|
||||
/**
|
||||
* 漏桶策略。每秒处理固定数量的请求,超出请求返回错误信息。
|
||||
*/
|
||||
LEAKY_BUCKET(1),
|
||||
/**
|
||||
* 令牌桶策略,每秒放置固定数量的令牌数,不足的令牌数做等待处理,直到拿到令牌为止。
|
||||
*/
|
||||
TOKEN_BUCKET(2);
|
||||
|
||||
private byte type;
|
||||
|
||||
LimitType(int type) {
|
||||
this.type = (byte)type;
|
||||
}
|
||||
|
||||
public byte getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
}
|
@@ -42,14 +42,14 @@ public abstract class BaseRouteManager<R extends BaseServiceRouteInfo<E>, E exte
|
||||
/**
|
||||
* 返回路由根对象class
|
||||
*
|
||||
* @return
|
||||
* @return 返回R.class
|
||||
*/
|
||||
protected abstract Class<R> getServiceRouteInfoClass();
|
||||
|
||||
/**
|
||||
* 返回路由Item对象class
|
||||
*
|
||||
* @return
|
||||
* @return 返回E.class
|
||||
*/
|
||||
protected abstract Class<E> getRouteDefinitionClass();
|
||||
|
||||
@@ -58,7 +58,7 @@ public abstract class BaseRouteManager<R extends BaseServiceRouteInfo<E>, E exte
|
||||
*
|
||||
* @param serviceRouteInfo
|
||||
* @param routeDefinition
|
||||
* @return
|
||||
* @return 返回目标路由对象
|
||||
*/
|
||||
protected abstract T buildRouteDefinition(R serviceRouteInfo, E routeDefinition);
|
||||
|
||||
|
@@ -29,13 +29,19 @@ public class DefaultRouteConfigManager implements RouteConfigManager {
|
||||
|
||||
@Override
|
||||
public void update(RouteConfigDto routeConfigDto) {
|
||||
String key = routeConfigDto.getRouteId();
|
||||
RouteConfig routeConfig = routeConfigMap.get(key);
|
||||
this.doUpdate(routeConfigDto.getRouteId(), routeConfigDto);
|
||||
}
|
||||
|
||||
protected void doUpdate(String routeId, Object res) {
|
||||
RouteConfig routeConfig = routeConfigMap.get(routeId);
|
||||
if (routeConfig == null) {
|
||||
routeConfig = newRouteConfig();
|
||||
routeConfigMap.put(key, routeConfig);
|
||||
routeConfig.setRouteId(routeId);
|
||||
routeConfigMap.put(routeId, routeConfig);
|
||||
} else {
|
||||
MyBeanUtil.copyPropertiesIgnoreNull(res, routeConfig);
|
||||
}
|
||||
MyBeanUtil.copyPropertiesIgnoreNull(routeConfigDto, routeConfig);
|
||||
routeConfig.initRateLimiter();
|
||||
}
|
||||
|
||||
protected RouteConfig newRouteConfig() {
|
||||
|
@@ -14,21 +14,21 @@ public interface IsvRoutePermissionManager {
|
||||
|
||||
/**
|
||||
* 加载权限
|
||||
* @param isvRoutePermission
|
||||
* @param isvRoutePermission isvRoutePermission
|
||||
*/
|
||||
void update(IsvRoutePermission isvRoutePermission);
|
||||
|
||||
/**
|
||||
* 判断是否有权限
|
||||
* @param appKey
|
||||
* @param routeId
|
||||
* @return
|
||||
* @param appKey appKey
|
||||
* @param routeId 路由id
|
||||
* @return true:有
|
||||
*/
|
||||
boolean hasPermission(String appKey, String routeId);
|
||||
|
||||
/**
|
||||
* 删除权限
|
||||
* @param appKey
|
||||
* @param appKey appKey
|
||||
*/
|
||||
void remove(String appKey);
|
||||
}
|
||||
|
@@ -8,18 +8,21 @@ import com.gitee.sop.gatewaycommon.bean.RouteConfigDto;
|
||||
* @author tanghc
|
||||
*/
|
||||
public interface RouteConfigManager {
|
||||
/**
|
||||
* 加载
|
||||
*/
|
||||
void load();
|
||||
|
||||
/**
|
||||
* 更新路由配置
|
||||
* @param routeConfigDto
|
||||
* @param routeConfigDto 路由配置
|
||||
*/
|
||||
void update(RouteConfigDto routeConfigDto);
|
||||
|
||||
/**
|
||||
* 获取路由配置
|
||||
* @param routeId
|
||||
* @return
|
||||
* @param routeId 路由id
|
||||
* @return 返回RouteConfig
|
||||
*/
|
||||
RouteConfig get(String routeId);
|
||||
}
|
||||
|
@@ -2,6 +2,8 @@ package com.gitee.sop.gatewaycommon.manager;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.TargetRoute;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@@ -9,20 +11,26 @@ public interface RouteRepository<T extends TargetRoute> {
|
||||
/**
|
||||
* 获取路由信息
|
||||
* @param id 路由id
|
||||
* @return
|
||||
* @return 返回路由信息
|
||||
*/
|
||||
T get(String id);
|
||||
|
||||
/**
|
||||
* 返回所有路由信息
|
||||
* @return 返回所有路由信息
|
||||
*/
|
||||
Collection<T> getAll();
|
||||
|
||||
/**
|
||||
* 添加路由
|
||||
* @param targetRoute
|
||||
* @return
|
||||
* @param targetRoute 模板路由对象
|
||||
* @return 返回路由id
|
||||
*/
|
||||
String add(T targetRoute);
|
||||
|
||||
/**
|
||||
* 更新路由
|
||||
* @param targetRoute
|
||||
* @param targetRoute 模板路由对象
|
||||
*/
|
||||
void update(T targetRoute);
|
||||
|
||||
@@ -34,7 +42,7 @@ public interface RouteRepository<T extends TargetRoute> {
|
||||
|
||||
/**
|
||||
* 删除service下的所有路由
|
||||
* @param serviceId
|
||||
* @param serviceId 服务id
|
||||
*/
|
||||
void deleteAll(String serviceId);
|
||||
}
|
||||
|
@@ -99,7 +99,7 @@ public class ZookeeperContext {
|
||||
*
|
||||
* @param path 已存在的
|
||||
* @param data
|
||||
* @return
|
||||
* @return 返回Stat
|
||||
* @throws Exception
|
||||
*/
|
||||
public static Stat updatePathData(String path, String data) throws Exception {
|
||||
@@ -128,7 +128,7 @@ public class ZookeeperContext {
|
||||
* 新建或保存节点
|
||||
* @param path
|
||||
* @param data
|
||||
* @return
|
||||
* @return 返回path
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String createOrUpdateData(String path, String data) throws Exception {
|
||||
@@ -144,7 +144,7 @@ public class ZookeeperContext {
|
||||
* 监听一个节点
|
||||
* @param path
|
||||
* @param onChange 节点修改后触发
|
||||
* @return
|
||||
* @return 返回path
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String listenPath(String path, Consumer<NodeCache> onChange) throws Exception {
|
||||
@@ -172,7 +172,7 @@ public class ZookeeperContext {
|
||||
* 获取子节点数据
|
||||
*
|
||||
* @param parentPath 父节点
|
||||
* @return
|
||||
* @return 返回子节点数据
|
||||
* @throws Exception
|
||||
*/
|
||||
public static List<ChildData> getChildrenData(String parentPath) throws Exception {
|
||||
|
@@ -7,6 +7,9 @@ import lombok.Data;
|
||||
*/
|
||||
@Data
|
||||
public class ErrorImpl implements Error {
|
||||
public static final String ISP_SERVICE_NOT_AVAILABLE = "isp.service-not-available";
|
||||
public static final String SERVICE_NOT_AVAILABLE = "service not available";
|
||||
|
||||
private String code;
|
||||
private String msg;
|
||||
private String sub_code;
|
||||
@@ -16,6 +19,10 @@ public class ErrorImpl implements Error {
|
||||
public ErrorImpl() {
|
||||
}
|
||||
|
||||
public ErrorImpl(String sub_code, String sub_msg) {
|
||||
this(ISP_SERVICE_NOT_AVAILABLE, SERVICE_NOT_AVAILABLE, sub_code, sub_msg, null);
|
||||
}
|
||||
|
||||
public ErrorImpl(String code, String msg, String sub_code, String sub_msg, String solution) {
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package com.gitee.sop.gatewaycommon.param;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import com.gitee.sop.gatewaycommon.bean.SopConstants;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@@ -28,6 +29,11 @@ public class ApiParam extends JSONObject implements Param {
|
||||
|
||||
private transient ApiUploadContext apiUploadContext;
|
||||
|
||||
@JSONField(serialize = false)
|
||||
public String getRouteId() {
|
||||
return this.fetchNameVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取sign,并从param中删除
|
||||
*
|
||||
|
@@ -36,14 +36,14 @@ public abstract class BaseExecutorAdapter<T, R> implements ResultExecutor<T, R>
|
||||
/**
|
||||
* 获取业务方约定的返回码
|
||||
* @param t
|
||||
* @return
|
||||
* @return 返回返回码
|
||||
*/
|
||||
public abstract int getResponseStatus(T t);
|
||||
|
||||
/**
|
||||
* 返回Api参数
|
||||
* @param t
|
||||
* @return
|
||||
* @return 返回api参数
|
||||
*/
|
||||
public abstract Map<String, ?> getApiParam(T t);
|
||||
|
||||
@@ -79,7 +79,7 @@ public abstract class BaseExecutorAdapter<T, R> implements ResultExecutor<T, R>
|
||||
/**
|
||||
* 该路由是否合并结果
|
||||
* @param request
|
||||
* @return
|
||||
* @return true:需要合并
|
||||
*/
|
||||
protected boolean isMergeResult(T request) {
|
||||
// 默认全局设置
|
||||
@@ -130,9 +130,9 @@ public abstract class BaseExecutorAdapter<T, R> implements ResultExecutor<T, R>
|
||||
if (method != null) {
|
||||
name = String.valueOf(method);
|
||||
}
|
||||
Object _sign = params.get(ParamNames.SIGN_NAME);
|
||||
if (_sign != null) {
|
||||
sign = String.valueOf(_sign);
|
||||
Object clientSign = params.get(ParamNames.SIGN_NAME);
|
||||
if (clientSign != null) {
|
||||
sign = String.valueOf(clientSign);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -107,7 +107,7 @@ public class ApiHttpSession implements HttpSession, Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @return 返回HttpSessionContext。已废弃不能使用
|
||||
* @deprecated 已废弃不能使用
|
||||
*/
|
||||
@Override
|
||||
|
@@ -60,8 +60,8 @@ public class RedisHttpSession implements HttpSession, Serializable {
|
||||
/**
|
||||
* 创建新的session
|
||||
*
|
||||
* @param servletContext
|
||||
* @param sessionId
|
||||
* @param servletContext servletContext
|
||||
* @param sessionId sessionId
|
||||
* @param sessionTimeout 过期时间,单位秒
|
||||
* @param redisTemplate redis客户端
|
||||
* @param keyPrefix 存入的key前缀
|
||||
@@ -93,8 +93,8 @@ public class RedisHttpSession implements HttpSession, Serializable {
|
||||
/**
|
||||
* 创建已经存在的session,数据在redis里面
|
||||
*
|
||||
* @param sessionId
|
||||
* @param servletContext
|
||||
* @param sessionId sessionId
|
||||
* @param servletContext servletContext
|
||||
* @param redisTemplate redis客户端
|
||||
* @param keyPrefix 存入的key前缀
|
||||
* @return 返回session
|
||||
@@ -161,8 +161,8 @@ public class RedisHttpSession implements HttpSession, Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 返回HttpSessionContext。已废弃不能使用
|
||||
* @deprecated 已废弃,始终返回null
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
|
@@ -26,7 +26,7 @@ public class RequestUtil {
|
||||
* 将get类型的参数转换成map,
|
||||
*
|
||||
* @param query charset=utf-8&biz_content=xxx
|
||||
* @return
|
||||
* @return 返回map参数
|
||||
*/
|
||||
public static Map<String, String> parseQueryToMap(String query) {
|
||||
if (query == null) {
|
||||
|
@@ -40,7 +40,7 @@ public class ResponseUtil {
|
||||
* map转成xml
|
||||
*
|
||||
* @param parameters
|
||||
* @return
|
||||
* @return 返回xml内容
|
||||
*/
|
||||
public static String mapToXml(JSONObject parameters) {
|
||||
String content = doMap2xml(parameters);
|
||||
|
@@ -1,5 +1,11 @@
|
||||
package com.gitee.sop.gatewaycommon.util;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
|
||||
import com.gitee.sop.gatewaycommon.bean.RouteConfig;
|
||||
import com.gitee.sop.gatewaycommon.manager.RouteConfigManager;
|
||||
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@@ -7,10 +13,23 @@ public class RouteUtil {
|
||||
|
||||
private RouteUtil(){}
|
||||
|
||||
public static final String REGEX = "\\#";
|
||||
private static final String REGEX = "\\#";
|
||||
|
||||
public static final String PROTOCOL_LOAD_BALANCE = "lb://";
|
||||
|
||||
/**
|
||||
* 检测能否访问
|
||||
* @param param 接口参数
|
||||
*/
|
||||
public static void checkEnable(ApiParam param) {
|
||||
String routeId = param.fetchNameVersion();
|
||||
RouteConfigManager routeConfigManager = ApiConfig.getInstance().getRouteConfigManager();
|
||||
RouteConfig routeConfig = routeConfigManager.get(routeId);
|
||||
if (!routeConfig.enable()) {
|
||||
throw ErrorEnum.ISP_API_DISABLED.getErrorMeta().getException();
|
||||
}
|
||||
}
|
||||
|
||||
public static String findPath(String uri) {
|
||||
// #后面是对应的path
|
||||
String[] uriArr = uri.split(REGEX);
|
||||
|
@@ -14,9 +14,9 @@ public abstract class AbstractSigner implements Signer {
|
||||
/**
|
||||
* 构建服务端签名串
|
||||
*
|
||||
* @param params
|
||||
* @param secret
|
||||
* @return
|
||||
* @param params 接口参数
|
||||
* @param secret 秘钥
|
||||
* @return 返回服务端签名串
|
||||
*/
|
||||
protected abstract String buildServerSign(ApiParam params, String secret);
|
||||
|
||||
@@ -30,7 +30,7 @@ public abstract class AbstractSigner implements Signer {
|
||||
return clientSign.equals(serverSign);
|
||||
}
|
||||
|
||||
public static String byte2hex(byte[] bytes) {
|
||||
protected static String byte2hex(byte[] bytes) {
|
||||
StringBuilder sign = new StringBuilder();
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
String hex = Integer.toHexString(bytes[i] & 0xFF);
|
||||
|
@@ -60,6 +60,7 @@ public class ApiValidator implements Validator {
|
||||
checkFormat(param);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 校验上传文件内容
|
||||
*
|
||||
|
@@ -5,6 +5,7 @@ import com.gitee.sop.gatewaycommon.manager.AbstractConfiguration;
|
||||
import com.gitee.sop.gatewaycommon.manager.RouteRepositoryContext;
|
||||
import com.gitee.sop.gatewaycommon.zuul.filter.ErrorFilter;
|
||||
import com.gitee.sop.gatewaycommon.zuul.filter.PostResultFilter;
|
||||
import com.gitee.sop.gatewaycommon.zuul.filter.PreLimitFilter;
|
||||
import com.gitee.sop.gatewaycommon.zuul.filter.PreRoutePermissionFilter;
|
||||
import com.gitee.sop.gatewaycommon.zuul.filter.PreValidateFilter;
|
||||
import com.gitee.sop.gatewaycommon.zuul.route.SopRouteLocator;
|
||||
@@ -48,7 +49,7 @@ public class BaseZuulConfiguration extends AbstractConfiguration {
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public PreDecorationFilter preDecorationFilter(ZuulRouteRepository zuulRouteRepository, ProxyRequestHelper proxyRequestHelper) {
|
||||
PreDecorationFilter preDecorationFilter(ZuulRouteRepository zuulRouteRepository, ProxyRequestHelper proxyRequestHelper) {
|
||||
// 自定义路由
|
||||
RouteLocator routeLocator = new SopRouteLocator(zuulRouteRepository);
|
||||
return new PreDecorationFilter(routeLocator,
|
||||
@@ -61,7 +62,6 @@ public class BaseZuulConfiguration extends AbstractConfiguration {
|
||||
* 路由管理
|
||||
* @param environment
|
||||
* @param zuulRouteRepository
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
ZuulZookeeperRouteManager zuulZookeeperRouteManager(Environment environment, ZuulRouteRepository zuulRouteRepository) {
|
||||
@@ -70,16 +70,22 @@ public class BaseZuulConfiguration extends AbstractConfiguration {
|
||||
|
||||
/**
|
||||
* 前置校验
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
PreValidateFilter preValidateFilter() {
|
||||
return new PreValidateFilter();
|
||||
}
|
||||
|
||||
/**
|
||||
* 开启限流
|
||||
*/
|
||||
@Bean
|
||||
PreLimitFilter preLimitFilter() {
|
||||
return new PreLimitFilter();
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限校验
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
PreRoutePermissionFilter preRoutePermissionFilter() {
|
||||
@@ -88,7 +94,6 @@ public class BaseZuulConfiguration extends AbstractConfiguration {
|
||||
|
||||
/**
|
||||
* 错误处理扩展
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
ErrorFilter errorFilter() {
|
||||
@@ -97,7 +102,6 @@ public class BaseZuulConfiguration extends AbstractConfiguration {
|
||||
|
||||
/**
|
||||
* 结果返回
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
PostResultFilter postResultFilter() {
|
||||
@@ -106,7 +110,6 @@ public class BaseZuulConfiguration extends AbstractConfiguration {
|
||||
|
||||
/**
|
||||
* 统一错误处理
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
ZuulErrorController baseZuulController() {
|
||||
|
@@ -15,8 +15,15 @@ public abstract class BaseZuulFilter extends ZuulFilter {
|
||||
|
||||
protected Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
/** 签名验证过滤 */
|
||||
public static final int PRE_VALIDATE_FILTER_ORDER = -1000;
|
||||
|
||||
/** 权限验证过滤 */
|
||||
public static final int PRE_ROUTE_PERMISSION_FILTER_ORDER = PRE_VALIDATE_FILTER_ORDER + 1;
|
||||
|
||||
/** 限流过滤 */
|
||||
public static final int PRE_LIMIT_FILTER_ORDER = PRE_ROUTE_PERMISSION_FILTER_ORDER + 1;
|
||||
|
||||
private Integer filterOrder;
|
||||
|
||||
/**
|
||||
@@ -28,7 +35,7 @@ public abstract class BaseZuulFilter extends ZuulFilter {
|
||||
|
||||
/**
|
||||
* 获取过滤器顺序
|
||||
* @return
|
||||
* @return 返回顺序,越小优先执行
|
||||
* @see ZuulFilter#filterOrder() filterOrder()
|
||||
*/
|
||||
protected abstract int getFilterOrder();
|
||||
@@ -36,7 +43,7 @@ public abstract class BaseZuulFilter extends ZuulFilter {
|
||||
/**
|
||||
* 执行run
|
||||
* @param requestContext
|
||||
* @return
|
||||
* @return Some arbitrary artifact may be returned. Current implementation ignores it.
|
||||
* @throws ZuulException
|
||||
*/
|
||||
protected abstract Object doRun(RequestContext requestContext) throws ZuulException;
|
||||
|
@@ -0,0 +1,62 @@
|
||||
package com.gitee.sop.gatewaycommon.zuul.filter;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
|
||||
import com.gitee.sop.gatewaycommon.bean.RouteConfig;
|
||||
import com.gitee.sop.gatewaycommon.bean.TargetRoute;
|
||||
import com.gitee.sop.gatewaycommon.exception.ApiException;
|
||||
import com.gitee.sop.gatewaycommon.limit.LimitManager;
|
||||
import com.gitee.sop.gatewaycommon.limit.LimitType;
|
||||
import com.gitee.sop.gatewaycommon.manager.RouteConfigManager;
|
||||
import com.gitee.sop.gatewaycommon.manager.RouteRepositoryContext;
|
||||
import com.gitee.sop.gatewaycommon.message.ErrorImpl;
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||
import com.gitee.sop.gatewaycommon.zuul.ZuulContext;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
import com.netflix.zuul.exception.ZuulException;
|
||||
|
||||
/**
|
||||
* 限流拦截器
|
||||
* @author tanghc
|
||||
*/
|
||||
public class PreLimitFilter extends BaseZuulFilter {
|
||||
@Override
|
||||
protected FilterType getFilterType() {
|
||||
return FilterType.PRE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getFilterOrder() {
|
||||
return PRE_LIMIT_FILTER_ORDER;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doRun(RequestContext requestContext) throws ZuulException {
|
||||
ApiConfig apiConfig = ApiConfig.getInstance();
|
||||
// 限流功能未开启,直接返回
|
||||
if (!apiConfig.isOpenLimit()) {
|
||||
return null;
|
||||
}
|
||||
ApiParam apiParam = ZuulContext.getApiParam();
|
||||
String routeId = apiParam.getRouteId();
|
||||
RouteConfigManager routeConfigManager = apiConfig.getRouteConfigManager();
|
||||
RouteConfig routeConfig = routeConfigManager.get(routeId);
|
||||
if (routeConfig == null) {
|
||||
return null;
|
||||
}
|
||||
// 某个路由限流功能未开启
|
||||
if (routeConfig.getLimitStatus() == RouteConfig.LIMIT_STATUS_CLOSE) {
|
||||
return null;
|
||||
}
|
||||
byte limitType = routeConfig.getLimitType().byteValue();
|
||||
LimitManager limitManager = ApiConfig.getInstance().getLimitManager();
|
||||
if (limitType == LimitType.LEAKY_BUCKET.getType()) {
|
||||
boolean acquire = limitManager.acquire(routeConfig);
|
||||
if (!acquire) {
|
||||
throw new ApiException(new ErrorImpl(routeConfig.getLimitCode(), routeConfig.getLimitMsg()));
|
||||
}
|
||||
} else if (limitType == LimitType.TOKEN_BUCKET.getType()) {
|
||||
limitManager.acquireToken(routeConfig);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -2,8 +2,12 @@ package com.gitee.sop.gatewaycommon.zuul.filter;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiContext;
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
|
||||
import com.gitee.sop.gatewaycommon.bean.RouteConfig;
|
||||
import com.gitee.sop.gatewaycommon.exception.ApiException;
|
||||
import com.gitee.sop.gatewaycommon.manager.RouteConfigManager;
|
||||
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||
import com.gitee.sop.gatewaycommon.util.RouteUtil;
|
||||
import com.gitee.sop.gatewaycommon.validate.Validator;
|
||||
import com.gitee.sop.gatewaycommon.zuul.ZuulContext;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
@@ -31,6 +35,7 @@ public class PreValidateFilter extends BaseZuulFilter {
|
||||
// 解析参数
|
||||
ApiParam param = apiConfig.getZuulParamBuilder().build(requestContext);
|
||||
ZuulContext.setApiParam(param);
|
||||
RouteUtil.checkEnable(param);
|
||||
// 验证操作,这里有负责验证签名参数
|
||||
Validator validator = apiConfig.getValidator();
|
||||
try {
|
||||
|
@@ -1,9 +1,5 @@
|
||||
package com.gitee.sop.gatewaycommon.zuul.route;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
|
||||
import com.gitee.sop.gatewaycommon.bean.RouteConfig;
|
||||
import com.gitee.sop.gatewaycommon.manager.RouteConfigManager;
|
||||
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||
import com.gitee.sop.gatewaycommon.zuul.ZuulContext;
|
||||
import org.springframework.cloud.netflix.zuul.filters.Route;
|
||||
@@ -17,6 +13,7 @@ import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 路由定位
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
public class SopRouteLocator implements RouteLocator, Ordered {
|
||||
@@ -34,7 +31,7 @@ public class SopRouteLocator implements RouteLocator, Ordered {
|
||||
|
||||
@Override
|
||||
public List<Route> getRoutes() {
|
||||
return zuulRouteRepository.listAll()
|
||||
return zuulRouteRepository.getAll()
|
||||
.parallelStream()
|
||||
.map(zuulTargetRoute -> zuulTargetRoute.getTargetRouteDefinition())
|
||||
.collect(Collectors.toList());
|
||||
@@ -42,8 +39,9 @@ public class SopRouteLocator implements RouteLocator, Ordered {
|
||||
|
||||
/**
|
||||
* 这里决定使用哪个路由
|
||||
*
|
||||
* @param path
|
||||
* @return
|
||||
* @return 返回跳转的路由
|
||||
*/
|
||||
@Override
|
||||
public Route getMatchingRoute(String path) {
|
||||
@@ -53,12 +51,6 @@ public class SopRouteLocator implements RouteLocator, Ordered {
|
||||
if (zuulTargetRoute == null) {
|
||||
return null;
|
||||
}
|
||||
// 路由是否启用
|
||||
RouteConfigManager routeConfigManager = ApiConfig.getInstance().getRouteConfigManager();
|
||||
RouteConfig routeConfig = routeConfigManager.get(zuulTargetRoute.getRouteDefinition().getId());
|
||||
if (!routeConfig.enable()) {
|
||||
throw ErrorEnum.ISP_API_DISABLED.getErrorMeta().getException();
|
||||
}
|
||||
return zuulTargetRoute.getTargetRouteDefinition();
|
||||
}
|
||||
|
||||
|
@@ -19,35 +19,36 @@ public class ZuulRouteRepository implements RouteRepository<ZuulTargetRoute> {
|
||||
/**
|
||||
* key:nameVersion
|
||||
*/
|
||||
private Map<String, ZuulTargetRoute> nameVersionServiceIdMap = new ConcurrentHashMap<>(128);
|
||||
|
||||
public List<ZuulTargetRoute> listAll() {
|
||||
return new ArrayList<>(nameVersionServiceIdMap.values());
|
||||
}
|
||||
private Map<String, ZuulTargetRoute> nameVersionTargetRouteMap = new ConcurrentHashMap<>(128);
|
||||
|
||||
@Override
|
||||
public ZuulTargetRoute get(String id) {
|
||||
ZuulTargetRoute route = nameVersionServiceIdMap.get(id);
|
||||
ZuulTargetRoute route = nameVersionTargetRouteMap.get(id);
|
||||
if (route == null) {
|
||||
throw ErrorEnum.ISV_INVALID_METHOD.getErrorMeta().getException();
|
||||
}
|
||||
return route;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ZuulTargetRoute> getAll() {
|
||||
return nameVersionTargetRouteMap.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String add(ZuulTargetRoute targetRoute) {
|
||||
nameVersionServiceIdMap.put(targetRoute.getRouteDefinition().getId(), targetRoute);
|
||||
return null;
|
||||
nameVersionTargetRouteMap.put(targetRoute.getRouteDefinition().getId(), targetRoute);
|
||||
return targetRoute.getRouteDefinition().getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(ZuulTargetRoute targetRoute) {
|
||||
nameVersionServiceIdMap.put(targetRoute.getRouteDefinition().getId(), targetRoute);
|
||||
nameVersionTargetRouteMap.put(targetRoute.getRouteDefinition().getId(), targetRoute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAll(String serviceId) {
|
||||
Collection<ZuulTargetRoute> values = nameVersionServiceIdMap.values();
|
||||
Collection<ZuulTargetRoute> values = nameVersionTargetRouteMap.values();
|
||||
List<String> idList = values.stream()
|
||||
.map(zuulTargetRoute -> zuulTargetRoute.getRouteDefinition().getId())
|
||||
.collect(Collectors.toList());
|
||||
@@ -59,6 +60,6 @@ public class ZuulRouteRepository implements RouteRepository<ZuulTargetRoute> {
|
||||
|
||||
@Override
|
||||
public void delete(String id) {
|
||||
nameVersionServiceIdMap.remove(id);
|
||||
nameVersionTargetRouteMap.remove(id);
|
||||
}
|
||||
}
|
||||
|
@@ -9,9 +9,8 @@
|
||||
<version>1.2.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<groupId>com.gitee.sop</groupId>
|
||||
<artifactId>sop-service-common</artifactId>
|
||||
<version>${parent.version}</version>
|
||||
<version>1.2.0-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>sop-service-common</name>
|
||||
|
@@ -9,7 +9,9 @@ import com.gitee.sop.gatewaycommon.manager.IsvRoutePermissionManager;
|
||||
import com.gitee.sop.gatewaycommon.secret.IsvManager;
|
||||
import com.gitee.sop.gatewaycommon.zuul.configuration.AlipayZuulConfiguration;
|
||||
import com.gitee.sop.gatewaycommon.zuul.configuration.TaobaoZuulConfiguration;
|
||||
import com.gitee.sop.gatewaycommon.zuul.filter.PreLimitFilter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -44,6 +46,7 @@ public class ZuulConfig extends AlipayZuulConfiguration {
|
||||
protected void doAfter() {
|
||||
managerInitializer.init();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -28,8 +28,8 @@ public class ConfigRouteLimit {
|
||||
/** 路由id, 数据库字段:route_id */
|
||||
private String routeId;
|
||||
|
||||
/** 限流策略,1:漏桶策略,2:令牌桶策略, 数据库字段:type */
|
||||
private Byte type;
|
||||
/** 限流策略,1:漏桶策略,2:令牌桶策略, 数据库字段:limit_type */
|
||||
private Byte limitType;
|
||||
|
||||
/** 每秒可处理请求数, 数据库字段:exec_count_per_second */
|
||||
private Integer execCountPerSecond;
|
||||
|
@@ -4,18 +4,21 @@ import com.alibaba.fastjson.JSON;
|
||||
import com.gitee.fastmybatis.core.query.Query;
|
||||
import com.gitee.sop.gateway.mapper.ConfigRouteBaseMapper;
|
||||
import com.gitee.sop.gateway.mapper.ConfigRouteLimitMapper;
|
||||
import com.gitee.sop.gatewaycommon.bean.BaseRouteDefinition;
|
||||
import com.gitee.sop.gatewaycommon.bean.ChannelMsg;
|
||||
import com.gitee.sop.gatewaycommon.bean.RouteConfig;
|
||||
import com.gitee.sop.gatewaycommon.bean.RouteConfigDto;
|
||||
import com.gitee.sop.gatewaycommon.bean.TargetRoute;
|
||||
import com.gitee.sop.gatewaycommon.manager.DefaultRouteConfigManager;
|
||||
import com.gitee.sop.gatewaycommon.manager.RouteRepositoryContext;
|
||||
import com.gitee.sop.gatewaycommon.manager.ZookeeperContext;
|
||||
import com.gitee.sop.gatewaycommon.util.MyBeanUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
@@ -35,6 +38,8 @@ public class DbRouteConfigManager extends DefaultRouteConfigManager {
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
loadAllRoute();
|
||||
|
||||
Query query = new Query();
|
||||
|
||||
configRouteBaseMapper.list(query)
|
||||
@@ -53,10 +58,24 @@ public class DbRouteConfigManager extends DefaultRouteConfigManager {
|
||||
|
||||
}
|
||||
|
||||
protected void putVal(String key, Object object) {
|
||||
RouteConfig routeConfig = routeConfigMap.getOrDefault(key, newRouteConfig());
|
||||
MyBeanUtil.copyPropertiesIgnoreNull(object, routeConfig);
|
||||
routeConfigMap.put(key, routeConfig);
|
||||
protected void loadAllRoute() {
|
||||
Collection<? extends TargetRoute> targetRoutes = RouteRepositoryContext.getRouteRepository().getAll();
|
||||
targetRoutes.stream()
|
||||
.forEach(targetRoute -> {
|
||||
BaseRouteDefinition routeDefinition = targetRoute.getRouteDefinition();
|
||||
initRouteConfig(routeDefinition);
|
||||
});
|
||||
}
|
||||
|
||||
protected void initRouteConfig(BaseRouteDefinition routeDefinition) {
|
||||
String routeId = routeDefinition.getId();
|
||||
RouteConfig routeConfig = newRouteConfig();
|
||||
routeConfig.setRouteId(routeId);
|
||||
routeConfigMap.put(routeId, routeConfig);
|
||||
}
|
||||
|
||||
protected void putVal(String routeId, Object object) {
|
||||
this.doUpdate(routeId, object);
|
||||
}
|
||||
|
||||
|
||||
|
80
sop-test/src/test/java/com/gitee/sop/LimitDemoPostTest.java
Normal file
80
sop-test/src/test/java/com/gitee/sop/LimitDemoPostTest.java
Normal file
@@ -0,0 +1,80 @@
|
||||
package com.gitee.sop;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.gitee.sop.alipay.AlipaySignature;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* 限流测试
|
||||
*/
|
||||
public class LimitDemoPostTest extends TestBase {
|
||||
|
||||
String url = "http://localhost:8081/api"; // zuul
|
||||
String appId = "2019032617262200001";
|
||||
// 支付宝私钥
|
||||
String privateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXJv1pQFqWNA/++OYEV7WYXwexZK/J8LY1OWlP9X0T6wHFOvxNKRvMkJ5544SbgsJpVcvRDPrcxmhPbi/sAhdO4x2PiPKIz9Yni2OtYCCeaiE056B+e1O2jXoLeXbfi9fPivJZkxH/tb4xfLkH3bA8ZAQnQsoXA0SguykMRZntF0TndUfvDrLqwhlR8r5iRdZLB6F8o8qXH6UPDfNEnf/K8wX5T4EB1b8x8QJ7Ua4GcIUqeUxGHdQpzNbJdaQvoi06lgccmL+PHzminkFYON7alj1CjDN833j7QMHdPtS9l7B67fOU/p2LAAkPMtoVBfxQt9aFj7B8rEhGCz02iJIBAgMBAAECggEARqOuIpY0v6WtJBfmR3lGIOOokLrhfJrGTLF8CiZMQha+SRJ7/wOLPlsH9SbjPlopyViTXCuYwbzn2tdABigkBHYXxpDV6CJZjzmRZ+FY3S/0POlTFElGojYUJ3CooWiVfyUMhdg5vSuOq0oCny53woFrf32zPHYGiKdvU5Djku1onbDU0Lw8w+5tguuEZ76kZ/lUcccGy5978FFmYpzY/65RHCpvLiLqYyWTtaNT1aQ/9pw4jX9HO9NfdJ9gYFK8r/2f36ZE4hxluAfeOXQfRC/WhPmiw/ReUhxPznG/WgKaa/OaRtAx3inbQ+JuCND7uuKeRe4osP2jLPHPP6AUwQKBgQDUNu3BkLoKaimjGOjCTAwtp71g1oo+k5/uEInAo7lyEwpV0EuUMwLA/HCqUgR4K9pyYV+Oyb8d6f0+Hz0BMD92I2pqlXrD7xV2WzDvyXM3s63NvorRooKcyfd9i6ccMjAyTR2qfLkxv0hlbBbsPHz4BbU63xhTJp3Ghi0/ey/1HQKBgQC2VsgqC6ykfSidZUNLmQZe3J0p/Qf9VLkfrQ+xaHapOs6AzDU2H2osuysqXTLJHsGfrwVaTs00ER2z8ljTJPBUtNtOLrwNRlvgdnzyVAKHfOgDBGwJgiwpeE9voB1oAV/mXqSaUWNnuwlOIhvQEBwekqNyWvhLqC7nCAIhj3yvNQKBgQCqYbeec56LAhWP903Zwcj9VvG7sESqXUhIkUqoOkuIBTWFFIm54QLTA1tJxDQGb98heoCIWf5x/A3xNI98RsqNBX5JON6qNWjb7/dobitti3t99v/ptDp9u8JTMC7penoryLKK0Ty3bkan95Kn9SC42YxaSghzqkt+uvfVQgiNGQKBgGxU6P2aDAt6VNwWosHSe+d2WWXt8IZBhO9d6dn0f7ORvcjmCqNKTNGgrkewMZEuVcliueJquR47IROdY8qmwqcBAN7Vg2K7r7CPlTKAWTRYMJxCT1Hi5gwJb+CZF3+IeYqsJk2NF2s0w5WJTE70k1BSvQsfIzAIDz2yE1oPHvwVAoGAA6e+xQkVH4fMEph55RJIZ5goI4Y76BSvt2N5OKZKd4HtaV+eIhM3SDsVYRLIm9ZquJHMiZQGyUGnsvrKL6AAVNK7eQZCRDk9KQz+0GKOGqku0nOZjUbAu6A2/vtXAaAuFSFx1rUQVVjFulLexkXR3KcztL1Qu2k5pB6Si0K/uwQ=";
|
||||
|
||||
@Test
|
||||
public void testLimit() throws InterruptedException {
|
||||
int threadsCount = 10; // threadsCount个线程同时提交
|
||||
final CountDownLatch countDownLatch = new CountDownLatch(1);
|
||||
final CountDownLatch count = new CountDownLatch(threadsCount);
|
||||
final AtomicInteger success = new AtomicInteger();
|
||||
for (int i = 0; i < threadsCount; i++) {
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
countDownLatch.await(); // 等在这里,执行countDownLatch.countDown();集体触发
|
||||
// 业务方法
|
||||
doBusiness(Thread.currentThread().getName());
|
||||
success.incrementAndGet();
|
||||
} catch (Exception e) {
|
||||
} finally {
|
||||
count.countDown();
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
countDownLatch.countDown();
|
||||
count.await();
|
||||
System.out.println("成功次数:" + success);
|
||||
}
|
||||
|
||||
// 这个请求会路由到story服务
|
||||
public void doBusiness(String threadName) throws Exception {
|
||||
|
||||
// 公共请求参数
|
||||
Map<String, String> params = new HashMap<String, String>();
|
||||
params.put("app_id", appId);
|
||||
params.put("method", "alipay.story.get");
|
||||
params.put("format", "json");
|
||||
params.put("charset", "utf-8");
|
||||
params.put("sign_type", "RSA2");
|
||||
params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
|
||||
params.put("version", "1.2");
|
||||
|
||||
// 业务参数
|
||||
Map<String, String> bizContent = new HashMap<>();
|
||||
bizContent.put("id", "1");
|
||||
bizContent.put("name", "葫芦娃");
|
||||
|
||||
params.put("biz_content", JSON.toJSONString(bizContent));
|
||||
|
||||
String content = AlipaySignature.getSignContent(params);
|
||||
String sign = AlipaySignature.rsa256Sign(content, privateKey, "utf-8");
|
||||
|
||||
params.put("sign", sign);
|
||||
|
||||
String responseData = post(url, params);// 发送请求
|
||||
System.out.println(responseData);
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user