공부하면서 그려본 Kafka의 구조이다.
정말 간단하게 설명하자면, Producer가 메시지를 발행하면 Zookeeper가 Topic과 Partition을 결정한다.
결정된 Partition에 실제 메세지를 저장하고, Consumer는 이 저장된 데이터를 offset 기반으로 순차적으로 읽는다.
사실 이 내부 동작까지는 알 필요는 없고 Kafka에 Topic을 설정해 놓고, 각 Topic에 메세지를 발행하고, Consumer가 받고 싶은 Topic을 구독하여 메세지를 받으면 되는 구조이다.
사실은 HTTP + SAGA 방식으로도 충분히 보상 거래같은 분산 시스템의 정합성을 맞출 수 있다. 하지만 비동기 처리를 통해 배치 처리가 가능하고, 높은 처리량이 가능하다. 또한 각각의 서비스가 느려져도 메세지는 큐에 쌓이고 요청을 대기하는 서비스가 느려지지 않는다.
우리 서비스에 빗대자면, 증권 계좌 서버가 느려지더라도 오케스트레이터 서버는 느려지지 않는다!
추가로 금융 거래 같은 경우 모든 거래 이력을 영구 보관해야 할 때, 별도 로깅 시스템 없이 모든 이벤트가 자동으로 로그에 남는다.
만약 유저가 투자를 성공적으로 마친 후 입출금 계좌로 돈을 빼려고 한다. 이 때의 흐름이다. 두 서비스를 거쳐야 하는 작업이므로 오케스트레이터에 요청을 보내고, 오케스트레이터가 Kafka에 각각의 서비스에 출금, 입금 서비스를 호출 하는 식으로 설계하였다.
만약 입금 서비스가 실패한다면, 출금된 계좌를 통해 다시 입금 요청(보상 거래)을 보내 분산 트랜잭션을 보내주어 데이터 일관성을 지킨다!
우리는 이 아키텍처를 보고 고민에 빠졌다.
만약에 메세지큐에 두 서비스 호출을 부른 상태에서 입금 서비스가 호출이 먼저 된 후 그 순간에 돈이 빠진 후에 다시 보상거래가 들어오면 어떡하지..?
-> 출금 서비스를 무조건 먼저 호출하자!
-> 그렇다면 출금 서비스를 호출하고 다시 응답을 받을때까지 기다리고 응답을 받으면 입금 서비스를 호출해야 할 거 같은데..
-> 그러면 카프카를 왜 써, 동기식으로 묶일 거 같으면 http 통신으로 하지
오랫동안 고민 한 후 비동기 적으로 동기처럼 해결할 수 있는 방식을 고안했다.
다음은 해결된 아키텍처이다.
오케스트레이터가 송금 요청을 받으면 일단 주식 투자 서비스로 출금 서비스를 호출한다. 호출 한 후 역할은 끝난다. 그리고 kafka에 구독해 놓은 Topic에서 주식 투자 서비스에서 출금이 완료되었다는 메세지를 받으면 입금 요청을 보내는 식으로 비동기 방식에서 동기 처럼 작동되게 하여 Kafka에 이점을 살리면서 동기 방식으로 안전한 금융 거래 방식을 설계하였다.