[Nodejs] libuv Design Overview 똥번역

libuv design overview를 번역했지만, 번역이 터무니없으니 원본을 읽으세요ㅎㅎ

리버브는 Node.js를 위해 만들어진 cross-platform 보조 라이브러리입니다. 이벤트기반 비동기 IO 모델을 중심으로 설계되었습니다.

libuv는 I/O 폴링 메커니즘을 넘어선 아주 간단한 추성화를 제공해줍니다: ‘handles’, ‘streams’는 소켓등 여러 엔티티를 위한 아주 높은 단계의 추상화를 제공해주죠.

아래 이미지는 libuv를 구성하는 여러 부분들과 관련된 하위 시스템을 보여주는 다이어그램입니다.

libuv1


Handles and requests

libuv는 이용자들에게 이벤트루프와 함께 일을 할 2가지의 추상화를 제공해줍니다. 바로 handlesrequests입니다.

Handles 긴 수명을가진 객체를 나타냅니다. 활성 상태에서 특정 작업을 수행하는 것들이죠. 예를 들어,

  • “prepare handle”은 이벤트 루프가 활성화되었을 때 매 반복마다 해당하는 콜백을 호출합니다.
  • “TCP server handle”은 새로운 연결이 있을 때마다 해당 콜백을 호출합니다.

Requests는 (보통) 짧은 수명을 가진 작업을 나타냅니다. 이 작업들은 보통 핸들을 통해 수행됩니다. (반드시는 아님) 예를 들어 write request는 handle에 데이터를 쓰는데 이용됩니다. 혹은 request 홀로 독립적으로 event loop에서 실행될수도 있습니다.


The I/O loop

I/O (event) loop는 libuv의 핵심 요소입니다. 모든 I/O 작업을 위한 내용을 설정합니다. 그리고 싱글 스레드와 연결시킵니다. 각자가 서로 다른 스레드에서 실행되기만 한다면 여러 이벤트 루프를 실행할 수 있습니다. libuv 이벤트 루프는 특별한 셋팅을 하지않는다면 일반적으로 스레드 안전하지 않으니 유의하시기 바랍니다.

thread safe: 멀티스레드에서 어떤 함수 혹은 변수의 접근동시성에 문제가 없는 상황.

이벤트 루프는 전형적인 비동기 I/O 접근 방식을 따릅니다. 모든 I/O는 개별 OS에 맞는 Non Blocking 소켓을 이용하게 됩니다. Linux에서는 epoll, OSX에선 kqueue등이 있죠.

이벤트 루프가 어떻게 작동하는지 이해하기 위해 아래 다이어그램이 루프 반복의 모든 단계를 보여줍니다.

libuv2

  1. 루프의 현재 시간이 새로 업데이트 됩니다. 이벤트 루프는 매 tick마다 실행시간을 캐싱해두고 시간 관련된 작업을 할때마다 이용하게 됩니다.

  2. 루프가 활성화되어있다면, 한번의 반복이 시작됩니다. 그렇지 않다면 즉시 끝나게 되죠. 그렇다면 언제가 활성화되어있는 상태일까요? 루프가 활성화되어있을 때, handle이나 request을 참조하고 있는 상태일 때, 혹은 handlers를 마무리할때가 활성화되어있다고 간주됩니다.

  3. Due timer가 실행됩니다. 1번에 캐싱된 시간보다 이전에 예약된 작업들의 콜백들이 실행됩니다.

  4. Pending callbacks가 호출됩니다. 모든 I/O 콜백이 대부분 I/O polling 직후에 호출됩니다. 하지만 다음번 루프 반복을 위해 미뤄질때가 있게 됩니다. 그때 미뤄진 콜백들이 이 단계에서 실행됩니다.

  5. Idle handle 콜백들이 호출됩니다. 이름의 뜻이 특이합니다만. 매 반복마다 활성화되어있다면 호출됩니다.

  6. Prepare handle 콜백들이 호출됩니다. 루프가 I/O를 차단하기 직전에 호출됩니다.

  7. Poll timeout이 계산됩니다. I/O를 막기전에 이벤트 루프는 얼마나 막을지 시간을 계산하게 됩니다. 아래 목록이 그 시간을 계산하는 기준입니다.
    • 이벤트 루프가 UV_RUN)NOWAIT로 실행되었다면 timeout은 0.
    • 이벤트 루프가 멈춰질거라면 (uv_stop() 이 호출된다면), timeout은 0.
    • 활성된 handles, request가 없다면 0.
    • 만약 idle handle이 하나라도 있다면 0,
    • 만약 closed() 되기로한 handle이 있다면 0,
    • 위의 경우중 하나라도 매치되지않는다면, 가장 가까운 타이머의 timeout이 적용되고, 활성타이머가 없다면 무한으로 설정됩니다.
  8. 이벤트 루프가 I/O를 block합니다. 7번째 단계에서 계산된 값만큼 blocking을 진행하게 됩니다. All I/O related handles that were monitoring a given file descriptor for a read or write operation get their callbacks called at this point. (번역 힘듬..)

  9. Check handle 콜백이 호출됩니다. 이벤트루프 blocking이 끝나마자마 호출됩니다. check handle은 기본적으로 prepare handle의 짝이라고 생각하면 좋습니다.

  10. close 콜백이 호출됩니다. handle이 uv_close()로 인해 끝나게되면 호출됩니다.

  11. UV_RUN_ONCE로 실행된 경우에는, I/O를 차단한 후 I/O 콜백이 실행되지 않았을 수 있지만 일정 시간이 지나야 기한이 있는 타이머가 있을 수 있으며 해당 타이머는 콜백을 받습니다.

  12. 반복이 종료됩니다. 만약 이벤트 루프가 UV_RUN_NOWAIT 혹은 UV_RUN_ONCE로 실행된다면, 반복이 종료되고 uv_run()이 반환될것입니다. 만약 이벤트 루프가 UV_RUN_DEFAULT로 실행되었다면, 활성화된 상태에서는 처음부터 다시 시작될 것이고 그렇지 않다면 종료될것입니다.