2025년 3월 10일 월요일

Node.js 서버의 동시 접속 한도 및 설정

Node.js 서버의 동시 접속 한도

Node.js 서버의 동시 접속 한도는 기본 설정으로 특정 숫자가 정해져 있지 않지만, 여러 요소에 의해 제한:

  1. HTTP 서버 기본 설정:

    • Node.js의 HTTP/HTTPS 서버는 기본적으로 연결 수에 엄격한 제한을 두지 않음
    • Express 같은 프레임워크는 기본 설정으로 대기열에 5,000개의 요청을 가질 수 있음
  2. 운영체제 제한:

    • 가장 일반적인 제한 요소는 운영체제의 최대 동시 열린 파일(소켓) 수
    • Linux에서는 보통 ulimit -n으로 확인 가능하며, 기본값은 1,024
    • 이는 서버가 1,024개의 동시 연결을 처리할 수 있음을 의미
  3. Node.js 이벤트 루프:

    • Node.js는 단일 스레드 이벤트 루프 모델을 사용하므로, 처리 능력은 CPU 작업 부하와 I/O 작업의 효율성에 따라 달라짐
  4. 하드웨어 리소스:

    • 메모리, CPU, 네트워크 대역폭 등 서버의 하드웨어 자원도 제한 요소

프로덕션 환경에서 더 많은 동시 연결 처리 방법

// 서버 생성 시 최대 연결 수 증가
const server = http.createServer(app);
server.maxConnections = 10000;

// OS 파일 디스크립터 한도 증가 (Linux/Unix)
// $ ulimit -n 10000 (터미널에서 실행)

// Keep-Alive 타임아웃 설정
server.keepAliveTimeout = 5000; // 5초
        

실제로는 클러스터 모듈이나 PM2 같은 프로세스 매니저를 사용하여 여러 개의 Node.js 인스턴스를 실행하고, Nginx나 HAProxy 같은 로드 밸런서를 앞단에 두는 것이 일반적

멀티파트 업로드 시스템의 경우, 특히 SSE(Server-Sent Events)를 사용한 진행률 모니터링 때문에 일반적인 HTTP 요청보다 더 많은 리소스를 사용할 수 있으므로, 프로덕션 환경에서는 서버 설정을 적절히 조정하는 것이 중요

현재 구조라면 로드밸런서(LB)만 추가해도 기본적으로 정상 동작할 수 있지만 대용량 트래픽과 멀티파트 업로드를 효율적으로 처리하기 위해서는 몇 가지 추가 설정이 필요

로드밸런서 추가 시 고려사항

  1. Connection 수 조정:

    • Nginx의 경우 worker_connections 값을 조정 (기본값은 보통 512나 1024)
    • HAProxy의 경우 maxconn 값을 조정
  2. 세션 관리:

    • 멀티파트 업로드의 경우 각 청크가 같은 서버로 가야 함
    • 특히 uploadId를 기준으로 동일 사용자의 요청이 동일 서버로 라우팅되어야 함
  3. 타임아웃 설정:

    • 대용량 파일 업로드는 일반 요청보다 오래 걸리므로 타임아웃 설정 확인 필요
    • Nginx의 경우 client_body_timeout, proxy_read_timeout 등을 조정

Nginx 설정 예시

http {
  # 워커 프로세스당 최대 연결 수 증가
  worker_connections 10000;

  # 요청 본문 크기 제한 증가 (대용량 청크 허용)
  client_max_body_size 10M;

  # 타임아웃 설정 증가
  client_body_timeout 120s;
  proxy_connect_timeout 120s;
  proxy_send_timeout 120s;
  proxy_read_timeout 120s;

  # 업로드 ID 기반 세션 일관성 설정
  upstream app_servers {
    ip_hash; # 또는 더 나은 방법으로 sticky sessions 사용
    server app1.example.com:3000;
    server app2.example.com:3000;
    # 추가 서버...
  }

  # SSE 관련 설정 (진행률 모니터링용)
  server {
    # ...

    location /api/progress {
      proxy_pass http://app_servers;
      proxy_http_version 1.1;
      proxy_set_header Connection '';
      proxy_buffering off;
      proxy_cache off;
      proxy_read_timeout 24h; # SSE 연결 유지를 위한 긴 타임아웃
    }

    # 일반 API 엔드포인트
    location /api {
      proxy_pass http://app_servers;
      proxy_http_version 1.1;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $host;
    }
  }
}
        

HAProxy 설정 예시

global
  maxconn 10000

defaults
  timeout connect 10s
  timeout client 30m # 클라이언트 타임아웃 증가
  timeout server 30m # 서버 타임아웃 증가

frontend http-in
  bind *:80
  default_backend app_servers

backend app_servers
  balance source # IP 기반 세션 고정
  option http-server-close
  server app1 app1.example.com:3000 check
  server app2 app2.example.com:3000 check
  # 추가 서버...
        

Node.js 서버에서도 고려할 점

로드밸런서를 추가하더라도 Node.js 서버 자체에서도 다음 설정을 확인해야 함:

// app.js 또는 서버 설정 파일
const server = http.createServer(app);

// 연결 시간 제한 증가
server.keepAliveTimeout = 60000; // 60초
server.headersTimeout = 65000; // 65초

// Express나 다른 미들웨어 설정
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
        

현재 코드에서는 SSE를 사용한 업로드 진행 모니터링이 있으므로, 특히 이 부분이 로드밸런서를 통해 제대로 작동하는지 확인하는 것이 중요. 로드밸런서는 SSE 연결을 장시간 유지할 수 있도록 설정

로드밸런서만 추가해도 기본적인 동작은 가능하지만, 효율적인 운영을 위해서는 로드밸런서와 Node.js 서버 모두에서 연결 수, 타임아웃, 세션 지속성 등의 설정을 적절히 조정해야 함

타임아웃 설정시 일관성이 필요. SSE 연결이 제대로 유지되려면 Node.js 서버의 타임아웃도 로드밸런서의 타임아웃과 일치하거나 그보다 길어야 함

// Node.js 서버 설정
const server = http.createServer(app);

// SSE 연결을 위한 긴 타임아웃 설정
server.keepAliveTimeout = 86400000; // 24시간 (밀리초 단위)
server.headersTimeout = 86460000; // 약간 더 길게 설정 (24시간 + 1분)
        

현재 구조에서는 다음과 같은 타임아웃 설정이 일관성 있게 적용되어야 함:

  1. 로드밸런서(Nginx/HAProxy): 24시간 타임아웃
  2. Node.js 서버: 24시간 타임아웃
  3. 업로드 상태 관리: 코드에서 expiresIn: 6 * 60 * 60 (6시간)으로 설정되어 있음

특히 uploadStateService에서는 비활성 상태에 대한 타임아웃이 6시간으로 설정되어 있는데, 이 시간도 일관성 있게 조정하는 것이 좋음

리소스 관리 측면에서는 실제 사용 패턴에 따라 이 타임아웃 값을 조정해야 함. 대부분의 업로드가 몇 시간 내에 완료된다면, 24시간보다 짧게 설정하는 것이 리소스 낭비를 줄일 수 있음

Docker로 설치된 PostgreSQL에 CSV 파일을 insert하는 과정

1. Docker 컨테이너 실행 : - PostgreSQL 컨테이너가 실행 중이 아니라면 , 다음 명령어로 컨테이너를 시작 ``` docker start <container_name> ``` 2. 컨테...