From 59a41de04e306d6ada3709a315e8be1c1ad29294 Mon Sep 17 00:00:00 2001 From: tanghc Date: Thu, 11 Apr 2019 19:07:53 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E9=99=90=E6=B5=81=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/service/limitManager.html | 4 +- .../pages/service/limitManager.js | 32 ++++++-- .../sop/adminserver/api/service/LimitApi.java | 2 +- .../api/service/param/LimitParam.java | 4 +- .../api/service/result/LimitVO.java | 6 +- .../sop/adminserver/bean/RouteConfigDto.java | 4 +- .../adminserver/entity/ConfigRouteLimit.java | 4 +- sop-common/pom.xml | 20 +---- sop-common/sop-gateway-common/pom.xml | 9 ++- .../sop/gatewaycommon/bean/ApiConfig.java | 12 +++ .../com/gitee/sop/gatewaycommon/bean/Isv.java | 6 +- .../sop/gatewaycommon/bean/RouteConfig.java | 64 +++++++++++++-- .../gatewaycommon/bean/RouteConfigDto.java | 4 +- .../BaseGatewayConfiguration.java | 9 +-- .../gateway/filter/LimitFilter.java | 67 ++++++++++++++++ .../gatewaycommon/gateway/filter/Orders.java | 14 ++++ .../gateway/filter/ValidateFilter.java | 7 +- .../handler/GatewayExceptionHandler.java | 28 +++---- .../gateway/route/GatewayRouteRepository.java | 9 ++- .../NameVersionRoutePredicateFactory.java | 15 +--- .../route/ReadBodyRoutePredicateFactory.java | 2 +- .../limit/DefaultLimitManager.java | 52 ++++++++++++ .../sop/gatewaycommon/limit/LimitManager.java | 29 +++++++ .../sop/gatewaycommon/limit/LimitType.java | 28 +++++++ .../manager/BaseRouteManager.java | 6 +- .../manager/DefaultRouteConfigManager.java | 14 +++- .../manager/IsvRoutePermissionManager.java | 10 +-- .../manager/RouteConfigManager.java | 9 ++- .../manager/RouteRepository.java | 18 +++-- .../manager/ZookeeperContext.java | 8 +- .../sop/gatewaycommon/message/ErrorImpl.java | 7 ++ .../sop/gatewaycommon/param/ApiParam.java | 6 ++ .../result/BaseExecutorAdapter.java | 12 +-- .../gatewaycommon/session/ApiHttpSession.java | 2 +- .../session/RedisHttpSession.java | 10 +-- .../sop/gatewaycommon/util/RequestUtil.java | 2 +- .../sop/gatewaycommon/util/ResponseUtil.java | 2 +- .../sop/gatewaycommon/util/RouteUtil.java | 21 ++++- .../validate/AbstractSigner.java | 8 +- .../gatewaycommon/validate/ApiValidator.java | 1 + .../configuration/BaseZuulConfiguration.java | 17 ++-- .../zuul/filter/BaseZuulFilter.java | 11 ++- .../zuul/filter/PreLimitFilter.java | 62 ++++++++++++++ .../zuul/filter/PreValidateFilter.java | 5 ++ .../zuul/route/SopRouteLocator.java | 16 +--- .../zuul/route/ZuulRouteRepository.java | 23 +++--- sop-common/sop-service-common/pom.xml | 3 +- .../gitee/sop/gateway/config/ZuulConfig.java | 3 + .../sop/gateway/entity/ConfigRouteLimit.java | 4 +- .../gateway/manager/DbRouteConfigManager.java | 29 +++++-- .../java/com/gitee/sop/LimitDemoPostTest.java | 80 +++++++++++++++++++ 51 files changed, 643 insertions(+), 177 deletions(-) create mode 100644 sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/LimitFilter.java create mode 100644 sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/Orders.java create mode 100644 sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/limit/DefaultLimitManager.java create mode 100644 sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/limit/LimitManager.java create mode 100644 sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/limit/LimitType.java create mode 100644 sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/PreLimitFilter.java create mode 100644 sop-test/src/test/java/com/gitee/sop/LimitDemoPostTest.java diff --git a/sop-admin/sop-admin-front/pages/service/limitManager.html b/sop-admin/sop-admin-front/pages/service/limitManager.html index 5404e22e..376e7d54 100644 --- a/sop-admin/sop-admin-front/pages/service/limitManager.html +++ b/sop-admin/sop-admin-front/pages/service/limitManager.html @@ -57,8 +57,8 @@
- - + +
diff --git a/sop-admin/sop-admin-front/pages/service/limitManager.js b/sop-admin/sop-admin-front/pages/service/limitManager.js index 55c036f9..b9c29aa8 100644 --- a/sop-admin/sop-admin-front/pages/service/limitManager.js +++ b/sop-admin/sop-admin-front/pages/service/limitManager.js @@ -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 = '' + 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'); -}); \ No newline at end of file +}); + +function showLimitTypeTip() { + var leakyRemark = '漏桶策略:每秒处理固定数量的请求,超出请求返回错误信息。'; + var tokenRemark = '令牌桶策略:每秒放置固定数量的令牌数,不足的令牌数做等待处理,直到拿到令牌为止。'; + var content = '
' + + leakyRemark + + '
' + + tokenRemark + + '
'; + + layer.open({ + title: '限流策略说明' + ,area: ['600px', 'auto'] + ,content: content + }); +} \ No newline at end of file diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/LimitApi.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/LimitApi.java index e01112fe..75090c59 100644 --- a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/LimitApi.java +++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/LimitApi.java @@ -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; } diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/LimitParam.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/LimitParam.java index 32233037..b6577f4b 100644 --- a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/LimitParam.java +++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/LimitParam.java @@ -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 = "每秒可处理请求数") diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/result/LimitVO.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/result/LimitVO.java index 72362cfa..166f72cc 100644 --- a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/result/LimitVO.java +++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/result/LimitVO.java @@ -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 diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/bean/RouteConfigDto.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/bean/RouteConfigDto.java index 5455f487..5a6fc6ca 100644 --- a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/bean/RouteConfigDto.java +++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/bean/RouteConfigDto.java @@ -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; diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/entity/ConfigRouteLimit.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/entity/ConfigRouteLimit.java index 6168695b..6b927d1c 100644 --- a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/entity/ConfigRouteLimit.java +++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/entity/ConfigRouteLimit.java @@ -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; diff --git a/sop-common/pom.xml b/sop-common/pom.xml index 1feaed2e..fc4fea87 100644 --- a/sop-common/pom.xml +++ b/sop-common/pom.xml @@ -138,6 +138,7 @@ org.apache.maven.plugins maven-surefire-plugin + 2.12.4 true @@ -145,6 +146,7 @@ org.apache.maven.plugins maven-compiler-plugin + 3.1 ${java.version} ${java.version} @@ -164,24 +166,6 @@ - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.9 - - - attach-javadocs - - jar - - - - -Xdoclint:none - - - - diff --git a/sop-common/sop-gateway-common/pom.xml b/sop-common/sop-gateway-common/pom.xml index 3af455eb..479a61f4 100644 --- a/sop-common/sop-gateway-common/pom.xml +++ b/sop-common/sop-gateway-common/pom.xml @@ -8,9 +8,8 @@ 1.2.0-SNAPSHOT ../pom.xml - com.gitee.sop sop-gateway-common - ${parent.version} + 1.2.0-SNAPSHOT jar sop-gateway-common @@ -21,6 +20,12 @@ + + com.google.guava + guava + 27.1-jre + + commons-fileupload commons-fileupload diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiConfig.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiConfig.java index f9326a63..e976cad9 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiConfig.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiConfig.java @@ -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 appSecretPair) { for (Map.Entry entry : appSecretPair.entrySet()) { this.isvManager.update(new IsvDefinition(entry.getKey(), entry.getValue())); diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/Isv.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/Isv.java index 9ceae332..5eb8e287 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/Isv.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/Isv.java @@ -7,19 +7,19 @@ public interface Isv { /** * appKey - * @return + * @return 返回appKey */ String getAppKey(); /** * 秘钥 - * @return + * @return 返回秘钥 */ String getSecretInfo(); /** * 0启用,1禁用 - * @return + * @return 返回状态 */ Byte getStatus(); } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/RouteConfig.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/RouteConfig.java index 0093daa9..7b9aad95 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/RouteConfig.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/RouteConfig.java @@ -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 counter = CacheBuilder.newBuilder() + .expireAfterWrite(2, TimeUnit.SECONDS) + .build(new CacheLoader() { + @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; } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/RouteConfigDto.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/RouteConfigDto.java index 664bb478..65750bee 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/RouteConfigDto.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/RouteConfigDto.java @@ -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; diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/configuration/BaseGatewayConfiguration.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/configuration/BaseGatewayConfiguration.java index 5e07b075..7694f84b 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/configuration/BaseGatewayConfiguration.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/configuration/BaseGatewayConfiguration.java @@ -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() { diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/LimitFilter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/LimitFilter.java new file mode 100644 index 00000000..92102de3 --- /dev/null +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/LimitFilter.java @@ -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 filter(ServerWebExchange exchange, GatewayFilterChain chain) { + ApiConfig apiConfig = ApiConfig.getInstance(); + // 限流功能未开启,直接返回 + if (!apiConfig.isOpenLimit()) { + return chain.filter(exchange); + } + Map 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; + } +} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/Orders.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/Orders.java new file mode 100644 index 00000000..1485d969 --- /dev/null +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/Orders.java @@ -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; +} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/ValidateFilter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/ValidateFilter.java index f1345eec..7004ef83 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/ValidateFilter.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/ValidateFilter.java @@ -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; } } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/handler/GatewayExceptionHandler.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/handler/GatewayExceptionHandler.java index a1eec192..be8331e7 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/handler/GatewayExceptionHandler.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/handler/GatewayExceptionHandler.java @@ -36,21 +36,15 @@ public class GatewayExceptionHandler implements ErrorWebExceptionHandler { ResultExecutor 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> 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 viewResolvers) { this.viewResolvers = viewResolvers; @@ -94,7 +88,7 @@ public class GatewayExceptionHandler implements ErrorWebExceptionHandler { /** * 参考AbstractErrorWebExceptionHandler * - * @param messageWriters + * @param messageWriters messageWriters */ public void setMessageWriters(List> 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 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 write(ServerWebExchange exchange, ServerResponse response) { diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayRouteRepository.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayRouteRepository.java index 54e333f4..8d8c913a 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayRouteRepository.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayRouteRepository.java @@ -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 getRouteDefinitions() { List 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 getAll() { + return routes.values(); + } + /** * 增加路由 */ diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/NameVersionRoutePredicateFactory.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/NameVersionRoutePredicateFactory.java index 1ee16ce3..b13a53c6 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/NameVersionRoutePredicateFactory.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/NameVersionRoutePredicateFactory.java @@ -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 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; }; } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/ReadBodyRoutePredicateFactory.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/ReadBodyRoutePredicateFactory.java index d53e1988..dc5b3259 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/ReadBodyRoutePredicateFactory.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/ReadBodyRoutePredicateFactory.java @@ -6,7 +6,7 @@ import java.util.Objects; /** * 获取form表单插件,使用方式: - * @Bean + * @Bean * ReadBodyRoutePredicateFactory readBodyRoutePredicateFactory() { * return new ReadBodyRoutePredicateFactory(); * } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/limit/DefaultLimitManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/limit/DefaultLimitManager.java new file mode 100644 index 00000000..89b0eeed --- /dev/null +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/limit/DefaultLimitManager.java @@ -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 counter = routeConfig.getCounter(); + // 被限流了 + if (counter.get(currentSeconds).incrementAndGet() > execCountPerSecond) { + return false; + } else { + return true; + } + } catch (ExecutionException e) { + log.error("漏桶限流出错,routeConfig", routeConfig, e); + return false; + } + } + +} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/limit/LimitManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/limit/LimitManager.java new file mode 100644 index 00000000..7a333548 --- /dev/null +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/limit/LimitManager.java @@ -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); + +} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/limit/LimitType.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/limit/LimitType.java new file mode 100644 index 00000000..e7a768ee --- /dev/null +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/limit/LimitType.java @@ -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; + } + +} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/BaseRouteManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/BaseRouteManager.java index ccc493e7..fe905877 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/BaseRouteManager.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/BaseRouteManager.java @@ -42,14 +42,14 @@ public abstract class BaseRouteManager, E exte /** * 返回路由根对象class * - * @return + * @return 返回R.class */ protected abstract Class getServiceRouteInfoClass(); /** * 返回路由Item对象class * - * @return + * @return 返回E.class */ protected abstract Class getRouteDefinitionClass(); @@ -58,7 +58,7 @@ public abstract class BaseRouteManager, E exte * * @param serviceRouteInfo * @param routeDefinition - * @return + * @return 返回目标路由对象 */ protected abstract T buildRouteDefinition(R serviceRouteInfo, E routeDefinition); diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultRouteConfigManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultRouteConfigManager.java index 701c772e..5b56bb66 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultRouteConfigManager.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultRouteConfigManager.java @@ -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() { diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/IsvRoutePermissionManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/IsvRoutePermissionManager.java index 6ff3b314..283f1bfe 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/IsvRoutePermissionManager.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/IsvRoutePermissionManager.java @@ -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); } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteConfigManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteConfigManager.java index 67ace4df..941fbe59 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteConfigManager.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteConfigManager.java @@ -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); } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteRepository.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteRepository.java index b8158ec6..d2a26770 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteRepository.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/RouteRepository.java @@ -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 { /** * 获取路由信息 * @param id 路由id - * @return + * @return 返回路由信息 */ T get(String id); + /** + * 返回所有路由信息 + * @return 返回所有路由信息 + */ + Collection 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 { /** * 删除service下的所有路由 - * @param serviceId + * @param serviceId 服务id */ void deleteAll(String serviceId); } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/ZookeeperContext.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/ZookeeperContext.java index 33a717f2..53ba87be 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/ZookeeperContext.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/ZookeeperContext.java @@ -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 onChange) throws Exception { @@ -172,7 +172,7 @@ public class ZookeeperContext { * 获取子节点数据 * * @param parentPath 父节点 - * @return + * @return 返回子节点数据 * @throws Exception */ public static List getChildrenData(String parentPath) throws Exception { diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/message/ErrorImpl.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/message/ErrorImpl.java index cf5c72ed..110f9eec 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/message/ErrorImpl.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/message/ErrorImpl.java @@ -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; diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ApiParam.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ApiParam.java index c91564f6..b62eabb4 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ApiParam.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ApiParam.java @@ -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中删除 * diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/BaseExecutorAdapter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/BaseExecutorAdapter.java index f23a33cc..5978dd3a 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/BaseExecutorAdapter.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/BaseExecutorAdapter.java @@ -36,14 +36,14 @@ public abstract class BaseExecutorAdapter implements ResultExecutor /** * 获取业务方约定的返回码 * @param t - * @return + * @return 返回返回码 */ public abstract int getResponseStatus(T t); /** * 返回Api参数 * @param t - * @return + * @return 返回api参数 */ public abstract Map getApiParam(T t); @@ -79,7 +79,7 @@ public abstract class BaseExecutorAdapter implements ResultExecutor /** * 该路由是否合并结果 * @param request - * @return + * @return true:需要合并 */ protected boolean isMergeResult(T request) { // 默认全局设置 @@ -130,9 +130,9 @@ public abstract class BaseExecutorAdapter implements ResultExecutor 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); } } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/session/ApiHttpSession.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/session/ApiHttpSession.java index d235f572..1e9ea661 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/session/ApiHttpSession.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/session/ApiHttpSession.java @@ -107,7 +107,7 @@ public class ApiHttpSession implements HttpSession, Serializable { } /** - * @return + * @return 返回HttpSessionContext。已废弃不能使用 * @deprecated 已废弃不能使用 */ @Override diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/session/RedisHttpSession.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/session/RedisHttpSession.java index fab86419..d684b060 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/session/RedisHttpSession.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/session/RedisHttpSession.java @@ -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 diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RequestUtil.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RequestUtil.java index 2670b487..28a60acc 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RequestUtil.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RequestUtil.java @@ -26,7 +26,7 @@ public class RequestUtil { * 将get类型的参数转换成map, * * @param query charset=utf-8&biz_content=xxx - * @return + * @return 返回map参数 */ public static Map parseQueryToMap(String query) { if (query == null) { diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/ResponseUtil.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/ResponseUtil.java index 085c4a48..9048f128 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/ResponseUtil.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/ResponseUtil.java @@ -40,7 +40,7 @@ public class ResponseUtil { * map转成xml * * @param parameters - * @return + * @return 返回xml内容 */ public static String mapToXml(JSONObject parameters) { String content = doMap2xml(parameters); diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RouteUtil.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RouteUtil.java index bc003390..d763a7a6 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RouteUtil.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/util/RouteUtil.java @@ -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); diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/AbstractSigner.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/AbstractSigner.java index e507db8b..4d6b1bf7 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/AbstractSigner.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/AbstractSigner.java @@ -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); diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/ApiValidator.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/ApiValidator.java index 6bf21014..627becc0 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/ApiValidator.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/ApiValidator.java @@ -60,6 +60,7 @@ public class ApiValidator implements Validator { checkFormat(param); } + /** * 校验上传文件内容 * diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/BaseZuulConfiguration.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/BaseZuulConfiguration.java index 402e6e79..45d17017 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/BaseZuulConfiguration.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/configuration/BaseZuulConfiguration.java @@ -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() { diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/BaseZuulFilter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/BaseZuulFilter.java index 3a2b974c..0abc09a4 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/BaseZuulFilter.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/BaseZuulFilter.java @@ -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; diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/PreLimitFilter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/PreLimitFilter.java new file mode 100644 index 00000000..d0542feb --- /dev/null +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/PreLimitFilter.java @@ -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; + } +} diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/PreValidateFilter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/PreValidateFilter.java index 1874d8f5..228a1478 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/PreValidateFilter.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/PreValidateFilter.java @@ -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 { diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/route/SopRouteLocator.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/route/SopRouteLocator.java index e22c57de..e28a7b50 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/route/SopRouteLocator.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/route/SopRouteLocator.java @@ -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 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(); } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/route/ZuulRouteRepository.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/route/ZuulRouteRepository.java index 6ecdc6ea..4f62544f 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/route/ZuulRouteRepository.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/route/ZuulRouteRepository.java @@ -19,35 +19,36 @@ public class ZuulRouteRepository implements RouteRepository { /** * key:nameVersion */ - private Map nameVersionServiceIdMap = new ConcurrentHashMap<>(128); - - public List listAll() { - return new ArrayList<>(nameVersionServiceIdMap.values()); - } + private Map 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 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 values = nameVersionServiceIdMap.values(); + Collection values = nameVersionTargetRouteMap.values(); List idList = values.stream() .map(zuulTargetRoute -> zuulTargetRoute.getRouteDefinition().getId()) .collect(Collectors.toList()); @@ -59,6 +60,6 @@ public class ZuulRouteRepository implements RouteRepository { @Override public void delete(String id) { - nameVersionServiceIdMap.remove(id); + nameVersionTargetRouteMap.remove(id); } } diff --git a/sop-common/sop-service-common/pom.xml b/sop-common/sop-service-common/pom.xml index 689053ee..78e6b080 100644 --- a/sop-common/sop-service-common/pom.xml +++ b/sop-common/sop-service-common/pom.xml @@ -9,9 +9,8 @@ 1.2.0-SNAPSHOT ../pom.xml - com.gitee.sop sop-service-common - ${parent.version} + 1.2.0-SNAPSHOT jar sop-service-common diff --git a/sop-gateway/src/main/java/com/gitee/sop/gateway/config/ZuulConfig.java b/sop-gateway/src/main/java/com/gitee/sop/gateway/config/ZuulConfig.java index 63bd1ad8..5c481ef6 100644 --- a/sop-gateway/src/main/java/com/gitee/sop/gateway/config/ZuulConfig.java +++ b/sop-gateway/src/main/java/com/gitee/sop/gateway/config/ZuulConfig.java @@ -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(); } + } /** diff --git a/sop-gateway/src/main/java/com/gitee/sop/gateway/entity/ConfigRouteLimit.java b/sop-gateway/src/main/java/com/gitee/sop/gateway/entity/ConfigRouteLimit.java index 8926bc43..da4584e5 100644 --- a/sop-gateway/src/main/java/com/gitee/sop/gateway/entity/ConfigRouteLimit.java +++ b/sop-gateway/src/main/java/com/gitee/sop/gateway/entity/ConfigRouteLimit.java @@ -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; diff --git a/sop-gateway/src/main/java/com/gitee/sop/gateway/manager/DbRouteConfigManager.java b/sop-gateway/src/main/java/com/gitee/sop/gateway/manager/DbRouteConfigManager.java index a76e78cd..670d18c1 100644 --- a/sop-gateway/src/main/java/com/gitee/sop/gateway/manager/DbRouteConfigManager.java +++ b/sop-gateway/src/main/java/com/gitee/sop/gateway/manager/DbRouteConfigManager.java @@ -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 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); } diff --git a/sop-test/src/test/java/com/gitee/sop/LimitDemoPostTest.java b/sop-test/src/test/java/com/gitee/sop/LimitDemoPostTest.java new file mode 100644 index 00000000..2fb2e50f --- /dev/null +++ b/sop-test/src/test/java/com/gitee/sop/LimitDemoPostTest.java @@ -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 params = new HashMap(); + 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 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); + } + +}