Skip to content

Commit

Permalink
Merge pull request #612 from SWM-Morandi/dev
Browse files Browse the repository at this point in the history
Redis 분산락에 Redisson 라이브러리를 활용
  • Loading branch information
aj4941 authored Nov 25, 2024
2 parents db80e18 + dae1a98 commit c62f94f
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 26 deletions.
Binary file modified .DS_Store
Binary file not shown.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ dependencies {

implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.11.5'
implementation 'org.redisson:redisson-spring-boot-starter:3.21.1'
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.5'
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.5'
if ( System.getProperty("os.name") == "Mac OS X" && System.getProperty("os.arch") == "aarch64") {
Expand Down
38 changes: 15 additions & 23 deletions src/main/java/swm_nm/morandi/aop/lock/MemberLockAspect.java
Original file line number Diff line number Diff line change
@@ -1,60 +1,52 @@
package swm_nm.morandi.aop.lock;


import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import swm_nm.morandi.global.exception.MorandiException;
import swm_nm.morandi.global.exception.errorcode.LockErrorCode;
import swm_nm.morandi.global.utils.SecurityUtils;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;

@Aspect
@Component
@Slf4j
@RequiredArgsConstructor
public class MemberLockAspect {

private final StringRedisTemplate redisTemplate;
private final RedissonClient redissonClient;
private final String MEMBER_LOCK_KEY = "memberLock";
private final Integer MEMBER_LOCK_TTL = 5;

@Pointcut("@annotation(swm_nm.morandi.aop.annotation.MemberLock)")
public void memberLockPointcut() {
}

@Around("memberLockPointcut()")
public Object MEMBERLock(ProceedingJoinPoint joinPoint) throws Throwable {
public Object MEMBERLock(ProceedingJoinPoint joinPoint) throws Throwable {
Long memberId = SecurityUtils.getCurrentMemberId();
String memberLockKey = String.format("%s:%d", MEMBER_LOCK_KEY,memberId);
String memberLockKey = String.format("%s:%d", MEMBER_LOCK_KEY, memberId);
RLock lock = redissonClient.getLock(memberLockKey);
boolean locked = false;
try {
if (tryLock(memberLockKey)) {
locked = true;
return joinPoint.proceed();
} else {
locked = lock.tryLock(2, 5, TimeUnit.SECONDS);
if (!locked) {
throw new MorandiException(LockErrorCode.MEMBER_LOCKED);
}
return joinPoint.proceed();
} catch (InterruptedException e) {
throw new MorandiException(LockErrorCode.INTERRUPT_ERROR);
} finally {
if(locked) {
unlock(memberLockKey);
if (locked) {
lock.unlock();
}
}
}

private Boolean tryLock(String key) {
return redisTemplate.opsForValue().setIfAbsent(key, "locked", MEMBER_LOCK_TTL, TimeUnit.SECONDS);
}

private void unlock(String key) {
redisTemplate.delete(key);
}

}

}
23 changes: 23 additions & 0 deletions src/main/java/swm_nm/morandi/config/async/AsyncConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package swm_nm.morandi.config.async;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

@Configuration
@EnableAsync
public class AsyncConfig {

@Bean(name = "taskExecutor")
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5); // 기본 스레드 풀 크기
executor.setMaxPoolSize(10); // 최대 스레드 풀 크기
executor.setQueueCapacity(25); // 큐의 용량
executor.setThreadNamePrefix("Async-");
executor.initialize();
return executor;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package swm_nm.morandi.domain.codeSubmit.controller;

import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import swm_nm.morandi.domain.codeSubmit.service.TestService;

@RestController
@RequiredArgsConstructor
public class TestController {

private final TestService testService;

@PostMapping("/hello-world/{value}")
public String getData(@PathVariable("value") Integer value) throws InterruptedException {
return testService.test(value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package swm_nm.morandi.domain.codeSubmit.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class TestService {
public String test(int value) throws InterruptedException {
System.out.println("Response Message SSEID : " + 1 + " Count : " + value);
return "hello";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@
@RequiredArgsConstructor
@Getter
public enum LockErrorCode implements ErrorCode {
MEMBER_LOCKED(HttpStatus.CONFLICT, "한 번에 여러 번의 요청을 받을 수 없습니다. 잠시만 기다려주세요.");
MEMBER_LOCKED(HttpStatus.CONFLICT, "한 번에 여러 번의 요청을 받을 수 없습니다. 잠시만 기다려주세요."),
INTERRUPT_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "인터럽트 예외가 발생했습니다.");

private final HttpStatus httpStatus;
private final String message;


}

0 comments on commit c62f94f

Please sign in to comment.