Intern/Project
[Intern] 캐싱 및 redis 사용 검색 속도 향상 적용
dbfl9911
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
추가된 최적화 내용:
- Redis 캐싱:
- Redis를 활용하여 데이터를 저장 및 검색하며, TTL을 설정해 주기적으로 갱신.
- NodeCache와 함께 사용하여 Redis가 사용 불가능할 경우 로컬 캐싱도 활용 가능.
- 병렬 처리:
- getMultipleTransactions 메서드 추가로 여러 트랜잭션을 동시에 처리.
- 캐싱 계층 구조:
- Redis > NodeCache 순으로 데이터 조회를 시도하여 캐싱 효율성을 극대화.
기대 효과:
- Redis 캐싱과 Keep-Alive 설정으로 네트워크 및 데이터 처리 속도 개선.
- 병렬 처리를 통한 다중 요청 효율화.
Redis 캐시를 사용한 성능 개선의 주요 포인트
- 원래 소요 시간: 2874ms
- API 호출, 네트워크 지연, 데이터 처리 등이 포함된 시간.
- 이 과정이 생략되지 않고 실행되면 오래 걸릴 수밖에 없습니다.
- 개선된 소요 시간: 5ms
- Redis 캐시에서 데이터를 가져오므로 네트워크 요청 및 데이터 처리가 생략됨.
- 메모리에서 읽기만 수행하므로 매우 빠르게 결과를 반환.
어떻게 개선되었는지 정리
- Redis 캐싱 적용:
- 동일한 txHash에 대해 한 번만 API 호출.
- 이후 요청은 Redis에서 바로 데이터를 가져와 반환.
- 네트워크 지연 제거:
- API 호출 과정이 제거되어 네트워크와 서버 응답 시간이 사라짐.
- 데이터 처리 시간 단축:
- API로 받은 데이터를 정리하는 단계가 생략됨.
반응형