The method should return true or false to indicate the event is limited or not.
Copy is_ratelimited(1, "login", "3/m", true), return false.
is_ratelimited(11, "login", "3/m", true), return false.
is_ratelimited(21, "login", "3/m", true), return false.
is_ratelimited(30, "login", "3/m", true), return true.
is_ratelimited(65, "login", "3/m", true), return false.
is_ratelimited(300, "login", "3/m", true), return false.
这题,跟网上普遍说的token bucket啥的有点不同,感觉人家不会突然改频率什么的。嘛,花了很长时间才看懂为那个increment是搞毛的。自己的不知道是哪里错了,所以先把答案贴上来了。另外加一句这个用来干嘛,可以防用户刷票,防暴力破解还能限制数据库expensive操作的执行频繁度。
Copy public class RateLimiter {
private HashMap < String , List < Integer >> map = new HashMap <>();
/**
* @param timestamp the current timestamp
* @param event the string to distinct different event
* @param rate the format is [integer]/[s/m/h/d]
* @param increment whether we should increase the counter
* @return true of false to indicate the event is limited or not
*/
public boolean isRatelimited ( int current_timestamp , String event , String rate , boolean increment) {
// Write your code here
int start = rate . indexOf ( "/" );
int limit = Integer . parseInt ( rate . substring ( 0 , start));
String type = rate . substring (start + 1 , rate . length ());
int duration = 1 ;
if ( type . equals ( "m" ))
duration = duration * 60 ;
else if ( type . equals ( "h" ))
duration = duration * 60 * 60 ;
else if ( type . equals ( "d" ))
duration = duration * 60 * 60 * 24 ;
int start_timestamp = current_timestamp - duration + 1 ;
if ( ! map . containsKey (event))
map . put (event , new ArrayList < Integer >());
int count = count_events( map . get(event) , start_timestamp) ;
boolean is_ratelimited = count >= limit;
if (increment && ! is_ratelimited)
insert_event( map . get(event) , current_timestamp) ;
return is_ratelimited;
}
public void insert_event ( List < Integer > event , int timestamp) {
event . add (timestamp);
}
// use binary search algorithm to count how many events happened
// after start_timestamp because event is sorted by timestamp
public int count_events ( List < Integer > event , int start_timestamp) {
int l = 0 , r = event . size () - 1 ;
if (r == - 1 )
return 0 ;
if ( event . get (r) < start_timestamp)
return 0 ;
int ans = 0 ;
while (l <= r) {
int mid = (l + r) >> 1 ;
if ( event . get (mid) >= start_timestamp) {
ans = mid;
r = mid - 1 ;
} else
l = mid + 1 ;
}
return event . size () - 1 - ans + 1 ;
}
Copy static class TokenBucket {
private final int capacity;
private final int tokensPerSeconds;
private int tokens = 0 ;
private long timestamp = System . currentTimeMillis ();
public TokenBucket ( int tokensPerUnit , TimeUnit unit) {
capacity = tokensPerSeconds = ( int ) (tokensPerUnit / unit . toSeconds ( 1L ));
}
public boolean take () {
long now = System . currentTimeMillis ();
tokens += ( int ) ((now - timestamp) * tokensPerSeconds / 1000 );
if (tokens > capacity) tokens = capacity;
timestamp = now;
if (tokens < 1 ) return false ;
tokens -- ;
return true ;
}
}
public static void main( String [] args) throws InterruptedException {
TokenBucket bucket = new TokenBucket( 250 , TimeUnit . MINUTES ) ;
Thread . sleep ( 1000L );
for ( int i = 0 ; i < 5 ; i ++ ) {
System . out . println ( bucket . take ());
}
Thread . sleep ( 1000L );
for ( int i = 0 ; i < 5 ; i ++ ) {
System . out . println ( bucket . take ());
}
}