支持限流持续时间

pull/1/head
tanghc 5 years ago
parent f5b5a1286f
commit 89be4288a7
  1. 4
      changelog.md
  2. 1
      sop-2.4.0.sql
  3. 6
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/LimitNewAddParam.java
  4. 6
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/LimitNewUpdateParam.java
  5. 3
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/bean/ConfigLimitDto.java
  6. 4
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/entity/ConfigLimit.java
  7. 2
      sop-admin/sop-admin-server/src/main/resources/public/index.html
  8. 2
      sop-admin/sop-admin-server/src/main/resources/public/static/css/chunk-0faf9ba2.ed1510dd.css
  9. 2
      sop-admin/sop-admin-server/src/main/resources/public/static/js/app.f05d6d01.js
  10. 1
      sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-09380dd0.6637f3dc.js
  11. 1
      sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-0faf9ba2.88458f58.js
  12. 17
      sop-admin/sop-admin-vue/src/views/service/limit.vue
  13. 35
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ConfigLimitDto.java
  14. 6
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/limit/RedisLimitManager.java
  15. 6
      sop-common/sop-gateway-common/src/main/resources/sop/limit.lua
  16. 10
      sop-test/src/test/java/com/gitee/sop/test/LimitDemoPostTest.java
  17. 1
      sop.sql

@ -1,5 +1,9 @@
# changelog # changelog
## 2.4.0
- 支持限流持续时间(每n秒允许m个请求,需要执行`sop-2.4.0.sql`升级脚本)
## 2.3.2 ## 2.3.2
- 支持spring cloud gateway下restful接口调用 - 支持spring cloud gateway下restful接口调用

@ -0,0 +1 @@
ALTER TABLE `sop`.`config_limit` ADD COLUMN `duration_seconds` INT NOT NULL DEFAULT 1 COMMENT '限流持续时间,默认1秒,即每durationSeconds秒允许多少请求(当limit_type=1时有效)' AFTER `exec_count_per_second`;

@ -39,6 +39,12 @@ public class LimitNewAddParam {
@ApiDocField(description = "每秒可处理请求数") @ApiDocField(description = "每秒可处理请求数")
private Integer execCountPerSecond; private Integer execCountPerSecond;
/** 限流持续时间,默认1秒,即每durationSeconds秒允许多少请求(当limit_type=1时有效) */
@ApiDocField(description = "限流持续时间,默认1秒,即每durationSeconds秒允许多少请求(当limit_type=1时有效")
@NotNull(message = "durationSeconds不能为空")
@Min(value = 1, message = "durationSeconds最小是1")
private Integer durationSeconds;
/** 返回的错误码, 数据库字段:limit_code */ /** 返回的错误码, 数据库字段:limit_code */
@ApiDocField(description = "返回的错误码") @ApiDocField(description = "返回的错误码")
@Length(max = 64, message = "limitCode长度不能超过64") @Length(max = 64, message = "limitCode长度不能超过64")

@ -44,6 +44,12 @@ public class LimitNewUpdateParam {
@ApiDocField(description = "每秒可处理请求数") @ApiDocField(description = "每秒可处理请求数")
private Integer execCountPerSecond; private Integer execCountPerSecond;
/** 限流持续时间,默认1秒,即每durationSeconds秒允许多少请求(当limit_type=1时有效) */
@ApiDocField(description = "限流持续时间,默认1秒,即每durationSeconds秒允许多少请求(当limit_type=1时有效")
@NotNull(message = "durationSeconds不能为空")
@Min(value = 1, message = "durationSeconds最小是1")
private Integer durationSeconds;
/** 返回的错误码, 数据库字段:limit_code */ /** 返回的错误码, 数据库字段:limit_code */
@ApiDocField(description = "返回的错误码") @ApiDocField(description = "返回的错误码")
@Length(max = 64, message = "limitCode长度不能超过64") @Length(max = 64, message = "limitCode长度不能超过64")

@ -27,6 +27,9 @@ public class ConfigLimitDto {
/** 每秒可处理请求数, 数据库字段:exec_count_per_second */ /** 每秒可处理请求数, 数据库字段:exec_count_per_second */
private Integer execCountPerSecond; private Integer execCountPerSecond;
/** 限流持续时间,默认1秒,即每durationSeconds秒允许多少请求(当limit_type=1时有效), 数据库字段:durationSeconds */
private Integer durationSeconds;
/** 返回的错误码, 数据库字段:limit_code */ /** 返回的错误码, 数据库字段:limit_code */
private String limitCode; private String limitCode;

@ -43,6 +43,9 @@ public class ConfigLimit {
/** 每秒可处理请求数, 数据库字段:exec_count_per_second */ /** 每秒可处理请求数, 数据库字段:exec_count_per_second */
private Integer execCountPerSecond; private Integer execCountPerSecond;
/** 限流持续时间,默认1秒,即每durationSeconds秒允许多少请求(当limit_type=1时有效), 数据库字段:durationSeconds */
private Integer durationSeconds;
/** 返回的错误码, 数据库字段:limit_code */ /** 返回的错误码, 数据库字段:limit_code */
private String limitCode; private String limitCode;
@ -66,4 +69,5 @@ public class ConfigLimit {
/** 数据库字段:gmt_modified */ /** 数据库字段:gmt_modified */
private Date gmtModified; private Date gmtModified;
} }

@ -1 +1 @@
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><link rel=icon href=favicon.ico><title>SOP Admin</title><link href=static/css/chunk-elementUI.81cf475c.css rel=stylesheet><link href=static/css/chunk-libs.3dfb7769.css rel=stylesheet><link href=static/css/app.c6dfb7ee.css rel=stylesheet></head><body><noscript><strong>We're sorry but SOP Admin doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script>(function(e){function n(n){for(var r,c,a=n[0],f=n[1],i=n[2],d=0,l=[];d<a.length;d++)c=a[d],u[c]&&l.push(u[c][0]),u[c]=0;for(r in f)Object.prototype.hasOwnProperty.call(f,r)&&(e[r]=f[r]);h&&h(n);while(l.length)l.shift()();return o.push.apply(o,i||[]),t()}function t(){for(var e,n=0;n<o.length;n++){for(var t=o[n],r=!0,c=1;c<t.length;c++){var a=t[c];0!==u[a]&&(r=!1)}r&&(o.splice(n--,1),e=f(f.s=t[0]))}return e}var r={},c={runtime:0},u={runtime:0},o=[];function a(e){return f.p+"static/js/"+({}[e]||e)+"."+{"chunk-09380dd0":"6637f3dc","chunk-25908fca":"05d6b15b","chunk-2c1f2e8f":"f092c0a0","chunk-2d2085ef":"91d75f3c","chunk-2d221c34":"20057287","chunk-4de1c2b6":"e74e3d03","chunk-6f78c9fe":"3ac83b41","chunk-73b2dcec":"05b5ee28","chunk-9b31c83a":"e60adb43","chunk-9f479afe":"0d0ed757","chunk-c3ce42fe":"9517b588"}[e]+".js"}function f(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,f),t.l=!0,t.exports}f.e=function(e){var n=[],t={"chunk-09380dd0":1,"chunk-25908fca":1,"chunk-2c1f2e8f":1,"chunk-4de1c2b6":1,"chunk-73b2dcec":1,"chunk-9b31c83a":1,"chunk-c3ce42fe":1};c[e]?n.push(c[e]):0!==c[e]&&t[e]&&n.push(c[e]=new Promise(function(n,t){for(var r="static/css/"+({}[e]||e)+"."+{"chunk-09380dd0":"390fcb96","chunk-25908fca":"89ab33e8","chunk-2c1f2e8f":"0314067f","chunk-2d2085ef":"31d6cfe0","chunk-2d221c34":"31d6cfe0","chunk-4de1c2b6":"a37cd815","chunk-6f78c9fe":"31d6cfe0","chunk-73b2dcec":"99cf6327","chunk-9b31c83a":"3b12267b","chunk-9f479afe":"31d6cfe0","chunk-c3ce42fe":"6b789903"}[e]+".css",u=f.p+r,o=document.getElementsByTagName("link"),a=0;a<o.length;a++){var i=o[a],d=i.getAttribute("data-href")||i.getAttribute("href");if("stylesheet"===i.rel&&(d===r||d===u))return n()}var l=document.getElementsByTagName("style");for(a=0;a<l.length;a++){i=l[a],d=i.getAttribute("data-href");if(d===r||d===u)return n()}var h=document.createElement("link");h.rel="stylesheet",h.type="text/css",h.onload=n,h.onerror=function(n){var r=n&&n.target&&n.target.src||u,o=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");o.code="CSS_CHUNK_LOAD_FAILED",o.request=r,delete c[e],h.parentNode.removeChild(h),t(o)},h.href=u;var s=document.getElementsByTagName("head")[0];s.appendChild(h)}).then(function(){c[e]=0}));var r=u[e];if(0!==r)if(r)n.push(r[2]);else{var o=new Promise(function(n,t){r=u[e]=[n,t]});n.push(r[2]=o);var i,d=document.createElement("script");d.charset="utf-8",d.timeout=120,f.nc&&d.setAttribute("nonce",f.nc),d.src=a(e),i=function(n){d.onerror=d.onload=null,clearTimeout(l);var t=u[e];if(0!==t){if(t){var r=n&&("load"===n.type?"missing":n.type),c=n&&n.target&&n.target.src,o=new Error("Loading chunk "+e+" failed.\n("+r+": "+c+")");o.type=r,o.request=c,t[1](o)}u[e]=void 0}};var l=setTimeout(function(){i({type:"timeout",target:d})},12e4);d.onerror=d.onload=i,document.head.appendChild(d)}return Promise.all(n)},f.m=e,f.c=r,f.d=function(e,n,t){f.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},f.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(e,n){if(1&n&&(e=f(e)),8&n)return e;if(4&n&&"object"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var r in e)f.d(t,r,function(n){return e[n]}.bind(null,r));return t},f.n=function(e){var n=e&&e.__esModule?function(){return e["default"]}:function(){return e};return f.d(n,"a",n),n},f.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},f.p="",f.oe=function(e){throw console.error(e),e};var i=window["webpackJsonp"]=window["webpackJsonp"]||[],d=i.push.bind(i);i.push=n,i=i.slice();for(var l=0;l<i.length;l++)n(i[l]);var h=d;t()})([]);</script><script src=static/js/chunk-elementUI.298ac98c.js></script><script src=static/js/chunk-libs.75deb05f.js></script><script src=static/js/app.eb1d37b5.js></script></body></html> <!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><link rel=icon href=favicon.ico><title>SOP Admin</title><link href=static/css/chunk-elementUI.81cf475c.css rel=stylesheet><link href=static/css/chunk-libs.3dfb7769.css rel=stylesheet><link href=static/css/app.c6dfb7ee.css rel=stylesheet></head><body><noscript><strong>We're sorry but SOP Admin doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script>(function(e){function n(n){for(var r,c,a=n[0],f=n[1],i=n[2],d=0,l=[];d<a.length;d++)c=a[d],u[c]&&l.push(u[c][0]),u[c]=0;for(r in f)Object.prototype.hasOwnProperty.call(f,r)&&(e[r]=f[r]);h&&h(n);while(l.length)l.shift()();return o.push.apply(o,i||[]),t()}function t(){for(var e,n=0;n<o.length;n++){for(var t=o[n],r=!0,c=1;c<t.length;c++){var a=t[c];0!==u[a]&&(r=!1)}r&&(o.splice(n--,1),e=f(f.s=t[0]))}return e}var r={},c={runtime:0},u={runtime:0},o=[];function a(e){return f.p+"static/js/"+({}[e]||e)+"."+{"chunk-0faf9ba2":"88458f58","chunk-25908fca":"05d6b15b","chunk-2c1f2e8f":"f092c0a0","chunk-2d2085ef":"91d75f3c","chunk-2d221c34":"20057287","chunk-4de1c2b6":"e74e3d03","chunk-6f78c9fe":"3ac83b41","chunk-73b2dcec":"05b5ee28","chunk-9b31c83a":"e60adb43","chunk-9f479afe":"0d0ed757","chunk-c3ce42fe":"9517b588"}[e]+".js"}function f(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,f),t.l=!0,t.exports}f.e=function(e){var n=[],t={"chunk-0faf9ba2":1,"chunk-25908fca":1,"chunk-2c1f2e8f":1,"chunk-4de1c2b6":1,"chunk-73b2dcec":1,"chunk-9b31c83a":1,"chunk-c3ce42fe":1};c[e]?n.push(c[e]):0!==c[e]&&t[e]&&n.push(c[e]=new Promise(function(n,t){for(var r="static/css/"+({}[e]||e)+"."+{"chunk-0faf9ba2":"ed1510dd","chunk-25908fca":"89ab33e8","chunk-2c1f2e8f":"0314067f","chunk-2d2085ef":"31d6cfe0","chunk-2d221c34":"31d6cfe0","chunk-4de1c2b6":"a37cd815","chunk-6f78c9fe":"31d6cfe0","chunk-73b2dcec":"99cf6327","chunk-9b31c83a":"3b12267b","chunk-9f479afe":"31d6cfe0","chunk-c3ce42fe":"6b789903"}[e]+".css",u=f.p+r,o=document.getElementsByTagName("link"),a=0;a<o.length;a++){var i=o[a],d=i.getAttribute("data-href")||i.getAttribute("href");if("stylesheet"===i.rel&&(d===r||d===u))return n()}var l=document.getElementsByTagName("style");for(a=0;a<l.length;a++){i=l[a],d=i.getAttribute("data-href");if(d===r||d===u)return n()}var h=document.createElement("link");h.rel="stylesheet",h.type="text/css",h.onload=n,h.onerror=function(n){var r=n&&n.target&&n.target.src||u,o=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");o.code="CSS_CHUNK_LOAD_FAILED",o.request=r,delete c[e],h.parentNode.removeChild(h),t(o)},h.href=u;var s=document.getElementsByTagName("head")[0];s.appendChild(h)}).then(function(){c[e]=0}));var r=u[e];if(0!==r)if(r)n.push(r[2]);else{var o=new Promise(function(n,t){r=u[e]=[n,t]});n.push(r[2]=o);var i,d=document.createElement("script");d.charset="utf-8",d.timeout=120,f.nc&&d.setAttribute("nonce",f.nc),d.src=a(e),i=function(n){d.onerror=d.onload=null,clearTimeout(l);var t=u[e];if(0!==t){if(t){var r=n&&("load"===n.type?"missing":n.type),c=n&&n.target&&n.target.src,o=new Error("Loading chunk "+e+" failed.\n("+r+": "+c+")");o.type=r,o.request=c,t[1](o)}u[e]=void 0}};var l=setTimeout(function(){i({type:"timeout",target:d})},12e4);d.onerror=d.onload=i,document.head.appendChild(d)}return Promise.all(n)},f.m=e,f.c=r,f.d=function(e,n,t){f.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},f.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(e,n){if(1&n&&(e=f(e)),8&n)return e;if(4&n&&"object"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var r in e)f.d(t,r,function(n){return e[n]}.bind(null,r));return t},f.n=function(e){var n=e&&e.__esModule?function(){return e["default"]}:function(){return e};return f.d(n,"a",n),n},f.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},f.p="",f.oe=function(e){throw console.error(e),e};var i=window["webpackJsonp"]=window["webpackJsonp"]||[],d=i.push.bind(i);i.push=n,i=i.slice();for(var l=0;l<i.length;l++)n(i[l]);var h=d;t()})([]);</script><script src=static/js/chunk-elementUI.298ac98c.js></script><script src=static/js/chunk-libs.75deb05f.js></script><script src=static/js/app.f05d6d01.js></script></body></html>

@ -1 +1 @@
.custom-tree-node{-webkit-box-flex:1;-ms-flex:1;flex:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;font-size:14px;padding-right:8px}.el-input.is-disabled .el-input__inner,.el-radio__input.is-disabled+span.el-radio__label{color:#909399}.limit-tip[data-v-0fad0a08]{cursor:pointer;margin-left:10px} .custom-tree-node{-webkit-box-flex:1;-ms-flex:1;flex:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;font-size:14px;padding-right:8px}.el-input.is-disabled .el-input__inner,.el-radio__input.is-disabled+span.el-radio__label{color:#909399}.limit-tip[data-v-9527c228]{cursor:pointer;margin-left:10px}

@ -206,8 +206,9 @@
<i class="el-icon-question limit-tip"></i> <i class="el-icon-question limit-tip"></i>
</el-tooltip> </el-tooltip>
</el-form-item> </el-form-item>
<el-form-item v-show="isWindowType()" label="每秒可处理请求数" prop="execCountPerSecond" :rules="isWindowType() ? rulesLimit.execCountPerSecond : []"> <el-form-item v-show="isWindowType()" label="请求数" prop="execCountPerSecond" :rules="isWindowType() ? rulesLimit.execCountPerSecond : []">
<el-input-number v-model="limitDialogFormData.execCountPerSecond" controls-position="right" :min="1" /> <el-input-number v-model="limitDialogFormData.durationSeconds" controls-position="right" :min="1" /> 秒可处理
<el-input-number v-model="limitDialogFormData.execCountPerSecond" controls-position="right" :min="1" /> 个请求
</el-form-item> </el-form-item>
<el-form-item v-show="isWindowType()" label="错误码" prop="limitCode" :rules="isWindowType() ? rulesLimit.limitCode : []"> <el-form-item v-show="isWindowType()" label="错误码" prop="limitCode" :rules="isWindowType() ? rulesLimit.limitCode : []">
<el-input v-model="limitDialogFormData.limitCode" /> <el-input v-model="limitDialogFormData.limitCode" />
@ -275,6 +276,7 @@ export default {
limitIp: '', limitIp: '',
limitKey: '', limitKey: '',
execCountPerSecond: 5, execCountPerSecond: 5,
durationSeconds: 1,
limitCode: '', limitCode: '',
limitMsg: '', limitMsg: '',
tokenBucketCount: 5, tokenBucketCount: 5,
@ -438,15 +440,14 @@ export default {
this.limitDialogFormData.limitStatus = 0 this.limitDialogFormData.limitStatus = 0
}, },
infoRender: function(row) { infoRender: function(row) {
const html = []
if (row.limitType === 1) { if (row.limitType === 1) {
html.push('每秒可处理请求数:' + row.execCountPerSecond) const durationSeconds = row.durationSeconds
html.push('<br>subCode:' + row.limitCode) return `${durationSeconds} 秒可处理 ${row.execCountPerSecond} 个请求
html.push('<br>subMsg:' + row.limitMsg) <br>subCode${row.limitCode}
<br>subMsg${row.limitMsg}`
} else if (row.limitType === 2) { } else if (row.limitType === 2) {
html.push('令牌桶容量:' + row.tokenBucketCount) return `令牌桶容量:${row.tokenBucketCount}`
} }
return html.join('')
}, },
onLimitDialogSave: function() { onLimitDialogSave: function() {
this.$refs['limitDialogForm'].validate((valid) => { this.$refs['limitDialogForm'].validate((valid) => {

@ -43,6 +43,9 @@ public class ConfigLimitDto {
/** 每秒可处理请求数, 数据库字段:exec_count_per_second */ /** 每秒可处理请求数, 数据库字段:exec_count_per_second */
private Integer execCountPerSecond; private Integer execCountPerSecond;
/** 限流过期时间,默认1秒,即每durationSeconds秒允许多少请求(当limit_type=1时有效), 数据库字段:durationSeconds */
private Integer durationSeconds;
/** 返回的错误码, 数据库字段:limit_code */ /** 返回的错误码, 数据库字段:limit_code */
private String limitCode; private String limitCode;
@ -66,16 +69,42 @@ public class ConfigLimitDto {
/** /**
* 漏桶计数器 * 窗口计数器
*/
private volatile LoadingCache<Long, AtomicLong> counter;
/**
* 获取持续时间1秒内限制请求则duration设置2
*
* @return 返回缓存保存的值
*/ */
private LoadingCache<Long, AtomicLong> counter = CacheBuilder.newBuilder() public int fetchDuration() {
.expireAfterWrite(2, TimeUnit.SECONDS) Integer durationSeconds = this.durationSeconds;
if (durationSeconds == null || durationSeconds < 1) {
durationSeconds = 1;
}
// 1秒内限制请求,则duration设置2
return durationSeconds + 1;
}
public LoadingCache<Long, AtomicLong> getCounter() {
if (counter == null) {
synchronized (this) {
if (counter == null) {
int duration = fetchDuration();
counter = CacheBuilder.newBuilder()
.expireAfterWrite(duration, TimeUnit.SECONDS)
.build(new CacheLoader<Long, AtomicLong>() { .build(new CacheLoader<Long, AtomicLong>() {
@Override @Override
public AtomicLong load(Long seconds) throws Exception { public AtomicLong load(Long seconds) throws Exception {
return new AtomicLong(0); return new AtomicLong(0);
} }
}); });
}
}
}
return counter;
}
/** /**
* 令牌桶 * 令牌桶

@ -50,7 +50,7 @@ public class RedisLimitManager extends DefaultLimitManager {
public boolean acquire(ConfigLimitDto routeConfig) { public boolean acquire(ConfigLimitDto routeConfig) {
String key = "sop:lmt:" + routeConfig.getRouteId(); String key = "sop:lmt:" + routeConfig.getRouteId();
int limitCount = routeConfig.getExecCountPerSecond(); int limitCount = routeConfig.getExecCountPerSecond();
int duration = routeConfig.fetchDuration();
Object result = redisTemplate.execute( Object result = redisTemplate.execute(
new RedisScript<Long>() { new RedisScript<Long>() {
@Override @Override
@ -71,7 +71,9 @@ public class RedisLimitManager extends DefaultLimitManager {
// KEYS[1] key // KEYS[1] key
Collections.singletonList(key), Collections.singletonList(key),
// ARGV[1] limit // ARGV[1] limit
String.valueOf(limitCount) String.valueOf(limitCount),
// ARGV[2] expire
String.valueOf(duration)
); );
return REDIS_SUCCESS.equals(result); return REDIS_SUCCESS.equals(result);
} }

@ -3,8 +3,8 @@ local limit = tonumber(ARGV[1]) --限流大小
local current = tonumber(redis.call("get", key)) or 0 local current = tonumber(redis.call("get", key)) or 0
if current + 1 > limit then --如果超出限流大小 if current + 1 > limit then --如果超出限流大小
return 0 return 0
else --请求数+1,并设置2秒过期 else --请求数+1,并设置n秒过期
redis.call("INCRBY", key,"1") redis.call("INCRBY", key, "1")
redis.call("expire", key,"2") redis.call("expire", key, ARGV[2])
return 1 return 1
end end

@ -1,6 +1,7 @@
package com.gitee.sop.test; package com.gitee.sop.test;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.gitee.sop.test.alipay.AlipaySignature; import com.gitee.sop.test.alipay.AlipaySignature;
import org.junit.Test; import org.junit.Test;
@ -34,8 +35,10 @@ public class LimitDemoPostTest extends TestBase {
try { try {
countDownLatch.await(); // 等在这里,执行countDownLatch.countDown();集体触发 countDownLatch.await(); // 等在这里,执行countDownLatch.countDown();集体触发
// 业务方法 // 业务方法
doBusiness(Thread.currentThread().getName()); boolean result = doBusiness(Thread.currentThread().getName());
if (result) {
success.incrementAndGet(); success.incrementAndGet();
}
} catch (Exception e) { } catch (Exception e) {
} finally { } finally {
count.countDown(); count.countDown();
@ -46,10 +49,11 @@ public class LimitDemoPostTest extends TestBase {
countDownLatch.countDown(); countDownLatch.countDown();
count.await(); count.await();
System.out.println("成功次数:" + success); System.out.println("成功次数:" + success);
} }
// 这个请求会路由到story服务 // 这个请求会路由到story服务
public void doBusiness(String threadName) throws Exception { public boolean doBusiness(String threadName) throws Exception {
// 公共请求参数 // 公共请求参数
Map<String, String> params = new HashMap<String, String>(); Map<String, String> params = new HashMap<String, String>();
@ -75,6 +79,8 @@ public class LimitDemoPostTest extends TestBase {
String responseData = post(url, params);// 发送请求 String responseData = post(url, params);// 发送请求
System.out.println(responseData); System.out.println(responseData);
JSONObject jsonObject = JSON.parseObject(responseData).getJSONObject("alipay_story_get_response");
return "10000".equals(jsonObject.getString("code"));
} }
} }

@ -95,6 +95,7 @@ CREATE TABLE `config_limit` (
`service_id` varchar(64) NOT NULL DEFAULT '' COMMENT '服务id', `service_id` varchar(64) NOT NULL DEFAULT '' COMMENT '服务id',
`limit_type` tinyint(4) NOT NULL DEFAULT '1' COMMENT '限流策略,1:漏桶策略,2:令牌桶策略', `limit_type` tinyint(4) NOT NULL DEFAULT '1' COMMENT '限流策略,1:漏桶策略,2:令牌桶策略',
`exec_count_per_second` int(11) DEFAULT NULL COMMENT '每秒可处理请求数', `exec_count_per_second` int(11) DEFAULT NULL COMMENT '每秒可处理请求数',
`duration_seconds` int(11) NOT NULL DEFAULT '1' COMMENT '限流持续时间,默认1秒,即每durationSeconds秒允许多少请求(当limit_type=1时有效)',
`limit_code` varchar(64) DEFAULT NULL COMMENT '返回的错误码', `limit_code` varchar(64) DEFAULT NULL COMMENT '返回的错误码',
`limit_msg` varchar(100) DEFAULT NULL COMMENT '返回的错误信息', `limit_msg` varchar(100) DEFAULT NULL COMMENT '返回的错误信息',
`token_bucket_count` int(11) DEFAULT NULL COMMENT '令牌桶容量', `token_bucket_count` int(11) DEFAULT NULL COMMENT '令牌桶容量',

Loading…
Cancel
Save