ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Intern] 캐싱 및 redis 사용 검색 속도 향상 적용
    Intern/Project 2024. 12. 12. 21:15

    근무 내용

    • 최종 코드 구현 확인
    • 캐싱 및 redis 사용해 cctp api 사용해 조회시 검색 속도 향상 적용

     

    근무 결과

    • 메소드 별 실행 시간 측정
    async getTransactionInfoFromRange(txHash: string) {
        const start = Date.now(); // 시작 시간 기록
        const url = '<https://usdc.range.org/usdc/api/transfers>';
        const { data } = await firstValueFrom(
          this.httpService.get(url, {
            params: {
              txHash,
              txnType: 'MAINNET',
              limit: 1,
              direction: 'first',
              source: 'ethereum,base,arbitrum', // 소스 네트워크
              destination: 'ethereum,base,arbitrum', // 목적지 네트워크
              status: '',
              min_usd: '',
              max_usd: ''
            }
          }).pipe(
            catchError((error: AxiosError) => {
              const errMsg = "Failed to fetch transaction info from Range API\\nMessage: " + error.message;
              console.error(errMsg);
              throw new CCTPapiError(errMsg);
            })
          )
        );
        const duration = Date.now() - start; // 실행 시간 계산
        console.log(`getTransactionInfoFromRange 실행 시간: ${duration}ms`);
        return data.resources[0];
      }
    
    getTransactionInfoFromRange 실행 시간: 2874ms
    

    ⇒ 속도 개선 방법 : 캐싱을 사용해 검색 성능 향상 시킴

    //-- api.service.ts 파일 
    import NodeCache from 'node-cache';
    import * as http from 'http';
    
    private cache: NodeCache;
      private httpAgent: http.Agent;
    
      constructor(
        private readonly httpService: HttpService,
        private readonly methodMapperService: MethodMapperService
      ) {
        // 캐싱을 위한 NodeCache 인스턴스 초기화
        this.cache = new NodeCache({ stdTTL: 300 }); // TTL 5분
    
        // HTTP Keep-Alive를 위한 에이전트 설정
        this.httpAgent = new http.Agent({ keepAlive: true });
      }
      //... 생략 기존 코드와 동일 
    
      async getTransactionInfoFromRange(txHash: string) {
        const start = Date.now(); // 시작 시간 기록
        // 캐싱된 데이터 확인
        const cachedData = this.cache.get(txHash);
        if (cachedData) {
          console.log("캐시 사용");
          console.log(`getTransactionInfoFromRange 실행 시간 (캐시): ${Date.now() - start}ms`);
          return cachedData;
        }
    
        const url = '<https://usdc.range.org/usdc/api/transfers>';
    
         try {
          // HTTP 요청 시작
          const { data } = await firstValueFrom(
            this.httpService.get(url, {
              params: {
                txHash,
                txnType: 'MAINNET',
                limit: 1,
                direction: 'first',
                source: 'ethereum,base,arbitrum',
                destination: 'ethereum,base,arbitrum',
                status: '',
                min_usd: '',
                max_usd: ''
              },
              headers: {
                'Accept-Encoding': 'gzip, deflate, br', // 데이터 압축 요청
              },
              timeout: 3000, // 요청 타임아웃 설정 (3초)
              httpAgent: this.httpAgent, // Keep-Alive 설정
            }).pipe(
              // 에러 처리
              catchError((error: AxiosError) => {
                const errMsg = "Failed to fetch transaction info from Range API\\nMessage: " + error.message;
                console.error(errMsg);
                throw new Error(errMsg);
              })
            )
          );
    
          // 응답 데이터 캐싱
          this.cache.set(txHash, data.resources[0]);
    
          console.log(`getTransactionInfoFromRange 실행 시간: ${Date.now() - start}ms`);
          return data.resources[0];
        } catch (error) {
          console.error("getTransactionInfoFromRange 에러:", error.message);
          console.log(`getTransactionInfoFromRange 실패 시간: ${Date.now() - start}ms`);
          throw error;
        }
    }
    
    • 캐싱(NodeCache):
      • 동일한 txHash 요청에 대해 캐싱된 데이터를 반환하여 불필요한 HTTP 요청 방지.
      • TTL(Time To Live)을 5분으로 설정.
    • HTTP Keep-Alive:
      • 지속적인 연결을 유지하여 네트워크 지연 감소.
    • 압축 활성화:
      • Accept-Encoding 헤더를 추가하여 Gzip, Brotli 등의 압축 데이터 요청.
    • 타임아웃 설정:
      • timeout: 3000으로 타임아웃을 설정하여 비정상적인 대기 시간 방지.
    • 성능 로그 추가:
      • 실행 시간을 기록하여 성능을 지속적으로 모니터링.

    결과는 아래와 같다.

    getTransactionInfoFromRange 실행 시간: 2113ms
    

    기대 효과

    • 캐싱 적용으로 동일한 요청에 대해 실행 시간이 획기적으로 단축.
    • HTTP Keep-Alive로 네트워크 연결 지연 최소화.
    • 데이터 압축과 타임아웃 설정으로 요청 효율성 및 안정성 향상.

    더 속도를 올릴 수 없을까 해서 Redis를 사용해보기로 했다.

    import { createClient } from 'redis';
    //...
      private redisClient: any;
    
      constructor(
        private readonly httpService: HttpService,
        private readonly methodMapperService: MethodMapperService
      ) {
        // 캐싱을 위한 NodeCache 인스턴스 초기화
        this.cache = new NodeCache({ stdTTL: 300 }); // TTL 5분
    
        // HTTP Keep-Alive를 위한 에이전트 설정
        this.httpAgent = new http.Agent({ keepAlive: true });
    
        // Redis 클라이언트 초기화
        this.redisClient = createClient();
        this.redisClient.connect().catch((err) => {
          console.error("Redis 연결 실패:", err);
        });
        
      }
    

    Redis 서버 실행후 프로젝트 실행시켜야 함

    redis-server
    
    async getTransactionInfoFromRange(txHash: string) {
        const start = Date.now(); // 시작 시간 기록
        
        // Redis 캐시 확인
        const redisCachedData = await this.redisClient.get(txHash);
        if (redisCachedData) {
          console.log("Redis 캐시 사용");
          console.log(`getTransactionInfoFromRange 실행 시간 (Redis 캐시): ${Date.now() - start}ms`);
          return JSON.parse(redisCachedData);
        }
    
        // NodeCache 확인
        const nodeCachedData = this.cache.get(txHash);
        if (nodeCachedData) {
          console.log("NodeCache 캐시 사용");
          console.log(`getTransactionInfoFromRange 실행 시간 (NodeCache): ${Date.now() - start}ms`);
          return nodeCachedData;
        }
    
        // 캐싱된 데이터 확인
        const cachedData = this.cache.get(txHash);
        if (cachedData) {
          console.log("캐시 사용");
          console.log(`getTransactionInfoFromRange 실행 시간 (캐시): ${Date.now() - start}ms`);
          return cachedData;
        }
    
        const url = '<https://usdc.range.org/usdc/api/transfers>';
    
         try {
          // HTTP 요청 시작
          const { data } = await firstValueFrom(
            this.httpService.get(url, {
              params: {
                txHash,
                txnType: 'MAINNET',
                limit: 1,
                direction: 'first',
                source: 'ethereum,base,arbitrum',
                destination: 'ethereum,base,arbitrum',
                status: '',
                min_usd: '',
                max_usd: ''
              },
              headers: {
                'Accept-Encoding': 'gzip, deflate, br', // 데이터 압축 요청
              },
              timeout: 3000, // 요청 타임아웃 설정 (3초)
              httpAgent: this.httpAgent, // Keep-Alive 설정
            }).pipe(
              // 에러 처리
              catchError((error: AxiosError) => {
                const errMsg = "Failed to fetch transaction info from Range API\\nMessage: " + error.message;
                console.error(errMsg);
                throw new Error(errMsg);
              })
            )
          );
    
          // 응답 데이터 Redis와 NodeCache에 캐싱
          const cachedValue = JSON.stringify(data.resources[0]);
          await this.redisClient.set(txHash, cachedValue, { EX: 300 }); // Redis TTL 5분
          this.cache.set(txHash, data.resources[0]); // NodeCache 저장
    
          console.log(`getTransactionInfoFromRange 실행 시간: ${Date.now() - start}ms`);
          return data.resources[0];
        } catch (error) {
          console.error("getTransactionInfoFromRange 에러:", error.message);
          console.log(`getTransactionInfoFromRange 실패 시간: ${Date.now() - start}ms`);
          throw error;
        }
    }
    
    Redis 캐시 사용
    getTransactionInfoFromRange 실행 시간 (Redis 캐시): 5ms
    

    추가된 최적화 내용:

    1. Redis 캐싱:
      • Redis를 활용하여 데이터를 저장 및 검색하며, TTL을 설정해 주기적으로 갱신.
      • NodeCache와 함께 사용하여 Redis가 사용 불가능할 경우 로컬 캐싱도 활용 가능.
    2. 병렬 처리:
      • getMultipleTransactions 메서드 추가로 여러 트랜잭션을 동시에 처리.
    3. 캐싱 계층 구조:
      • Redis > NodeCache 순으로 데이터 조회를 시도하여 캐싱 효율성을 극대화.

    기대 효과:

    • Redis 캐싱과 Keep-Alive 설정으로 네트워크 및 데이터 처리 속도 개선.
    • 병렬 처리를 통한 다중 요청 효율화.

    Redis 캐시를 사용한 성능 개선의 주요 포인트

    1. 원래 소요 시간: 2874ms
      • API 호출, 네트워크 지연, 데이터 처리 등이 포함된 시간.
      • 이 과정이 생략되지 않고 실행되면 오래 걸릴 수밖에 없습니다.
    2. 개선된 소요 시간: 5ms
      • Redis 캐시에서 데이터를 가져오므로 네트워크 요청 및 데이터 처리가 생략됨.
      • 메모리에서 읽기만 수행하므로 매우 빠르게 결과를 반환.

    어떻게 개선되었는지 정리

    • Redis 캐싱 적용:
      • 동일한 txHash에 대해 한 번만 API 호출.
      • 이후 요청은 Redis에서 바로 데이터를 가져와 반환.
    • 네트워크 지연 제거:
      • API 호출 과정이 제거되어 네트워크와 서버 응답 시간이 사라짐.
    • 데이터 처리 시간 단축:
      • API로 받은 데이터를 정리하는 단계가 생략됨.
    반응형
Designed by Tistory.