2024년 10월 3일 목요일

주식거래 프로그램 RxJS

 [주식거래 프로그램]


10초에 한번씩 주식 거래를 시작한다


한번의 주식 거래에서는 1000번의 API call을 수행한다

1000번의 API call을 함에 있어서 동시 요청은 10회 이하로 제한한다

10회의 요청이 끝날때마다 5ms 동안 휴식을 한다

1000번의 요청 중에 에러가 발생하면 요청을 다시 시작하되 최대 2번까지 반복한다

(물론 동시 요청 10회 이하의 조건은 만족해야 한다)


주식 거래를 성공한 뒤에는 10개씩 나누어 결과를 저장하되, 주식 거래할 때 영향을 주지 않도록 비동기로 저장한다.


이 시나리오는 주식 거래 시스템에서 10초마다 1000번의 API 호출을 하되, 동시 요청이 10개 이하로 제한되고, 에러 발생 시 재시도(최대 2회)가 필요하며, 성공한 요청 결과는 10개씩 비동기로 저장해야 한다는 복잡한 요구사항을 포함하고 있습니다. 이를 RxJS로 구현할 수 있습니다.


### 요구사항

1. **10초마다 주식 거래 시작**.

2. **동시에 10개의 API call**만 수행하며, 1000개의 요청을 처리.

3. **에러 발생 시 재시도**, 최대 2회.

4. **10개의 요청 완료 후 5ms 대기**.

5. **결과를 10개씩 나누어 비동기로 저장**.


### RxJS 코드 구현


```typescript

import { of, interval, from, forkJoin } from 'rxjs';

import { map, mergeMap, delay, catchError, retryWhen, bufferCount, tap } from 'rxjs/operators';


// 가상의 API call 함수 (성공하거나 실패 가능)

const apiCall = (id: number) => {

  return Math.random() > 0.1  // 10% 확률로 실패

    ? of(`Success: Call ${id}`).pipe(delay(50))  // 성공 시 50ms 지연

    : of(`Error: Call ${id}`).pipe(delay(50), map(() => { throw new Error(`Call ${id} failed`); }));

};


// 재시도 로직

const retryStrategy = (maxRetries: number, delayMs: number) => errors => 

  errors.pipe(

    delay(delayMs),

    tap(err => console.warn('Retrying due to error:', err)),

    mergeMap((error, index) => index < maxRetries ? of(error) : throwError(error)) // 최대 2회 재시도

  );


// 주식 거래 한 번 실행 (1000개의 API 요청)

const stockTrade = () => {

  return from(Array.from({ length: 1000 }, (_, i) => i + 1)).pipe(

    mergeMap(id => 

      apiCall(id).pipe(

        retryWhen(retryStrategy(2, 100)),  // 최대 2회 재시도, 100ms 대기

        catchError(err => of(`Failed after retries: ${err}`))  // 에러 발생 시 로그로 남기고 처리 계속

      ), 

      10 // 동시에 최대 10개의 API 요청 실행

    ),

    bufferCount(10),  // 10개의 요청마다 묶기

    mergeMap(buffer => of(buffer).pipe(delay(5))),  // 10개 완료 후 5ms 대기

  );

};


// 결과 저장 (비동기)

const saveResults = (results: string[]) => {

  return of(`Saving results: ${results.join(', ')}`).pipe(delay(100));

};


// 주식 거래를 10초마다 실행

interval(10000).pipe(

  mergeMap(() => {

    console.log('Starting new stock trade');

    return stockTrade().pipe(

      tap(results => {

        // 10개씩 묶어서 비동기로 저장

        saveResults(results).subscribe(res => console.log(res));

      })

    );

  })

).subscribe();

```


### 코드 설명


1. **`apiCall` 함수**: 50ms 동안 가상 API 요청을 시뮬레이션하며, 10% 확률로 실패할 수 있습니다.

   

2. **`retryStrategy` 함수**: 요청이 실패하면 최대 2번 재시도하며, 각 재시도 사이에 100ms의 지연이 있습니다.


3. **`stockTrade` 함수**: 1000개의 API 요청을 동시에 최대 10개씩 수행하고, 10개의 요청이 완료될 때마다 5ms 지연을 줍니다. 각 요청은 `retryWhen`을 사용하여 실패 시 최대 2번 재시도합니다. 요청이 완료되면 10개씩 결과를 묶어 처리합니다.


4. **`saveResults` 함수**: 10개의 결과를 비동기로 저장합니다. 저장은 `100ms`의 지연을 가진 가상의 비동기 작업으로 처리됩니다.


5. **`interval(10000)`**: 10초마다 주식 거래를 시작합니다. 각 거래는 `stockTrade()`로 1000개의 API 요청을 처리하며, 완료된 결과는 `tap`을 통해 비동기로 저장합니다.


### 주요 기능

- **동시성 제어**: `mergeMap(..., 10)`을 사용하여 동시에 최대 10개의 요청만 실행되도록 설정.

- **에러 처리 및 재시도**: `retryWhen`을 사용하여 최대 2번까지 실패한 요청을 재시도하고, 재시도 간 지연을 추가.

- **결과 저장**: 10개의 요청이 완료될 때마다 `bufferCount(10)`으로 묶어서 비동기로 결과를 저장.

- **5ms 지연**: 10개의 요청이 끝날 때마다 5ms의 지연을 추가하여 시스템 부하를 조정.


### 실행 흐름

1. 10초마다 1000번의 API 요청이 시작됩니다.

2. 동시에 최대 10개의 요청만 실행되며, 각 요청은 실패 시 최대 2번까지 재시도됩니다.

3. 10개의 요청이 완료되면 5ms의 지연 후 다음 10개의 요청을 처리합니다.

4. 완료된 결과는 10개씩 묶어서 비동기로 저장됩니다.


이 구조는 복잡한 비동기 작업 흐름을 효율적으로 관리하며, RxJS의 강력한 연산자를 사용하여 동시성, 에러 처리, 재시도 및 비동기 작업을 쉽게 구현할 수 있습니다.

댓글 없음:

댓글 쓰기