본문 바로가기

Programing/Framework

[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

 

 

EventLoop의 계층 구조
NonBlockingThread의 계층 구조