Skip to content

Commit

Permalink
Merge pull request #21 from softeerbootcamp4th/feat/logging
Browse files Browse the repository at this point in the history
[Feature] timestamp 기반 로깅 시스템 설정 및 ngrinder 테스트
  • Loading branch information
eckrin authored Aug 13, 2024
2 parents 781cfaa + 114e78d commit e75a6ea
Show file tree
Hide file tree
Showing 11 changed files with 207 additions and 99 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public CompletableFuture<CommonResponse<ArrivalApplicationResponseDto>> arrivalE
return arrivalEventService.applyEvent(authInfo)
.thenApply(result -> new CommonResponse<>(result))
.exceptionally(ex -> {
// 내부 예외 처리
if(ex.getCause() instanceof ExistingUserException) {
throw new ExistingUserException("[비동기 에러] 유저가 이미 존재합니다.");
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package com.softeer.podoarrival.event.model.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Data
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class ArrivalApplicationResponseDto {
private String response;
private boolean success;
private String name;
private String phoneNum;
private int grade;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
import com.softeer.podoarrival.event.repository.EventRepository;
import com.softeer.podoarrival.event.repository.EventRewardRepository;
import com.softeer.podoarrival.event.repository.EventTypeRepository;
import com.softeer.podoarrival.event.service.ArrivalEventReleaseService;
import com.softeer.podoarrival.event.service.ArrivalEventReleaseServiceJavaImpl;
import com.softeer.podoarrival.event.service.ArrivalEventReleaseServiceRedisImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
Expand Down Expand Up @@ -44,6 +45,7 @@ public void setEventArrivalCount() {
// 찾은 이벤트에 해당하는 reword개수 조회
int rewordCount = eventRewardRepository.countByEvent(findEvent);

ArrivalEventReleaseService.setMaxArrival(rewordCount);
ArrivalEventReleaseServiceRedisImpl.setMaxArrival(rewordCount);
ArrivalEventReleaseServiceJavaImpl.setMaxArrival(rewordCount);
}
}
Original file line number Diff line number Diff line change
@@ -1,84 +1,11 @@
package com.softeer.podoarrival.event.service;

import com.softeer.podoarrival.event.exception.ExistingUserException;
import com.softeer.podoarrival.event.model.dto.ArrivalApplicationResponseDto;
import com.softeer.podoarrival.event.model.entity.ArrivalUser;
import com.softeer.podoarrival.event.model.entity.Role;
import com.softeer.podoarrival.event.repository.ArrivalUserRepository;
import com.softeer.podoarrival.security.AuthInfo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.BatchResult;
import org.redisson.api.RBatch;
import org.redisson.api.RedissonClient;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.util.concurrent.CompletableFuture;

@Slf4j
@Service
@RequiredArgsConstructor
public class ArrivalEventReleaseService {
public interface ArrivalEventReleaseService {

private final RedissonClient redissonClient;
private final ArrivalUserRepository arrivalUserRepository;

private final String ARRIVAL_SET = "arrivalset";
private boolean CHECK = false;

private static int MAX_ARRIVAL = 100; // default

/**
* 비동기로 Redis 호출하는 메서드
* 반환값은 ArrivalEventService에서 받아서 선착순 처리
*/
@Async("arrivalExecutor")
public CompletableFuture<ArrivalApplicationResponseDto> applyEvent(AuthInfo authInfo) {
return CompletableFuture.supplyAsync(() -> {
String redisKey = LocalDate.now() + ARRIVAL_SET;

if(CHECK){
return new ArrivalApplicationResponseDto("선착순 응모에 실패했습니다.", -1);
}

RBatch batch = redissonClient.createBatch();
batch.getSet(redisKey).addAsync(authInfo.getPhoneNum());
batch.getSet(redisKey).sizeAsync();
BatchResult<?> res = batch.execute();

//첫번째 응답
if(!(boolean) res.getResponses().get(0)){
throw new ExistingUserException("이미 응모한 전화번호입니다.");
}

int grade = (int) res.getResponses().get(1);
// 선착순 순위에 들었다면
if(grade <= MAX_ARRIVAL){
arrivalUserRepository.save(
ArrivalUser.builder()
.name(authInfo.getName())
.phoneNum(authInfo.getPhoneNum())
.role(Role.ROLE_USER)
.arrivalRank(grade)
.build()
);
log.info("전화번호 = {}", authInfo.getPhoneNum());
return new ArrivalApplicationResponseDto("선착순 응모에 성공했습니다.", grade);
} else {
CHECK = true;
return new ArrivalApplicationResponseDto("선착순 응모에 실패했습니다.", -1);
}
});
}

public static void setMaxArrival(int val) {
MAX_ARRIVAL = val;
}

public static int getMaxArrival() {
return MAX_ARRIVAL;
}
CompletableFuture<ArrivalApplicationResponseDto> applyEvent(AuthInfo authInfo);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package com.softeer.podoarrival.event.service;

import com.softeer.podoarrival.event.exception.ExistingUserException;
import com.softeer.podoarrival.event.model.dto.ArrivalApplicationResponseDto;
import com.softeer.podoarrival.event.model.entity.ArrivalUser;
import com.softeer.podoarrival.event.model.entity.Role;
import com.softeer.podoarrival.event.repository.ArrivalUserRepository;
import com.softeer.podoarrival.security.AuthInfo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
@Service
@RequiredArgsConstructor
public class ArrivalEventReleaseServiceJavaImpl implements ArrivalEventReleaseService {

private final ArrivalUserRepository arrivalUserRepository;

private static int MAX_ARRIVAL = 100; // default
private boolean CHECK = false;

private static AtomicInteger count = new AtomicInteger(1);
private static ConcurrentHashMap<String, Integer> hashMap = new ConcurrentHashMap<>();

/**
* 비동기와 Atomic 변수를 통해서 동시성을 처리
* Hash function 통해서 전화번호 중복을 확인
*/
@Async("arrivalExecutor")
@Override
public CompletableFuture<ArrivalApplicationResponseDto> applyEvent(AuthInfo authInfo) {
return CompletableFuture.supplyAsync(() -> {

if(CHECK){
return new ArrivalApplicationResponseDto(false, authInfo.getName(), authInfo.getPhoneNum(), -1);
}

//첫번째 응답
if(hashMap.containsKey(authInfo.getPhoneNum())){
throw new ExistingUserException("이미 응모한 전화번호입니다.");
}

int grade = count.getAndIncrement();
// 선착순 순위에 들었다면
if(grade <= MAX_ARRIVAL){
arrivalUserRepository.save(
ArrivalUser.builder()
.name(authInfo.getName())
.phoneNum(authInfo.getPhoneNum())
.role(Role.ROLE_USER)
.arrivalRank(grade)
.build()
);
log.info("전화번호 = {}", authInfo.getPhoneNum());
return new ArrivalApplicationResponseDto(true, authInfo.getName(), authInfo.getPhoneNum(), grade);
} else {
CHECK = true;
return new ArrivalApplicationResponseDto(false, authInfo.getName(), authInfo.getPhoneNum(), grade);
}
});
}

public static void setMaxArrival(int val) {
MAX_ARRIVAL = val;
}

public static int getMaxArrival() {
return MAX_ARRIVAL;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package com.softeer.podoarrival.event.service;

import com.softeer.podoarrival.event.exception.ExistingUserException;
import com.softeer.podoarrival.event.model.dto.ArrivalApplicationResponseDto;
import com.softeer.podoarrival.event.model.entity.ArrivalUser;
import com.softeer.podoarrival.event.model.entity.Role;
import com.softeer.podoarrival.event.repository.ArrivalUserRepository;
import com.softeer.podoarrival.security.AuthInfo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.BatchResult;
import org.redisson.api.RBatch;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.util.concurrent.CompletableFuture;

@Slf4j
@Service
@RequiredArgsConstructor
public class ArrivalEventReleaseServiceRedisImpl implements ArrivalEventReleaseService {

private static final Logger specialLogger = LoggerFactory.getLogger("arrivalEventLogger");

private final RedissonClient redissonClient;
private final ArrivalUserRepository arrivalUserRepository;

private final String ARRIVAL_SET = "arrivalset";
private boolean CHECK = false;
private static int MAX_ARRIVAL = 100; // default

/**
* 비동기로 Redis 호출하는 메서드
* Redisson set을 통해서 전화번호 중복 처리
* Redisson lock 대신 set을 사용하여 인원수 처리
*/
@Async("arrivalExecutor")
@Override
public CompletableFuture<ArrivalApplicationResponseDto> applyEvent(AuthInfo authInfo) {
return CompletableFuture.supplyAsync(() -> {
String redisKey = LocalDate.now() + ARRIVAL_SET;

if(CHECK){
return new ArrivalApplicationResponseDto(false, authInfo.getName(), authInfo.getPhoneNum(), -1);
}

RBatch batch = redissonClient.createBatch();
batch.getSet(redisKey).addAsync(authInfo.getPhoneNum());
batch.getSet(redisKey).sizeAsync();
BatchResult<?> res = batch.execute();

//첫번째 응답
if(!(boolean) res.getResponses().get(0)){
throw new ExistingUserException("이미 응모한 전화번호입니다.");
}

// 로깅 추가
specialLogger.info("[응모] 유저 전화번호: {}", authInfo.getPhoneNum());

int grade = (int) res.getResponses().get(1);
// 선착순 순위에 들었다면
if(grade <= MAX_ARRIVAL){
arrivalUserRepository.save(
ArrivalUser.builder()
.name(authInfo.getName())
.phoneNum(authInfo.getPhoneNum())
.role(Role.ROLE_USER)
.arrivalRank(grade)
.build()
);
log.info("[당첨] 유저 전화번호 = {}, 등수 = {}", authInfo.getPhoneNum(), grade);
return new ArrivalApplicationResponseDto(true, authInfo.getName(), authInfo.getPhoneNum(), grade);
} else {
CHECK = true;
return new ArrivalApplicationResponseDto(false, authInfo.getName(), authInfo.getPhoneNum(), grade);
}
});
}

public static void setMaxArrival(int val) {
MAX_ARRIVAL = val;
}

public static int getMaxArrival() {
return MAX_ARRIVAL;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.concurrent.CompletableFuture;

Expand All @@ -14,10 +13,9 @@
@RequiredArgsConstructor
public class ArrivalEventService {

private final ArrivalEventReleaseService arrivalEventReleaseService;
private final ArrivalEventReleaseService arrivalEventReleaseServiceRedisImpl;

@Transactional
public CompletableFuture<ArrivalApplicationResponseDto> applyEvent(AuthInfo authInfo) {
return arrivalEventReleaseService.applyEvent(authInfo);
return arrivalEventReleaseServiceRedisImpl.applyEvent(authInfo);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ void applicationTest() throws InterruptedException {
Role.ROLE_USER
)
);
if(futureResponse.get().getResponse().equals("선착순 응모에 성공했습니다.")) count.getAndIncrement();
if(futureResponse.get().isSuccess()) count.getAndIncrement();
} catch (ExecutionException | InterruptedException e) {
throw new RuntimeException(e);
} finally {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import com.softeer.podoarrival.event.model.entity.Role;
import com.softeer.podoarrival.event.repository.ArrivalUserRepository;
import com.softeer.podoarrival.event.service.ArrivalEventReleaseService;
import com.softeer.podoarrival.event.service.ArrivalEventReleaseServiceRedisImpl;
import com.softeer.podoarrival.security.AuthInfo;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
Expand Down Expand Up @@ -33,13 +33,13 @@ public class ArrivalEventBase {
protected BatchResult<Object> batchResult;

@InjectMocks
protected ArrivalEventReleaseService arrivalEventReleaseService;
protected ArrivalEventReleaseServiceRedisImpl arrivalEventReleaseServiceRedisImpl;

protected AuthInfo authInfo;

@BeforeEach
public void setUp() {
authInfo = new AuthInfo("user", "01012345678", Role.ROLE_USER);
ArrivalEventReleaseService.setMaxArrival(5);
ArrivalEventReleaseServiceRedisImpl.setMaxArrival(5);
}
}
Loading

0 comments on commit e75a6ea

Please sign in to comment.