Web/Spring

(번역)넷플릭스의 Zuul 2 도입기를 통해 보는 Non blocking의 장단점, blocking 쉬운 설명

TLdkt 2022. 10. 15. 08:49
728x90
반응형



🚀들어가며

최근 reactive programming을 배우면서 넌블러킹이 시스템의 성능에 어떤 때에 도움이 되고, 어떤 때 도움이 되지 않는지 자세하게 알고 싶어졌다. 특히 MSA 애플리케이션의 경우 유용하게 사용된다는 이야기를 듣고 나니 문득 궁금해졌다. 통신에 있어서도 나름의 순서와 처리로직이 있을 것인데, 단순히 async, non blocking 방식으로 구현한다고 해서 그렇게 효과가 좋을까?

관련 키워드로 검색을 하다 넷플릭스의 Zuul 2 도입기를 읽게 되었고, 마침 최근 공부하던 운영체제와 관련된 내용이 나와 정리 겸 번역 및 요약해보았다.

넷플릭스처럼 소비 트래픽이 굉장히 큰 회사에서는 왜 비동기-논블로킹 프레임워크를 차용했고, 그 효과는 어땠을까? 그로부터 배울 점은 무엇일까? 천천히 정리해보겠다.

읽다가 세줄요약 필요하시면... 나름대로의 정리를 가장 하단에 적어두었으니 그 부분만 읽어보셔도 좋지 않을까! 하는 마음이다👀



*참고로 Zull은 넷플릭스의 클라우드 게이트웨이다.
클라우드에 있는 서버에서 사용자의 디바이스에서 서버로 들어오는 요청을 처리해준다.

기존의 Zull에서 Zull2로 새롭게 배포하면서 겪은 각종 문제점과 기대효과, real world performance에 대한 글이다.
진짜 시작!

✨우리 Zull2는요...

Netty (비동기, 논블로킹 프레임워크) 기반
영속적 커넥션 유지
평균 디바이스 요청 감소

기대했던 효과

-장애 회복탄력성
-레이턴시 성능 향상
-처리량 향상
-비용 개선
but!
예상했던 만큼의 좋은 결과가 나지는 않았음
왜였을까? 이를 위해 몇 가지 개념을 알아봐야 한다

👀Blocking, Non-Blocking 시스템 간 차이

✔️Zuul1 : Servlet framework

블로킹, 멀티스레드 방식
1커넥션, 1스레드 사용

작동방식

- 워커 스레드를 고르고
-요청 스레드는 블록됨
-워커 스레드는 요청 처리 끝나면 알림 보냄
-멀티 코어 인스턴스에서 잘 실행됐지만 단점 존재

단점

-백엔드 레이턴시 증가, 기기 재시도 폭발 시 액티브 커넥션, 쓰레드 수 폭발적 증가
➡️서버 부하 올라가서 터질 수도...
-보완을 위해 조절 메커니즘, 라이브러리 등을 써봤지만 잘 해결되지 않음

✔️Async Systems?

-코어당 스레드 1개로 처리(커넥션당 1개 X)
-이벤트와 콜백으로 처리

장점

-연결에 드는 비용이 저렴해짐(file descriptor와 listner 추가 정도)
-스레드가 쌓이는 것(Blocking model)보다 큐에 이벤트가 쌓이고 커넥션이 많아지는 게 더 나음
-같은 CPU에 데이터가 있어서 컨텍스트 스위칭이 줄어들고, 캐시 활용도도 좋아짐
블로킹 모델이 스레드가 계속 생성됨+메모리 무거워짐+시스템 오버헤드가 있는 것에 비해 파격적

단점

-블로킹 방식은 이해하기 쉽고, 디버깅도 편함(eay to grok and debug) 반면 Event driven 모델은 어려움
-블로킹 방식에서는 스레드가 하나의 명령만 처리하다 보니 여러 스레드의 흐름을 따라 요청을 확인할 수 있는데, 이벤트 루프로 처리되는 방식에서는 스택 트레이스가 의미가 없어짐(콜백의 순서가 없고, 어떤 이벤트 처리가 잘못됐는지 알기 어려움)
-엣지케이스, 처리되지 못한 에러, 잘못 처리된 state들은 ByteBuf 누수, 파일 디스크립터 유실 등을 유발




✔️Async만으로는 부족해! : Non-Blocking Zull

-특히 넷플릭스 생태계 내의 서비스들이 이미 블로킹 방식(tomcat)을 전제로 구성되어 있다보니 넌블럭킹, 비동기 방식에서 작동을 안 했고, 거대한 넷플릭스 시스템에서 어디가 블로킹 방식으로 구현되었는지 탐색하는 비용이 많이 들었음
-블로킹 로직➡️ 논블로킹 네트워킹 코드 변경 시 일괄적인 해결책이 없고 각각 리팩터링해야 했음

블로킹 시스템은 코드를 비동기 실행 가능하다는 점에 착안 ➡️ Zull Filter를 비동기로 실행하도록 변경

-Zuul Filters를 두 가지 방식 모두에서 작동할 수 있게끔 수정

-IO에 쓰는 비동기 필터, logiical (Non IO)명령들을 수행하기 위한 동기화방식 필터 둘 다로 쓰이도록 만듦
-모든 것을 넌블러킹으로 바로 만들 수 없으니 우선 비동기로 전부 적용되게 만든 후 나머지 부분을 넌블러킹으로 수정하는 방식으로 구현

Zull2의 수확

-커넥션 확장에는 원래 목적을 달성함
-실시간 유저경험 개선, 클라우드 비용 감소
-네트워크 트래픽 비용 감소
-손상에 대한 회복탄력성 증가

그렇지만...

??? 왜 이런지 아직 얘기를 안했다!

👏결론

-CPU 연산이 많이 필요한 시스템의 경우 Zuul2의 방식이 기존 방식과 효율성 면에서 큰 차이가 없었음
-반면 로깅 작업과 같은 입출력이 많은 작업의 경우 nonblocking 방식으로 작동하기 때문에 가능한 처리량이 25% 증가했고, CPU 사용률은 25% 감소했음

즉, 시스템이 CPU 처리가 많이 필요한 워크로드에 치중되어 있는지 혹은 입출력(CPU 덜 사용)위주의 워크로드 비율이 많은지에 따라 비동기 넌블로킹 방식이 반드시 이득이라고 보기는 어렵다는 것이다.
유튜브 영상도 좀 봤는데, 연사는 재치있게 이 밈을 사용했다.
비동기 넌블로킹이 그렇게 좋은가요? 질문한다면,

그때그때 달러요~


여기까지가 서투른 번역이었다. 혹시 오역이 있다면 댓글로 살포시,, 알려주십시오😂😂


그러니까 이게 다 무슨 말이냐면요...

쭉 읽어보면서 알쏭달쏭한 내용들이 보였지만, 거칠게나마 이해한 내용을 정리해보자면 이렇다.

서버는 하나의 컴퓨터이기 때문에 들어오는 요청을 처리해야 한다.
그 요청은 사용자의 디바이스(핸드폰, 컴퓨터 등등등)에서 날아오게 된다.

이렇게 사용자의 디바이스와 서버 컴퓨터, 서로 다른 기기가 네트워크 통신을 할 수 있게 해주는 게 게이트웨이라는 앤데, 넷플릭스의 게이트웨이 이름은 Zuul이다.


어떤 요청이 됐든 서버 컴퓨터의 CPU가 어떤 방식으로든 관여하게 되는데, 들어오는 요청마다 많이 관여하기도 하고, 적게 관여하기도 한다.

요청을 받고 처리하고 응답을 내보낸다는 점에서 서버 컴퓨터가 소문난 치킨집이라고 해보자
치킨집 사장이 CPU라면, 보통 두 종류의 일을 할 수 있다.

맛집이라 배달주문을 받고 치킨을 열심히 튀겨 라이더에게 내놓기도 하고

갓물주 아주머니가 홀에서 " 여 티브이 화면이 크잖여~ 좀 봐도 될까?" 하면서 들어올 때도 "아 예 보세요~~" 인사 정도는 해줘야 하고, 나가실 때 "어이구 들어가세요!!" 도 해줘야 마땅하다.

이렇게 치킨집 사장이 집중해서 처리해야 하는 CPU bound 워크로드(치킨 튀기기)
오시는지 가시는지 인사만 깍듯이! 하면 되는 I/O bound 워크로드(갓물주님이 홀에서 티비보시는 거 신경쓰기)가 있다고 이해해보자.


즉, 각각의 경우에
사장은 치킨 튀기기(닭을 손질하기/ 튀김옷을 입히기/ 기름에 넣기/ 체로 건지기/ 포장하기 이벤트)라는 과정을 처리할 수도 있고

건물주의 티비시청 이벤트를 처리(인사하기)할 수도 있다.

이때 블로킹이라는 것은 치킨 튀기기 과정의 여러 이벤트가 진행되는 동안, 혹은 티비시청 이벤트가 진행되는 동안  사장이 모든 것을 올스탑하고 집중하는 것이다.  

이쯤에서 블로킹을 해야 하는 이벤트, 아닌 이벤트가 구분이 될 것이다.

사장은 [건물주의 티비시청]이 언제 완료될지도 모르고, 애초에 한두 시간으로 끝날 것 같지 않아보인다. 근데 건물주님이 티비를 보시는 동안 언제 인사하면 될지 얌전히 앉아서 지켜보고 있다면..? 돈을 못 버는 것도 모자라 밀린 배달로 화난 고객들에게 별점테러를 당할지도 모른다...



이렇게 갓물주의 티비보기 입출력 이벤트에서 블로킹을 한다는 건 극강의 비효율을 낳는다. 어차피 티비보기 이벤트에 사장이 필요한 것도 아니고 말이다.

이 경우가 I/O bound workload이며, 이럴 때 nonblocking programming을 활용한다면 건물주 눈치만 살살 보던 치킨집 사장을 효율적으로 굴려 돈방석에 앉힐 수도 있다!

넷플릭스 얘기로 돌아가면
기존의 zuul 서버는 블로킹 방식이라고 했다.
불쌍한 zuul의 서버는 건물주(I/O)만 오면 무조건 꼼짝없이 건물주님이 티비 입출력을 마치고 나가주시길 기다려왔던(blocked) 거다.

이제 zuul2는 넌블로킹 방식을 활용함으로써 건물주가 와도 그 시간에 쿠팡이츠 후기 답글 남기고, 치킨도 튀기고, 급하면 배달도 나갈 수 있게 됐다. 주문처리량은 확 늘고 매출은 더 오를 것이다.

그렇지만 넌블로킹 방식을 활용하려면, 중간에 블로킹 시퀀스가 끼면 안 된다.  따라서 기존에 블로킹 방식으로 잘만 하던 치킨 튀기기 이벤트도 넌블로킹으로 해야 하는데... 이미 집중해서(블로킹으로) 하던 방식이 익숙한 나머지 고치기도 쉽지않다. 같은 맥락으로 넷플릭스의 기존 블로킹(톰캣) 기반 코드를 전부 수정하고 적정하게 유지보수하기 어려울 것이라는 점 또한 예상할 수 있다.




정리하다 보니 재밌고... 은근히 비유하기가 어려워서 너무 길어졌는데 도움이 됐길 바란다. 근데 이게 끝이 아니다 ㅠ 동기 비동기가 공부할수록 헷갈렸던 이유를 어렴풋이 알게 되어 다음 포스팅도 준비하고 있다.. 기대해주시길....😇어느새 새벽 3시라니... 잠은 죽어서 자자!



참고한 자료


https://ozofweird.tistory.com/entry/GCP-%EC%9B%90%EB%8D%B0%EC%9D%B4-CPU%EB%A5%BC-%EA%B7%B9%EB%8B%A8%EC%A0%81%EC%9C%BC%EB%A1%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98

[GCP 원데이] CPU를 극단적으로 사용하는 애플리케이션

1. CPU를 극단적으로 사용하는 애플리케이션 1) 컴퓨터 부품 컴퓨터에서 가장 주요한 부품은 하드디스크, 메모리, CPU이다. 애플리케이션은 일반적으로 하드디스크에 담겨있고 이를 프로그램이라

ozofweird.tistory.com



동영상 참고자료
https://youtu.be/2oXqbLhMS_A


번역 원문
https://netflixtechblog.com/zuul-2-the-netflix-journey-to-asynchronous-non-blocking-systems-45947377fb5c

Zuul 2

The Netflix Journey to Asynchronous, Non-Blocking Systems

netflixtechblog.com



zuul 오픈소스 링크
https://github.com/Netflix/zuul

728x90
반응형