728x90
반응형
728x90
반응형
반응형

gRPC proxy? gateway?

두 명칭이 혼용되는 것 같으나, 개념을 보면 같은 것을 알 수 있음

Grpc-gateway, a server for gRPC and RESTful styles Grpc-gateway is a server that routes HTTP/1.1 request with JSON bodies to gRPC handlers with protobuf bodies. This means you can define your entire API as gRPC methods with requests and responses defined as protobuf, and then implement those gRPC handlers on your API server. Grpc-gateway can then transform your gRPC service into a RESTful API.

gRPC gateway는 client로부터 restfulAPI형식으로(HTTP JSON) 정보를 받고, 이 정보를 gRPC server 서비스의 형식대로 protobuf로 변환한 다음에 그 message를 gRPC server에 gRPC로 보내는 서버다. 주로 rpc 통신이 힘든 경우 converter로 사용한다. 

gRPC gateway

gRPC Gateway 플러그인을 사용하면 gRPC 서비스에 REST API 인터페이스를 제공할 수 있도록 go 런타임에서 작동하는 프록시 서버와 Swagger 문서를 generate 해준다. (go 이외 다른 언어는 미지원) 

https://medium.com/@thinhda/compare-grpc-web-with-grpc-gateway-fa1e2acdf29f

 

Compare gRPC-Web with grpc-gateway

gRPC-Web and grpc-gateway have advantages and disadvantages. Depending on your business, what you have and what you need, …

medium.com

https://deepbaksu.github.io/2021/05/01/how-to-REST-from-gRPC/

 

gRPC에서 REST까지

gRPC를 통해 REST 서버를 만들어 보자. gRPC 클라이언트로 연결할 수 있으면 좋지만, REST가 보편적이기 때문에 REST API를 구현해줘야 할 필요가 있다. 또한, Heroku에서는 HTTP/2 를 지원하지 않기 때문에

deepbaksu.github.io

 

js에서 바로 gRPC 서버로 통신을 하려고 하는 경우?

JavaScript running in the browser does not provide full control over HTTP2.
The gRPC protocol uses features of HTTP/2 that cannot be controlled by JavaScript.
So that a proxy is required.

gRPC-WEB은 gRPC를 브라우저에서 사용가능하도록 브라우저 지원을 추가한 스펙이다. 현재 브라우저 API가 HTTP/2 엑세스를 지원하지 않기 때문에 gRPC-WEB을 사용하여 브라우저에서 서버로 직접 통신을 할 수는 없다.

즉, js단에서 gRPC-WEB 으로 개발해도 브라우저가  HTTP/2 통신을 지원하지 않기 때문에 proxy가 필요한 것.

https://velog.io/@kyusung/grpc-web-example

 

gRPC-web 삽질기

Javascript 기반으로 브라우저에서 gRPC를 사용하려고 하였지만, 적용하지 못한 내용에 대한 기록입니다.gRPC에 대해서 간략히 설명하고 gRPC 라이브러리를 이용하여 node.js 기반의 Application을 만드는

velog.io

https://medium.com/@denis.zhbankov/grpc-web-via-http2-b05c8c8f9e6

 

gRPC-Web via HTTP2

Lately, my backend colleagues and I decided to give gRPC framework a try instead of Protocol Buffers serialiser over WebSockets transport.

medium.com


사실 이 모든 글이 보고있던 프로젝트의 아래 설정 값 하나로부터 시작되었는데.. 혹시 spring/grpc에 haproxy 설정이 가능한지 궁금하여 이모저모 살펴본 것이다. 그러던 중 위 내용을 확인하였고 아래 값은 그냥 (개발자가 만든) 커스텀 값임을 파악하게 되었다.

grpc.use-haproxy=true

그래도 관련 내용 살펴본게 아까워서 추가적으로 적어본다.

HAProxy?

= software loadbalaner, reverse proxy

https://leffept.tistory.com/309

 

[HAProxy]HAProxy 란?

HAProxy 란? HAProxy는 기존의 하드웨어 스위치를 대체하는 소프트웨어 로드 밸런서로, 네트워크 스위치에서 제공하는 L4, L7 기능 및 로드 밸런서 기능을 제공한다. 설치가 쉽고 빠르기에 서비스 이

leffept.tistory.com

https://prohannah.tistory.com/65

 

로드밸런서 (L4, L7, NginX, HAProxy)

네트워크 로드밸런싱에서 주로 언급되는 로드밸런서 L4, L7, HAProxy 위주로 설명하겠다. L4 (Transport Layer) L4는 IP, Port, Session 기반으로 로드밸런싱하며 웬만한 서비스에서는 이것만으로 부하 분산이

prohannah.tistory.com

 

HAProxy vs nginx?

Nginx는 웹서버로서 load balancing, reverse proxy 기능 또한 제공.
굳이 웹 서버로서의 역할이 필요가 없다면(캐싱) HAProxy의 load balancing이 헬스체크가 가능하기도 하고 좀 더 가벼움

https://seokjun.kim/haproxy-and-nginx-load-balancing/

 

HAProxy 와 Nginx 의 로드밸런싱

NGINX Nginx 는 대표적인 웹서버인 Apache 의 문제점을 해결하면서 만들어진 웹서버로 비동기 방식으로 개발되어 가볍고 빠른 것으로 유명한 오픈소스 어플리케이션이다. Nginx 는 http 나 reverse proxy 같

seokjun.kim


완전 다른 내용/ 참고..

ingress proxy?

일반적인 Ingress는 외부로부터 서버 내부로 유입되는 네트워크 트래픽을, egress는 서버 내부에서 외부로 나가는 트래픽을 의미. 
쿠버네티스에서의 Ingress는 외부에서 실행 중인 Deployment와 Service에 접근하기 위한, 일종의 관문 (Gateway) 같은 역할을 하며 L7에서의 요청을 처리한다.

https://kubernetes.io/ko/docs/concepts/services-networking/ingress/

 

인그레스(Ingress)

FEATURE STATE: Kubernetes v1.19 [stable] 클러스터 내의 서비스에 대한 외부 접근을 관리하는 API 오브젝트이며, 일반적으로 HTTP를 관리함. 인그레스는 부하 분산, SSL 종료, 명칭 기반의 가상 호스팅을 제공

kubernetes.io

https://blog.naver.com/alice_k106/221502890249

 

162. [Kubernetes] 1편 : 쿠버네티스 Ingress 개념 및 사용 방법, 온-프레미스 환경에서 Ingress 구축하기

이번 포스트에서는 쿠버네티스에서 인그레스 (Ingress) 를 사용하는 방법, 그리고 Public 클라우드를 쓰...

blog.naver.com

 

728x90
반응형
반응형
gRPC is a modern open source high performance Remote Procedure Call (RPC) framework that can run in any environment.

 

RPC(Remote Procedure Call)?

  • 별도의 원격 제어를 위한 코딩 없이 다른 주소 공간에서 함수나 프로시저를 실행할 수 있게하는 프로세스 간 통신 기술
  • 개발자가 원격 상호 작용에 대한 세부 정보를 명시적으로 코딩하지 않아도 프레임워크가 자동 핸들링함
  • 클라이언트 코드에서는 직접 서버 코드의 함수를 호출하는 것처럼 보임
  • 언어에 구애받지 않고 원격의 프로시져를 호출가능(즉, 클라이언트 코드 언어와 서버 코드 언어가 다른 언어로 쓰일 수 있음 )

grpc

gRPC?

  • RPC인데 구글이 만든 RPC이다. g가 구글인줄 알았는데 그건 또 아니라고 반박함ㅋㅋㅋ
    https://github.com/grpc/grpc/blob/master/doc/g_stands_for.md
  • IDL: proto 사용
  • http/2 기반 통신
  • json 기반의 통신보다 가볍고 속도가 빠르다.(성능이점)
  • 클라이언트에는 서버와 통신 시 Stub을 inject 하여 통신하며 소스에 바로 로딩하여 사용가능
  • protoc 파일을 작성 시 request이나 response앞에 붙은 'stream' 유무에 따라 4가지 방식이 있음
    • unary(단방향): 클라이언트가 서버에 단일 요청을 보내고 일반 함수 호출처럼 단일 응답을 받음(1개 request , 1개 respone)
      • rpc SayHello(HelloRequest) returns (HelloResponse);
    • server stream(서버 스트리밍): 서버가 클라이언트의 요청에 대한 응답으로 메시지 스트림을 반환(1개 request, n개 response)
      • rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse);
    • client stream(클라이언트 스트리밍): 클라이언트가 단일 메시지 대신 서버에 메시지 스트림을 보냄(n개 request, 1개 response)
      • rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse);
    • bi-directional(양방향): 서버, 클라이언트 양쪽에서 메시지 스트림을 보냄(n개 request, n개 response)
      • rpc BidiHello(stream HelloRequest) returns (stream HelloResponse);
  • 클라이언트 stub: 서버를 추상화해놓은 것 / 클라이언트의 수신처리 방법
    • BlockingStub: 동기적으로 통신하는 방법으로, 서버로부터 응답이 올 때까지 대기.
      • Unary RPC와 Server Streaming RPC에서만 사용
    • AsyncStub (Stub): 비동기적으로 통신하는 방법으로, 서버로부터 오는 응답을 StreamObserver 객체가 대신 받아서 처리
      • 모든 방식의 RPC에서 사용
    • FutureStub: 비동기적으로 통신하는 방법으로, 서버로부터의 응답 도달에 상관 없이 일단 ListenableFuture로 래핑된 객체를 반환. 서버로부터 오는 응답이 오면 ListenableFuture 객체를 통해 전달받은 메시지를 언래핑할 수 있음.
      • Unary RPC에서만 사용

즉 위 내용을 모두 합치면 아래와 같은 7가지 통신방법이 존재한다.

  unary server stream client stream bi-directional
blocking o o x x
asyn o o o o
future o x x x

 

gRPC 단점

  • 브라우저에서 직접 gRPC 통신 불가, proxy 를 통하거나 브라우저 -> 클라이언트 연동 서버 -> gRPC 서버 순으로 요청해야 함
  • protobuf(이진형식)으로 인코딩되어 송수신에 효율적이나 사람이 읽을 수 없는 데이터라 네트워크 단에서 볼 때 어려움

 

왜 MSA에서 많이 쓰이나 싶었는데, MSA구조의 특정 상 안 그래도 많은 api 콜을 gRPC로 대체하면 성능 향상에 도움이 될 것 같다..

다음 글에서는 간단하게 서버와 클라이언트를 구현해본다.

2022.01.20 - [개발/spring] - [grpc] springboot2 grpc server/client coding

 

[grpc] springboot2 grpc server/client coding

목표: java11 / gradle 7.3.3 multi project / springboot 2.6.2 / grpc server & client 개발 참고 블로그: https://velog.io/@chb1828/Spring-boot%EB%A1%9C-Grpc%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4%EB%B3%B..

bangpurin.tistory.com


IDL(Interface Definition Language)?
: 정보를 저장하는 규칙

1. xml
2. json
3. proto

Protocol buffers(proto)?

  • 직렬화 데이터 구조, 데이터를 전송할 때 구조화된 데이터를 전환하는 방법 중 하나
  • XML의 문제점을 개선하기 위해 제안된 IDL이며, XML보다 월등한 성능을 지님
  •  .proto 파일에 protocol buffer 메세지 타입/구조를 정의
  • message = '이름-값'의 쌍을 포함하는 작은 논리적 레코드
  • protoc 컴파일러로 컴파일 하면 데이터에 접근할 수 있는 각 언어에 맞는 형태의 데이터 클래스를 생성해주며 만들어진 클래스는 각 필드를 위한 접근자 뿐 아니라 전체 구조를 바이트로 직렬화하거나 바이트로부터 전체 구조를 파싱하는 메서드들을 제공함

xml 보다..

  • XML보다 작고 빠르고 간단함
  • 파일 크기가 3에서 10배 정도 작음
  • 속도가 20에서 100배 정도 빠름
  • XML보다 가독성이 좋고 명시적

 


참고자료

https://tech.lattechiffon.com/2021/06/30/grpc-%EC%84%B8-%EA%B0%80%EC%A7%80-streaming-rpc-%EA%B5%AC%ED%98%84-java/

 

gRPC 세 가지 Streaming RPC 구현 (Java) – 라떼쉬폰의 코드 베이커리

이전 글에서 gRPC를 이용하여 Unary RPC를 구현했던 것에 이어서, 이번에는 Server Streaming, Client Streaming 및 Bidirectional Streaming RPC를 Java로 구현해보도록 하겠습니다. Streaming 패턴 구현에 앞서 gRPC에서

tech.lattechiffon.com

 

https://qwer9412.tistory.com/40

 

4. grpc의 여러가지 통신 기법

grpc는 4개의 통신을 지원한다. - unary (1개 request , 1개 respone) - server stream (1개 request, n개 response) - client stream (n개 request, 1개 response) - bi stream (n개 request, n개 response) 그리..

qwer9412.tistory.com

 

728x90
반응형
반응형

axon을 공부하다 보면 DDD패턴과 aggregate이라는 개념이 계속 나온다. 간단하게 이해해보자.

 

DDD(Domain Driven Design)란?

  • 도메인 중심으로 설계하는 디자인 방법론
  • 특히 여기서는 비즈니스 Domain별로 나누어 설계하는 것에 가까움(개발 중심이 아닌, 타 부서와의 communication을 위해)
  • DDD의 핵심 목표는 "Loosly coupling", "High cohesion"(어플리케이션 또는 그 안의 모듈 간의 의존성은 최소화하고, 응집성은 최대화)

 

Domain이란?

  • 사전적 의미는 영역 / 집합
  • DDD에서의 Domain은 비즈니스 Domain으로, 유사한 업무의 집합으로 이해할 수 있음
  • 어플리케이션은 비즈니스 Domain별로 나누어 설계 및 개발될 수 있음
  • 예) 어떤 서비스에서 회원가입, 회원탈퇴를 하는 작업은 모두 '회원'과 관련된 작업이다. 여기서 회원이 도메인이다.

 

Aggregate 란?

  • Aggregate는 서로 관련이 있는 도메인 모델들의 집합
  • 대부분의 경우 하나의 애그리거트는 하나의 엔티티와 여러 개의 밸류로 구성된다. 드물게 하나의 애그리거트에 여러 개의 엔티티가 존재하기도 한다.
  • Agreegate의 키 값을 가진 엔티티를 Agreegate Root이라고 하며 다른 연관된 엔티티의 키를 들고 있다.
  • Aggregate 패턴의 특징
    • Aggregate 는 내부의 도메인 객체들을 완전히 독점 소유한다. 객체들은 오직 하나의 Aggregate 에만 속하게 되고 객체들의 lifetime 은 속해있는 Aggregate의 lifetime에 제한된다(Aggregate 가 삭제되면 내부의 객체들도 같이 삭제됨).
    • Aggregate는 항상 내부 객체들의 불변성을 강제해야 한다. Aggregate 내부는 항상 비즈니스 룰에 부합하는 상태를 유지해야 한다.
    • Aggregate 각각이 transaction boundary 가 된다. 즉 하나의 트랜잭션에서는 하나의 Aggregate 만 변경할 수 있다. 여러 개의 Aggregate 들을 한 트랜잭션으로 변경하는 것은 불가능하다.

 

 

MSA에서 서비스와 DDD에서 Aggregate의 차이점

  1. 서비스 (MSA)
    • MSA 서비스는 비즈니스 기능을 캡슐화한 독립적인 배포 단위입니다. 각 서비스는 자신만의 데이터베이스를 가지고 있으며, 다른 서비스와 통신할 때는 API, 메시지 큐, 또는 이벤트 버스 등을 사용합니다.
    • MSA의 서비스는 특정 비즈니스 도메인을 구현하는 것이 목표입니다. 예를 들어, 주문 서비스, 결제 서비스, 배송 서비스처럼 각 서비스는 독립적으로 개발되고 배포됩니다.
  2. 애그리게잇 (DDD)
    • **애그리게잇(Aggregate)**은 하나의 도메인 모델 내에서 일관성을 유지하기 위한 경계입니다. 애그리게잇은 도메인 모델의 일부로 여러 엔티티와 값 객체(Value Object)를 묶어놓은 것으로, 그 내부에서 비즈니스 규칙을 강제합니다. 한 트랜잭션 내에서 일관성을 유지하는 것이 목적입니다.
    • 애그리게잇은 하나의 서비스 내에서도 여러 개가 존재할 수 있습니다. 예를 들어, 주문 서비스 안에 Order 애그리게잇, OrderItem 애그리게잇이 있을 수 있습니다.

서비스가 애그리게잇과 다른 이유

  • 경계의 범위: MSA 서비스는 독립적인 배포 및 실행 단위를 의미하며, 도메인 모델 내의 애그리게잇보다 더 큰 경계를 가지고 있습니다. 반면, 애그리게잇은 하나의 서비스 내에서 일관성을 유지하기 위한 내부 경계입니다.
  • 트랜잭션 관리: 애그리게잇은 하나의 트랜잭션 내에서 일관성을 유지하기 위해 설계된 반면, 서비스는 서로 다른 트랜잭션을 가지고 상호작용할 수 있습니다. 예를 들어, 주문 서비스와 결제 서비스는 서로 다른 트랜잭션을 사용하지만, 각 서비스 내의 애그리게잇은 그 안에서 트랜잭션을 공유합니다.
  • 서비스 간 통신: MSA에서는 서비스 간 통신을 위해 API 호출이나 메시징 시스템을 사용하지만, 애그리게잇은 동일한 도메인 모델 내에서 직접 참조됩니다.

언제 서비스가 애그리게잇처럼 보일 수 있는가?

특정 상황에서는 MSA 서비스가 하나의 애그리게잇과 비슷하게 보일 수 있습니다. 특히, 서비스가 아주 작고, 하나의 도메인 모델에 집중하여 트랜잭션 경계를 관리하는 경우라면, 그 서비스가 그 도메인의 애그리게잇처럼 느껴질 수 있습니다. 예를 들어, 주문 서비스가 "주문"이라는 단일 애그리게잇을 중심으로 동작하고, 그 내부에서 일관성 있는 트랜잭션 처리를 한다면, 서비스와 애그리게잇의 경계가 겹칠 수 있습니다.

하지만 일반적으로, MSA에서의 서비스는 여러 애그리게잇을 포함할 수 있으며, 그 자체로 Aggregate의 개념을 대체하는 것은 아닙니다.


참고

https://sgc109.github.io/2020/08/09/ddd-aggregate/

 

DDD 의 Aggregate

본 글에서는 도메인 주도 설계(Domain Driven Design) 에서 굉장히 중요한 개념인 애그리거트(Aggregate)에 대해 알아본다.

sgc109.github.io

https://khalilstemmler.com/articles/typescript-domain-driven-design/aggregate-design-persistence/

 

How to Design & Persist Aggregates - Domain-Driven Design w/ TypeScript | Khalil Stemmler

In this article, you'll learn how identify the aggregate root and encapsulate a boundary around related entities. You'll also learn how to structure and persist aggregates using the Sequelize ORM on White Label, the open-source Vinyl Trading app.

khalilstemmler.com

https://docs.microsoft.com/ko-kr/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/microservice-domain-model

 

마이크로 서비스 도메인 모델 디자인

컨테이너화된 .NET 애플리케이션용 .NET 마이크로 서비스 아키텍처 | DDD 지향 도메인 모델을 디자인할 때 주요 개념을 이해합니다.

docs.microsoft.com

https://www.secmem.org/blog/2020/02/19/ddd-aggregate-pattern/

 

728x90
반응형
반응형

이전 글: 2022.01.11 - [architecture/MSA] - [arch] what is cqrs - 조회와 비조회의 엄연한 분리

 

[arch] what is cqrs - 조회와 비조회의 엄연한 분리

CQRS는 Command and Query Responsibility Segregation(명령과 조회의 책임 분리)의 약자이다. 즉 쉽게 말해서 data에 대한 read와 write는 분리되어 개발되어야 한다는 것이다. 1. Before 설명을 하기 전에 그림..

bangpurin.tistory.com

이전 글에서 cqrs에 대해 생각해보았는데, 이를 구현하는 방법을 보던 중 Axon framework라는 것을 알게 되었다.

Based on architectural principles, such as Domain-Driven Design (DDD) and Command-Query Responsibility Separation (CQRS), Axon Framework provides the building blocks that CQRS requires and helps to create scalable and extensible applications while maintaining application consistency in distributed systems. 

 

사실 cqrs나 event driven developlment을 검색했을 때 주로 나오는 기술이 Kafka나 rabbitMQ였고 이전 회사에서도 Kafka를 도입하려고 했었던지라 비동기 메세징에 대해서는 Kafka가 제일 먼저 떠오른게 사실이다.

그만큼 요즘, 딱 지금 이 시기에는 Kafka가 대세아닌 대세를 누리는 것 같은데 그 틈으로 보이는 Axon이 흥미로워 보였고, 차이점이 있지 않을까 싶었다.

 

거창한 설명과 비교는 인터넷에 널린 자료를 참고하면 되겠고, 내가 생각하는 큰 차이점은 아래와 같다.

  • Axon은 개발할 때부터, 즉 뼈속부터 cqrs를 지키며 개발할 수 있게 해주는 framework라면
  • Kafka는 이미 있는 그들의 플랫폼에, 그들이 제공하는 api를 이용해서 event를 publish / handling 할 수 있는데 message streaming 위주의 기능을 한다는 것.

(직접 사용해보지 않아서 아닐수도 있음에 주의 ㅋㅋ)

 

이 글은 Kafka보다는 Axon에 대해 알고 싶은 글이므로 Axon에 대해 더 찾아본다.

Axon Framework란 EventSourcing, CQRS, DDD(Domain Driven Design) 세 가지 Concept을 중심으로 어플리케이션을 개발할 수 있게 도와주는 프레임워크이다. 

Axon Server는 그러한 프레임워크를 작동하게 하는 서버이며 메세지 라우팅, 이벤트 저장소 등의 역할을 수행한다.

Axon Server는 다음과 같은 특징을 갖는다.

  • Event Store(이벤트 저장, 이벤트 전파, 스냅샷 저장 등)에 특화
    • AxonServer 내부에는 Event 저장을 위한 별도 DB가 없으며, File을 직접 다룸
    • EventStore는 오직 데이터 추가만이 가능하기에 수정, 삭제와 관련된 그 어떠한 API도 제공되지 않음
  • 높은 가용성
  • 클러스터 모드를 지원함
  • 핸들러가 먹통이 되었더라도 큐에 메세지를 저장
  • 서비스간 통신은 gRPC 방식을 사용

 

참고로 Axon Framework + Axon Server 조합이 필수는 아니다.

Axon Server대신 Kafka + NoSQL 등을 조합하여 사용할 수도 있다(Axon framework가 제공하는 Kafka-connector를 사용하여 kafka와 연동가능하다).

 

개념은 이정도로 살펴보고(사실 봐도 와닿지 않는다.. 따라서 개발하면서 더 찾아보고) 몸빵하면서 익혀야겠다.

springboot2, gradle multi project 등 이미 나에게 익숙한 환경에서 axon dependency 를 활용한 샘플 프로젝트 클론코딩을 진행하려고 한다.

 


클론코딩 참고 블로그: https://cla9.tistory.com/2?category=814447 

 

1. Axon Framework 개요

1. 개요 앞으로 진행될 포스팅은 MSA에 관심 많은 분을 대상으로 DDD, CQRS 및 Event Sourcing 내용을 알고 있다고 가정하고, Spring 환경에서 AxonFramework를 활용해 개념 구현하는 방법에 대해 소개하고자

cla9.tistory.com

 

axon guide: https://docs.axoniq.io/reference-guide/

 

Introduction - Axon Reference Guide

The standard version, called "Axon Server", is open source and free to download and use. It is provided under an AxonIQ-specific open source license. While this license allows you to run the software freely in any environment, it is less permissive than th

docs.axoniq.io

 

axon vs kafka in axon's point of view: https://axoniq.io/blog-overview/axon-and-kafka-how-does-axon-compare-to-apache-kafka

 

Axon and Kafka How does Axon compare to Apache Kafka?

We use cookies to analyze our traffic, to optimize the site functionality and to make sure you get the best experience on our website. We do not place cookies to invade your privacy. By continuing to use this site you are giving us your consent to our cook

axoniq.io

 

728x90
반응형
반응형
CQRS는 Command and Query Responsibility Segregation(명령과 조회의 책임 분리)의 약자이다. 

 

**명령(Command)**와 **조회(Query)**의 책임을 분리하여 시스템의 확장성과 유지보수성을 높이는 데 목적이 있으며..

쉽게 말해서 data에 대한 read와 write는 분리되어 개발되어야 한다는 것이다.

eventually consistent!

 

1. Before

설명을 하기 전에 그림을 먼저보자. 먼저 아래는 일반적인 우리의 서버 구조이다.

before CQRS

api 만 다를 뿐 결국 같은 서비스 안에서 같은 디비를 바라보고 있다. 백 만 건의 조회가 생겨 디비 행이 걸리면 업데이트에도 지연이 있을 수 있고 반대로 몇 천 건을 수정할 경우 디비 락이 걸려 조회가 안될 수도 있다. 이를 개발하는 소스 안에서도 게으른 개발자는 등록과 조회가 같은 dto를 사용할 수도 있고 한 dto 안에서 등록용 함수, 조회용 함수 등이 덕지덕지 섞여 어느샌가 리팩토링 조차 힘든 상황이 올 수도 있다.

 

2. Initial CQRS

그렇다면 CQRS는 어떤 개념인가. 아래 그림을 보자.

initial CQRS

CQRS를 처음 제시한 사람은 CQRS에 대해 아주 단순하게 말했다.

It has one simple assumption: instead of having one big model for reads and writes, you should have two separate models. One for writes and one for reads.

단순하게 말하면 등록/조회 같은 dto 쓰지마라..ㅋ (사실 더 나아가선 application도 분리해야한다; 녹색이 어플리케이션, 노란색이 dto라고 보면 된다)

 

그리고 두 가지 개념을 들고온다: query와 command

Query should not modify anything, just return the data.
Command is the opposite one: it should make changes in the system, but not return any data.

나는 query는 조회용, command는 업데이트(수정,등록 등)용이라고 이해했다(다른 원문에서는 read와 update 라고 분리하기도하는데 공통된 의미라고 생각한다).

위 그림에서도 내부적인(service; dto, 함수 등) 분리는 된 듯 한데, DB의 분리는 아직이다. 사실 이 정도만 분리해도 괜찮다(현실적이다)는 평도 있다.

 

3. Ultimate CQRS

하지만 모든 원론이 그러하듯 궁극적인  CQRS는 한 단계 더 진화한다. 필자도 경험해봤지만 결국 대용량 서비스는 조회의 싸움이다.

특히 MSA 구조에서 데이터 조회란, 수 많은 테이블의 join의 결과체이며 이로인해 DB cpu가 100을 찍는 경우도 허다하다.

궁극적인 CQRS는 아래와 같이 제안한다.

ultimate CQRS

차이점은 read, write의 완전한 분리 그리고 event sourcing

여기서 핵심 내용은 write 와 read 할 때 사용되는 db(물리적인 db, in-memory db 등 데이터를 저장하는 모든 것을 일컬음)는 communicate이 불가하고 오로지 event를 통해서만 communicate 할 수 있다는 점이다.

위 플로우를 write -> event storage(업데이트 로그) -> events -> event handler -> write 라고 설명하는 사람도 있는데, 이 때 event storage가 전체 플로우 중 transactional 이 가능한 유일한 아이라고 설명하는 사람도 있다(write할 때도 event storage에 event생성만 하고 끝이니 딱히 transacitonal할 필요 없음). 적당히 이해하고 보면 될 듯 하다.

정리하면 아래와 같다.

write event 생성, throw error, void 만 가능
event를 생성하고 event store에 publish해서 전파하는 방식으로 데이터를 업데이트
read 만들어진 event를 subscribe 하여 적용된 데이터를 조회
query는 데이터만 return 하고 다름 side effect은 없음
event  한번만 실행됨을 보장해야 함
모든 consumer를 update 해야 함

 

위 처럼 분리되었을 경우의 아래와 같은 장점을 얻을 수 있게 된다.

  • 각각의 상황에 맞게 optimize가 가능(ex. read - view with cache)
  • read와 write가 서로 독립적(즉 서로 영향을 미치지 않는다; 조회하다 부하가 걸려 업데이트가 안 될 일이 없다).
  • 독립적인 scaling(조회가 많으면 조회 쪽 성능에 대한 개선만 하면 됨/ 관련 인프라의 scale out)이 가능

우리가 만든 프로젝트를 cqrs화 하자면..

이를 공부하면서 의문이 생기는 것은 배달 주문 메뉴 수정/실시간 잔액 조회와 같이 실시간 반영이 필요할 때 event driven의 경우 아무래도 write -> read로 가는 약간의 지연이 있을 것 같은데 이는 어떻게 해결할 수 있는지, 무시할만한 수준인건지 궁금하다..(왠지 진짜 실시간은 write 테이블을 보고 약간의 지연을 허용하면 read 테이블을 보고 그런식으로 할 것 같다..는 과거의 나ㅋㅋ)

+ 후에 실습을 해보니 write -> read 로 이벤트 전파가 거의 동시에 이루어지는 것을 확인할 수 있었다. 다만 실 운영 환경에서는 트래픽이나 시스템의 환경에 따라 약간씩 다르지 않을까 생각된다.


참고

https://threedots.tech/post/basic-cqrs-in-go/

 

Introducing basic CQRS by refactoring a Go project

It’s highly likely you know at least one service that: has one big, unmaintainable model that is hard to understand and change, or where work in parallel on new features is limited, or can’t be scaled optimally. But often, bad things come in threes. It

threedots.tech

https://www.youtube.com/watch?v=qJA6MaQ90YY 

 

728x90
반응형

+ Recent posts