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

2022.01.20 - [architecture/micro service] - [gRPC] what is gRPC?

 

[gRPC] what is gRPC?

gRPC is a modern open source high performance Remote Procedure Call (RPC) framework that can run in any environment. RPC(Remote Procedure Call)? 별도의 원격 제어를 위한 코딩 없이 다른 주소 공간에서..

bangpurin.tistory.com

이전 글에서 gRPC에 대한 개념을 간략히 살펴보았다. 이번 글에서는 간단히 구현해본다.

목표: 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%B4%EC%9E%90

 

Spring boot로 Grpc를 사용해보자

오늘은 Grpc에 대해서 글을 써보려고한다. Grpc를 간단하게 설명하면 서로 다른 위치에 존재하는 공간에서 동일한 객체를 가져다 와서 사용하는 것이다.

velog.io

 

위 블로그는 서버 프로젝트 / 클라이언트 프로젝트 / interface용 jar 를 만드는 프로젝트를 각각 구성하였다면, 여기서는 그래들 멀티 프로젝트로 구성한다. 

깃허브 주소: https://github.com/haileyjhbang/grpc-test.git

 

GitHub - haileyjhbang/grpc-test

Contribute to haileyjhbang/grpc-test development by creating an account on GitHub.

github.com

프로젝트 구조는 아래와 같다.

각 소스 내용물은 위 깃허브에서 확인가능하며 특이사항은 아래와 같다.

  • 공통 사항은 common 폴더 안에 있고, server / client 프로젝트 빌드 전 common을 먼저 빌드해 common-plain.jar를 생산해야함.
  • 각 프로젝트 build.gradle 에 보면 해당 jar을 import하게 되어있음.
        - common 의 내용물은 spring/java 에 관련된 내용은 없고 단순히 proto만 빌드함
        - server / client 프로젝트에서 common 의 컴파일된 java 파일을 사용해야하기 때문에 common을 멀티 프로젝트로 구성하는 것은 불가능하다고 판단(멀티 프로젝트로 import 시 src/main 아래의 폴더를 바라보나 common에는 src/proto만 있음)
  • client stub은 blocking stub 사용, unary 방식으로 통신으로 구현
  • client의 컨트롤러에서 request param으로 name을 넘기면 서버로 전달되어 추가 작업 후 화면에 표시
  • server 프로젝트와 client 프로젝트는 별도의 각각 jar을 생산하며 서로 다른 포트로 뜨게 되어 있음
  • [GET] http://localhost:9091/test?name=jhbang 로 확인

 

여기서는 구현하면서 만났던 문제와 해결방법에 대해 기술한다.

1. 멀티 프로젝트 구성 시

Execution failed for task ':common:bootJar'.
> Error while evaluating property 'mainClass' of task ':common:bootJar'
   > Failed to calculate the value of task ':common:bootJar' property 'mainClass'.
      > Main class name has not been configured and it could not be resolved

common/build.gradle 스크립트에 아래 추가

//common 프로젝트는 proto 번환만 한다.
//실행할 메인 클래스가 없을때는 아래와 같이 한다.

bootJar.enabled = false

 

완성하고 실행하면 아래와 같이 나온다.


참고

proto3 rule: https://developers.google.com/protocol-buffers/docs/proto3

 

Language Guide (proto3)  |  Protocol Buffers  |  Google Developers

Language Guide (proto3) This guide describes how to use the protocol buffer language to structure your protocol buffer data, including .proto file syntax and how to generate data access classes from your .proto files. It covers the proto3 version of the pr

developers.google.com

 

proto java guide: https://developers.google.com/protocol-buffers/docs/javatutorial

 

Protocol Buffer Basics: Java  |  Protocol Buffers  |  Google Developers

Protocol Buffer Basics: Java This tutorial provides a basic Java programmer's introduction to working with protocol buffers. By walking through creating a simple example application, it shows you how to Define message formats in a .proto file. Use the prot

developers.google.com

 

gradle multiproject setting: https://clack2933.tistory.com/15

 

Gradle Multi Module 프로젝트

머리말 하나의 단일 모듈로는 관리에 어려움을 겪게 되었고 이문제를 해결하기 위해서 모듈을 분류하는 multi module을 공부하게 되었습니다. 프로젝트 생성 Spring Boot 2.4.xx Gradle 6.8 Intellij Java11 초

clack2933.tistory.com

 

728x90
반응형
반응형

ㅋㅋㅋㅋㅋㅋ

당첨되어따 헤헤헤

게더타운 첨해보는데 유리도시 생각나고 재밌었다..ㅋㅋ 딱히 넷워킹하는건 없었지만..ㅠ

 

 

참고로 당첨선물은 오라클 다이어리였다...(사용하지 않고 방치 중..ㅋ)

728x90
반응형

'일상 > 잡담' 카테고리의 다른 글

30대의 운전면허 도전기(온수)  (1) 2022.06.11
반응형

2022.01.18 - [개발/spring] - [axon] snapshot - clone coding

 

[axon] snapshot - clone coding

2022.01.12 - [개발/spring] - [axon] difficulties while clone coding [axon] difficulties while clone coding 2022.01.12 - [architecture/MSA] - [cqrs] axon framework [cqrs] axon framework 2022.01.11 -..

bangpurin.tistory.com

 

저번 글에 이어서 state stored aggregate 방식에 대해 알아본다.

 클론 코딩 참고 블로그는 다음와 같다: https://cla9.tistory.com/13?category=814447

 

9. Command 어플리케이션 구현 - 4

1. 서론 이번 포스팅은 데모 프로젝트 진행에 있어 필수적으로 구현해야하는 코드는 없습니다. 따라서 Skip해도 괜찮습니다. 이번 시간에는 상태를 저장하는 State-Stored Aggregate에 대해서 알아보겠

cla9.tistory.com

 

state store aggregate 으로 전환하고 실행, api 쏴보니 아래와 같은 jpa 에러가 난다.

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.cqrs.command.aggregate.HolderAggregate.accounts, could not initialize proxy - no Session

fetch type을 eager로 바꾼다.

    @OneToMany(mappedBy = "holder", orphanRemoval = true, fetch = FetchType.EAGER)
    private List<AccountAggregate> accounts = new ArrayList<>();

참고: https://ankonichijyou.tistory.com/entry/JPA-OneToMany-%EC%98%A4%EB%A5%98

 

다시 하는데 아래와 같은 에러가 난다... 열심히 구글링했는데 별다른 힌트를 얻지 못해서.. axonserver 를 재시작했는데.. 세상에 잘 된다..

(설정을 변경해보려다 실패해서 얻어걸린 재시작 ㅎ) .. 내 2시간..ㅎ

2022-01-19 14:15:50.006  WARN 6587 --- [ault-executor-3] o.a.a.c.u.ResubscribableStreamObserver   : A problem occurred in the stream.

io.grpc.StatusRuntimeException: CANCELLED: HTTP/2 error code: CANCEL
Received Rst Stream
..

Hibernate: insert into account (balance, holder_id, account_id) values (?, ?, ?)
2022-01-19 14:15:50.010 ERROR 6587 --- [nio-8080-exec-5] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] threw exception

org.axonframework.axonserver.connector.command.AxonServerCommandDispatchException: CANCELLED: HTTP/2 error code: CANCEL
Received Rst Stream
...
2022-01-19 14:15:50.010 ERROR 6587 --- [nio-8080-exec-5] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is AxonServerCommandDispatchException{message=CANCELLED: HTTP/2 error code: CANCEL
Received Rst Stream, errorCode='AXONIQ-4003', server='6587@AL01590036.local'}] with root cause

org.axonframework.axonserver.connector.command.AxonServerCommandDispatchException: CANCELLED: HTTP/2 error code: CANCEL
Received Rst Stream
...
2022-01-19 14:35:34.782  WARN 6587 --- [ault-executor-7] o.a.a.c.u.ResubscribableStreamObserver   : A problem occurred in the stream.

io.grpc.StatusRuntimeException: UNAVAILABLE: HTTP/2 error code: NO_ERROR
Received Goaway
app_requested

 

hibernate sql log가 지나간다.

Hibernate: insert into holder (address, holder_name, tel, holder_id) values (?, ?, ?, ?)
---
Hibernate: select holderaggr_.holder_id, holderaggr_.address as address2_2_, holderaggr_.holder_name as holder_n3_2_, holderaggr_.tel as tel4_2_ from holder holderaggr_ where holderaggr_.holder_id=?
Hibernate: insert into account (balance, holder_id, account_id) values (?, ?, ?)
---
Hibernate: select accountagg0_.account_id as account_1_0_0_, accountagg0_.balance as balance2_0_0_, accountagg0_.holder_id as holder_i3_0_0_ from account accountagg0_ where accountagg0_.account_id=? for update
Hibernate: select holderaggr0_.holder_id as holder_i1_2_0_, holderaggr0_.address as address2_2_0_, holderaggr0_.holder_name as holder_n3_2_0_, holderaggr0_.tel as tel4_2_0_, accounts1_.holder_id as holder_i3_0_1_, accounts1_.account_id as account_1_0_1_, accounts1_.account_id as account_1_0_2_, accounts1_.balance as balance2_0_2_, accounts1_.holder_id as holder_i3_0_2_ from holder holderaggr0_ left outer join account accounts1_ on holderaggr0_.holder_id=accounts1_.holder_id where holderaggr0_.holder_id=?

Hibernate: update account set balance=?, holder_id=? where account_id=?
---

db를 봐도 잘 들어가 있다.

 

event sourced aggregate 방식과 다르게 매 스탭의 결과를 디비에 저장하기 때문에 매번 작업을 새롭게 하지 않는다는 장점이 있으나, 디비 연결에 대한 네트워크 이슈(속도), 디비가 날아가면 히스토리를 알기 어려움 등의 단점도 알아둬야겠다.

디비에 저장하면서 이벤트 발생 히스토리도 별도로 저장하면 좋지 않을까?

 

추가)

다시 소스를 보니 @CommandHandler 어노테이션만 있으면 별도의 repository나 save 등의 액션이 없이도 디비에 수정사항이 생기고 변화하는 것을 알 수 있다.

원문을 보면 아래와 같은 문구를 확인할 수 있다. 없으면 자동 생성해서 해주는 듯 하다.

Axon will automatically register all the @CommandHandler annotated methods with the command bus
and set up a repository if none is present.

 

또한 각 aggregate에 repository를 명시할 수도 있다.

@Aggregate(repository = "repositoryForGiftCard")


원문: https://docs.axoniq.io/reference-guide/v/4.0/configuring-infrastructure-components/command-processing/command-model-configuration

 

Command Model Configuration - Axon Reference Guide

To fully customize the repository used, you can define one in the application context. For Axon Framework to use this repository for the intended aggregate, define the bean name of the repository in the repository attribute on @Aggregate Annotation. Altern

docs.axoniq.io

 

 

728x90
반응형
반응형

이전글: 2022.01.12 - [개발/spring] - [axon] command/query project 생성 - clone coding

 

[axon] command/query project 생성 - clone coding

2022.01.12 - [architecture/MSA] - [cqrs] axon framework [cqrs] axon framework 2022.01.11 - [architecture/MSA] - [arch] what is cqrs - 조회와 비조회의 엄연한 분리 [arch] what is cqrs - 조회와 비조회..

bangpurin.tistory.com

이전 글에 이어서 axon framework의 command에 대한 클론 코딩을 이어가던 중 실습 내용에 대한 고찰을 해본다.

참고)

  필수 어노테이션
command @TargetAggregateIdentifier
aggregate @Aggregate
@AggregateIdentifier

 

클론 코딩 참고 블로그는 아래와 같다: https://cla9.tistory.com/12?category=814447 

 

8. Command 어플리케이션 구현 - 3

1. 서론 지난 포스팅에서 Command Application에 대한 전반적인 구현을 마무리했습니다. 하지만 해당 프로그램은 근본적인 문제점을 안고 있습니다. 이번 포스팅에서는 발생되는 문제점과 이를 해결

cla9.tistory.com

따라해본다.

POST http://localhost:8080/holder
POST http://localhost:8080/account
POST http://localhost:8080/deposit
POST http://localhost:8080/withdrawal
POST http://localhost:8080/withdrawal
POST http://localhost:8080/withdrawal

위 글에도 써있지만 아래와 같은 순서로 잔고를 인출했을 때의 로그는 아래와 같다(withdrawal을 10원씩 4번째 호출했을 때이다).

다시 말하지만 api 한번 호출 시의 로그이다.

[nio-8080-exec-3] o.a.commandhandling.SimpleCommandBus     : Handling command [com.cqrs.command.commands.WithdrawMoneyCommand]
[nio-8080-exec-3] o.a.m.unitofwork.AbstractUnitOfWork      : Starting Unit Of Work
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.messaging.unitofwork.AbstractUnitOfWork$$Lambda$1197/0x00000008008b5040 for phase ROLLBACK
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext         : Notifying handlers for phase STARTED
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.messaging.unitofwork.UnitOfWork$$Lambda$1201/0x00000008008b4040 for phase COMMIT
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.messaging.unitofwork.UnitOfWork$$Lambda$1202/0x00000008008b4440 for phase ROLLBACK
[nio-8080-exec-3] o.a.a.c.event.axon.AxonServerEventStore  : Reading events for aggregate id 2b158c88-b810-4643-9db4-4096edea5621
[nio-8080-exec-3] c.c.command.aggregate.AccountAggregate   : >> applying AccountCreationEvent(holderID=76147ef5-280f-439e-b017-bcd04028f176, accountID=2b158c88-b810-4643-9db4-4096edea5621)
[nio-8080-exec-3] c.c.command.aggregate.AccountAggregate   : applying DepositMoneyEvent(holderID=76147ef5-280f-439e-b017-bcd04028f176, accountID=2b158c88-b810-4643-9db4-4096edea5621, amount=300)
[nio-8080-exec-3] c.c.command.aggregate.AccountAggregate   : balance 300
[nio-8080-exec-3] c.c.command.aggregate.AccountAggregate   : applying WithdrawMoneyEvent(holderID=76147ef5-280f-439e-b017-bcd04028f176, accountID=2b158c88-b810-4643-9db4-4096edea5621, amount=10)
[nio-8080-exec-3] c.c.command.aggregate.AccountAggregate   : balance 290
[nio-8080-exec-3] c.c.command.aggregate.AccountAggregate   : applying WithdrawMoneyEvent(holderID=76147ef5-280f-439e-b017-bcd04028f176, accountID=2b158c88-b810-4643-9db4-4096edea5621, amount=10)
[nio-8080-exec-3] c.c.command.aggregate.AccountAggregate   : balance 280
[nio-8080-exec-3] c.c.command.aggregate.AccountAggregate   : applying WithdrawMoneyEvent(holderID=76147ef5-280f-439e-b017-bcd04028f176, accountID=2b158c88-b810-4643-9db4-4096edea5621, amount=10)
[nio-8080-exec-3] c.c.command.aggregate.AccountAggregate   : balance 270
[nio-8080-exec-3] org.axonframework.messaging.Scope        : Clearing out ThreadLocal current Scope, as no Scopes are present
[ault-executor-3] o.a.a.c.e.AxonServerEventStoreClient     : Done request for 2b158c88-b810-4643-9db4-4096edea5621: 22ms, 5 events
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.modelling.command.LockingRepository$$Lambda$1300/0x0000000800a0d440 for phase CLEANUP
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.modelling.command.AbstractRepository$$Lambda$1301/0x0000000800a0d840 for phase ROLLBACK
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.modelling.command.AbstractRepository$$Lambda$1302/0x0000000800a0dc40 for phase PREPARE_COMMIT
[nio-8080-exec-3] c.c.command.aggregate.AccountAggregate   : handling WithdrawMoneyCommand(accountID=2b158c88-b810-4643-9db4-4096edea5621, holderID=76147ef5-280f-439e-b017-bcd04028f176, amount=10)
[nio-8080-exec-3] c.c.command.aggregate.AccountAggregate   : applying WithdrawMoneyEvent(holderID=76147ef5-280f-439e-b017-bcd04028f176, accountID=2b158c88-b810-4643-9db4-4096edea5621, amount=10)
[nio-8080-exec-3] c.c.command.aggregate.AccountAggregate   : balance 260
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1222/0x00000008008f3440 for phase AFTER_COMMIT
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1223/0x00000008008f3840 for phase ROLLBACK
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1225/0x00000008008f4040 for phase PREPARE_COMMIT
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1226/0x00000008008f4440 for phase COMMIT
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1227/0x00000008008f4840 for phase AFTER_COMMIT
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1228/0x00000008008f4c40 for phase CLEANUP
[nio-8080-exec-3] org.axonframework.messaging.Scope        : Clearing out ThreadLocal current Scope, as no Scopes are present
[nio-8080-exec-3] o.a.m.unitofwork.AbstractUnitOfWork      : Committing Unit Of Work
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext         : Notifying handlers for phase PREPARE_COMMIT
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.axonserver.connector.event.axon.AxonServerEventStore$AxonIQEventStorageEngine$$Lambda$1245/0x000000080091f040 for phase ROLLBACK
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.axonserver.connector.event.axon.AxonServerEventStore$AxonIQEventStorageEngine$$Lambda$1246/0x000000080091f440 for phase COMMIT
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext         : Notifying handlers for phase COMMIT
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext         : Notifying handlers for phase AFTER_COMMIT
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext         : Notifying handlers for phase CLEANUP
[nio-8080-exec-3] o.a.m.u.MessageProcessingContext         : Notifying handlers for phase CLOSED

관련 dto..

WithdrawMoneyCommand.java
    @TargetAggregateIdentifier
    private String accountID;

/withdraw api의 aggregateId 인 acoountId 에 대한 모든 이벤트를 다시 불러오는 듯 하다. 즉 위 로그처럼 acoountId에 대한 모든 이벤트를 롤백하고(마치 계좌가 없던 것 처럼) 다시 첨부터 하는 것을 반복한다. 즉 여기서는 계좌 생성(/account), 입금(/deposit), 출금 n번(/withdraw)이다.

 

event sourced aggregate

신박하다.. 데이터의 정합성은 보장될 것 같은데 효율성은 떨어질 것 같다.

 

snapshot

그래서 클론 코딩에서 알려준대로 스냅샷을 적용하고 새로운 마음으로 계정생성1, 입금2, 인출3, 인출4, 인출5 를 해본다.

아래는 인출5 api실행 시 로그다.

[nio-8080-exec-5] o.a.commandhandling.SimpleCommandBus     : Handling command [com.cqrs.command.commands.WithdrawMoneyCommand]
[nio-8080-exec-5] o.a.m.unitofwork.AbstractUnitOfWork      : Starting Unit Of Work
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.messaging.unitofwork.AbstractUnitOfWork$$Lambda$1192/0x000000080087ac40 for phase ROLLBACK
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext         : Notifying handlers for phase STARTED
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.messaging.unitofwork.UnitOfWork$$Lambda$1196/0x0000000800879c40 for phase COMMIT
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.messaging.unitofwork.UnitOfWork$$Lambda$1197/0x0000000800879040 for phase ROLLBACK
[nio-8080-exec-5] o.a.a.c.event.axon.AxonServerEventStore  : Reading events for aggregate id f00088d7-c81a-432c-b010-28a88012f0eb
[ault-executor-4] o.a.a.c.e.AxonServerEventStoreClient     : Done request for f00088d7-c81a-432c-b010-28a88012f0eb: 12ms, 4 events
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate   : >> applying AccountCreationEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb)
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate   : applying DepositMoneyEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb, amount=400)
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate   : balance 400
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate   : applying WithdrawMoneyEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb, amount=10)
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate   : balance 390
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate   : applying WithdrawMoneyEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb, amount=10)
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate   : balance 380
[nio-8080-exec-5] org.axonframework.messaging.Scope        : Clearing out ThreadLocal current Scope, as no Scopes are present
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.modelling.command.LockingRepository$$Lambda$1246/0x00000008009c2c40 for phase CLEANUP
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.modelling.command.AbstractRepository$$Lambda$1247/0x00000008009c3040 for phase ROLLBACK
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.modelling.command.AbstractRepository$$Lambda$1249/0x00000008009c3840 for phase PREPARE_COMMIT
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate   : handling WithdrawMoneyCommand(accountID=f00088d7-c81a-432c-b010-28a88012f0eb, holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, amount=10)
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate   : applying WithdrawMoneyEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb, amount=10)
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate   : balance 370
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1260/0x00000008009c6440 for phase AFTER_COMMIT
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1261/0x00000008009c6840 for phase ROLLBACK
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1263/0x00000008009c7040 for phase PREPARE_COMMIT
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1264/0x00000008009c7440 for phase COMMIT
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1265/0x00000008009c7840 for phase AFTER_COMMIT
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1266/0x00000008009c7c40 for phase CLEANUP
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.eventsourcing.EventCountSnapshotTriggerDefinition$EventCountSnapshotTrigger$$Lambda$1245/0x00000008009c2840 for phase PREPARE_COMMIT
[nio-8080-exec-5] org.axonframework.messaging.Scope        : Clearing out ThreadLocal current Scope, as no Scopes are present
[nio-8080-exec-5] o.a.m.unitofwork.AbstractUnitOfWork      : Committing Unit Of Work
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext         : Notifying handlers for phase PREPARE_COMMIT
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.axonserver.connector.event.axon.AxonServerEventStore$AxonIQEventStorageEngine$$Lambda$1276/0x00000008009ce840 for phase ROLLBACK
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.axonserver.connector.event.axon.AxonServerEventStore$AxonIQEventStorageEngine$$Lambda$1277/0x00000008009cec40 for phase COMMIT
[nio-8080-exec-5] o.a.a.c.event.axon.AxonServerEventStore  : Reading events for aggregate id f00088d7-c81a-432c-b010-28a88012f0eb
[ault-executor-4] o.a.a.c.e.AxonServerEventStoreClient     : Done request for f00088d7-c81a-432c-b010-28a88012f0eb: 10ms, 4 events
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate   : >> applying AccountCreationEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb)
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate   : applying DepositMoneyEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb, amount=400)
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate   : balance 400
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate   : applying WithdrawMoneyEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb, amount=10)
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate   : balance 390
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate   : applying WithdrawMoneyEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb, amount=10)
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate   : balance 380
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate   : applying WithdrawMoneyEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb, amount=10)
[nio-8080-exec-5] c.c.command.aggregate.AccountAggregate   : balance 370
[nio-8080-exec-5] org.axonframework.messaging.Scope        : Clearing out ThreadLocal current Scope, as no Scopes are present
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext         : Notifying handlers for phase COMMIT
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext         : Notifying handlers for phase AFTER_COMMIT
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext         : Notifying handlers for phase CLEANUP
[nio-8080-exec-5] o.a.m.u.MessageProcessingContext         : Notifying handlers for phase CLOSED
[ault-executor-4] o.a.a.c.event.axon.AxonServerEventStore  : Snapshot created

 왜 400 -> 370 까지 로그가 두 번 찍히는지는 모르겠지만 마지막에 snapshot created.

이 후 인출6 시 로그. 확실히 400 -> 370 까지 다시 하지 않고 바로 360으로 간다.

[nio-8080-exec-7] o.a.commandhandling.SimpleCommandBus     : Handling command [com.cqrs.command.commands.WithdrawMoneyCommand]
[nio-8080-exec-7] o.a.m.unitofwork.AbstractUnitOfWork      : Starting Unit Of Work
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.messaging.unitofwork.AbstractUnitOfWork$$Lambda$1192/0x000000080087ac40 for phase ROLLBACK
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext         : Notifying handlers for phase STARTED
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.messaging.unitofwork.UnitOfWork$$Lambda$1196/0x0000000800879c40 for phase COMMIT
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.messaging.unitofwork.UnitOfWork$$Lambda$1197/0x0000000800879040 for phase ROLLBACK
[nio-8080-exec-7] o.a.a.c.event.axon.AxonServerEventStore  : Reading events for aggregate id f00088d7-c81a-432c-b010-28a88012f0eb
[ault-executor-5] o.a.a.c.e.AxonServerEventStoreClient     : Done request for f00088d7-c81a-432c-b010-28a88012f0eb: 15ms, 1 events
[nio-8080-exec-7] org.axonframework.messaging.Scope        : Clearing out ThreadLocal current Scope, as no Scopes are present
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.modelling.command.LockingRepository$$Lambda$1246/0x00000008009c2c40 for phase CLEANUP
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.modelling.command.AbstractRepository$$Lambda$1247/0x00000008009c3040 for phase ROLLBACK
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.modelling.command.AbstractRepository$$Lambda$1249/0x00000008009c3840 for phase PREPARE_COMMIT
[nio-8080-exec-7] c.c.command.aggregate.AccountAggregate   : handling WithdrawMoneyCommand(accountID=f00088d7-c81a-432c-b010-28a88012f0eb, holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, amount=10)
[nio-8080-exec-7] c.c.command.aggregate.AccountAggregate   : applying WithdrawMoneyEvent(holderID=b232a9e8-d280-48bc-9d17-df03c9fe4071, accountID=f00088d7-c81a-432c-b010-28a88012f0eb, amount=10)
[nio-8080-exec-7] c.c.command.aggregate.AccountAggregate   : balance 360
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1260/0x00000008009c6440 for phase AFTER_COMMIT
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1261/0x00000008009c6840 for phase ROLLBACK
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1263/0x00000008009c7040 for phase PREPARE_COMMIT
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1264/0x00000008009c7440 for phase COMMIT
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1265/0x00000008009c7840 for phase AFTER_COMMIT
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.eventhandling.AbstractEventBus$$Lambda$1266/0x00000008009c7c40 for phase CLEANUP
[nio-8080-exec-7] org.axonframework.messaging.Scope        : Clearing out ThreadLocal current Scope, as no Scopes are present
[nio-8080-exec-7] o.a.m.unitofwork.AbstractUnitOfWork      : Committing Unit Of Work
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext         : Notifying handlers for phase PREPARE_COMMIT
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.axonserver.connector.event.axon.AxonServerEventStore$AxonIQEventStorageEngine$$Lambda$1276/0x00000008009ce840 for phase ROLLBACK
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext         : Adding handler org.axonframework.axonserver.connector.event.axon.AxonServerEventStore$AxonIQEventStorageEngine$$Lambda$1277/0x00000008009cec40 for phase COMMIT
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext         : Notifying handlers for phase COMMIT
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext         : Notifying handlers for phase AFTER_COMMIT
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext         : Notifying handlers for phase CLEANUP
[nio-8080-exec-7] o.a.m.u.MessageProcessingContext         : Notifying handlers for phase CLOSED

그 이후로도 입금/출금 계속 했더니 위처럼 반복되다가 5번째마다 스냅샷이 잘 찍히는 것을 알 수 있다.


실패기록:

api를 몇 번 랜덤하게 쐈더니 아래와 같은 에러가 났다.

AxonServerException OUT_OF_RANGE: [AXONIQ-2000] Invalid sequence number while storing snapshot.

..왜 나에게만 이런 시련이

스택오버플로우에서 알려준대로 application.yml에 아래를 추가하고 재시작하니 해결되었다.. (하지만 그거시 아니었다..ㅎ)

axon:
    eventhandling:
        processors:
            name:
                mode: tracking

무슨 의미인지 찾아보던 중, 원문에 default가 tracking이라는데 저걸 또 명시하는게 무슨 의미인가 싶은데... 또 에러가 해결은 되고... ㅎ

Event Processors come in roughly two forms: Subscribing and Tracking. The Subscribing Event Processors subscribe themselves to a source of Events and are invoked by the thread managed by the publishing mechanism. Tracking Event Processors, on the other hand, pull their messages from a source using a thread that it manages itself.
...
By default, Axon will use Tracking Event Processors.

event processor 설명: https://cla9.tistory.com/15?category=814447 

 

11. Query 어플리케이션 구현(Event) - 1

1. 서론 이번 포스팅부터 Query App 구현을 다루겠습니다. Query App은 Event를 수신받아 Read Model에 반영하는 Projection 작업과 Read Model을 읽는 Query 2가지로 기능이 나뉩니다. 기능별로 실제 구현 코드..

cla9.tistory.com

 

아니, 해결된 줄 알고 또 몇 번 해보니 아래의 에러가 나온다. 구글링해도 뭐가 안 나온다.. 돌겠네..ㅋㅋㅋㅋ 

ConcurrencyException: OUT_OF_RANGE: [AXONIQ-2000] Invalid sequence number while storing snapshot.

 

내가 같은 holderId로 계좌 생성을 두번했는데, 서로 다른 계좌가 생성되긴 했지만,, 뭔가 그 후로 sequence 가 꼬인 듯.. 싶다.

무튼 저거 버리고 다시 처음부터 하니 그 후론 재연도 안된다 ㅎㅅㅎ;

 

궁금한게, 분명 axon server 안에서 저 이벤트/스냅샷을 저장하고 있다가 복기하는 것 같은데.. 어디에 있는지 모르겠다.

그걸 확인해서 꼬인 부분을 해결해보려고 했는데, 프로퍼티에 설정한 디비 테이블을 뒤져도 다 empty라서.. 어디에 저장하는지 궁금하다.

+ 찾아보니,

현재 구현 버전은 event-sourced-aggregate 이라하여 axon server의 event store에 저장하는 구현 방식이다. 즉 설정파일(axonserver.properties)에 지정한 경로에 파일로 각 이벤트를 저장하는 것이다. 

아래는 현재 내 axonserver의 설정과 데이터 값의 예시이다.

[root@localhost axonserver]# cat axonserver.properties 

logging.file=/var/log/axonserver/axonserver.log
logging.file.max-history=10
logging.file.max-size=10MB
axoniq.axonserver.event.storage=./events
axoniq.axonserver.snapshot.storage=./events
axoniq.axonserver.controldb-path=./data
-------------------------------------------------
[root@localhost default]# pwd
/var/lib/axonserver/events/default
[root@localhost default]# ll
-rw-r--r--. 1 axonserver axonserver 268435456  1월 18 17:11 00000000000000000000.events
-rw-r--r--. 1 axonserver axonserver 268435456  1월 18 17:11 00000000000000000000.snapshots

/// event 파일 예시

$a1fe8538-2955-4311-b371-b55811b5d1a1^R$f00088d7-c81a-432c-b010-28a88012f0eb^X^N"^PAccountAggregate(å<84><9d>áæ/2÷^A
"com.cqrs.events.WithdrawMoneyEvent^ZÐ^A<com.cqrs.events.WithdrawMoneyEvent><holderID>b232a9e8-d280-48bc-9d17-df03c9fe4071</holderID><accountID>f00088d7-c81a-432c-b010-28a88012f0eb</accountID><amount>10</amount></com.cqrs.events.WithdrawMoneyEvent>rW<96>^]^@^@^Ae^B^@^A^@^@^Aa

/// snapshot 파일 예시

$67179188-1d02-4a6a-ade3-49cd5586b88c^R$f00088d7-c81a-432c-b010-28a88012f0eb^X^H"^PAccountAggregate(<93>ñ<87>àæ/2<95>^B
+com.cqrs.command.aggregate.AccountAggregate^Zå^A<com.cqrs.command.aggregate.AccountAggregate><accountID>f00088d7-c81a-432c-b010-28a88012f0eb</accountID><holderID>b232a9e8-d280-48bc-9d17-df03c9fe4071</holderID><balance>740</balance></com.cqrs.command.aggregate.AccountAggregate>±C>Ò^@^@^A<84>^B^@^A^@^@^A<80>

 

위의 나의 궁금증을 해결하기 위해서는 다음 글에서 설명하는 (디비에 저장하는) state-stored-aggregate을 공부하면 될 듯 하다.


참고자료

설명

https://docs.axoniq.io/reference-guide/v/4.0/configuring-infrastructure-components/event-processing/event-processors

 

Event Processors - Axon Reference Guide

By default, exceptions that are raised by Event Handlers are logged, and processing continues with the next events. Exceptions that are thrown when a processor is trying to commit a transaction, update a token, or in any other other part of the process, th

docs.axoniq.io

boot 설정 값

https://docs.axoniq.io/reference-guide/v/3.3/part-iii-infrastructure-components/spring-boot-autoconfiguration

 

Spring Boot AutoConfiguration - Axon Reference Guide

To fully customize the repository used, you can define one in the Application Context. For Axon Framework to use this repository for the intended Aggregate, define the bean name of the repository in the repository attribute on @Aggregate Annotation. Altern

docs.axoniq.io

 

728x90
반응형
반응형

내가 맡고 있는 프로젝트 중 지금은 보기 힘든 프래임워크를 사용하는 것이 있는데, 구조가 낯설기에 매번 어려움을 느낀다.

(20년은 된 소스인데 지금까지 잘 돌아가는거 보면 잘 짜져있음은 분명하다ㅋㅋ)

오늘은 해당 프로젝트를 오랜만에 보면서 MVC framework 중 webwork에 대해 기술한다.

  • 환경: webwork-2.2.7 / xwork-1.1.1(or 1.2.3? 1.2.4? 관련 jar의 버전이 다 달라 혼동스럽다)

 

struts / webwork / xwork 이것들의 차이는 무엇인가..

팀장님이 처음에 나에게 설명해주실 때 이 소스를 두고 struts를 사용했다고 하셨는데, 직접적인 struts*.jar이 임포트 되어 있진 않다. webwork를 기반으로 하고 있으며 그냥 구조가 struts와 유사하여 그렇게 부른 것 같다..

그래서 더 혼동스러웠다.

요즘에는 잘 사용하지 않는 framework여서인지 정확한 정보는 현재로서 찾기는 힘드나 여러 글들을 종합한 바 아래와 같다.

  • 우선 webwork와 xwork는 만든 회사가 같다(opensymphony; 지금 찾아보니 인수되었거나 망한 듯.. 홈피가 안나온다).
  • webwork은 커맨드 패턴에 기반하여 설계되었고, xwork의 wrapper다. 그래서인지 webwork으로 통합되었다는 말도 많고 webwork와 xwork 를 같게 보는 글이 많다. 하지만 계속 헷갈리는 건 소스안의 파일에 두 이름이 혼용되어 사용되고 있기 때문이다.
And webwork is just a wrapper for xwork into servlet environment.
WebWork is designed around the principle of the command pattern. In fact, it is really a wrapper XWork - a generic command-pattern framework.
  • struts는 webwork을 기반을 만들어진 프래임워크다. 초기의 struts framework 는 webwork framework 와 같다고 여겨진다.

관련파일

web.xml : 필터 매핑, 서블랫 매핑

xwork.xml : 각 화면별 인터셉터 정의, url에 맞는 액션 클래스 매핑, execute()함수 결과값에 따른 화면 정의

webwork.properties : 확장자 정의, method지정 변수 설정, 인코딩, i18n 등 프래임워크 안의 세팅 값


webwork

보통 디버그할 때 xwork.xml을 열어서 url 매핑을 확인하고 액션.java 나 연관된 화면(.jsp)을 열어서 분석한다.

보던 소스가 인터셉터 덩어리라(한 화면에 기본30개) 액션에 대한 로그를 확인하기가 어려운 문제가 있다. 근데 로그를 잘 보니 인터셉터가 여러번 불리는 것 같은데(정확히는 인터셉터 -> 액션 -> 인터셉터) 이해가 잘 안간다.

자세히 설명하자면...

모니터링 - before interceptor > 점검 interceptor > 유저 interceptor > action > 유저 - PreResultListener interceptor > 모니터링 - after interceptor > 모니터링 - before interceptor > 점검 interceptor > 유저 interceptor > 유저 - PreResultListener interceptor > 모니터링 - after interceptor

위처럼 인터셉터만 두번 돌았다.. 소스만 봤을 때에는 위 회색 부분은 안 돌았어야 하는데 말이다.

혹시 다른 요청이 또 들어오나 싶어 네트워크 창을 같이 봤는데, 딱히 다른 액션 요청은 없고, 액션 요청 후 리소스를 불러오는 부분 뿐이었다.

 

디버그 걸린상태에서 관찰한 결과 첫번째 루프(정상이라고 생각한)에서 액션이 바로 200 응답을 받고(qna.nhn; 사진의 보라색 동그라미시점) 두번째 루프(회색)가 시작할 때 보니 .css, .js 등 모든 리소스파일이 pending 이었다. 두번째 루프의 디버그를 풀어갈 때 쯤 점차적으로 불러져왔다.

지금 추측은 리소스 파일을 불러오려는 시점에 내부 콜?이 더 있지 않을까 싶은데, 딱히 잡히는건 없다.. ㅠㅠ

 


참고: 커맨드 패턴

https://huisam.tistory.com/entry/CommandPattern

 

Command Pattern - 커맨드 패턴

Command Pattern? - 커맨드 패턴(Command Pattern)은 Client가 보낸 요청을 객체로 캡슐화하여 이를 나중에 이용할 수 있도록 필요한 정보를 저장, 로깅, 취소할 수 있게 하는 패턴입니다. * 한마디로, 요청을

huisam.tistory.com

 

webwork

https://blog.naver.com/tyboss/70047272213

 

WebWork

간단한 소개 XWork라고 불리는 커맨드 패턴 프레임웤 API의 위에 있는 강력한 웹기반의 MVC 프레임...

blog.naver.com

 

webwork properties 항목

https://babtingdev.tistory.com/66

 

webwork.properties 전체 내용

### Webwork default properties ###(can be overridden by a webwork.properties file in the root of the classpath) ### ### Specifies the Configuration used to configure webwork ### one could extend com..

babtingdev.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.03 - [세팅/vm on mac] - [vm] virtual box centos7 세팅

 

[vm] virtual box centos7 세팅

1. oracle virtual box for mac 설치 2. centos7 iso 받기 https://ftp-srv2.kddilabs.jp/Linux/packages/CentOS/7.9.2009/isos/x86_64/ 참고로 gui 리눅스를 사용하기 위해 풀버전으로 받았음 ​ 3. https://mine..

bangpurin.tistory.com

2022.01.12 - [세팅/vm on mac] - [vm] virtual box 브릿지 네트워크 설정

 

[vm] virtual box 브릿지 네트워크 설정

virtual box에서 아래와 같이 설정한다. vm을 고정 IP로 지정하기 위해서 아래와 같이 세팅한다. 1. 우선 맥북(호스트)의 ip 확인 > 192.168.35.xx였다 ifconfig 2. linux(게스트)에서 고정 ip 설정 cd /etc/sysc..

bangpurin.tistory.com

 

최소설치 / 32기가 / 브릿지 네트워크로 세팅하고 시작한다.

 

1. prerequsite

업데이트 후 자바 11 설치

yum update -y
yum list | grep jdk
yum install java-11-openjdk.x86_64
java -version

 

2. axonserver 유저 생성, 폴더 생성 후 ownership 부여 / 로그 경로 생성 및 권한 부여

$ sudo adduser -d /var/lib/axonserver -U axonserver
$ sudo mkdir -p /var/log/axonserver
$ sudo chown axonserver:axonserver /var/log/axonserver

 

3. 프로퍼티 파일 생성

vi /var/lib/axonserver/axonserver.properties

//아래 내용 추가
logging.file=/var/log/axonserver/axonserver.log
logging.file.max-history=10
logging.file.max-size=10MB
axoniq.axonserver.event.storage=./events
axoniq.axonserver.snapshot.storage=./events
axoniq.axonserver.controldb-path=./data

 

4. start shell 작성

vi /var/lib/axonserver/start-axonserver.sh

//아래 내용 작성
#!/bin/bash

AXONSERVER_HOME=/var/lib/axonserver
cd ${AXONSERVER_HOME}

PIDFILE=${AXONSERVER_HOME}/axonserver.pid
AXONIQ_PIDFILE=${AXONSERVER_HOME}/AxonIQ.pid

if [ -s ${PIDFILE} ] ; then
    PID=$(cat ${PIDFILE})
    if ps -p ${PID} > /dev/null ; then
        echo "AxonServer is already running (PID=${PID})"
        exit 0
    fi

    echo "Cleaning up old PID files"
    rm -f ${PIDFILE} ${AXONIQ_PIDFILE}
fi

java -jar ${AXONSERVER_HOME}/axonserver.jar &
PID=$!

echo ${PID} > ${PIDFILE}

 

5. jar 공수

wget으로 아래 zip 받은 후 unzip 해서 사용할 수 있겠으나, 나는 그냥 로컬에 받고 jar 파일 두개를 sftp로 옮겼다.

(주의, 아래 링크 클릭 시 다운로드 됨)

https://download.axoniq.io/axonserver/AxonServer.zip

[root@localhost axonserver]# pwd
/var/lib/axonserver
[root@localhost axonserver]# ls -tr
axonserver.properties  axonserver-cli.jar  axonserver.jar  start-axonserver.sh

 

위 경로에 모든 것을 준비한다.

이대로 jar을 실행하면 되는거지만, 서버의 서비스에 등록해보도록 한다.

 

6. 서비스 등록 파일 작성

# /etc/systemd/system/axonserver.service
[Unit]
Description=AxonServer Service
#Requires=google-startup-scripts.service
#After=google-startup-scripts.service

[Service]
Type=forking
User=axonserver
Group=axonserver
ExecStart=/var/lib/axonserver/start-axonserver.sh
PIDFile=/var/lib/axonserver/axonserver.pid
StandardOutput=null
StandardError=null
TimeoutStartSec=10

[Install]
WantedBy=multi-user.target

공식 사이트에서는 google-startup-scripts.service 를 사용한 서비스 스크립트를 가이드해주는데, 아무래도 구글 vm(?) 을 사용했을 때를 전제로 하는 것 같다. 저 부분을 주석처리하고 진행해도 별 문제 없었다.

 

7. 서비스 등록 및 실행

//등록
sudo systemctl enable /etc/systemd/system/axonserver.service
//실행
sudo systemctl start axonserver

//상태 파악
sudo systemctl status axonserver
ps -eaf | grep axon

//종료
sudo systemctl stop axonserver

 

8. 실행 후 확인, vm에 띄웠기 때문에 vm ip로 접속. (기본포트 8024)

 

 


참고

설치 관련 공식사이트 원문

https://axoniq.io/blog-overview/running-axon-server-in-a-virtual-machine

 

Running AxonServer in a Virtual Machine - Managed or DIY, AxonServer runs fine in the cloud, private or public

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
반응형

'서버 세팅 & tool > vm on mac' 카테고리의 다른 글

[vm] nginx 설치  (0) 2022.02.24
[parallels] nox...... 99%.....  (0) 2022.02.23
[vm] virtual box 브릿지 네트워크 설정  (0) 2022.01.12
[vm] jenkins 설치  (0) 2022.01.10
[vm] 네트워크 고찰  (0) 2022.01.04
반응형

virtual box에서 아래와 같이 설정한다.

 

vm을 고정 IP로 지정하기 위해서 아래와 같이 세팅한다.

1. 우선 맥북(호스트)의 ip 확인 > 192.168.35.xx였다

ifconfig

 

2. linux(게스트)에서 기본 정보 확인

  • 현재 ip/prefix 확인(넷마스크)

Prefix 24의 Netmask 주소는 255.255.255.0 이다.

ip addr

 

  • 게이트웨이 정보 (via 다음 ip)
ip route

 

  • 네임서버 정보
cat /etc/resolv.config

 

 

3. 게스트 파일 수정

cd /etc/sysconfig/network-scripts
vi ifcfg-enp0s3

열고 위에서 알게된 정보를 기반으로 아래와 같이 설정했다.

 

4. 게스트 재시작 및 확인

systemctl restart network
ip addr
ping 8.8.8.8   //(구글)

 

5. 호스트에서 접속 확인

ssh root@192.168.35.100
728x90
반응형

'서버 세팅 & tool > vm on mac' 카테고리의 다른 글

[parallels] nox...... 99%.....  (0) 2022.02.23
[vm] axon server 설치  (0) 2022.01.12
[vm] jenkins 설치  (0) 2022.01.10
[vm] 네트워크 고찰  (0) 2022.01.04
[vm] virtual box 네트워크 NAT 설정 - mac  (0) 2022.01.03
반응형

2022.01.12 - [architecture/MSA] - [cqrs] axon framework

 

[cqrs] axon framework

2022.01.11 - [architecture/MSA] - [arch] what is cqrs - 조회와 비조회의 엄연한 분리 [arch] what is cqrs - 조회와 비조회의 엄연한 분리 CQRS는 Command and Query Responsibility Segregation(명령과 조회..

bangpurin.tistory.com

 

이전 글에서 axon-springboot 에 대한 클론코딩을 진행한다고 했는데, 아무래도 글쓴이는 19년도에 실습을 하였고 나는 22년에 진행을 하기에 그 사이 버전업으로 인해 달라진 점이나, 설명이 미흡하여 헤맨 부분을 따로 정리하도록 한다.

 

개발환경: springboot 2.6.2 / gradle 7.3.2 / postgre-14(on linux)

postgresql은 vm 리눅스에 설치했다.

vi /var/lib/pgsql/14/data/pg_hba.conf vi /var/lib/pgsql/14/data/pg_hba.conf 
//아래와 같이 수정
# TYPE  DATABASE        USER            ADDRESS                 METHOD
# "local" is for Unix domain socket connections only
local   all             command, query                 			md5
local   all             all                                     peer

# IPv4 local connections:
host    all             all             127.0.0.1/32            scram-sha-256
# IPv6 local connections:
host    all             all             ::1/128                 scram-sha-256
# Allow replication connections from localhost, by a user with the
# replication privilege.
local   replication     all                                     peer
host    replication     all             127.0.0.1/32            scram-sha-256
host    replication     all             ::1/128                 scram-sha-256

host    all             all     0.0.0.0/0              			md5

소스: https://github.com/haileyjhbang/cqrs-clone.git


실습내용: https://cla9.tistory.com/7?category=814447 

 

4. Gradle Multi Project 생성

1. 서론 CQRS 구성을 위해 일반적으로 Command, Query 두가지 Application을 구성합니다. 이때 Application 사이 매개체 역할을 Event가 담당합니다. 따라서 Event 정보를 알기 위해서는 Event 정보가 두 App에 모..

cla9.tistory.com

 

문제:

1. root project의 gradle build가 되지 않는 문제

2. main class 의 run버튼이 안 나타나는 문제


실습내용: https://cla9.tistory.com/8

 

5. Axon Framework 기본 구성 및 Axon Server 연동

1. 서론 이번 시간에는 지난 포스팅에 이어서 AxonFramework 구성 및 AxonServer 연동에 대해 다루도록 하겠습니다. 진행하기 앞서 몇가지 사전 작업이 필요합니다. Postgresql 설치 Postgresql Command, Query DB..

cla9.tistory.com

 

문제:

1. application.yml 항목 deprecated

2. springboot 2.6 이상일 경우 아래 에러 발생

Error creating bean with name 'entityManagerFactory': Requested bean is currently in creation
server:
  port: 8080

spring:
  application:
    name: eventsourcing-cqrs-command
  datasource:
    url: jdbc:postgresql://192.168.35.100:5432/command
    username: command
    password: command
    driverClassName: org.postgresql.Driver
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
  main:
    allow-circular-references: true

axon:
  serializer:
    general: xstream
  axonserver:
    servers: 192.168.35.100:8124

 

command/query 모두 위와 비슷하게 변경

 

 

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
반응형

+ Recent posts