이 블로그는 마이크로서비스패턴 (길벗) 책 내용을 스터디를 위해 정리 한 내용 입니다.
책구매는 바로가기 에서 구매 가능합니다.
마이크로 서비스는 클라이언트에서 API 호출시 분산되어 있는 여러 API 중 적절한 시스템을 찾아 호출 해야 한다. 이번 장에는 API 를 적절하게 호출 할 수 있는지 그 구현과 설계 방법을 알아본다.
외부 API 설계 이슈
FTGO 라는 어플리케이션은 다양한 환경에서 API 호출이 한다.
- 웹 어플리케이션
- 브라우저에서 수행중인 자바스크립트
- 소비자용/배달원용 클라이언트 모바일 앱
- 서드파트 어플리케이션
웹어플리케이션은 방화벽 내부의 전용선(LAN) 환경에서 호출 하므로 수행 속도가 빠르지만, 나머지 브라우저 자바스크립트, 클라이언트 앱, 서드파트 어플리케이션은 공용망(인터넷)을 통해 호출 해야 한다.
또, 클라이언트가 직접 API 를 호출하게되면
- 필요한 데이터를 가져오려면 필요한 정보를 제공하는 API 에 어려번 요청해야 하고, 효율이 떨어진다.
- 클라이언트 서비스가 API 를 알아야 하므로, 캡슐화되지 않고, API를 변경하기 어렵다
- 방화벽등의 허들이 있는 환경에서 서비스를 사용중일 수 있다.
API 설계이슈 : FTGO 모바일 클라이언트
모놀리식은 하나의 요청으로 모든 정보를 조회할 수 있지만 마이크로서비스에서는 여러 서비스에 정보가 분산되어있다.
클라이언트앱이 서비스를 직접 호출 하는 구조라면, 서비스를 여러번 호출해서 데이터를 가져 올 수 밖에 없다. 결국 클라이언트가 API 조합기 역할이 된 것이다.
이런 구조에서는 다음과 같은 단점이 있다.
클라이언트가 요청을 여러번 전송하기 때문에 UX가 나빠진다.
클라이언트 어플리케이션과 서버간의 상호 작용이 너무 많아지면, 어플리케이션이 로딩이 될때까지 기다리며 멎은것 처럼 보일 수도 있다. 클라이언트는 공용망 (인터넷망) 으로 전송 되므로, 지연시간이 많이 발생 될 수도 있다.
또, 클라이언트 개발에만 집중해야할 개발자가 본연의 입무에 집중하는데 방해가 된다.
캡슐화 되어 있지 않아 프런트엔드 개발자가 백엔드와 맞물려 코드를 변경해야한다.
API 가 분리 되거나 합쳐질때, 클라이언트에 영향이 가게 된다. 모바일 앱의 경우, 배포가 자주 일어나지 않기떄문에 배포 업무에 제약 사항이 생길 수 밖에 없다.
클라이언트에 비친화적인 IPC 를 사용중인 클라이언트도 있다.
일반적으로 HTTP, 웹소켓 기반으로 IPC 를 사용하지만, gRPC나 AMQP (Advenced Message Queing Protocol) 같은 프로토콜을 쓰는 서비스도 있을것이다. 이런겅우 웹어플리케이션에서는 자유로울수 있으나 모바일 앱같은 다른 환경의 클라이언트들은 경우 적용하기 어려울 수 있다.
API 설계이슈 : 다른 종류의 클라이언트
웹 어플리케이션
?무슨말인지 모르겠음
브라우저 기반의 자바스크립트 어플리케이션
브라우저에서는 대부분 자바스크립이 사용되고 있고, 이 자바스크립트로 API 를 호출한다. 이때 인터넷을 통해 접근 되기때문에 네트워크 지연 현상은 발생 할 수 밖에 없다. 또 자바 스크립트로는 API 를 효율적으로 조합하기 어렵다.
서드파티 어플리케이션
서드파티의 경우 안정적인 API를 제공 해야한다. 그런데 API 가 수정시, 서드파티 개발자에게 무조건 새 API 로 업그레이드 하라고 강요 할 수 는 없다. 때문에, 직접 API를 제공하는 대신 퍼블릭 API를 제공 해야 한다.
API 게이트웨이 패턴
API 게이트웨이는, 외부 API 클라이언트의 진입점에 해당하는 서비스를 구현하는 것을 말한다.
API 게이트웨이 패턴 개요
API 게이트웨이는 외부 클라이언트가 애플리케이션 API를 요청하는 단일 창구 역할을 한다.
API 게이트웨이는 요청라우팅, API 조합, 프로토콜 변환을 관장한다.
요청 라우팅
API 게이트웨이는 요청을 요청 라우팅 맵을 찾아 어느 서비스로 보낼지 결정한다. 리버스 프록시(reverse proxy) 와 같은 기능이다.
API조합
단일 API 를 호출하면 게이트웨이는 지정된 여러 API 를 조회하여 한꺼번에 데이터를 내려보낸다.
프로토콜
프로토콜 변환 기능도 제공한다. 애플리케이션 내부에서 REST와 gRPC 를 혼용 할 경우에도 외부에 단일 REST API 를 서비스 할 수 있다.
API 게이트웨이는 클라이언트마다 적합한 API를 제공한다.
서비스별로 API 요구 조건은 다양할 수 있다. 예를 들어 주문 전체를 원하는 어플리케이션, 일부 주문건만 원하는 어플리케이션이 있을수 있다. 이럴때 API 게이트웨이로 API 별 맞춤형으로 제공 할 수도 있다.
엑지기능 구현 (주변기능)
- 인증 : 요청 권한확인
- 인가 : 허가받은 클라이언트인지 확인
- 사용량 제한 : 초당 호출수 제한
- 캐싱 : 응답값 캐싱
- 지표수집 : 과금등을 위한 API 사용지표 수집
- 요청로깅 : 요청기록
이런 기능들이 개발 되는곳은 다음과 같다.
- 백엔드서비스 : 캐싱, 지표수집, 인증같은 경우 백엔드에 있어야 할거같지만, 백엔드 도달 전에 수행하는편이 더 안전하다.
- API 게이트웨이 상류 (Upstream) : 외부 API 와 맞닿아있는 API 게이트웨이 진입점이다. API 게이트웨이에 인입전 처리한다.
- 전용 엣지 서비스 : API 게이트웨이는 라우팅/조합에 집중하고, 나머지 기능을 엣지 기능에 중앙화 할 수 있다. 대신 홉 카운트 (네트워크가 거치는 단계) 가 늘어나기 떄문에 네트워크 지연이 발생한다.
결론적으로 엣지 서비스를 사용하되, 인증 등의 기능은 API 게이트웨이 안에서 구현하는 편이 간편하고 성능상 잇점이 있다.
API 게이트웨이 아키텍처
API 계층 (API layer) 과 공통 계층 (Common layer) 으로 구성된 구조로 되어있다. 공통계층에 엣지 기능이 구현된다.
각 모듈은 라우팅 규칙이 있는 파일을 읽어 자신이 맞는 API 서비스에 요청 한다.
API 조합과 같은 복잡한 API 작업은 직접 코딩으로 작성한다.
API 게이트웨이 소유권모델
API 게이트웨이 개발/운영의 경우 몇가지 방법이 있다.
API 게이트웨이 전담팀을 따로 구성하는 방법이다. 이 방법의 경우 클라이언트들의 요구사항이 생길 경우, 전담팀에서 수정할때까지 기다릴 수 밖에 없다.
또하나의 방법은 API 게이트웨이 전담팀이 공통Layer 를 소유하고, API 가 표출된 모듈 (API Layer)는 해당 클라이언트팀 (모바일, 웹 팀 등) 이 소유 하는 방식이다. 각 영역별로 개별 팀이 해당 로직에 집중 할 수 있다.
프런트엔드 패턴을 위한 백엔드 (BFF)
API 게이트웨이를 클라이언트마다 운영하는 방식을 BFF (Backends For Frontends) 이라고 한다. 개별 클라이언트팀이 자신이 필요한 모듈만 탑재된 의 API 게이트웨이를 운영하는 방식이다.
모듈이 각각 개별 시스템으로 분리되어, 신뢰성이 향상된다. 또 서비스가 작아지므로 성능도 올라가게 된다.
대신 공통 기능이 중복 될 수 있으므로 동일 기술 스택을 적용 하는 편이 바람직 하다.
API 게이트웨이의 장단점
API 게이트 웨이의 장점
가장 큰 장점은 마이크로서비스 어플리케이션의 내부 구조가 캡슐화 되는 것이다. 또, 클라이언트가 특정 서비스를 호출 할 필요 없이 API 게이트웨이만 호출 하면 된다.
API 게이트 웨이의 단점
개발, 배포, 관리 해야 하는 컴포넌트가 하나 더 늘어난다. 또, 개발이나 트래픽에서 병목점이 될 가능 성도 있다.
API 게이트웨이 사례 : 넷플릭스
넷플릭스 클라이언트는 재생 할 수 있는 환경이 광범위 하기도 하고, 요건도 제각각이여서 API 를 만능으로 개발 하는데 한계가 있었다. 때문에 클라이언트 별로 API 게이트웨이를 구성하였고, 해당 팀이 API 게이트웨이를 소유/개발 하고 있다.
넷플릭스는 처음에 각 클라이언트 팀이 API 라우팅/조합을 하는 그루비스크립트를 작성해 게이트웨이를 만들었다. 스크립트는 서비스팀에서 받은 자바클라이언트 라이브러리로 하나 이상의 API 를 호출 하는 방식이였다. 이런 모놀리식 서비스는 넷플릭스의 트래픽이 높아짐에 따라 관리하기 어려워 졌다.
지금도 넷플릭스는 BFF패턴과 유사한 아키텍져로 이전 하고 있다.
각 클라이언트팀은 Node.js 로 모듈을 개발한다. 스크립트가 직접 호출 하는 방식이 아닌, 넷플릭스 팔코(Netflix Falcor) 를 이용해 다른 API 게이트웨이를 호출한다. 넷플릭스 팔코(Netflix Falcor) 는 선언적으로 API 를 동적 조합해 API 를 호출 하는 기술로 여러 API 서비스를 호출 할 수 있따.
API 게이트웨이 설계 이슈
API 게이트웨이를 설계할때 다음과 같은 문제를 검토해야 한다.
성능과 확장성
- 동기 I/O 모델 : 각 네트워크 모델마다 스레드를 하나씩 배정하는 방식. 개발이 편하고 프로그램이 잘 작동한다. 단, 쓰레드 개수 및 동시접속자 가능 개수가 제한적이다.
- 비동기 (논블로킹) I/O 모델 (NIO) : 단일 이벤트 루프 쓰레드가 요청을 각 이벤트 핸들러로 디스패치. 다중 쓰레드를 사용하지 않기 때문에 오버헤드가 없어 확장성이 좋다. 단, 비동기/콜백 로직은 훨씬 복잡해서 개발하기도 어렵고 코드를 이해하기도 어렵다.
동기나 비동기나 를 선택하는것은 API 요청 처리 로직에 달렸다. 넷플릭스의 경우 NIO 를 도입하고나서 처리율은 25% 증가하고, CPU 사용량은 25% 감소하였다.
리액티브 프로그래밍 추상체를 이용하여 관리 가능한 코드 작성
API 게이트웨이는 백엔드 서비스를 호출한 결과를 조합한다. API 끝점의 메소드에의해 결정된 순서대로 서비스를 호출 하게 된다.
getOrderDetails는 순서대로 서비스를 호출 하는 방식의 핸들러이다.
@RestController
public class OrderDetailsController {
@RequestMapping("/order/{orderId}")
public OrderDetails getOrderDetails(@PathVariable String orderId) {
OrderInfo orderInfo = orderService.findOrderById(orderId);
TicketInfo ticketInfo = kitchenService.findTicketByOrderId(orderId);
DeliveryInfo deliveryInfo = deliveryService.findDeliveryByOrderId(orderId);
BillInfo billInfo = accountingService.findBillByOrderId(orderId);
OrderDetails orderDetails = OrderDetails.makeOrderDetails(
orderInfo
, ticketInfo
, deliveryInfo
, billInfo );
return orderDetails;
}
...
서비스를 순차 호출 하면, 결국 각 서비스가 응답 올때까지의 호출 시간의 합만큼 시간이 소요된다. 때문에 가능한 모든 서비스를 동시에 호출 해야 하고, 각 서비스 응답아 수신되면 응답값을 쌓아놓았다가 모든 응답이 수신되면 응답값을 클라이언트에 반환 해야 한다.
기존 콜백과 같은 방식으로 구현하면, 코드가 뒤섞여 알아보기 힘들고, 병렬, 순차 호출이 혼용될 시 에러가 날수 있다.
때문에 리액티브하게 선언형 스타일로 작성 하는 편이 낫다.
다음ㅇ은 대표적 JVM 용 리액티브 추상체 이다.
- Java 8 CompletableFutures
- Project Reactor Monos
- RxJava (Reactive Extensions for Java) Observables, created by Netflix specifically
to solve this problem in its API gateway - Scala Futures
이 추상체를 이용하면 쉽고 단순한 동시성 코드 작성이 가능하다.
부분 실패 처리
API 게이트웨이를 부하분산기 후면에 여러 게이트웨이 인스턴스를 두고 가동 해야 한다. 특정 인스턴스 오류사 다른 인스턴스로 대체 될 것이다.
또, 응답 지연시간도 적절히 처리해야 한다. 회로 차단기 패턴을 이용한다.
애플리케이션 아키텍처에서 선량한 시민 되기
서비스 디스커버리 등을 이용하여 호출한 서비스의 네트워크 위치를 파악할수 있게 한다.
또 관측성 패턴을 이용해 모니터링을 한다.
이렇게 아키텍처에 맞게 선정된 다양한 패턴을 적용해 개발해 나간다. ㅎㅎ;
API 게이트웨이 구현
기성 API 게이트웨이 제품/서비스 활용
AWS API 게이트웨이
아마존 웹서비스에서 제공하는 게이트웨이 API 이다. 라우팅 기능이 있으며, 템플릿기반으로 요청/응답을 변환 할 수 있다. 또, 요청 인증 기능도 있다.
단점으로는 API 조합을 지원하지 않으며, 주로 JSON 형태의 HTTP(s) 만 지원 가능하며, 서버쪽 디스커버리 패턴만 지원된다.
조합이 필요없는 단일 API 요청에 대한 처리만 한다면 쓸만한 API 게이트웨이이다.
AWS ALB (Amazon Web Service Application Load Balancer), 애플리케이션 부하분산기
HTTP, HTTPS, 웹소켓, HTTP/2 용 부하 분산기이다. ALB 에 정해진 규칙대로 라우팅 한다. AWS 의 서비스중 하나이므로, 직접 설치/운영할 필요가 없다.
하지만 인증이나 조합, HTTP 메서드 기반의 라우팅은 제공되지 않는다.
기타 다른 게이트웨이
콩(Kong), 트래픽(Traefik) 등 설치형 게이트웨이등도 있다. 대부분 라우팅, 엣지 기능은 포함되어 있으나 조합은 포함되어 있지 않아 직접 개발 해야 한다.
API 게이트웨이 자체 개발
NETFLIX ZUUL, 넷플릭스 주울
라우팅, 사용량 제한, 인증 등 엣지 기능이 포함되어 있다.
HTTP 요청을 변환하는 필터체인을 적절히 조합해 요청을 처리하고, 백엔드 호출후 클라이언트에 반환하기 직전에 응답을 가공한다. 단, 경로기반의 라우팅만 제공되며, METHOD 기반은 제공하지 않는다. (GET > POST 등)
Spring Cloud Gateway, 스프링 클라우드 게이트웨이
Spring 5, Spring boot 2, Spring Webflux (논블로킹 방식의 리액티브 스택 웹 프레임워크) 를 기반으로 작성된 API 게이트웨이이다.
Spring Cloud Gateway 는 라우팅, API 조합, 인증 등의 엣지 기능을 제공한다.