728x90
반응형

개요

  • Transcoding 요청 개수가 증가하며 분배를 위한 계산에 소요되는 시간이 증가하게 됨.

    • 대략 10분 - 15분의 시간 소요
  • 이는 전체 미할당 데이터를 전부 쿼리하여, 분배를 위해 전체 데이터 개수들에 대한 작업이 동작하게 되므로 비효율적일 뿐더러 속도 역시 매우 느려질 수 밖에 없음

    • 이와 더불어 이후에 들어온 작업 요청들의 대기도 길어지기 때문에, user 별 기민한 처리에 불리함
  • 따라서 각 user 별 작업 요청에 대하여, 가용 TR 워커 개수만큼 조회하여 분배 작업을 수행해야 효율적일 것으로 예상

    • 이와 같은 방식으로 처리된다면 현재 사용 가능한 TR 워커 개수에 맞게 작업 내용을 처리 가능하며, 각 user 별로 개수의 제한을 둬 처리할 수 있기 때문에 특정 user 의 작업 요청만 밀린다는 등의 이슈를 해결할 수 있을 것으로 기대

#현황

  • 기존의 작업 할당 방식은, 전체 테이블에서 할당되지 않은 데이터를 전체 쿼리해여 데이터를 처리하는 방식
    • 그러나 전체 데이터 개수가 커질수록 쿼리에 소요되는 시간과, 가져온 이후 처리하는 시간이 기하급수적으로 커짐
    • 따라서 이러한 방식으로 프로덕 레벨의 작업 요청을 처리하게 되면 의 현상이 재현됨 (약 10-15 분 가량 분배를 위한 계산 로직 수행)
SELECT 
  id, group_nm, account_id, content_id, transcoder_type, request_body, 
  UNIX_TIMESTAMP(created_at) as created_ts, priority, retry_cnt 
FROM jobs
WHERE     assign_at      IS NULL 
AND      register_at IS NULL 
AND      complete_at IS NULL 
AND      cancelled=0 
ORDER BY priority, created_at

작업

  • 따라서, 미할당 내역을 user 별로 TR의 워커 개수만큼 조회해 가져와서, 분배 작업을 수행
    • 이 경우, 전체 조회해야 하는 row의 개수도 줄어들며 가져온 이후 처리해야 하는 데이터의 사이즈도 현격히 줄일 수 있음

 SELECT 
      id, group_nm, account_id, content_id, transcoder_type, request_body, created_ts, priority, retry_cnt
  FROM (
      SELECT 
          id, group_nm, account_id, content_id, transcoder_type, request_body, 
          UNIX_TIMESTAMP(created_at) as created_ts, priority, retry_cnt,
          @rownum := IF(@prev_acc = account_id, @rownum + 1, 1) AS row_num,
          @prev_acc := account_id
      FROM 
          jobs, 
          (SELECT @rownum := 0, @prev_acc := '') AS r
      WHERE   assign_at IS NULL 
        AND   register_at IS NULL 
        AND   complete_at IS NULL 
        AND   cancelled = 0
      ORDER BY account_id, priority ASC, created_at ASC
  ) AS t
  WHERE t.row_num <= 100
  ORDER BY t.priority, t.created_ts

테스트 수행

1차 테스트

  • 더미 데이터 약 15만개를 입력하고서, 동일한 데이터 테이블에 대해 기존 로직과 개선된 로직의 성능 테스트 진행함
    • 테스트 환경: dev
    • 테스트 대상 테이블: test_table.jobs_test
    • 미할당 작업 데이터 개수: 157,129
    • TR 워커 개수: 28개
  • 기존 로직
    • 조회되는 각 group 별 개수
      • group1: 78535
      • group2: 78593
    • 전체 로직 완료까지 소요된 시간: 약 10분 27초
  • 개선된 로직
    • 조회되는 각 group 별 개수
      • group1: 215
      • group2: 185
    • 전체 로직 완료까지 소요된 시간: 약 6초 783
구분 워커 개수 전체 데이터 개수 소요 시간
기존 쿼리 로직 28 157,129 10분 39초
개선 쿼리 로직 28 157,129 6초 783

2차 테스트

  • 더미 데이터 약 15만개를 입력하고서, 동일한 데이터 테이블에 대해 기존 로직과 개선된 로직의 성능 테스트 진행함
    • 테스트 환경: dev
    • 테스트 대상 테이블: test_table.jobs_test
    • 미할당 작업 데이터 개수: 157,129
  • TR 워커 개수: 100개
  • 개선된 로직
    • 전체 로직 완료까지 소요된 시간: 약 3초 664
구분 워커 개수 전체 데이터 개수 소요 시간
개선 쿼리 로직 100 157,129 3초 664

3차 추가 테스트

  • 더미 데이터 약 15만개를 입력하고서, 동일한 데이터 테이블에 대해 기존 로직과 개선된 로직의 성능 테스트 진행함
    • 테스트 환경: dev
    • 테스트 대상 테이블: test_table.jobs_test
    • 미할당 작업 데이터 개수: 157,129
  • TR 워커 개수: 200개
  • 개선된 로직
    • 전체 로직 완료까지 소요된 시간: 약 3초 737
구분 워커 개수 전체 데이터 개수 소요 시간
개선 쿼리 로직 200 157,129 3초 737

테스트 분석 결과

  • 개선된 로직은 평균 약 4.728초가 소요됨
    • 그러나 1차 테스트의 경우 전체 조회 로직(기존 로직)의 영향이 있을 수도 있으므로, 이상치로 가정함 (추후 추가 테스트 진행 예정)
  • 따라서 워커 개수의 약 두배만큼 조회 데이터 개수를 늘리더라도, 성능상의 차이는 크지 않음
    • 다만 워커 개수가 100개에서 200개로 늘렸을 때 약 0.073초 감소함
    • 해당 속도의 감소는 성능 저하를 의미할 수도 있으므로, 개선된 로직 내에서 적절값을 찾아가는 것이 필요해 보임
    • 물론 TR 워커 개수가 유동적이므로, 고정 값으로 사용하지 않는 이상 계산 로직이 필요할 수도 있음

결론

  • 그 결과 변경한 방식이 약 150 - 200배 이상의 성능 개선을 보이는 것을 확인하였다.
  • 위 쿼리 개선 로직을 적용한다면, 작업 분배 계산 시간을 줄일 수 있거 TR 요청 처리 성능을 크게 개선시킬 수 있을 것으로 기대함
  • 조회하는 user 데이터의 개수의 성능 임계점을 파악할 테스트 추후 진행 필요
728x90
반응형
728x90
반응형

gRPC vs REST

1. RPC(Remote Procedure Call)란?

  • 정의: 원격 프로시저 호출. 네트워크로 연결된 다른 서버의 함수/메서드를 마치 로컬 환경의 함수처럼 직접 호출하는 기술
  • 특징: 개발자가 소켓 통신, 직렬화/역직렬화 같은 하위 네트워크 계층을 직접 다루지 않고도 원격 메서드를 실행할 수 있도록 추상화 제공

2. REST vs gRPC 핵심 개념

REST (Representational State Transfer)

  • 웹의 표준 아키텍처 스타일로, 자원(Resource) 중심의 통신 방식
  • URI로 자원을 명시하고, HTTP 메서드(GET, POST, PUT, DELETE)를 통해 행위를 정의
  • 데이터 포맷으로 주로 사람이 읽기 쉬운 JSON을 사용
  • 브라우저 호환성이 뛰어나고 직관적이지만, 텍스트 기반 직렬화/역직렬화 오버헤드가 발생하며 기본적으로 단방향(Request-Response) 통신 위주

gRPC (Google Remote Procedure Call)

  • 구글이 개발한 고성능 오픈소스 RPC 프레임워크로, 동작(Action/Procedure) 중심의 통신 방식
  • 전송 계층으로 HTTP/2를 채택하여 멀티플렉싱(Multiplexing), 헤더 압축 지원
  • 데이터 직렬화에 바이너리 포맷인 Protocol Buffers(Protobuf)를 사용하여 페이로드 크기가 작고 속도가 빠름
  • IDL(.proto 파일)을 기반으로 다양한 언어의 통신 코드(Stub/Skeleton)를 자동 생성해 엄격한 타입 안정성(Type-safe) 제공
  • 단항(Unary) RPC뿐만 아니라 양방향 스트리밍(Bidirectional Streaming) 지원

3. 핵심 비교 요약

비교 항목 REST API gRPC
중심 설계 자원(Resource) 중심 행위/프로시저(Action) 중심
전송 프로토콜 HTTP/1.1 (일부 HTTP/2) HTTP/2
데이터 포맷 JSON, XML (텍스트 기반) Protocol Buffers (바이너리 기반)
통신 패턴 요청-응답 (Request-Response) 단항(Unary), 서버/클라이언트/양방향 스트리밍
인터페이스 정의 OpenAPI/Swagger (선택적) .proto 파일 (필수, 엄격한 스키마)
코드 생성 서드파티 툴 필요 Protoc 컴파일러를 통한 공식 자동 생성
브라우저 지원 네이티브 지원 (우수) 제한적 (gRPC-Web 프록시 필요)
디버깅 난이도 텍스트 기반이라 도구 없이도 확인 용이 바이너리 데이터로 전용 도구(BloomRPC 등) 필요

4. 언제 어떤 기술을 선택해야 할까?

  • REST API를 권장하는 경우:

    • 공개 API(Public API), 웹 프론트엔드-백엔드 간 통신처럼 범용적인 클라이언트 호환성이 필요할 때
    • 인프라 설정이 간단하고, 표준 브라우저 환경에서 직접 호출해야 할 때
    • 캐싱(HTTP Caching)을 적극적으로 활용해야 하는 정적/준정적 리소스 중심 서비스
  • gRPC를 권장하는 경우:

    • MSA(마이크로서비스 아키텍처) 환경에서 내부 서비스 간의 고성능·저지연 통신이 필수적일 때
    • 대용량 데이터 전송이나 실시간 스트리밍(양방향 데이터 동기화)이 필요할 때
    • Polyglot(다양한 개발 언어 사용) 환경에서 엄격한 스키마 공유와 타입 안정성을 유지하고 싶을 때

References

728x90
반응형

'백엔드 Backend' 카테고리의 다른 글

[보안] 보안 공격 종류  (0) 2024.11.04
[테스팅] 어플리케이션 테스팅  (2) 2024.11.01
[결합도/응집도] 결합도와 응집도란  (0) 2024.10.24
악성코드 종류  (0) 2024.10.16
SOLID 원칙  (0) 2024.10.08
728x90
반응형

발단

  • 데이터베이스 인스턴스를 띄워두고 rabbitmq 3개를 한 서버에 밀어넣어 사용중이었음
  • 그런데 계속 커넥션이 끊기는 것 같아, 분산 처리를 위해 각 서버에 띄우려고 했음
  • 그러다 보니, 집 ip 내부의 private ip에 있는 노드와 클라우드 위의 컨테이너를 어떻게 서로 하나의 클러스터로 묶을지 고민하게 됨

방법

  • tailscale 이라는 vpn 서비스를 통해 각 서버들을 하나의 가상 네트워크로 묶어버림
  • 이렇게 되면 가상 ip 가 발급되고, 같은 vpn 내부의 노드끼리는 찾아갈 수 있음
    • 외부에서 해당 ip 로는 당연히 못 찾아감

c500c552-1b39-4be4-99a2-6e74e26d4684.svg

  • 따라서 가상 네트워크로 묶을 모든 노드에 하나씩 설치하고 아래 방식으로 하나씩 인증해 묶으면 됨
curl -fsSL https://tailscale.com/install.sh | sh
  • 설치 완료 후 tailscale up 하면 브라우저로 인증 후 하나의 계정 아래에서 묶어 사용할 수 있게 됨

    sudo tailscale up
  • 가상 ip를 찾는 방

    • 100.xxx.xxx.xx 처럼 나옴
sudo tailscale status

포트 오픈

  • vpn 내부에서만 통신 가능하게 적용
    • public ip 혹은 포트포워딩이 필요 없음
    • NAT을 우회해 암호화 터널을 통과하기 때문
# Tailscale 인터페이스(tailscale0)를 통한 모든 접속 허용

sudo ufw allow in on tailscale0
  • rabbitmq 포트 설정
포트 번호 용도 설명
4369 EPMD (Erlang Port Mapper Daemon) 노드 네임 서버 (어느 노드가 몇 번 포트를 쓰는지 탐색)
25672 CLI & Inter-node Communication 노드 간 데이터 및 상태 동기화 핵심 포트
5672 AMQP Protocol 앱(클라이언트) 메시지 송수신 포트

컨테이너 설정

# 클라우드 서버 (rabbitmq-1) docker-compose.yml
services:
  rabbitmq-1:
    image: rabbitmq:3.13.3-management
    container_name: rabbitmq-1
    hostname: rabbitmq-1
    restart: always
    environment:
      - RABBITMQ_ERLANG_COOKIE=rabbitmqCookie
      - RABBITMQ_DEFAULT_USER=dong
      - RABBITMQ_DEFAULT_PASS=dong
    ports:
      - 15672:15672
      - 5672:5672
      - 4369:4369
      - 25672:25672
    extra_hosts:
      - "rabbitmq-2:192.168.0.11"
      - "rabbitmq-3:192.168.0.12"


# 개인 서버 2 (rabbitmq-3) docker-compose.yml
services:
  rabbitmq-2:
    image: rabbitmq:3.13.3-management
    container_name: rabbitmq-2
    hostname: rabbitmq-2
    restart: always
    environment:
      - RABBITMQ_ERLANG_COOKIE=rabbitmqCookie
      - RABBITMQ_DEFAULT_USER=dong
      - RABBITMQ_DEFAULT_PASS=dong
    ports:
      - 5672:5672
      - 4369:4369
      - 25672:25672
    extra_hosts:
      - "rabbitmq-1:192.168.0.10"
      - "rabbitmq-3:192.168.0.12"

클러스터 조인

docker exec -it rabbitmq-2 rabbitmqctl stop_app
docker exec -it rabbitmq-2 rabbitmqctl join_cluster rabbit@rabbitmq-1
docker exec -it rabbitmq-2 rabbitmqctl start_app
docker exec -it rabbitmq-3 rabbitmqctl stop_app
docker exec -it rabbitmq-3 rabbitmqctl join_cluster rabbit@rabbitmq-1
docker exec -it rabbitmq-3 rabbitmqctl start_app
  • 상태확인
docker exec -it rabbitmq-1 rabbitmqctl cluster_status
728x90
반응형

'데브옵스 devOps > Server' 카테고리의 다른 글

[STANDALONE] Standalone 프로그램이란?  (1) 2024.12.26
[파일전송] Rsync  (0) 2024.12.11
[배포전략] 무중단 배포란?  (1) 2024.10.05

+ Recent posts