CAP for Java

외부 API 직접 호출 그만 — Circuit Breaker #shorts #SAP #CAP

▶ YouTube에서 보기

외부 API 직접 호출의 문제점

CAP for Node.js 애플리케이션에서 외부 서비스(S/4HANA, 써드파티 API 등)를 호출할 때, 외부 서비스가 느려지거나 다운되면 CAP 앱 전체가 응답 불능에 빠질 수 있습니다. Circuit Breaker 패턴은 외부 서비스 장애가 내 앱으로 전파되는 것을 막는 안전장치입니다.

Circuit Breaker 없는 직접 호출 — 위험한 패턴

// 외부 API 직접 호출 — 외부 서비스 장애 시 내 앱도 블로킹
this.on('getWeatherForecast', async (req) => {
  const { cityCode } = req.data;

  // 외부 날씨 API가 10초 타임아웃이면 이 요청도 10초 블로킹
  const response = await fetch(
    `https://api.weather-service.com/forecast/${cityCode}`,
    { headers: { 'Authorization': `Bearer ${process.env.WEATHER_API_KEY}` } }
  );

  if (!response.ok) {
    req.error(response.status, '날씨 데이터 조회 실패');
    return;
  }

  return response.json();
});

opossum으로 Circuit Breaker 구현

// npm install opossum
const CircuitBreaker = require('opossum');

// 외부 API 호출 함수
async function fetchWeather(cityCode) {
  const response = await fetch(
    `https://api.weather-service.com/forecast/${cityCode}`,
    {
      headers: { 'Authorization': `Bearer ${process.env.WEATHER_API_KEY}` },
      signal: AbortSignal.timeout(5000)  // 5초 타임아웃
    }
  );

  if (!response.ok) {
    throw new Error(`Weather API 오류: ${response.status}`);
  }
  return response.json();
}

// Circuit Breaker 설정
const weatherBreaker = new CircuitBreaker(fetchWeather, {
  timeout: 5000,           // 5초 이상이면 실패 처리
  errorThresholdPercentage: 50,  // 50% 이상 실패 시 회로 차단
  resetTimeout: 30000,     // 30초 후 Half-Open 상태로 전환
  volumeThreshold: 5       // 최소 5번 호출 후 임계값 적용
});

// CAP 핸들러에서 사용
this.on('getWeatherForecast', async (req) => {
  const { cityCode } = req.data;

  try {
    const data = await weatherBreaker.fire(cityCode);
    return data;
  } catch (err) {
    if (weatherBreaker.opened) {
      // 회로가 열린 상태 — 빠른 실패
      return req.error(503, '날씨 서비스 일시적 장애. 잠시 후 다시 시도하세요.');
    }
    return req.error(500, `날씨 데이터 조회 실패: ${err.message}`);
  }
});

Circuit Breaker 상태와 폴백 처리

// 상태별 폴백 처리
weatherBreaker.fallback((cityCode) => {
  // 회로 차단 시 캐시된 데이터 반환
  return getCachedWeather(cityCode) || {
    cityCode,
    temperature: null,
    description: '날씨 정보를 일시적으로 사용할 수 없습니다.',
    cached: true
  };
});

// Circuit Breaker 이벤트 모니터링
weatherBreaker.on('open', () => {
  console.warn('[Circuit Breaker] 날씨 서비스 회로 차단 — 요청 차단 시작');
});

weatherBreaker.on('halfOpen', () => {
  console.info('[Circuit Breaker] 날씨 서비스 회로 반개 — 테스트 요청 허용');
});

weatherBreaker.on('close', () => {
  console.info('[Circuit Breaker] 날씨 서비스 회로 복구 — 정상 운영');
});

weatherBreaker.on('fallback', (result) => {
  console.warn('[Circuit Breaker] 폴백 응답 사용:', result);
});

CAP Destination 서비스와 Circuit Breaker 조합

const CircuitBreaker = require('opossum');
const cds = require('@sap/cds');

// CAP Destination 호출을 Circuit Breaker로 감싸기
async function callS4HANA(entityName, filter) {
  const S4 = await cds.connect.to('S4HANA_PROD');
  return S4.run(SELECT.from(entityName).where(filter));
}

const s4Breaker = new CircuitBreaker(callS4HANA, {
  timeout: 10000,
  errorThresholdPercentage: 30,
  resetTimeout: 60000
});

this.on('getSalesOrders', async (req) => {
  try {
    const orders = await s4Breaker.fire(
      'A_SalesOrder',
      { SoldToParty: req.data.customerId }
    );
    return orders;
  } catch (err) {
    if (s4Breaker.opened) {
      // S/4HANA 장애 시 로컬 캐시에서 반환
      return getLocalOrderCache(req.data.customerId);
    }
    throw err;
  }
});

Circuit Breaker 3가지 상태

  • Closed (정상): 모든 요청이 외부 서비스로 전달됨. 실패율이 임계값 미만.
  • Open (차단): 모든 요청이 즉시 실패 처리됨. 폴백 실행. 외부 서비스 보호.
  • Half-Open (테스트): resetTimeout 후 일부 요청만 외부로 전달. 복구 여부 확인.

공식 문서

opossum 라이브러리 전체 옵션은 nodeshift.dev/opossum에서 확인하세요. CAP 외부 서비스 연동은 cap.cloud.sap/docs/guides/using-services를 참고하세요.

댓글 0

아직 댓글이 없습니다.