Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
S
spring-boot-starter-dsp
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
shenjiaqing
spring-boot-starter-dsp
Commits
a4d5a4f5
You need to sign in or sign up before continuing.
Commit
a4d5a4f5
authored
May 26, 2022
by
shenjiaqing
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
提交代码
parent
a65410c5
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
40 additions
and
188 deletions
+40
-188
build.gradle
build.gradle
+2
-2
Commons.java
...om/duiba/spring/boot/starter/dsp/rateLimiter/Commons.java
+0
-4
LimitAspect.java
...uiba/spring/boot/starter/dsp/rateLimiter/LimitAspect.java
+16
-4
TokenBucketLimiter.java
...ring/boot/starter/dsp/rateLimiter/TokenBucketLimiter.java
+0
-27
TokenBucketLimiterPolicy.java
...oot/starter/dsp/rateLimiter/TokenBucketLimiterPolicy.java
+0
-60
limit.lua
...boot-starter-dsp-rateLimiter/src/main/resources/limit.lua
+13
-90
DubboLogSamplerContextFilter.java
...rter/dsp/sampler/filter/DubboLogSamplerContextFilter.java
+9
-1
No files found.
build.gradle
View file @
a4d5a4f5
...
...
@@ -38,7 +38,7 @@ allprojects {
}
group
=
"cn.com.duiba.boot"
version
=
"0.0.4
4
"
version
=
"0.0.4
9
"
}
subprojects
{
...
...
@@ -81,7 +81,7 @@ subprojects {
dependency
(
'org.projectlombok:lombok:1.18.12'
)
//升级算法版本
dependency
(
"cn.com.duiba.nezha-alg:alg-model:2.28.
7
"
)
dependency
(
"cn.com.duiba.nezha-alg:alg-model:2.28.
11-SNAPSHOT
"
)
dependency
(
'io.github.openfeign:feign-httpclient:10.10.1.dbfixed'
)
//log4j fix
...
...
spring-boot-starter-dsp-rateLimiter/src/main/java/cn/com/duiba/spring/boot/starter/dsp/rateLimiter/Commons.java
View file @
a4d5a4f5
...
...
@@ -18,9 +18,5 @@ public class Commons {
return
redisScript
;
}
@Bean
public
TokenBucketLimiter
createTokenBucketLimiter
()
{
return
new
TokenBucketLimiter
();
}
}
spring-boot-starter-dsp-rateLimiter/src/main/java/cn/com/duiba/spring/boot/starter/dsp/rateLimiter/LimitAspect.java
View file @
a4d5a4f5
...
...
@@ -8,9 +8,14 @@ import org.aspectj.lang.reflect.MethodSignature;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.data.redis.core.StringRedisTemplate
;
import
org.springframework.data.redis.core.script.DefaultRedisScript
;
import
org.springframework.stereotype.Component
;
import
javax.annotation.Resource
;
import
java.lang.reflect.Method
;
import
java.util.Collections
;
import
java.util.Map
;
import
java.util.Objects
;
import
java.util.concurrent.ConcurrentHashMap
;
import
java.util.concurrent.atomic.AtomicLong
;
...
...
@@ -27,6 +32,7 @@ public class LimitAspect {
private
final
static
Integer
REDIS_NODE_NUM
=
4
;
static
{
map
.
put
(
"rate.limit:cn.com.duiba.tuia.algo.engine.adx.service.v4.AdxAlgoService-execute-limit1"
,
new
AtomicLong
());
map
.
put
(
"rate.limit:cn.com.duiba.tuia.algo.engine.adx.service.v4.AdxAlgoService-aaa-limit1"
,
new
AtomicLong
());
redisNodeIndex
.
put
(
0L
,
14
);
redisNodeIndex
.
put
(
1L
,
18
);
redisNodeIndex
.
put
(
2L
,
296
);
...
...
@@ -48,8 +54,11 @@ public class LimitAspect {
@Autowired
private
RateLimitProperties
rateLimitProperties
;
@Autowired
private
TokenBucketLimiter
tokenBucketLimiter
;
@Resource
private
StringRedisTemplate
stringRedisTemplate
;
@Resource
private
DefaultRedisScript
<
Long
>
redisLuaScript
;
@Pointcut
(
value
=
"@annotation(cn.com.duiba.spring.boot.starter.dsp.rateLimiter.RateLimit)"
)
public
void
rateLimitPointcut
()
{
...
...
@@ -86,8 +95,9 @@ public class LimitAspect {
if
(
limitCount
<=
0
)
{
throw
new
RuntimeException
(
"已经到设置限流次数"
);
}
TokenBucketLimiterPolicy
tokenBucketLimiterPolicy
=
new
TokenBucketLimiterPolicy
(
limitCount
,
200
);
if
(
tokenBucketLimiter
.
acquire
(
redisKey
,
tokenBucketLimiterPolicy
))
{
Long
number
=
stringRedisTemplate
.
execute
(
redisLuaScript
,
Collections
.
singletonList
(
redisKey
),
String
.
valueOf
(
limitCount
),
String
.
valueOf
(
rateLimit
.
time
()));
if
(
Objects
.
nonNull
(
number
)
&&
number
>
0
)
{
logger
.
info
(
"限流时间段内访问第:{} 次"
,
number
);
return
joinPoint
.
proceed
();
}
...
...
@@ -95,4 +105,6 @@ public class LimitAspect {
throw
new
RuntimeException
(
"已经到设置限流次数"
);
}
}
spring-boot-starter-dsp-rateLimiter/src/main/java/cn/com/duiba/spring/boot/starter/dsp/rateLimiter/TokenBucketLimiter.java
deleted
100644 → 0
View file @
a65410c5
package
cn
.
com
.
duiba
.
spring
.
boot
.
starter
.
dsp
.
rateLimiter
;
import
com.google.common.collect.Lists
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.data.redis.core.StringRedisTemplate
;
import
org.springframework.data.redis.core.script.DefaultRedisScript
;
import
javax.annotation.Resource
;
import
java.util.Objects
;
@Slf4j
public
class
TokenBucketLimiter
{
@Resource
private
StringRedisTemplate
stringRedisTemplate
;
@Resource
private
DefaultRedisScript
<
Long
>
redisLuaScript
;
boolean
acquire
(
String
key
,
TokenBucketLimiterPolicy
policy
)
{
if
(
policy
==
null
)
{
return
true
;
}
Long
remain
=
stringRedisTemplate
.
execute
(
redisLuaScript
,
Lists
.
asList
(
key
,
new
String
[]{}),
policy
.
toParams
());
return
Objects
.
nonNull
(
remain
)
&&
remain
>
0
;
}
}
spring-boot-starter-dsp-rateLimiter/src/main/java/cn/com/duiba/spring/boot/starter/dsp/rateLimiter/TokenBucketLimiterPolicy.java
deleted
100644 → 0
View file @
a65410c5
package
cn
.
com
.
duiba
.
spring
.
boot
.
starter
.
dsp
.
rateLimiter
;
import
com.google.common.collect.Lists
;
import
lombok.Data
;
import
java.util.List
;
/**
* 令牌桶限流器的执行对象
*/
@Data
public
class
TokenBucketLimiterPolicy
{
/**
* 限流时间间隔
* (重置桶内令牌的时间间隔)
*/
private
long
resetBucketInterval
;
/**
* 最大令牌数量
*/
private
long
bucketMaxTokens
;
/**
* 初始可存储数量
*/
private
long
initTokens
;
/**
* 每个令牌产生的时间
*/
private
long
intervalPerPermit
;
public
TokenBucketLimiterPolicy
(
long
bucketMaxTokens
,
long
maxBurstTime
)
{
// 最大令牌数
this
.
bucketMaxTokens
=
bucketMaxTokens
;
// 限流时间间隔
this
.
resetBucketInterval
=
1000
;
if
(
bucketMaxTokens
>=
200
)
{
intervalPerPermit
=
50
;
initTokens
=
bucketMaxTokens
/
50
;
return
;
}
// 令牌的产生间隔 = 限流时间 / 最大令牌数
intervalPerPermit
=
resetBucketInterval
/
bucketMaxTokens
;
// 初始令牌数 = 最大的突发流量的持续时间 / 令牌产生间隔
// 用 最大的突发流量的持续时间 计算的结果更加合理,并不是每次初始化都要将桶装满
initTokens
=
Math
.
min
(
Math
.
max
(
maxBurstTime
*
bucketMaxTokens
/
resetBucketInterval
,
1
),
bucketMaxTokens
);
}
public
String
[]
toParams
()
{
List
<
String
>
list
=
Lists
.
newArrayList
();
list
.
add
(
String
.
valueOf
(
getIntervalPerPermit
()));
list
.
add
(
String
.
valueOf
(
System
.
currentTimeMillis
()));
list
.
add
(
String
.
valueOf
(
getInitTokens
()));
list
.
add
(
String
.
valueOf
(
getBucketMaxTokens
()));
list
.
add
(
String
.
valueOf
(
getResetBucketInterval
()));
return
list
.
toArray
(
new
String
[]{});
}
}
spring-boot-starter-dsp-rateLimiter/src/main/resources/limit.lua
View file @
a4d5a4f5
local
intervalPerTokens
=
tonumber
(
ARGV
[
1
])
local
curTime
=
tonumber
(
ARGV
[
2
])
local
initTokens
=
tonumber
(
ARGV
[
3
])
local
bucketMaxTokens
=
tonumber
(
ARGV
[
4
])
local
resetBucketInterval
=
tonumber
(
ARGV
[
5
])
local
bucket
=
redis
.
call
(
'hgetall'
,
KEYS
[
1
])
local
currentTokens
-- 若当前桶未初始化,先初始化令牌桶
if
table
.
maxn
(
bucket
)
==
0
then
-- 初始桶内令牌
currentTokens
=
initTokens
-- 设置桶最近的填充时间是当前
redis
.
call
(
'hset'
,
KEYS
[
1
],
'lastRefillTime'
,
curTime
)
-- 初始化令牌桶的过期时间, 设置为间隔的 1.5 倍
redis
.
call
(
'pexpire'
,
KEYS
[
1
],
resetBucketInterval
*
1
.
5
)
-- 若桶已初始化,开始计算桶内令牌
-- 为什么等于 4 ? 因为有两对 field, 加起来长度是 4
-- { "lastRefillTime(上一次更新时间)","curTime(更新时间值)","tokensRemaining(当前保留的令牌)","令牌数" }
elseif
table
.
maxn
(
bucket
)
==
4
then
-- 上次填充时间
local
lastRefillTime
=
tonumber
(
bucket
[
2
])
-- 剩余的令牌数
local
tokensRemaining
=
tonumber
(
bucket
[
4
])
-- 当前时间大于上次填充时间
if
curTime
>
lastRefillTime
then
-- 拿到当前时间与上次填充时间的时间间隔
-- 举例理解: curTime = 2620 , lastRefillTime = 2000, intervalSinceLast = 620
local
intervalSinceLast
=
curTime
-
lastRefillTime
-- 如果当前时间间隔 大于 令牌的生成间隔
-- 举例理解: intervalSinceLast = 620, resetBucketInterval = 1000
if
intervalSinceLast
>
resetBucketInterval
then
-- 将当前令牌填充满
currentTokens
=
initTokens
-- 更新重新填充时间
redis
.
call
(
'hset'
,
KEYS
[
1
],
'lastRefillTime'
,
curTime
)
-- 如果当前时间间隔 小于 令牌的生成间隔
else
-- 可授予的令牌 = 向下取整数( 上次填充时间与当前时间的时间间隔 / 两个令牌许可之间的时间间隔 )
-- 举例理解 : intervalPerTokens = 200 ms , 令牌间隔时间为 200ms
-- intervalSinceLast = 620 ms , 当前距离上一个填充时间差为 620ms
-- grantedTokens = 620/200 = 3.1 = 3
local
grantedTokens
=
math.floor
(
intervalSinceLast
/
intervalPerTokens
)
-- 可授予的令牌 > 0 时
-- 举例理解 : grantedTokens = 620/200 = 3.1 = 3
if
grantedTokens
>
0
then
-- 生成的令牌 = 上次填充时间与当前时间的时间间隔 % 两个令牌许可之间的时间间隔
-- 举例理解 : padMillis = 620%200 = 20
-- curTime = 2620
-- curTime - padMillis = 2600
local
padMillis
=
math.fmod
(
intervalSinceLast
,
intervalPerTokens
)
-- 将当前令牌桶更新到上一次生成时间
redis
.
call
(
'hset'
,
KEYS
[
1
],
'lastRefillTime'
,
curTime
-
padMillis
)
end
-- 更新当前令牌桶中的令牌数
-- Math.min(根据时间生成的令牌数 + 剩下的令牌数, 桶的限制) => 超出桶最大令牌的就丢弃
currentTokens
=
math.min
(
grantedTokens
+
tokensRemaining
,
bucketMaxTokens
)
end
else
-- 如果当前时间小于或等于上次更新的时间, 说明刚刚初始化, 当前令牌数量等于桶内令牌数
-- 不需要重新填充
currentTokens
=
tokensRemaining
end
end
-- 如果当前桶内令牌小于 0,抛出异常
assert
(
currentTokens
>=
0
)
-- 如果当前令牌 == 0 ,更新桶内令牌, 返回 0
if
currentTokens
==
0
then
redis
.
call
(
'hset'
,
KEYS
[
1
],
'tokensRemaining'
,
currentTokens
)
return
0
else
-- 如果当前令牌 大于 0, 更新当前桶内的令牌 -1 , 再返回当前桶内令牌数
redis
.
call
(
'hset'
,
KEYS
[
1
],
'tokensRemaining'
,
currentTokens
-
1
)
return
currentTokens
--Lua脚本
--- 时间窗最大并发数
local
limit
=
tonumber
(
ARGV
[
1
])
--- 时间窗内当前并发数
local
current
=
tonumber
(
redis
.
call
(
'get'
,
KEYS
[
1
])
or
"0"
)
--如果超出限流大小
if
current
+
1
>
limit
then
return
0
else
--请求数+1,并设置2秒过期
redis
.
call
(
"INCRBY"
,
KEYS
[
1
],
"1"
)
redis
.
call
(
"expire"
,
KEYS
[
1
],
"2"
)
return
current
+
1
end
spring-boot-starter-dsp-sampler/src/main/java/cn/com/duiba/spring/boot/starter/dsp/sampler/filter/DubboLogSamplerContextFilter.java
View file @
a4d5a4f5
...
...
@@ -2,16 +2,21 @@ package cn.com.duiba.spring.boot.starter.dsp.sampler.filter;
import
cn.com.duiba.spring.boot.starter.dsp.sampler.SamplerLog
;
import
cn.com.duiba.spring.boot.starter.dsp.sampler.constants.SamplerLogConstant
;
import
org.apache.commons.lang3.StringUtils
;
import
org.apache.dubbo.common.URL
;
import
org.apache.dubbo.common.constants.CommonConstants
;
import
org.apache.dubbo.common.extension.Activate
;
import
org.apache.dubbo.rpc.*
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
java.util.Objects
;
@Activate
(
group
=
{
CommonConstants
.
CONSUMER
,
CommonConstants
.
PROVIDER
},
order
=
-
2000
)
public
class
DubboLogSamplerContextFilter
implements
Filter
{
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
DubboLogSamplerContextFilter
.
class
);
@Override
public
Result
invoke
(
Invoker
<?>
invoker
,
Invocation
invocation
)
throws
RpcException
{
URL
url
=
invoker
.
getUrl
();
...
...
@@ -19,8 +24,9 @@ public class DubboLogSamplerContextFilter implements Filter {
boolean
isConsumerSide
=
Objects
.
equals
(
sideKey
,
CommonConstants
.
CONSUMER
);
// 1、判断是消费者还是服务提供者
if
(
isConsumerSide
)
{
if
(
isConsumerSide
&&
StringUtils
.
isNotBlank
(
SamplerLog
.
getSamplerId
())
)
{
// 如果是消费者,将打印日志标识符set至上下文中
RpcContext
.
getContext
().
setAttachment
(
SamplerLogConstant
.
DUBBO_SAMPLING_ID
,
SamplerLog
.
getSamplerId
());
}
else
{
// 如果是服务提供者,从上下文取出
...
...
@@ -36,6 +42,8 @@ public class DubboLogSamplerContextFilter implements Filter {
}
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment