Skip to content

Instantly share code, notes, and snippets.

@aneessh18
Created April 19, 2024 11:55
Show Gist options
  • Save aneessh18/cee4fc9935799c116a3dcea04306623f to your computer and use it in GitHub Desktop.
Save aneessh18/cee4fc9935799c116a3dcea04306623f to your computer and use it in GitHub Desktop.
Rate Limiter Module
/*
Rate Limiter Interface
*/
package io.aneessh18.postmaninterview;
public interface RateLimiter {
boolean isAllowed(Request request);
}
/*
Request Model
*/
package io.aneessh18.postmaninterview;
public class Request {
public long time;
public String msg;
public String userId;
}
/*
Rest API class
*/
package io.aneessh18.postmaninterview;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/v1/api")
public class Controller {
private final RateLimiter rateLimiter = new SlidingWindowRateLimiter(10, 10000);
private final RateLimiter userIdRateLimiter = new UserIdSlidingWindowRateLimiter(10, 10000);
@GetMapping("/echo-path/{path}")
public ResponseEntity<String> echo(@PathVariable("path") String path,@RequestParam(value = "param", required = false) String param){
Request request = new Request();
request.msg = "msg";
if (rateLimiter.isAllowed(request)){
return ResponseEntity.ok(String.format("Request to path %s with %s param", path, param));
}else{
return ResponseEntity.status(429).build();
}
}
@GetMapping("/echo-user/{user}")
public ResponseEntity<String> echoUser(@PathVariable("user") String userId){
Request request = new Request();
request.msg = "msg";
request.userId=userId;
if (userIdRateLimiter.isAllowed(request)){
return ResponseEntity.ok(String.format("Request to path %s", userId));
}else{
return ResponseEntity.status(429).build();
}
}
}
/*
Sliding Window Rate Limiter
*/
package io.aneessh18.postmaninterview;
import java.util.ArrayDeque;
import java.util.Queue;
public class SlidingWindowRateLimiter implements RateLimiter {
private final int numRequests;
private final int window;
private final Queue<Request> queue = new ArrayDeque<>();
public SlidingWindowRateLimiter(int numRequests, int window) {
this.numRequests = numRequests;
this.window = window;
}
@Override
public boolean isAllowed(Request request){
long time = System.currentTimeMillis();
// prune the queue
// list -> time-window
if(!queue.isEmpty() && queue.peek().time+window<=time){
queue.poll();
}
if(queue.size()< numRequests){
request.time=time;
queue.add(request);
return true;
} else{
return false;
}
}
}
/*
UserIdSlidingWindowRateLimiter
*/
package io.aneessh18.postmaninterview;
import java.util.ArrayDeque;
import java.util.HashMap;
import java.util.Queue;
public class UserIdSlidingWindowRateLimiter implements RateLimiter{
private final int numRequests;
private final int window;
private final HashMap<String, Queue<Request>> userQueue;
public UserIdSlidingWindowRateLimiter(int numRequests, int window) {
this.numRequests = numRequests;
this.window = window;
this.userQueue = new HashMap<>();
}
@Override
public boolean isAllowed(Request request){
String userId = request.userId;
userQueue.putIfAbsent(userId, new ArrayDeque<>());
Queue<Request> queue = userQueue.get(userId);
long time = System.currentTimeMillis();
// prune the queue
// list -> time-window
if(!queue.isEmpty() && queue.peek().time+window<=time){
queue.poll();
}
if(queue.size()< numRequests){
request.time=time;
queue.add(request);
return true;
} else{
return false;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment