Skip to content

Commit

Permalink
✨ [FEAT] Redis 분산락에 Redlock 알고리즘 적용
Browse files Browse the repository at this point in the history
  • Loading branch information
aj4941 committed Nov 25, 2024
1 parent a8b56ef commit 2f7b48b
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 15 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
26 changes: 11 additions & 15 deletions src/main/java/swm_nm/morandi/aop/lock/MemberLockAspect.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
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;
Expand All @@ -21,37 +22,32 @@
@RequiredArgsConstructor
public class MemberLockAspect {

private final RedissonClient redissonClient;

private final StringRedisTemplate redisTemplate;
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();
} finally {
if(locked) {
if (locked) {
unlock(memberLockKey);
}
}
}

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

private void unlock(String key) {
redisTemplate.delete(key);
}
Expand Down
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";
}
}

0 comments on commit 2f7b48b

Please sign in to comment.