-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[feat] Redis를 이용한 Rate Limiter 적용 (#34) #38
Conversation
private final RedisClient redisClient; | ||
|
||
@Bean | ||
public LettuceBasedProxyManager<String> proxyManager() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
버킷 생성 및 관리
@@ -33,4 +40,9 @@ public RedisTemplate<String, Long> redisTemplate(RedisConnectionFactory connecti | |||
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory connectionFactory) { | |||
return new StringRedisTemplate(connectionFactory); | |||
} | |||
|
|||
@Bean | |||
public RedisClient redisClient() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LettuceBasedProxyManager 객체 생성에 필요
|
||
@Override | ||
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { | ||
resolvers.add(puangUserArgumentResolver); | ||
} | ||
|
||
@Override | ||
public void addInterceptors(InterceptorRegistry registry) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interceptor 등록
|
||
@Getter | ||
@RequiredArgsConstructor | ||
public enum RateLimitPolicy { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rate Limit 정책
private final String BLOCKED_PREFIX = "rate-limiter-blocked:"; | ||
|
||
@Override | ||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
버킷의 key 생성
String servletPath = request.getServletPath(); | ||
String key = kakaoId + servletPath; | ||
|
||
if (isBlocked(key)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
차단된 상태인지 확인
} | ||
|
||
// key에 해당하는 bucket 로드. 없으면 생성 | ||
Bucket bucket = proxyManager.getProxy(LIMITER_PREFIX + key, () -> getRateLimitPolicy(servletPath)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
버킷 생성 또는 로드
// key에 해당하는 bucket 로드. 없으면 생성 | ||
Bucket bucket = proxyManager.getProxy(LIMITER_PREFIX + key, () -> getRateLimitPolicy(servletPath)); | ||
|
||
if (!bucket.tryConsume(1)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
토큰 소비 시도
토큰 소진 시 사용자 차단
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
기능 대비 쉽지 않은 부분이 많았고 또 제가 급박하게 드린 task였는데 잘 소화해주셔서 감사드립니다. 남은 시간 동안 Authentication & Authorization에 대한 단위 테스트를 작성해보셔도 좋을 것 같네요!
public abstract Bandwidth getLimit(); | ||
|
||
private final String planName; | ||
|
||
public static Bandwidth resolvePlan(final String targetPlan) { | ||
return Arrays.stream(RateLimitPolicy.values()) | ||
.filter(policy -> policy.getPlanName().equals(targetPlan)) | ||
.map(RateLimitPolicy::getLimit) | ||
.findFirst() | ||
.orElseThrow(() -> new RateLimiterException(ResponseCode.RATE_LIMITER_POLICY_ERROR)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
변수는 맨 위로 옮기는 게 좋을 것 같아요~!
private BucketConfiguration getRateLimitPolicy(String contextPath) { | ||
switch (contextPath) { | ||
case "/api/photo-request": | ||
return bucketConfiguration("heavy"); | ||
|
||
default: | ||
return bucketConfiguration("general"); | ||
} | ||
} | ||
|
||
private void blockClient(String key) { | ||
redisTemplate.opsForValue().set(BLOCKED_PREFIX + key, 0L, Duration.ofMinutes(5)); | ||
} | ||
|
||
private boolean isBlocked(String key) { | ||
return Boolean.TRUE.equals(redisTemplate.hasKey(BLOCKED_PREFIX + key)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이렇게 따로 코드에 메서드명을 설정해서 구체적인 의미를 표현하는 것 좋습니다~!
#️⃣ 연관 이슈
📝 작업 내용
참고 이미지 및 자료
💬 리뷰 요구사항