Skip to content

artillery {nodejs}


README#

본 문서는 NodeJS 부하분산 테스트 프레임워크 artillery를 사용하여 현재 swjungle에서 진행중인 레크리에이션 서비스가 동시에 최대 100명의 유저가 원활하게 socket.io 통신을 수행할 수 있는지를 검증하도록 만드는 과정을 작성한 페이지입니다.

Artillery를 사용하면, 격리된 가상의 유저를 만들어 대상 서버에 부하를 줄 수 있다고 한다.

No memory, network connections, cookies, or other state is shared between virtual users.

로드테스트는 rampTo 옵션을 넣어 점진적으로 증가하거나 급격히 증가하는 로드를 시뮬레이트 할 수 있으며, 우리 케이스는 게임을 시작할때 급격한 트래픽이 발생할 것이기 때문에 spike 테스트 위주로 진행하면 될 것 같다.

artillery quick#

별도의 yml 파일을 작성하지 않고도 기본적인 HTTP 요청을 테스트 해볼 수 있다. 여기에서 다음과 같은 테스트를 수행해봤다.

artillery quick \
    --count 20 \
    --num 100 \
    https://www.choiwheatley.store

다음 명령어는 우리 시연용 페이지의 루트 / 엔드포인트에 20명의 가상유저가 각각 100번의 요청을 처리하는 루틴을 수행한다.

테스트 결과는 다음과 같이 나온다. summary report가 10초에 하나씩 출력된다.

--------------------------------
Summary report @ 16:34:24(+0900)
--------------------------------

http.codes.200: ............................... 5000
http.downloaded_bytes: ........................ 0
http.request_rate: ............................ 81/sec
http.requests: ................................ 5000
http.response_time:
  min: ........................................ 33
  max: ........................................ 654
  mean: ....................................... 358.1
  median: ..................................... 376.2
  p95: ........................................ 528.6
  p99: ........................................ 572.6
http.responses: ............................... 5000
vusers.completed: ............................. 50
vusers.created: ............................... 50
vusers.created_by_name.0: ..................... 50
vusers.failed: ................................ 0
vusers.session_length:
  min: ........................................ 64326.4
  max: ........................................ 64921.8
  mean: ....................................... 64581.5
  median: ..................................... 64236
  p95: ........................................ 64236
  p99: ........................................ 65533.7

report 페이지도 생성할 수 있다. artillery run -o report.json을 통해 테스트 결과를 JSON으로 받아본 뒤에 artillery report report.json을 사용하여 HTML 파일을 받아볼 수 있다.

Engines :: Socket.IO#

https://www.artillery.io/docs/reference/engines/socketio

이제 본론으로 들어가보자.

제일 먼저, 열려있는 캐치마인드 방에 100명이 들어가는 코드부터 작성해보자. 가짜 uuid와 가짜 닉네임을 만드는 데 적격인 falso 라이브러리를 사용할 수 있다니 대단하군

안된다.

아래 코드는 내가 직접 작성한 열린 캐치마인드 방에 ready 요청을 날리는 테스트를 수행한다.

config:
  plugins:
    fake-data: {}
  target: "https://api.choiwheatley.store"
  phases:
    - name: "join room"
      duration: 10
      arrivalRate: 1
  socketio:
    transports: ["websocket"]
    query: "uuId={{ $randUuid() }}"
scenarios:
  - name: random player joins
    engine: socketio
    flow:
      - namespace: "/catch"
        emit:
          channel: "ready"
          data: 
            room_id: 8
            nickname: {{ $randFullName() }}

이게 보니까, falso의 모든 함수를 지원하는 것이 아니었다... scenarios.flow.log를 사용하며 삽질을 할 수 있었는데, 그 와중에 하나 건진것이 randomString(10)이었다. 이제 ready 요청을 보낼때 랜덤한 값의 페이로드를 보낼 수 있게 되었다.

참고로, artillery-engine-socketio-v3을 사용했다. 이제 emit을 할때 channeldata를 사용하지 못하는 대신 리스트를 활용하여 보내게 된다. 좋은 점은, 페이로드를 보낼때 js object 형태로 보내도 된다는 것이다. - emit: ["login", { username: "creds", password: "secretPword" }]

config:
  target: "https://api.choiwheatley.store"
  phases:
    - name: "join room"
      duration: 5
      arrivalRate: 1
  engines:
    socketio-v3:
      query: "uuId=uuId-choiwheatley"
      transports: ["websocket"]
  plugins:
    fake-data: {}
scenarios:
  - name: random player joins
    engine: socketio-v3
    flow:
      - log: "{{ $randomString(10) }}"
      - emit: 
        - join
        - "{{ $randomString(10) }}"
        namespace: "/catch"

multiple target 적어도 multiple query만이라도..#

산 넘어 산이다. https://github.com/artilleryio/artillery/discussions/1471 대화에 따르면 Multiple config or scenarios sections in on YAML file are not supported라고 못을 박아놨다. 따라서, 100명의 유저가 100개의 uuId를 query param에 담아서 보내는 건 어쩌면 불가능 할지도 모르겠다. HTTP engine은 개별적인 flow에 query param을 따로 설정할 수 있는 것은 물론, 아예 별개의 url을 설정할 수도 있었는데...

없다. 생각한 방식대로 하려면 별도의 JS 파일을 사용하여 artillery를 직접 호출해야한다. 이때 환경변수를 따로 설정하여야 하는데, 이러면 artillery를 사용하는 의미가 없지않나?