느렸던 웹플럭스 소개
웹플럭스가 웹 MVC보다 성능이 높다고 알려졌지만 코딩을 잘못하면 오히려 낮아진다는 문제의식으로 발표를 연다. 비동기 논블로킹 외에도 잘 알려지지 않은 여러 성능 저하 케이스를 공유하겠다고 예고한다.
LA의 NHN 글로벌 패션고 광고 시스템을 소개한다. 100ms를 넘으면 타임아웃으로 간주할 만큼 빠른 응답과 높은 처리량이 필요해, 데이터 저장소로 레디스, 노출 담당 서버로 스프링 웹플럭스를 선택한 배경을 설명한다.
스프링 MVC의 thread-per-request 모델은 톰캣 기본 200개 스레드가 적은 코어를 두고 경합하며 부하 시 컨텍스트 스위칭 오버헤드가 크다. 반면 웹플럭스는 코어 2배 스레드의 이벤트 루프와 논블로킹 IO로 스레드를 블록하지 않아 처리량이 높음을 대조한다.
게이틀링 부하 클라이언트, 레디스, API 서버로 구성한 측정 아키텍처를 설명한다. 레디스가 병목이 안 되게 물리 머신을 쓰고, 비교 기준이 되는 웹 MVC는 원코어에서 약 3775 TPS·하위 95% 288ms의 준수한 성능을 기록한다.
웹플럭스 1차 측정은 겨우 706 TPS로 참담했다. 구글링으로 찾은 스택오버플로우의 async adapter 조언대로 바꿔도 693 TPS로 더 나빠졌고, 여러 번 돌리자 로그백 async appender에 로그가 쌓여 JVM 힙이 터지는 현상까지 확인한다.
실제 범인은 blocking IO를 일으키는 로그 메서드였고 이를 제거하자 4453 TPS가 나왔지만 응답 속도는 여전히 느렸다. 원인은 비동기 처리에 `map`을 써 동기로 동작해버린 것으로, 불필요한 map 체인을 줄이고 `flatMap`으로 고쳐 성능을 회복한다.
올바로 코딩한 웹플럭스는 MVC 대비 약 160% 처리량에 응답 속도도 앞선다. TPS만 보면 함정에 빠질 수 있어 전반 지표를 봐야 하며, 스캐터 차트가 하단에 떠 있으면 블로킹, 초반이 튀는 건 레디스 lazy 커넥션·JIT 최적화 때문임을 짚는다.
블로킹 코드를 잡아주는 BlockHound를 테스트에 활용하는 법, lettuce의 커넥션 validation 설정이 동기 ping을 유발해 성능을 떨어뜨리는 문제, 그리고 블로킹 코드를 별도 스레드 풀로 격리하는 `publishOn`/`subscribeOn`으로 리액터 멜트다운을 피하는 방법을 설명한다.