BlockHound 정리해야지,, 생각만 하다가 어느덧 일주일이 흘러버렸습니다,,
문제는 BlockHound,, 기억이 나지 않는다는 것^^,,,
이런걸 진정한 복습이라 생각하며, 기억과 코드를 찾아보며 정리해 보고자합니다,,
할 수 있다,, 기억해 낼 수 있다,, 아자아자,,🐦🔥
1. BlockHound 정의
개발자가 직접 작성한 코드를 포함하여 JDK, Thrid-party 라이브러리에 사용된 블로킹 메소드 호출을 모두 찾아내서 알려주는 도구
💡 Spring Webflux 기반의 애플리케이션은 모든 코드가 Reactive 방식으로 작동해야 최상의 성능이 나옵니다. 즉, blocking 코드가 존재한다면 충분한 성능이 나오지 않을 수도 있다는 것입니다. 이를 위해 BlockHound를 사용합니다.
❓Reactive 방식
비동기(Asynchronous)
논블로킹(Non-blocking)
데이터 스트림(Streams): 데이터를 필요한 만큼 조금씩 전달하며 처리하는 방식
Backpressure: 데이터 소비자가 처리할 수 있는 속도를 조절하는 기능
https://github.com/reactor/BlockHound
GitHub - reactor/BlockHound: Java agent to detect blocking calls from non-blocking threads.
Java agent to detect blocking calls from non-blocking threads. - reactor/BlockHound
github.com
2. BlockHound 설정
build.gradle
테스트 시, 사용 예정이므로 testImplementation을 선언
버전은 Github를 참고하여 최신 버전을 입력(25.02.09기준 최신 버전 - 1.0.10)
dependencies {
testImplementation 'io.projectreactor.tools:blockhound:1.0.10.RELEASE'
}
3. 테스트 코드 작성
static void setUp() {
BlockHound.install();
}
@Test
void shouldDetectBlockingCall() {
Mono<Long> blockingMono = Mono.delay(Duration.ofSeconds(1))
.doOnNext(t -> {
try {
Thread.sleep(100); // 블로킹 호출
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
StepVerifier.create(blockingMono)
.expectError(BlockingOperationError.class)
.verify();
}
하지만, 살다보면 Webflux에서도 Blocking을 허용해야하는 순간이 올 것입니다..🍂
static {
BlockHound.install(builder ->
builder.allowBlockingCallsInside(UserControllerTest.class.getName(), "shouldAllowBlockingCallInsideAllowedMethod")
);
}
@Test
void shouldAllowBlockingCallInsideAllowedMethod() {
Mono<Long> allowedMono = Mono.delay(Duration.ofSeconds(1))
.doOnNext(t -> {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
StepVerifier.create(allowedMono)
.expectNextCount(0)
.verifyComplete();
}
🚨 분명 Blocking을 허용하겠다고 했는데, 아래와 같은 오류가 발생합니다.
expectation "expectComplete" failed (expected: onComplete();
actual: onError(reactor.blockhound.BlockingOperationError: Blocking call! java.lang.Thread.sleep))
allowBlockingCallsInside는 특정 클래스의 특정 메서드 내부에서 발생하는 블로킹 호출만 허용하는데, shouldAllowBlockingCallInsideAllowedMethod안에 있는 Thread.sleep(100)은 doOnNext의 람다 내부에서 호출되고 있습니다.
이 때, BlockHound는 람다 표현식을 익명 클래스로 취급하기 때문에, allowBlockingCallsInside가 해당 호출을 허용하지 않게 되어 오류를 발생시킵니다.
따라서 블로킹이 발생하는 부분을 따로 메서드로 분리합니다.
static {
BlockHound.install(builder ->
builder.allowBlockingCallsInside(UserControllerTest.class.getName(), "allowedBlockingMethod")
);
// 블로킹 호출을 별도 메서드로 분리
void allowedBlockingMethod() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
@Test
void shouldAllowBlockingCallInsideAllowedMethod() {
Mono<Long> allowedMono = Mono.delay(Duration.ofSeconds(1)) // 1초 후에 0L이라는 값을 방출
.doOnNext(__ -> allowedBlockingMethod());
StepVerifier.create(allowedMono)
.expectNextCount(1)
.verifyComplete();
}
📑
참고 자료
Chat GPT
https://velog.io/@be_have98/Spring-Webflux-BlockHound-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0
[Spring Webflux] BlockHound 적용하기
비동기 프로그램을 구현하기 위해 블로킹 메소드를 검출하는 BlockHound를 적용해보자
velog.io
https://velog.io/@be_have98/Spring-Webflux-BlockHound-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0
[Spring Webflux] BlockHound 적용하기
비동기 프로그램을 구현하기 위해 블로킹 메소드를 검출하는 BlockHound를 적용해보자
velog.io
'MinimiProject > 아이돌 티켓팅 접속자 대기열 시스템' 카테고리의 다른 글
[대기열 시스템] Task Execution and Scheduling (1) | 2025.02.16 |
---|---|
[대기열 시스템] Custom Exception (0) | 2025.02.13 |
[대기열 시스템] NullPointerException: Mocking (0) | 2025.02.10 |
[대기열 시스템] MVC vs Webflux - JMeter를 이용한 부하테스트 (1) | 2025.02.02 |
[대기열 시스템] R2DBC와 Custom Query (0) | 2025.01.31 |