티스토리 뷰

이길저길의 실시간 양방향 위치 공유 시스템 설계 과정에 수행한 성능테스트 결과를 공유하고자 한다.

성능테스트 결과 뿐만 아니라 이전 글의 분석까지 종합하여 기술을 선정하였다.
 

시스템의 요구사항은 간략하게 다음과 같다:

  • 각 클라이언트는 연결이 된 시점부터 3초에 한 번 자신의 위치를 공유한다.
  • 위치 공유 대상은 사용자가 생성한 그룹의 그룹원들이며, 설계 단계의 예상 평균 그룹 인원수는 4명이다.

 
실시간 통신 기술들을 성능테스트 사전에 비교했던 흐름은 다음과 같다:

  1. SSE  vs  Short Polling
    • SSE로 구현시 API를 총 2개를 구현하여 사용해야한다.
      1. SSE 커넥션 API
      2. 자신의 위치를 전송하여 그룹원들에게 알리는 API 
    • Short Polling 에 비해 커넥션을 하나 더 가지고 있게 되어 SSE는 비효율적이라 판단했다.
  2. Short Polling  vs  Long Polling
    • Long Polling로 구현시 그룹원의 위치 공유시 연결을 끊게 되며 다음과 같은 상황이 발생한다.
      • 그룹원 4명 기준으로 1.3초마다 연결을 끊게 되어 커넥션을 생성하고 끊는 주기가 매우 짧아진다.
      • 클라이언트당 3초에 한 번 요청 * 4명 => (1/3)s * 4 = 약 1.3s
    • 주기가 짧은 만큼 굳이 Long Polling으로 구현할만한 요구사항이 아니라고 보여졌다.
    • 하지만, 예상과 다를 수 있기 때문에 성능테스트 결과를 확인하기로 했다.
  3. Short Polling  vs  Websocket
    • Short Polling 구현시 고려할점이 따로 있었다.
      • Short Polling을 3초에 한 번 요청시 다른 기술들보다 실시간성이 떨어질 수 밖에 없었다.
      • 타 기술들은 그룹원들이 각 다른 시점에 공유를 시작하기 때문에 평균 1.3s 마다 새로운 데이터를 받아보지만, Short Polling의 경우 API 요청시마다 데이터를 받을 수 밖에 없기 때문에 3초에 한 번 요청한다는 점에서 실시간성이 떨어진다.
    • Short Polling의 주기를 3초에서 줄여보는 것을 고려는 했지만 Short Polling 특성상 HTTP 커넥션을 계속해서 만들었다 끊어야 하기에 CPU intensive 한 작업으로 예상 됐다.
      • 굳이 Short Polling을 더 짧은 주기로 구현하기보다는 우선 3초로 설정해 성능테스트 결과를 확인하기로 했다.
  4. Websocket  vs  STOMP
    • Websocket과 STOMP의 구현적 특징을 제외한 기술적 차이점을 생각해봤다.
      • STOMP의 경우 따로 Message Broker를 사용한다는 특징이 있다.
      • Websocket의 경우 직접 해당하는 그룹원의 WebsocketSession을 찾아 공유된 데이터를 알려줘야한다.
        • 여기서 Map과 같은 자료구조를 사용해야 한다.
    • 파악한 부분으로는 명확하지 않기에 성능테스트 결과를 확인하기로 했다.

 
 

성능테스트로 다음 기술들을 비교 하고자 한다.

  1. Short Polling  vs  Long Polling
  2. Short Polling  vs  Websocket
  3. Websocket  vs  STOMP

 

성능 테스트 시나리오

성능테스트 툴은 WebSocket, STOMP 등의 테스트가 용이했기에 K6를 사용했다.
  • 원활한 테스트를 위해 추상화한 부분 
    • 그룹의 개수를 성능 테스트의 VUS에 맞게 비율을 고려하여 정하였다. 이후 VUS 마다 랜덤하게 그룹에 들어가도록 했다.
      • 평균 그룹의 그룹원 인원수는 요구사항을 따라가도록 하였다.
      • 그룹 생성과 합류는 실제 서비스에서는 다른 CRUD API에서 수행하는데, 테스트를 간편히 하기 위해 이렇게 구성했다.
  • 테스트 흐름
    • 공통
      • 성능테스트 코드에서 유저는 랜덤한 그룹으로 설정된다.
        • 이후 단계는 기술별로 아래 단계를 따른다.
    • Short Polling
      • 3초에 한 번 자신의 위치를 전송하고 나머지 그룹원들의 위치를 조회해 받아온다.
    • STOMP, WebSocket
      • 커넥션을 생성한다.
      • 3초에 한 번 자신의 위치를 모든 그룹원에게 공유하기 위해 요청한다.
    • SSE, Long Polling
      • 커넥션을 생성한다.
      • 3초에 한 번 그룹원에게 자신의 위치를 알리는 API를 호출한다.
      • 그룹원들의 위치 공유마다 데이터를 받으며, SSE의 경우 커넥션이 지속되고 Long Polling은 다시 커넥션 생성을 요청한다.
  • 요청 시나리오
    • 1000, 5000, 10000 vus 가 있을 때를 가정하고 매 기술을 3 단계에 나누어 테스트하였다.
    • 요청은 다음 stages를 따른다:
      1. 20초간 300명의 유저가 될때까지 늘려간다.
        • 유저 및 요청이 점차 증가함
      2. 30초간 [1000, 5000, 10000] 를 순서대로 해당 유저수가 될때까지 늘려 실제 테스트하고자 하는 유저수를 테스트한다.
        • 유저 및 요청이 급격히 증가함
      3. 10초간 유저를 0까지 점차 줄인다.
        • 유저 및 요청이 줄어들게 하여 서버의 회복성을 확인한다.

성능테스트 결과 및 분석

성능테스트 결과는 1000, 5000도 어느정도 의미 있었지만 가장 결과 차이가 큰 10000으로 비교하기로 한다.

1. Short Polling  vs  Long Polling [10000 vus]

Short Polling 결과

     data_received..............: 19 MB   237 kB/s
     data_sent..................: 5.5 MB  67 kB/s
     http_req_blocked...........: avg=64.85µs  min=0s    med=4µs     max=162.66ms p(90)=185µs p(95)=288µs
     http_req_connecting........: avg=36.13µs  min=0s    med=0s      max=16.3ms   p(90)=142µs p(95)=214µs
     http_req_duration..........: avg=422.63ms min=392µs med=43.5ms  max=3.6s     p(90)=1.47s p(95)=1.94s
     http_req_failed............: 100.00% ✓ 63081      ✗ 0      
     http_req_receiving.........: avg=504.35µs min=4µs   med=28µs    max=574.14ms p(90)=182µs p(95)=652µs
     http_req_sending...........: avg=133.4µs  min=1µs   med=14µs    max=183.77ms p(90)=64µs  p(95)=134µs
     http_req_tls_handshaking...: avg=0s       min=0s    med=0s      max=0s       p(90)=0s    p(95)=0s   
     http_req_waiting...........: avg=421.99ms min=370µs med=42.99ms max=3.6s     p(90)=1.46s p(95)=1.94s
     http_reqs..................: 63081   770.071737/s
     iteration_duration.........: avg=3.42s    min=3s    med=3.04s   max=6.6s     p(90)=4.47s p(95)=4.94s
     iterations.................: 63081   770.071737/s
     vus........................: 252     min=9        max=9999 
     vus_max....................: 10000   min=10000    max=10000

 

Long Polling 결과

     data_received..................: 6.9 MB 78 kB/s
     data_sent......................: 6.7 MB 75 kB/s
     http_req_blocked...............: avg=75.59µs min=0s    med=2µs    max=99.86ms p(90)=257µs  p(95)=366µs   
     http_req_connecting............: avg=56.52µs min=0s    med=0s     max=97.54ms p(90)=203µs  p(95)=283µs   
     http_req_duration..............: avg=3.32s   min=402µs med=3.08s  max=13.36s  p(90)=5.67s  p(95)=8.06s   
       { expected_response:true }...: avg=2.54s   min=402µs med=2.4s   max=12.65s  p(90)=4.76s  p(95)=5.37s   
     http_req_failed................: 33.03% ✓ 18949      ✗ 38419  
     http_req_receiving.............: avg=1.15ms  min=3µs   med=39µs   max=2.05s   p(90)=217µs  p(95)=643.64µs
     http_req_sending...............: avg=53.32µs min=1µs   med=11µs   max=64.86ms p(90)=52µs   p(95)=89µs    
     http_req_tls_handshaking.......: avg=0s      min=0s    med=0s     max=0s      p(90)=0s     p(95)=0s      
     http_req_waiting...............: avg=3.32s   min=367µs med=3.08s  max=13.36s  p(90)=5.67s  p(95)=8.06s   
     http_reqs......................: 57368  643.211303/s
     iteration_duration.............: avg=12.96s  min=3.01s med=13.08s max=25.48s  p(90)=17.07s p(95)=19.92s  
     iterations.....................: 19122  214.396293/s
     vus............................: 1      min=1        max=10000
     vus_max........................: 10000  min=10000    max=10000

분석

  • iteration_duration(함수를 1회 실행 소요 시간), interations(함수 호출이 반복된 총 횟수)
    • iteration_duration
      • Short Polling -> p(95) = 4.94s, avg = 3.42s
      • Long Polling -> p(95) = 19.92s, avg = 12.96s
      • 3초보다 길어진 부분은 요청이 많아지며 처리가 지연된듯하다.
    • iterations
      • Short Polling = 63081
      • Long Polling = 19122
    • Short Polling의 처리량이 보다 높음을 알 수 있다.
  • Heap Used
    • Short Polling = 29.2 / Long Polling = 62.9
    • Long Polling의 경우 DeferredResult를 통해 커넥션을 유지하기 때문에 힙 사용량이 보다 많다.

처리량이나 힙 사용량 등에서 Long Polling의 사용할 이유가 더더욱 없어졌다.
CPU 사용률도 비슷했지만 Long Polling이 오히려 CPU도 더 많이 사용했다.

 

2. Short Polling  vs  Websocket [10000 vus]

굳이 Short Polling 결과를 다시 복사 붙여넣기 하지는 않겠다.

Websocket 결과

    data_received.........: 65 MB   717 kB/s
     data_sent.............: 2.6 MB  28 kB/s
     iteration_duration....: avg=18.16s min=3.03s    med=14.36s max=58.08s p(90)=38.85s p(95)=44.15s
     iterations............: 136     1.509431/s
     vus...................: 3007    min=9          max=10000
     vus_max...............: 10000   min=10000      max=10000
     ws_connecting.........: avg=11.86s min=590.66µs med=3.78s  max=43.12s p(90)=25.55s p(95)=25.77s
     ws_msgs_received......: 1336749 14836.253751/s
     ws_msgs_sent..........: 8705    96.614689/s
     ws_session_duration...: avg=18.31s min=1.71ms   med=14.66s max=54.13s p(90)=44.02s p(95)=48.3s 
     ws_sessions...........: 8771    97.347207/s

분석

  • System CPU Usage / Process CPU Usage
    • System CPU Usage
      • Short Polling = 최대 0.970, 평균 0.193
      • Websocket = 최대 0.318, 평균 0.121
    • Process CPU Usage
      • Short Polling = 최대 0.364, 평균 0.0343
      • Websocket = 최대 0.0965, 평균 0.0384
    • WebSocket이 CPU를 보다 적게 사용하면서 통신한다.
  • Heap Used
    • Short Polling = 29.2%
    • Websocket = 43.0%
    • WebSocket Session을 계속 유지하며 지속적으로 Heap에 존재하게 되므로 커넥션을 유지하지 않는 ShortPolling 보다 WebSocket이 Heap 사용량이 더 많다.

Short Polling의 CPU 사용률이 눈에 띄게 매우 높았다. 하드웨어 자원을 더 많이 사용하면 해결될 수 있지만 CPU 자원은 메모리 자원보다 비용이 더 들어간다.
Heap Used가 더 높은 Websocket의 Heap 사용량이 43%인 것을 보면 무조건 메모리를 늘려야하는 수치는 아니라고 판단했다.
 

3. Websocket  vs  STOMP [10000 vus]

굳이 Websocket 결과를 다시 복사 붙여넣기 하지는 않겠다.

STOMP 결과

     data_received.........: 823 MB  9.1 MB/s
     data_sent.............: 3.7 MB  42 kB/s
     iteration_duration....: avg=32.37s min=12.01s   med=33s      max=33.02s p(90)=33s p(95)=33s
     iterations............: 878     9.754204/s
     vus...................: 329     min=12         max=10000
     vus_max...............: 10000   min=10000      max=10000
     ws_connecting.........: avg=5.54s  min=434.79µs med=354.49ms max=43.78s p(90)=30s p(95)=30s
     ws_msgs_received......: 3409118 37873.839154/s
     ws_msgs_sent..........: 24696   274.361
     ws_session_duration...: avg=29.65s min=9.01s    med=30s      max=30.05s p(90)=30s p(95)=30s
     ws_sessions...........: 9844    109.362619/s

분석

  • ws_msgs_sent(전송된 Websocket 메시지수), ws_msgs_received(받은 Websocket 메시지수),
    iterations(함수 호출이 반복된 총 횟수)
    • ws_msgs_sent
      • Websocket = 8,705
      • STOMP = 24,696
    • ws_msgs_received
      • Websocket = 1,336,749
      • STOMP = 3,409,118
    • iterations
      • Websocket = 136
      • STOMP = 878
    • 처리량 관점에서 STOMP가 확실한 우위를 보였다.
  • ws_sessions (connection 연결 총 개수)
    • Websocket = 8,771
    • STOMP = 9,844
  • 커넥션 수용 관점에서도 STOMP가 더 원활했다.
  • Websocket의 경우 톰캣 NIO가 스레드를 할당한 이후 요청 처리가 끝날 때까지 스레드 하나가 할당된 채로 계속 물고 있지만, STOMP의 경우 요청과 응답에 비동기 I/O가 적용되어 훨씬 높은 처리량을 보였다고 판단했다.

처리량이나 커넥션을 수용하는 관점에서 STOMP가 Websocket에 비해 뛰어났다. 하드웨어 자원 사용률이 미세하게 STOMP가 더 높았으나, 처리량 차이가 컸기에 같은 처리량을 기준으로 한다면 더 효율적으로 사용하는 것이라 볼 수 있을 것 같다.

 
 

기술 선택 결과

테스트 진행 결과를 확인하며 실시간 통신 기술 중에 자원사용률과 처리량을 고려해 STOMP를 선택하게 되었다.

 

MQ를 사용해야 하기에 관리 포인트를 늘린다는 단점은 있지만, 그 만큼 Websocket보다 높은 처리량을 보이며 하드웨어 자원 사용률이 안정적이라 보여졌기에, trade-off를 통해 STOMP를 선택했다.


이길저길 서비스 요구사항에 맞게 시나리오를 구성하여 나온 결과이므로 다른 환경과 요구사항에서는 또 다른 결과가 충분히 나올 수 있다고 생각한다.

 

테스트 수행 스테이징 서버 코드 저장소:

https://github.com/ohksj77/realtime-communication

 

GitHub - ohksj77/realtime-communication: 실시간 통신 기술 상호 비교 및 분석

실시간 통신 기술 상호 비교 및 분석. Contribute to ohksj77/realtime-communication development by creating an account on GitHub.

github.com

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
TAG
more
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함