[WebFlux] block()/blockFirst()/blockLast() are blocking, which is not supported in thread
후기 백엔드를 개발하다가 이제 회원 백엔드를 맡으면서 Spring WebFlux를 다루게 되었다.
아직 Reactive Stack에 대한 경험이 부족해서 도메인 객체를 포함해서 DB연결 및 Redis 연결을 동기식으로 처리를 했다.
그런데 이전에 개발된 회원 번호에 대한 유효성 확인을 하는 함수가 Mono<Boolean> 를 반환하게 되어 있었다.
어디서 들은 지식으로 block()을 이용해서 블로킹 처리를 하려고 했더니 아래와 같은 에러 메시지가 발생했다.
block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-2
누가 이렇게 에러를 던지나 찾아보았다.
Mono의 block()은 아래와 같이 구현되어 있었다.
package reactor.core.publisher;
public abstract class Mono<T> implements CorePublisher<T> {
@Nullable
public T block() {
BlockingMonoSubscriber<T> subscriber = new BlockingMonoSubscriber<>();
subscribe((Subscriber<T>) subscriber);
return subscriber.blockingGet();
}
BlockingSingleSubscriber 의 blockingGet의 첫 번째 하는 역할은 스레드를 확인해서 블로킹이 불가능할 때 에러를 찍도록 구현이 되어 있었다.
package reactor.core.publisher;
abstract class BlockingSingleSubscriber<T> extends CountDownLatch
implements InnerConsumer<T>, Disposable {
@Nullable
final T blockingGet() {
if (Schedulers.isInNonBlockingThread()) {
throw new IllegalStateException("block()/blockFirst()/blockLast() are blocking, which is not supported in thread " + Thread.currentThread().getName());
}
if (getCount() != 0) {
try {
await();
}
catch (InterruptedException ex) {
dispose();
throw Exceptions.propagate(ex);
}
}
Throwable e = error;
if (e != null) {
RuntimeException re = Exceptions.propagate(e);
//this is ok, as re is always a new non-singleton instance
re.addSuppressed(new Exception("#block terminated with an error"));
throw re;
}
return value;
}
스케쥴러에서 논블로킹 스레드의 체크는 현재 스레드가 NonBlocking 인터페이스의 타입인지로 확인하고 있었다.
NonBlocking 는 표식을 위한 마커 인터페이스이다.
package reactor.core.scheduler;
public abstract class Schedulers {
public static boolean isInNonBlockingThread() {
return Thread.currentThread() instanceof NonBlocking;
}
프로젝트에서 NonBlocking 타입을 상속받은 것은 EventLoop와 NonBlockingThread가 있었다.
가령 WebClient를 사용할 경우 이벤트 루프를 사용하게 되는데 리액터 Netty 커넥터를 사용할 경우 reactor-http-nio- 로 시작하는 스레드를 확인할 수 있을 것이다. 클라이언트와 서버 모두 리액터 Netty를 사용하면 기본적으로 이벤트 루프 리소스를 공유하게 된다고 레퍼런스에 나와있다. ref. 1.1.7. Concurrency Model > Threading Model