콘텐츠로 이동

Node.js messaging bench

이 문서는 규격의 server-driven 모델(§10)을 Node.js에서 어떻게 구현했는지와 실행 방법을 적는다. 비교 대상·패턴·단위·판정 규칙은 규격이 소유하고, 이 문서는 Node 고유의 값만 담는다.

1. 비교 대상

구현 request send/command 사용 API
grpc-node BenchService.Echo unary(callback을 Promise로 감쌈) BenchService.Command unary, Empty reply @grpc/grpc-js, @grpc/proto-loader
zlink-node raw ROUTER socket.request(peer).message(...).timeout(...).submit() raw ROUTER socket.send(peer).message(...).submit() published @zlink-systems/zlink
zlink-framework-node framework channel request(예정) framework channel send(예정) @zlink-systems/framework, @zlink-systems/nestjs — 현재 전 셀 unsupported(§6)

gRPC와 raw는 같은 BenchPayload protobuf body와 body 앞의 29-byte header(규격 §6)를 쓴다. raw는 framework envelope와 protobuf body를 두 part로 보낸다.

2. 실행 방법

# 전체 matrix — 항상 perf 티켓 큐로
bash scripts/perf/perf-ticket.sh submit -p 1 -o <owner> -d "node with-grpc run" -- \
  bash framework/bench/grpc/node/run_local.sh

# 한 셀
env PAYLOADS=1024 SCENARIO=request-window IMPLEMENTATION=zlink-node \
  bash framework/bench/grpc/node/run_local.sh
입력 기본값 동작
RUNS 1 runner process가 수행할 run 수(측정 티켓은 run 단위)
RUN_DEALER 0 비교 계약상 0만 허용
DURATION 5 active 시간(초)
WARMUP 1000 active 전 warmup 호출 수
PAYLOADS 1024,4096 payload 목록. 두 값 밖은 preflight 실패
SCENARIO all all, request, 네 패턴 이름
IMPLEMENTATION all all 또는 세 구현 이름
WINDOW 100 request-window in-flight. 다른 값은 preflight 실패
STAMP 실행 시각 run ID와 결과 경로
OUTROOT framework/bench/grpc/log/node/with_grpc_node_<stamp> run root
SKIP_BUILD 0 1이면 npm ci·npm run build 생략

고정값: send concurrency 8, process 상한 300초, route/request/drain 상한 30초, settle quiet 200ms.

3. 프로세스 구성

구현 source A A 포트(trigger/stats) target B B 포트
grpc-node client/main.js — logical stream 수만큼 unary stub 5220/5221 grpc-server/main.js — echo/count gRPC 5222, stats 5223
zlink-node client/main.js — request용 raw ROUTER 1개 또는 send용 8개 5225/5226 zlink-raw-server/main.js — request·command ROUTER 분리 request 5227, command 5228, stats 5229
zlink-framework-node 시작하지 않음(unsupported) 5232/5233(예약) zlink-framework-server/main.js RouteMesh 5234, stats 5235(예약)

trigger·stats·phase 규칙은 shared/bench-http-application.js 한 곳(Node 표준 http)이 소유한다. trigger client는 runner의 curl이며 부하를 만들지 않는다. 셀 순서는 규격 §10.4 그대로이고 셀마다 새 A/B process 쌍을 쓴다. runner는 시작 전과 셀 종료 뒤 5220-5239의 LISTEN 소켓을 확인하고, 있으면 포트를 바꾸지 않고 중단한다.

패턴 streams.count streams.inFlightPerStream Node 구현
request-serial 1 1 Promise 순차 loop 하나
request-window 1 100 한 socket의 logical window를 Promise worker 100개가 공유
request-backpressure 1 없음 상한 없이 Promise 생성, 256회마다 event loop에 양보
send-saturation 8 1 stream마다 Promise worker와 gRPC stub 또는 raw ROUTER 하나

4. 언어별로 다르게 둔 값

항목
warmup 1000회 (smoke 100회)
gRPC source @grpc/grpc-js unary client stub, insecure loopback, 별도 tuning 없음
gRPC target @grpc/grpc-js Server 기본 옵션, insecure loopback
Node runtime v22.23.2 (2026-09-09 측정)
gRPC package @grpc/grpc-js 1.14.4, @grpc/proto-loader 0.7.15
ZLink binding published 0.17.6
framework workspace 소스(측정한 커밋을 원본에 기록)

5. 결과 위치

<OUTROOT>/
├── with_grpc_node_<stamp>.txt
├── unsupported.json            # 실행하지 않은 셀과 이유
└── <implementation>-<pattern>-<payload>-run<run>/
    ├── results.json            # with-grpc-cell-v1: role·trigger·streams·target_stats
    ├── report.txt
    ├── source.log / target.log
    └── target-stats.json
python3 framework/bench/grpc/tools/bench_aggregate.py --lang node --judgement-pattern request-window \
  --runs-glob '<run-1>/*' --runs-glob '<run-2>/*' --runs-glob '<run-3>/*' \
  --runs-glob 'framework/bench/grpc/log/c/<c-run>*' --format full

6. 알려진 제약

  • zlink-framework-node의 모든 셀은 unsupported다. framework의 protobuf codec이 bytes를 보존하지 못한다(제품 결함, 별도 작업). runner는 process를 시작하지 않고 unsupported.json에 이유를 남긴다.
  • zlink-node request-window는 binding 0.17.6에서 completion 유실(결정 기록 FB-049)이 재현되어 오류 셀로 기록된다. 결함 수정 전에는 그 행을 게재하지 않는다.
  • raw 비교는 ROUTER↔ROUTER로 고정한다(DEALER 보조 run 없음).