기본적으로 HTTP Protocol은 비연결성의 특징을 갖고 있으므로 실시간 통신을 하기에 적합하지 않은데, 이를 구현하는 방식 3가지를 알아보자 HTTP의 실시간 통신 방식
poilling 폴링
단점 : time interval을 어떻게 잡냐에 따라 서버의 부하가 올라가거나 실시간성이 떨어지는 trade off 관계를 갖는다 사용 : 실시간성이 조금 떨어져도 되고 시간을 늘려 여러대의 클라이언트와 통신을 할 때 사용
long polling 롱 폴링
단점 : 여러 클라이언트와 잦은 데이터 변경이 일어나면 서버의 부담이 크다 수백~수천대의 Client와 연결된 채팅 Server에서 한명이 채팅을 쓰면 데이터가 변경되어 Server는 변경된 데이터를 연결된 모든 Client에게 동시에 Response를 보내고, 다시 모든 Client에게 Request를 받으므로 순간적으로 Queue가 쌓여서 Server에 부담을 줄 수 있다 즉 서버의 부하도 줄이고, 실시간성도 높여주지만 대규모 클라이언트와 연결되있고 데이터가 자주 변경되는 경우에서는 오히려 서버에 부담감을 줄수 있다 사용 : 실시간성이 필요한 적은 수의 클라이언트와 연결되있는 경우에 사용
Streaming 스트리밍
단점 : 클라이언트에서 서버로 데이터를 보내는게 힘들다 → 따라서 실시간 양방향 통신이 아니라 실시간 단방향 통신이 주로 이뤄진다 결론 polling : 일정주기마다 서버에 계속 요청 (이벤트가 없어도 요청 → 서버, 클라 부담) long polling : 서버에 요청을 보내놓고 서버는 이벤트가 발생하면 응답, 다시 연결 (이벤트가 발생하면 연결된 모든 클라이언트에게 동시에 응답을 보내고 연결을 끊고, 새로 요청을 받는다 → 서버 부담) Streaming : 서버에 연결 요청을 보내놓고 계속 응답 데이터를 다운받는다. 서버는 이벤트가 발생하면 응답을 보낸다 (클라이언트가 서버에 데이터를 보내기가 힘들다) Web Socket
Half-Duplex : 양 방향 통신을 하지만 송수신을 동시에 할 수 없고, 무전기 방식처럼 해야한다 Full-Duplex : 동시에 송수신을 하며 양 방향 통신을 할 수 있다 특징
작동원리
결론 : 웹에서 실시간 통신을 위해 대부분 web socket방식을 사용하고 있다 전편(실시간 댓글 개발기(part.1) -DAU 60만 Alex 댓글의 실시간 댓글을 위한 이벤트 기반 아키텍처) 에서는 기본적인 테스트 환경 구축에 대한 설명을 드렸다면, 이번에는 테스트 진행에 대해서 공유하고자 합니다. 목표는 라이브 댓글 서버 한대당 10k 커넥션을 안정적으로 유지하고 2800TPS를 받을 수 있도록 하는 것인데요, 안정화를 위해서 Spring WebSocket 기능 뿐만 아니라 Tomcat, VIP, 물리장비, Docker 컨테이너 등에 대해서 메모리 및 file descriptor, configuration 등을 튜닝하였고 이를 위해 nGrinder, jcmd, Neo, FastThread.io, 직접 제작한 Golang Client 등 다양한 툴을 활용하였습니다. 너무 많은 것들을 동시다발적으로 튜닝하다보니 일관된 흐름으로 풀어내지 못한 것이 아쉽긴 하지만 가능한한 비슷한 것들을 묶어서 정리해보았습니다. 확인 및 튜닝 항목위에서 언급한 바와 같이 한개의 변수만을 바꾸어가며 차근차근 진행이 되지 못하고 한번에 다양한 방면의 변수들을 동시 다발적으로 테스트 한 경우가 많았지만, 진행된 테스트들을 크게 분류 해보자면 다음과 같습니다.
몇 개월 동안 위 항목들에 대한 수많은 테스트와 튜닝을 진행하면서 알게 된 사실들이 많지만 그 중 핵심 내용들만 다뤄보고자 합니다. 어플리케이션 분석 툴 소개Neo 도입우선 위 테스트들을 진행 하면서 소켓 관련 스프링 내부의 구조를 파악하기 위해 클라이언트를 소량으로 테스트하거나, 대량으로 테스트를 하더라도 커넥션이 성공적으로 맺어졌는지 집중적으로 확인할 필요가 있습니다. 또한 실제로 트래픽이 들어왔을 때 정확히 어떤 리소스가 어떻게 사용이 되는지에 대한 이해가 꼭 필요합니다. 이 부분을 더 정확하게 파악하기 위하여 당시 사내 자바 모니터링 시스템인 Neo를 사용해보기로 했습니다. Neo의 분석 메뉴에서 메소드 분석 탭에 들어가면 관리 기능을 통해서 특정 장비의 Thread Dump를 쉽게 뜰 수 있습니다. 분석 키워드는 기본적으로 kakao, daum 두 개가 등록되어 있는데, 우리는 spring messaging 패키지의 분석을 수행했으므로 messaging 키워드만 남겨놓고 분석 시작 버튼을 눌렀습니다. 분석이 끝나면 위 그림과 같이 분석 키워드에 매칭되는 메소드 목록을 보여주며, 그 중 하나를 클릭하면 쓰래드의 상태를 표시해줍니다. 위 그림은 현재 서버 상태의 스크린샷을 뜬 것으로써 특이사항은 없는데요, 아쉽게도 장애 시점의 스크린샷을 남긴 것이 없는 관계로 기억을 더듬어보면 위 녹색 RUNNABLE 대신에 BLOCKED 라는 라벨이 붙은 빨간색 표시가 있었습니다. 지금은 익숙해져서 비슷한 장애 발생시에도 Neo가 제공하는 정보만으로 장애 포인트를 찾을 수 있지 않을까 싶지만, 문제의 장애 시점에는 Neo의 차트를 정확히 이해하지 못하여 아래 소개된 FastThread를 이용하였습니다. fastThreadfastThread는 https://fastthread.io/주소로 서비스하고 있는 웹 기반 어플리케이션 분석 툴입니다. fastThread는 아래 그림과 같이 좀 더 시각적으로 풍부하게 정보를 제공합니다. 참고로 이 리포트를 보기 위해서 쓰레드 덤프를 떠야 하는데, 이때 쓰레드 덤프는 Neo에서 다운로드 받을 수 있습니다. 즉, 원본 데이터는 Neo와 동일한 것을 사용하며 시각화 방식만 차이가 납니다. fastThread가 제공하는 다양한 정보 중에 가장 큰 도움이 되었던 것은 아래처럼 생긴 (아마도) DEAD LOCK 리포트였습니다. 아래 그림을 보면 messageListenerContainer-1517364 쓰레드의 DestinationCache$1 클래스가 52개 쓰레드를 blocking 하고 있다는 것을 한눈에 보여주고 있는데요, 이를 통해 어느 코드가 문제인지 파악할 수 있었으며 blocking을 유발하는 코드를 제거함으로써 성능 개선을 할 수 있었습니다. 이에 대한 것은 후속 글(Spring의 동시성 접근 제어에 발목 잡힌 이야기)에서 더 심도 있게 다뤄볼 예정입니다. 시스템 튜닝
|