新增限流功能

1.x
tanghc 6 years ago
parent f8fd24ce32
commit 59a41de04e
  1. 4
      sop-admin/sop-admin-front/pages/service/limitManager.html
  2. 30
      sop-admin/sop-admin-front/pages/service/limitManager.js
  3. 2
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/LimitApi.java
  4. 4
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/LimitParam.java
  5. 6
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/result/LimitVO.java
  6. 4
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/bean/RouteConfigDto.java
  7. 4
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/entity/ConfigRouteLimit.java
  8. 20
      sop-common/pom.xml
  9. 9
      sop-common/sop-gateway-common/pom.xml
  10. 12
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiConfig.java
  11. 6
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/Isv.java
  12. 64
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/RouteConfig.java
  13. 4
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/RouteConfigDto.java
  14. 9
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/configuration/BaseGatewayConfiguration.java
  15. 67
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/LimitFilter.java
  16. 14
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/Orders.java
  17. 7
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/ValidateFilter.java
  18. 28
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/handler/GatewayExceptionHandler.java
  19. 9
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayRouteRepository.java
  20. 15
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/NameVersionRoutePredicateFactory.java
  21. 2
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/ReadBodyRoutePredicateFactory.java
  22. 52
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/limit/DefaultLimitManager.java
  23. 29
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/limit/LimitManager.java
  24. 28
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/limit/LimitType.java
  25. 6
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/BaseRouteManager.java
  26. 14
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultRouteConfigManager.java
  27. 10
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/IsvRoutePermissionManager.java
  28. 9
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteConfigManager.java
  29. 18
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteRepository.java
  30. 8
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/ZookeeperContext.java
  31. 7
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/message/ErrorImpl.java
  32. 6
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ApiParam.java
  33. 12
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/BaseExecutorAdapter.java
  34. 2
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/session/ApiHttpSession.java
  35. 10
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/session/RedisHttpSession.java
  36. 2
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RequestUtil.java
  37. 2
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/ResponseUtil.java
  38. 21
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RouteUtil.java
  39. 8
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/AbstractSigner.java
  40. 1
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/ApiValidator.java
  41. 17
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/BaseZuulConfiguration.java
  42. 11
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/BaseZuulFilter.java
  43. 62
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/PreLimitFilter.java
  44. 5
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/PreValidateFilter.java
  45. 16
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/route/SopRouteLocator.java
  46. 23
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/route/ZuulRouteRepository.java
  47. 3
      sop-common/sop-service-common/pom.xml
  48. 3
      sop-gateway/src/main/java/com/gitee/sop/gateway/config/ZuulConfig.java
  49. 4
      sop-gateway/src/main/java/com/gitee/sop/gateway/entity/ConfigRouteLimit.java
  50. 29
      sop-gateway/src/main/java/com/gitee/sop/gateway/manager/DbRouteConfigManager.java
  51. 80
      sop-test/src/test/java/com/gitee/sop/LimitDemoPostTest.java

@ -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
@ -192,3 +194,19 @@ 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
* &#64;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> {
/**
* keynameVersion
*/
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);
}

@ -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);
}
}
Loading…
Cancel
Save