Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Binance Archive Server

Binance Spot 체결 아카이브 데이터를 받아 ClickHouse에 적재하는 ASP.NET Core 인제스션 서버입니다.

이 서버는 큰 JSON 요청을 한 번에 객체 그래프로 역직렬화하지 않습니다. ASP.NET Core Minimal API에서 요청 body를 스트리밍으로 읽고, Utf8JsonReader로 row를 순차 파싱한 뒤 ClickHouse에 RowBinary 포맷으로 chunk 단위 insert합니다.

주요 특징

  • ASP.NET Core Minimal API: 얇은 HTTP 엔드포인트와 명시적인 서비스 구성
  • Streaming JSON parser: PipeReader와 Utf8JsonReader로 요청 body를 순차 처리
  • Chunked ingest: 요청 전체 row를 메모리에 쌓지 않고 CHUNK_ROWS 단위로 처리
  • ClickHouse RowBinary insert: HTTP API를 통해 RowBinary payload를 스트리밍 전송
  • 선택적 기존 데이터 확인: CHECK_EXISTING_IDS=true일 때 chunk 단위로 기존 trade_id 조회
  • Native AOT 이미지 지원: Dockerfile.aot로 더 작은 런타임 이미지를 빌드 가능

프로젝트 구조

.
├── src/ArchiveBinance.Api/        # ASP.NET Core API 서버
├── init.sql                       # 신규 ClickHouse 볼륨 초기화 스키마
├── migrations/                    # 기존 ClickHouse 볼륨 수동 마이그레이션
├── Dockerfile                     # 일반 ASP.NET Core 런타임 이미지
├── Dockerfile.aot                 # Native AOT 이미지
├── docker-compose.yml             # 공통 Compose 설정
├── docker-compose.dev.yml         # 개발용 일반 런타임 Compose override
├── docker-compose.prod.yml        # 운영용 Native AOT Compose override
└── .env.example                   # 로컬 실행 환경 변수 예시

실행

환경 변수 파일을 준비합니다.

cp .env.example .env

개발용 일반 런타임 컨테이너로 실행합니다.

docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build

중지할 때도 같은 파일 조합을 사용합니다.

docker compose -f docker-compose.yml -f docker-compose.dev.yml down

헬스 체크:

curl http://localhost:3000/healthz

기본 서비스:

  • API server: http://localhost:3000
  • ClickHouse HTTP: http://localhost:8123
  • ClickHouse Native: localhost:9000

운영용 AOT 실행

운영 후보 구성은 Native AOT 이미지를 사용합니다.

docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --build

중지할 때도 같은 파일 조합을 사용합니다.

docker compose -f docker-compose.yml -f docker-compose.prod.yml down

이미지만 직접 빌드하려면 다음 명령을 사용합니다.

docker build -f Dockerfile.aot -t archive-binance-app:latest .

API

POST /api/v1/archive

Binance 체결 row를 ClickHouse의 binance_spot_trades 테이블에 적재합니다.

인증 헤더는 둘 중 하나를 사용합니다.

x-api-key: <API key>
Authorization: Bearer <API key>

요청 예시:

{
  "symbol": "BTCUSDT",
  "rows": [
    [
      0,
      "BTCUSDT:6033469037",
      6033469037,
      "66973.26000000",
      "0.03017000",
      "2020.58325420",
      1772323200005709,
      false,
      true
    ]
  ]
}

row 배열 순서:

Index Field Type Note
0 _row_index number 오류 메시지용
1 _dedupe_key string 읽고 버림
2 trade_id number Binance trade id
3 price string number Float64로 파싱
4 quantity string number Float64로 파싱
5 quote_quantity string number Float64로 파싱
6 trade_time number ms 또는 us timestamp
7 is_buyer_maker boolean
8 is_best_match boolean

스트리밍 처리를 위해 symbol은 rows보다 먼저 와야 합니다.

성공 응답 예시:

Successfully processed 100000 trades for BTCUSDT. (Inserted: 100000, Skipped: 0, Chunks: 2)

환경 변수

Name Default Description
CLICKHOUSE_URL http://localhost:8123 ClickHouse HTTP endpoint
CLICKHOUSE_USER admin ClickHouse user
CLICKHOUSE_PASSWORD password ClickHouse password
CLICKHOUSE_DATABASE default ClickHouse database
API_KEYS required 쉼표 구분 API key 목록
MAX_BODY_BYTES 268435456 Kestrel request body 제한
MAX_ROWS_PER_REQUEST 500000 요청당 최대 row 수
CHUNK_ROWS 50000 내부 insert chunk 크기
EXISTING_ID_LOOKUP_ROWS 20000 기존 trade_id 조회 query batch 크기
CHECK_EXISTING_IDS true 기존 trade_id 조회 여부
PORT 3000 API server port

메모리 모델

요청 전체를 List로 보관하지 않기 때문에 메모리 사용량은 주로 CHUNK_ROWS와 동시 요청 수에 비례합니다.

서버가 요청 처리 중 보관하는 주요 데이터:

  • 현재 Kestrel/PipeReader buffer
  • 현재 chunk의 TradeRow struct 목록
  • chunk 내부 중복 제거용 HashSet<trade_id>
  • CHECK_EXISTING_IDS=true일 때 기존 id 조회 결과와 lookup batch
  • insert 대상 trade_id 집합

CHECK_EXISTING_IDS=true에서는 chunk 안의 trade_id를 EXISTING_ID_LOOKUP_ROWS 단위로 나눠 조회합니다. 이 값은 ClickHouse 기본 max_query_size를 넘기지 않도록 기본 20000으로 둡니다.

순수 append 성능이 더 중요하고 ClickHouse의 ReplacingMergeTree 병합 중복 제거에 맡길 수 있다면 CHECK_EXISTING_IDS=false를 사용할 수 있습니다.

ClickHouse 스키마

초기 스키마는 init.sql에 있습니다.

CREATE TABLE IF NOT EXISTS binance_spot_trades
(
    symbol String,
    trade_time Int64,
    trade_id UInt64,
    price Float64,
    quantity Float64,
    quote_quantity Float64,
    is_buyer_maker Bool,
    is_best_match Bool
)
ENGINE = ReplacingMergeTree
PARTITION BY toDate(fromUnixTimestamp64Micro(trade_time, 'UTC'))
ORDER BY (symbol, trade_id);

trade_time은 앱에서 microseconds로 정규화합니다. millisecond timestamp 입력도 허용합니다.

운영 메모

  • MAX_ROWS_PER_REQUEST를 올릴 때는 MAX_BODY_BYTES도 함께 조정해야 합니다.
  • CHUNK_ROWS를 크게 올리면 insert 효율은 좋아질 수 있지만 앱 메모리 사용량도 증가합니다.
  • EXISTING_ID_LOOKUP_ROWS를 크게 올리면 기존 ID 조회 round-trip은 줄지만 ClickHouse max_query_size에 가까워집니다.
  • CHECK_EXISTING_IDS=true에서 더 큰 lookup batch가 필요하면 ClickHouse의 max_query_size도 함께 검토해야 합니다.
  • 기존 ClickHouse 볼륨은 init.sql 변경이 자동 반영되지 않습니다. 필요한 경우 migrations/001_rebuild_binance_spot_trades.sql을 수동 적용합니다.

간단한 빌드 검증

일반 이미지:

docker build -t archive-binance-app:dev .

Native AOT 이미지:

docker build -f Dockerfile.aot -t archive-binance-app:latest .

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages