Files
example-library/README.md
2020-04-19 13:23:28 +09:00

344 lines
18 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# MSA/DDD/Event Storming 예제 - 음식배달
## 서비스 시나리오
## 체크포인트
https://workflowy.com/s/assessment-check-po/T5YrzcMewfo4J6LW
- 분석 설계
- 이벤트스토밍:
- 스티커 색상별 객체의 의미를 제대로 이해하여 헥사고날 아키텍처와의 연계 설계에 적절히 반영하고 있는가?
- 각 도메인 이벤트가 의미있는 수준으로 정의되었는가?
- 어그리게잇: Command와 Event 들을 ACID 트랜잭션 단위의 Aggregate 로 제대로 묶었는가?
- View 와 Command 를 구분하였는가?
- 기능적 요구사항과 비기능적 요구사항을 누락 없이 반영하였는가?
- 서브 도메인, 바운디드 컨텍스트 분리
- 팀별 KPI 와 관심사, 상이한 배포주기 등에 따른  Sub-domain 이나 Bounded Context 를 적절히 분리하였고 그 분리 기준의 합리성이 충분히 설명되는가?
- 적어도 3개 이상 서비스 분리
- 폴리글랏 설계: 각 마이크로 서비스들의 구현 목표와 기능 특성에 따른 각자의 기술 Stack 과 저장소 구조를 다양하게 채택하여 설계하였는가?
- 서비스 시나리오 중 ACID 트랜잭션이 크리티컬한 Use 케이스에 대하여 무리하게 서비스가 과다하게 조밀히 분리되지 않았는가?
- 컨텍스트 매핑 / 이벤트 드리븐 아키텍처
- 업무 중요성과  도메인간 서열을 구분할 수 있는가? (Core, Supporting, General Domain)
- Request-Response 방식과 이벤트 드리븐 방식을 구분하여 설계할 수 있는가?
- 장애격리: 서포팅 서비스를 제거 하여도 기존 서비스에 영향이 없도록 설계하였는가?
- 신규 서비스를 추가 하였을때 기존 서비스의 데이터베이스에 영향이 없도록 설계(열려있는 아키택처)할 수 있는가?
- 이벤트와 폴리시를 연결하기 위한 Correlation-key 연결을 제대로 설계하였는가?
- 헥사고날 아키텍처
- 설계 결과에 따른 헥사고날 아키텍처 다이어그램을 제대로 그렸는가?
- 구현
- [DDD] 분석단계에서의 스티커별 색상과 헥사고날 아키텍처에 따라 구현체가 매핑되게 개발되었는가?
- Entity Pattern 과 Repository Pattern 을 적용하여 JPA 를 통하여 데이터 접근 어댑터를 개발하였는가
- [헥사고날 아키텍처] REST Inbound adaptor 이외에 gRPC 등의 Inbound Adaptor 를 추가함에 있어서 도메인 모델의 손상을 주지 않고 새로운 프로토콜에 기존 구현체를 적응시킬 수 있는가?
- 분석단계에서의 유비쿼터스 랭귀지 (업무현장에서 쓰는 용어) 를 사용하여 소스코드가 서술되었는가?
- Request-Response 방식의 서비스 중심 아키텍처 구현
- 마이크로 서비스간 Request-Response 호출에 있어 대상 서비스를 어떠한 방식으로 찾아서 호출 하였는가? (Service Discovery, REST, FeignClient)
- 서킷브레이커를 통하여  장애를 격리시킬 수 있는가?
- 이벤트 드리븐 아키텍처의 구현
- 카프카를 이용하여 PubSub 으로 하나 이상의 서비스가 연동되었는가?
- Correlation-key: 각 이벤트 건 (메시지)가 어떠한 폴리시를 처리할때 어떤 건에 연결된 처리건인지를 구별하기 위한 Correlation-key 연결을 제대로 구현 하였는가?
- Message Consumer 마이크로서비스가 장애상황에서 수신받지 못했던 기존 이벤트들을 다시 수신받아 처리하는가?
- Scaling-out: Message Consumer 마이크로서비스의 Replica 를 추가했을때 중복없이 이벤트를 수신할 수 있는가
- CQRS: Materialized View 를 구현하여, 타 마이크로서비스의 데이터 원본에 접근없이(Composite 서비스나 조인SQL 등 없이) 도 내 서비스의 화면 구성과 잦은 조회가 가능한가?
- 폴리글랏 플로그래밍
- 각 마이크로 서비스들이 하나이상의 각자의 기술 Stack 으로 구성되었는가?
- 각 마이크로 서비스들이 각자의 저장소 구조를 자율적으로 채택하고 각자의 저장소 유형 (RDB, NoSQL, File System 등)을 선택하여 구현하였는가?
- API 게이트웨이
- API GW를 통하여 마이크로 서비스들의 집입점을 통일할 수 있는가?
- 게이트웨이와 인증서버(OAuth), JWT 토큰 인증을 통하여 마이크로서비스들을 보호할 수 있는가?
- 운영
- SLA 준수
- 셀프힐링: Liveness Probe 를 통하여 어떠한 서비스의 health 상태가 지속적으로 저하됨에 따라 어떠한 임계치에서 pod 가 재생되는 것을 증명할 수 있는가?
- 서킷브레이커, 레이트리밋 등을 통한 장애격리와 성능효율을 높힐 수 있는가?
- 오토스케일러 (HPA) 를 설정하여 확장적 운영이 가능한가?
- 모니터링, 앨럿팅:
- 무정지 운영 CI/CD (10)
- Readiness Probe 의 설정과 Rolling update을 통하여 신규 버전이 완전히 서비스를 받을 수 있는 상태일때 신규버전의 서비스로 전환됨을 siege 등으로 증명 (3)
- Contract Test : 자동화된 경계 테스트를 통하여 구현 오류나 API 계약위반를 미리 차단 가능한가? (5)
## 이벤트 스토밍 결과:
http://msaez.io/#/storming/nZJ2QhwVc4NlVJPbtTkZ8x9jclF2/every/a77281d704710b0c2e6a823b6e6d973a/-M5AV2z--su_i4BfQfeF
### 과정 소개:
-
-
-
## 구현:
### 폴리글랏 퍼시스턴스 / 플랫폼
```
cd 주문
mvn spring-boot:run # H2
cd 결제
mvn spring-boot:run # H2
cd 상점
mvn spring-boot:run # MySQL
cd 마케팅
python marketing # 파이썬
```
### 비동기식 호출 / 시간적 디커플링 / 장애격리 / 최종 (Eventual) 일관성 테스트
```
#상점 서비스를 잠시 내려놓음
kill (lsof -i )
#주문처리
http localhost:8080/주문s 품목=통닭 수량=1 주소=서울
http localhost:8080/주문s 품목=피자 수량=2 주소=서울
#주문상태 확인
http localhost:8080/주문s # 주문상태 안바뀜 확인
#상점 서비스 기동
cd 상점
mvn spring-boot:run
#주문상태 확인
http localhost:8080/주문s # 모든 주문의 상태가 배송됨으로 확인
```
### 동기식 호출 / 타임 커플링 / 결제 안되면 주문도 안됨
```
# 결제 서비스를 잠시 내려놓음
kill (lsof -i)
#주문처리
http localhost:8080/주문s 품목=통닭 수량=1 주소=서울 #오류
http localhost:8080/주문s 품목=피자 수량=2 주소=서울 #오류
#결제서비스 재기동
cd 결제
mvn spring-boot:run
#주문처리
http localhost:8080/주문s 품목=통닭 수량=1 주소=서울 #성공
http localhost:8080/주문s 품목=피자 수량=2 주소=서울 #성공
```
## 운영
### 동기식 호출 / 서킷 브레이킹 / 장애격
* 서킷 브레이킹 프레임워크의 선택: Spring FeignClient + Hystrix 옵션을 사용하여 구현함
시나리오는 단말앱(app)-->결제(pay) 시의 연결을 RESTful Request/Response 로 연동하여 요청이 쇄도할 경우 CB 를 통하여 장애격리하는 시나리오.
- 결제서비스를 호출하기 위하여 Stub과 (FeignClient) 를 이용하여 Service 대행 인터페이스 (Proxy) 를 구현
```
# (app) 결제이력Service.java
package fooddelivery.external;
@FeignClient(name="pay", url="http://localhost:8082")//, fallback = 결제이력ServiceFallback.class)
public interface 결제이력Service {
@RequestMapping(method= RequestMethod.POST, path="/결제이력s")
public void 결제(@RequestBody 결제이력 pay);
}
```
- 주문을 받은 직후(@PostPersist) 결제를 요청하도록 처리
```
# Order.java (Entity)
@PostPersist
public void onPostPersist(){
fooddelivery.external.결제이력 pay = new fooddelivery.external.결제이력();
pay.setOrderId(getOrderId());
Application.applicationContext.getBean(fooddelivery.external.결제이력Service.class)
.결제(pay);
}
```
- Hystrix 를 설정: 요청처리 쓰레드에서 처리시간이 610 밀리가 넘어서기 시작하여 어느정도 유지되면 CB 회로가 닫히도록 (요청을 빠르게 실패처리, 차단) 설정
```
# application.yml
hystrix:
command:
# 전역설정
default:
execution.isolation.thread.timeoutInMilliseconds: 610
```
- 피호출 서비스(결제:pay) 의 임의 부하 처리 - 400 밀리에서 증감 220 밀리 정도 왔다갔다 하게
```
# (pay) 결제이력.java (Entity)
@PrePersist
public void onPrePersist(){ //결제이력을 저장한 후 적당한 시간 끌기
...
try {
Thread.currentThread().sleep((long) (400 + Math.random() * 220));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
```
* 부하테스터 siege 툴을 통한 서킷 브레이커 동작 확인:
- 동시사용자 100명
- 60초 동안 실시
```
$ siege -c100 -t60S -r10 --content-type "application/json" 'http://localhost:8081/orders POST {"item": "chicken"}'
** SIEGE 4.0.5
** Preparing 100 concurrent users for battle.
The server is now under siege...
HTTP/1.1 201 0.68 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 0.68 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 0.70 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 0.70 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 0.73 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 0.75 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 0.77 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 0.97 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 0.81 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 0.87 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 1.12 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 1.16 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 1.17 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 1.26 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 1.25 secs: 207 bytes ==> POST http://localhost:8081/orders
* 요청이 과도하여 CB를 동작함 요청을 차단
HTTP/1.1 500 1.29 secs: 248 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 500 1.24 secs: 248 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 500 1.23 secs: 248 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 500 1.42 secs: 248 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 500 2.08 secs: 248 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 1.29 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 500 1.24 secs: 248 bytes ==> POST http://localhost:8081/orders
* 요청을 어느정도 돌려보내고나니, 기존에 밀린 일들이 처리되었고, 회로를 닫아 요청을 다시 받기 시작
HTTP/1.1 201 1.46 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 1.33 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 1.36 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 1.63 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 1.65 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 1.68 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 1.69 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 1.71 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 1.71 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 1.74 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 1.76 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 1.79 secs: 207 bytes ==> POST http://localhost:8081/orders
* 다시 요청이 쌓이기 시작하여 건당 처리시간이 610 밀리를 살짝 넘기기 시작 => 회로 열기 => 요청 실패처리
HTTP/1.1 500 1.93 secs: 248 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 500 1.92 secs: 248 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 500 1.93 secs: 248 bytes ==> POST http://localhost:8081/orders
* 생각보다 빨리 상태 호전됨 - (건당 (쓰레드당) 처리시간이 610 밀리 미만으로 회복) => 요청 수락
HTTP/1.1 201 2.24 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 2.32 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 2.16 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 2.19 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 2.19 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 2.19 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 2.21 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 2.29 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 2.30 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 2.38 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 2.59 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 2.61 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 2.62 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 2.64 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 4.01 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 4.27 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 4.33 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 4.45 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 4.52 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 4.57 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 4.69 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 4.70 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 4.69 secs: 207 bytes ==> POST http://localhost:8081/orders
* 이후 이러한 패턴이 계속 반복되면서 시스템은 도미노 현상이나 자원 소모의 폭주 없이 잘 운영됨
HTTP/1.1 500 4.76 secs: 248 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 500 4.23 secs: 248 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 4.76 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 4.74 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 500 4.82 secs: 248 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 4.82 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 4.84 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 4.66 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 500 5.03 secs: 248 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 500 4.22 secs: 248 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 500 4.19 secs: 248 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 500 4.18 secs: 248 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 4.69 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 4.65 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 5.13 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 500 4.84 secs: 248 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 500 4.25 secs: 248 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 500 4.25 secs: 248 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 4.80 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 500 4.87 secs: 248 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 500 4.33 secs: 248 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 4.86 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 500 4.96 secs: 248 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 500 4.34 secs: 248 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 500 4.04 secs: 248 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 4.50 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 4.95 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 4.54 secs: 207 bytes ==> POST http://localhost:8081/orders
HTTP/1.1 201 4.65 secs: 207 bytes ==> POST http://localhost:8081/orders
:
:
Transactions: 1025 hits
Availability: 63.55 %
Elapsed time: 59.78 secs
Data transferred: 0.34 MB
Response time: 5.60 secs
Transaction rate: 17.15 trans/sec
Throughput: 0.01 MB/sec
Concurrency: 96.02
Successful transactions: 1025
Failed transactions: 588
Longest transaction: 9.20
Shortest transaction: 0.00
```
- 63.55% 가 성공하였고, 운영시스템은 죽지 않고 지속적으로 CB 에 의하여 적절히 회로가 열림과 닫힘이 벌어지면서 자원을 보호하고 있음을 보여줌. 물론, 46% 의 실패율은 좋지 않기 때문에 동적 Scale out (replica의 자동적 추가,HPA) 을 통하여 시스템을 확장 해주는 후속처리가 필요.
### 무정지 재배포
```
```
###
```