항해99가 모두 끝이나고 2주가 흘렀는데 프로젝트 당시 처음 Webflux에 도전을 했지만 시간상 깊게 이해하지 못하고 기능이 작동하는지에만 집중을 했기 때문에 다시 기초부터 차근차근 학습을 하려한다.
먼저 핵심 개념에 대해 짚고 넘어 가야 할 것이 있다.
함수가 다른 함수를 호출하는 상황에서
Caller: 호출하는 함수
Callee: 호출당하는 함수
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
@FunctionalInterface
public interface Supplier<T> {
T get();
}
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
-함수형 프로그래밍을 지원하기 위해 java8부터 도입
-1개의 추상 메서드를 갖고 있는 인터페이스
-함수를 1급 객체로 사용할 수 있다 -> 함수를 변수에 할당하거나 인자로 전달하고 반환값으로 사용 가능
-Function, Consumer, Supplier, Runnable 등
-함수형 인터페이스로 구현한 익명 클래스를 람다식으로 변경 가능
위 두 이미지 차이에 대해 설명을 하자면
왼쪽 이미지는 메인이 Caller가 되고 getResult가 Callee가 되는데
Caller는 Callee를 호출하고 결과를 반환해서 결과를 가지고 다음 액션을 취하게 됩니다.
오른쪽 이미지는 Caller가 Callee를 호출하고 함수형 인터페이스를 실행하고 다시 메인으로 돌아오게 됩니다.
즉, Caller가 Callee에게 액션을 위임했다고 볼 수 있습니다.
결과를 말씀드리면 왼쪽 이미지가 동기 프로그래밍 오른쪽 이미지가 비동기 프로그래밍입니다. 이 두 이미지 토대로 유추할 수 있는 결과는
-Caller는 Callee의 결과에 관심이 있다
-Caller는 결과를 이용해서 action을 수행한다
-Caller는 Callee의 결과에 관심이 없다
-Callee는 결과를 이용해서 callback을 수행한다
위 두 이미지를 다르게 도식화해보면
이렇게 나타낼 수 있는데
A모델과 B모델의 차이점은 Caller가 Callee의 결과에 관심이 있는지 없는
지이고 공통점은 callee가 리턴할 때 까지 Caller가 작업을 할 수 없다는 것 이다.
나는 지금까지 비동기면 무조건 non-blocking이라고 알고 있었는데 잘 못
알고 있었던 것이었다. 동기여도 non-blocking일 수도 있고 비동기여도 blocking 할 수 있던 것 이었다.
C 모델을 통해 알 수 있다. Caller는 Callee의 결과에 관심이 있지만
Callee가 작업이 끝나기도 전에 계속해서 Caller가 계속해서 isDone인지 확인하는 작업을 하고 있다.
즉, 동기 non-blocking인 모델이다
D모델 같은 경우에는 비동기 non-blocking이다
Caller는 Callee의 결과에 관심이 없고 Callee가 작업을 하는 동안에도 Caller는 본인의 일을 할 수 있기 때문이다.
총 정리를 하자면
Caller는 아무 것도 할 수 없는 상태가 된다. 결과를 얻은 후 직접 처리한다.(Caller는 Callee의 결과에 관심이 있다)
Caller는 아무 것도 할 수 없는 상태가 된다. 결과는 Callee가 처리한다.(Caller는 Callee의 결과에 관심이 없다)
Caller는 본인의 일을 할 수 있다. 결과를 얻은 후 직접 처리한다.
Caller는 본인의 일을 할 수 있다. 결과는 Callee가 처리한다.
-thread가 대부분의 시간을 CPU를 점유하는데 사용
-연산이 많은 경우
-추가적인 코어를 투입
-thread가 대부분의 시간을 대기하는데 사용
-파일 읽기/쓰기, network 요청 처리, 요청 전달
-IO-bound non-blocking 가능
-하나의 함수에서 여러 함수를 호출하기도 하고, 함수 호출은 중첩적으로 발생
-callee는 caller가 될 수 있음
-blocking한 함수를 하나라도 호출한다면 caller는 blocking된다.
-함수가 non-blocking하려면 모든 함수가 non-blocking 해야한다.
-IO bound blocking 또한 발생하면 안됨.
-recvfrom 호출
-blocking socket을 이용해서 read/write 수행
-쓰레드가 block 처리된다
-recvfrom을 주기적으로 호출
-non-blocking socket을 이용해서 read/write 수행
-작업이 완료되지 않았다면 EAGAIN/EWOULDBLOCK 에러 반환
-aio_read 호출
-작업이 완료되면 커널이 완료 시그널을 보내거나 callback 함수 호출
멀티플렉싱 I/O가 동기 blocking I/O라는 말도 있고 결국 멀티플렉싱의 내부를 들여다보면동기 blocking으로 동작하기 때문.