1
1
/*
2
- * Copyright 1999-2018 Alibaba Group Holding Ltd.
2
+ * Copyright 1999-2024 Alibaba Group Holding Ltd.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
15
15
*/
16
16
package com .alibaba .csp .sentinel .slots .block .flow .param ;
17
17
18
- import java .lang .reflect .Array ;
19
- import java .util .ArrayList ;
20
- import java .util .Collection ;
21
- import java .util .Collections ;
22
- import java .util .List ;
23
- import java .util .Set ;
24
- import java .util .concurrent .TimeUnit ;
25
- import java .util .concurrent .atomic .AtomicInteger ;
26
- import java .util .concurrent .atomic .AtomicLong ;
27
-
28
18
import com .alibaba .csp .sentinel .cluster .ClusterStateManager ;
29
19
import com .alibaba .csp .sentinel .cluster .TokenResult ;
30
20
import com .alibaba .csp .sentinel .cluster .TokenResultStatus ;
37
27
import com .alibaba .csp .sentinel .slots .statistic .cache .CacheMap ;
38
28
import com .alibaba .csp .sentinel .util .TimeUtil ;
39
29
30
+ import java .lang .reflect .Array ;
31
+ import java .time .format .DateTimeFormatter ;
32
+ import java .util .*;
33
+ import java .util .concurrent .TimeUnit ;
34
+ import java .util .concurrent .atomic .AtomicLong ;
35
+ import java .util .concurrent .atomic .AtomicReference ;
36
+
40
37
/**
41
38
* Rule checker for parameter flow control.
42
39
*
46
43
public final class ParamFlowChecker {
47
44
48
45
public static boolean passCheck (ResourceWrapper resourceWrapper , /*@Valid*/ ParamFlowRule rule , /*@Valid*/ int count ,
49
- Object ... args ) {
46
+ Object ... args ) {
50
47
if (args == null ) {
51
48
return true ;
52
49
}
@@ -79,7 +76,7 @@ private static boolean passLocalCheck(ResourceWrapper resourceWrapper, ParamFlow
79
76
Object value ) {
80
77
try {
81
78
if (Collection .class .isAssignableFrom (value .getClass ())) {
82
- for (Object param : ((Collection )value )) {
79
+ for (Object param : ((Collection ) value )) {
83
80
if (!passSingleValueCheck (resourceWrapper , rule , count , param )) {
84
81
return false ;
85
82
}
@@ -117,7 +114,7 @@ static boolean passSingleValueCheck(ResourceWrapper resourceWrapper, ParamFlowRu
117
114
int itemThreshold = rule .getParsedHotItems ().get (value );
118
115
return ++threadCount <= itemThreshold ;
119
116
}
120
- long threshold = (long )rule .getCount ();
117
+ long threshold = (long ) rule .getCount ();
121
118
return ++threadCount <= threshold ;
122
119
}
123
120
@@ -127,16 +124,16 @@ static boolean passSingleValueCheck(ResourceWrapper resourceWrapper, ParamFlowRu
127
124
static boolean passDefaultLocalCheck (ResourceWrapper resourceWrapper , ParamFlowRule rule , int acquireCount ,
128
125
Object value ) {
129
126
ParameterMetric metric = getParameterMetric (resourceWrapper );
130
- CacheMap <Object , AtomicLong > tokenCounters = metric == null ? null : metric .getRuleTokenCounter (rule );
131
- CacheMap <Object , AtomicLong > timeCounters = metric == null ? null : metric .getRuleTimeCounter (rule );
127
+ CacheMap <Object , AtomicReference <TokenUpdateStatus >> tokenCounters = metric == null ? null : metric .getRuleStampedTokenCounter (rule );
132
128
133
- if (tokenCounters == null || timeCounters == null ) {
129
+ DateTimeFormatter dtf = DateTimeFormatter .ISO_DATE_TIME ;
130
+ if (tokenCounters == null ) {
134
131
return true ;
135
132
}
136
133
137
134
// Calculate max token count (threshold)
138
135
Set <Object > exclusionItems = rule .getParsedHotItems ().keySet ();
139
- long tokenCount = (long )rule .getCount ();
136
+ long tokenCount = (long ) rule .getCount ();
140
137
if (exclusionItems .contains (value )) {
141
138
tokenCount = rule .getParsedHotItems ().get (value );
142
139
}
@@ -153,49 +150,44 @@ static boolean passDefaultLocalCheck(ResourceWrapper resourceWrapper, ParamFlowR
153
150
while (true ) {
154
151
long currentTime = TimeUtil .currentTimeMillis ();
155
152
156
- AtomicLong lastAddTokenTime = timeCounters .putIfAbsent (value , new AtomicLong (currentTime ));
157
- if (lastAddTokenTime == null ) {
153
+ AtomicReference <TokenUpdateStatus > atomicLastStatus = tokenCounters .putIfAbsent (value , new AtomicReference <>(
154
+ new TokenUpdateStatus (currentTime , maxCount - acquireCount )
155
+ ));
156
+ if (atomicLastStatus == null ) {
158
157
// Token never added, just replenish the tokens and consume {@code acquireCount} immediately.
159
- tokenCounters .putIfAbsent (value , new AtomicLong (maxCount - acquireCount ));
160
158
return true ;
161
159
}
162
160
163
161
// Calculate the time duration since last token was added.
164
- long passTime = currentTime - lastAddTokenTime .get ();
162
+ TokenUpdateStatus lastStatus = atomicLastStatus .get ();
163
+ long passTime = currentTime - lastStatus .getLastAddTokenTime ();
165
164
// A simplified token bucket algorithm that will replenish the tokens only when statistic window has passed.
165
+ long newQps ;
166
166
if (passTime > rule .getDurationInSec () * 1000 ) {
167
- AtomicLong oldQps = tokenCounters .putIfAbsent (value , new AtomicLong (maxCount - acquireCount ));
168
- if (oldQps == null ) {
169
- // Might not be accurate here.
170
- lastAddTokenTime .set (currentTime );
171
- return true ;
172
- } else {
173
- long restQps = oldQps .get ();
174
- long toAddCount = (passTime * tokenCount ) / (rule .getDurationInSec () * 1000 );
175
- long newQps = toAddCount + restQps > maxCount ? (maxCount - acquireCount )
167
+ long restQps = lastStatus .getRestQps ();
168
+ long toAddCount = (passTime * tokenCount ) / (rule .getDurationInSec () * 1000 );
169
+ newQps = toAddCount + restQps > maxCount ? (maxCount - acquireCount )
176
170
: (restQps + toAddCount - acquireCount );
177
171
178
- if (newQps < 0 ) {
179
- return false ;
180
- }
181
- if (oldQps .compareAndSet (restQps , newQps )) {
182
- lastAddTokenTime .set (currentTime );
183
- return true ;
184
- }
185
- Thread .yield ();
172
+ if (newQps < 0 ) {
173
+ return false ;
186
174
}
175
+ TokenUpdateStatus newStatus = new TokenUpdateStatus (currentTime , newQps );
176
+ if (atomicLastStatus .compareAndSet (lastStatus , newStatus )) {
177
+ return true ;
178
+ }
179
+ Thread .yield ();
187
180
} else {
188
- AtomicLong oldQps = tokenCounters .get (value );
189
- if (oldQps != null ) {
190
- long oldQpsValue = oldQps .get ();
191
- if (oldQpsValue - acquireCount >= 0 ) {
192
- if (oldQps .compareAndSet (oldQpsValue , oldQpsValue - acquireCount )) {
193
- return true ;
194
- }
195
- } else {
196
- return false ;
181
+ newQps = lastStatus .getRestQps () - acquireCount ;
182
+ if (newQps >= 0 ) {
183
+ TokenUpdateStatus newStatus = new TokenUpdateStatus (lastStatus .getLastAddTokenTime (), newQps );
184
+ if (atomicLastStatus .compareAndSet (lastStatus , newStatus )) {
185
+ return true ;
197
186
}
187
+ } else {
188
+ return false ;
198
189
}
190
+
199
191
Thread .yield ();
200
192
}
201
193
}
@@ -211,7 +203,7 @@ static boolean passThrottleLocalCheck(ResourceWrapper resourceWrapper, ParamFlow
211
203
212
204
// Calculate max token count (threshold)
213
205
Set <Object > exclusionItems = rule .getParsedHotItems ().keySet ();
214
- long tokenCount = (long )rule .getCount ();
206
+ long tokenCount = (long ) rule .getCount ();
215
207
if (exclusionItems .contains (value )) {
216
208
tokenCount = rule .getParsedHotItems ().get (value );
217
209
}
@@ -261,7 +253,7 @@ private static ParameterMetric getParameterMetric(ResourceWrapper resourceWrappe
261
253
@ SuppressWarnings ("unchecked" )
262
254
private static Collection <Object > toCollection (Object value ) {
263
255
if (value instanceof Collection ) {
264
- return (Collection <Object >)value ;
256
+ return (Collection <Object >) value ;
265
257
} else if (value .getClass ().isArray ()) {
266
258
List <Object > params = new ArrayList <Object >();
267
259
int length = Array .getLength (value );
0 commit comments