Reactive Programing

[Reactive Programming] 속도비교

p8labs 2024. 11. 2. 15:25
728x90
반응형

속도비교

실제로 얼마나 속도차이가 나는지 blocking 코드와 reactor 코드를 비교해보자.

 

코드예제

고객에게 쿠폰을 발급하려고 한다.

 

조건은 가입일 5년 이상, 보유 포인트가 500 이상, 골드 멤버일 경우다.

걸리는 시간은 총 6초이다. 

 

isGoldMember: 골드 멤버인지 확인한다. 3초걸림.

isGreaterThan500point: 보유 포인트 확인. 2초 걸림

checkRegisterPeriod: 등록 기간 체크. 1초걸림.

package com.p8labs.reactive.compareCode;

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

import java.time.LocalDate;
import java.time.LocalDateTime;

@Slf4j
@Service
public class CouponService {

    public boolean isGoldMember(Member member) throws InterruptedException {
        Thread.sleep(3000L);
        log.info("CHECK GRADE");
        return member.getGrade().equalsIgnoreCase("GOLD");
    }

    public boolean isGreaterThan500point(Member member) throws InterruptedException {
        Thread.sleep(2000L);
        log.info("CHECK POINT");
        return member.getPoint() >= 500;
    }

    public boolean checkRegisterPeriod(Member member) {
        try {
            Thread.sleep(1000L);
            log.info("CHECK PERIOD");
            LocalDate registerDate = member.getRegisterDttm().toLocalDate();
            LocalDate conditionDate = LocalDateTime.now().minusYears(5L).toLocalDate();
            return registerDate.isBefore(conditionDate) || registerDate.equals(conditionDate);
        } catch (Exception e) {
            return false;
        }
    }
}

 

Blocking

    public boolean blocking(Member member) {
        try {
            boolean checkRegisterPeriod = couponService.checkRegisterPeriod(member);
            boolean isGoldMember = couponService.isGoldMember(member);
            boolean isGreaterThan500point = couponService.isGreaterThan500point(member);
            return checkRegisterPeriod && isGoldMember && isGreaterThan500point;
        } catch (Exception e) {
            log.info("ERROR!!!!");
            return false;
        }
    }

 

결과

총 6초가 걸린다.

CHECK PERIOD
CHECK GRADE
CHECK POINT
걸린시간 6

 

 

Mono Blocking

    public Mono<Boolean> monoBlocking(Member member) {
        // 각 조건을 Flux로 변환하여 비동기 호출
        return Mono.zip(
                        Mono.fromCallable(() -> couponService.checkRegisterPeriod(member)),
                        Mono.fromCallable(() -> couponService.isGoldMember(member)),
                        Mono.fromCallable(() -> couponService.isGreaterThan500point(member))
                )
                .map(tuple -> {
                    Boolean isRegisterPeriodValid = tuple.getT1();
                    Boolean isGoldMemberValid = tuple.getT2();
                    Boolean isGreaterThan500pointValid = tuple.getT3();

                    // 모든 조건이 참인지 확인
                    return isRegisterPeriodValid && isGoldMemberValid && isGreaterThan500pointValid;
                });
    }

 

결과

리액터 코드로 작성했지만 결과가 blocking 과 동일하다.

이유는 쓰레드가 하나여서 쓰레드가 종료될때까지 기다렸고 그 결과
순차적으로 수행이 되었기 때문이다.

[    Test worker] c.p.reactive.compareCode.CouponService   : CHECK PERIOD
[    Test worker] c.p.reactive.compareCode.CouponService   : CHECK GRADE
[    Test worker] c.p.reactive.compareCode.CouponService   : CHECK POINT
걸린시간 6

 

 

Mono 에 Executor 적용하기

@Configuration
public class TaskExecutorConfig {

    private static final int TASK_QUEUE_CAPACITY = 100;
    private static final int KEEP_ALIVE_SECOND = 60;
    private static final String THREAD_NAME_PRFIX = "custom-";


    @Bean
    public Executor customExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10); // 초기 스레드 수
        executor.setMaxPoolSize(50); // 최대 스레드 수
        executor.setQueueCapacity(100); // 대기열 크기
        executor.setThreadNamePrefix("CustomExecutor-");
        executor.initialize();
        return executor;
    }
}

 

 

subscribeOn(Schedulers.fromExecutor(customExecutor)) Executor를 =적용해주었다.

    public Mono<Boolean> mono(Member member) {
        // 각 조건을 Flux로 변환하여 비동기 호출
        return Mono.zip(
                        Mono.fromCallable(() -> couponService.checkRegisterPeriod(member))
                                .subscribeOn(Schedulers.fromExecutor(customExecutor)),
                        Mono.fromCallable(() -> couponService.isGoldMember(member))
                                .subscribeOn(Schedulers.fromExecutor(customExecutor)),
                        Mono.fromCallable(() -> couponService.isGreaterThan500point(member))
                                .subscribeOn(Schedulers.fromExecutor(customExecutor))
                )
                .map(tuple -> {
                    Boolean isRegisterPeriodValid = tuple.getT1();
                    Boolean isGoldMemberValid = tuple.getT2();
                    Boolean isGreaterThan500pointValid = tuple.getT3();

                    // 모든 조건이 참인지 확인
                    return isRegisterPeriodValid && isGoldMemberValid && isGreaterThan500pointValid;
                });
    }

 

결과

원하는대로 최대시간 3초가 걸렸다.

customExecutor1,2,3 쓰레드로 각각 함수가 수행이 되었다.

[customExecutor-1] c.p.reactive.compareCode.CouponService   : CHECK PERIOD
[customExecutor-3] c.p.reactive.compareCode.CouponService   : CHECK POINT
[customExecutor-2] c.p.reactive.compareCode.CouponService   : CHECK GRADE
걸린시간 3

 

 

 

결론

블록킹 코드와 비교하여 속도차이를 보았다. 

코드도 크게 달라지진 않았다.

중요한 점은 Mono 를 결합하여 쓸때는 반드시 여러개의 쓰레드에서 동작하는지
확인해보자.

728x90
반응형