Open zoro9483 opened 3 years ago
About This repository hosts the code for a food delivery app UI, built in flutter, with "Add to cart" and "Drag to delete" functionality. https://github.com/Ronak99/FoodDelivery-App-UI
Problem Statement
Design OO food delivery app catering to use cases -
User can search different restaurant
User can select a restaurant
User sees a menu
Restaurant can change the menu any time
User adds an item from menu
User orders the food
User can track the order in real time
User can cancel the order
User pays for the order
Calculation of Tax based on the type of Restraunt
Soulution:
Design Patterns involved in the design of this app:
Builder Design Pattern (For adding food item and ordering)
Interpreter Design Pattern (User can Search Different restaurant)
Iterator Pattern (User Sees Menu)
Observer Pattern (Track an order in Real Time)
(Command Design Pattern) Order and cancellation of Food
(Strategy Design Pattern) Calculation of Tax based on the type of Restraunt
About Design OO food delivery app with C# & Design Patterns
์์ - ์์๋ฐฐ๋ฌ ๋ณธ ์์ ๋ MSA/DDD/Event Storming/EDA ๋ฅผ ํฌ๊ดํ๋ ๋ถ์/์ค๊ณ/๊ตฌํ/์ด์ ์ ๋จ๊ณ๋ฅผ ์ปค๋ฒํ๋๋ก ๊ตฌ์ฑํ ์์ ์ ๋๋ค. ์ด๋ ํด๋ผ์ฐ๋ ๋ค์ดํฐ๋ธ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋ฐ์ ์๊ตฌ๋๋ ์ฒดํฌํฌ์ธํธ๋ค์ ํต๊ณผํ๊ธฐ ์ํ ์์ ๋ต์์ ํฌํจํฉ๋๋ค.
์ฒดํฌํฌ์ธํธ : https://workflowy.com/s/assessment-check-po/T5YrzcMewfo4J6LW
๋ณธ ์์ ๋ MSA/DDD/Event Storming/EDA ๋ฅผ ํฌ๊ดํ๋ ๋ถ์/์ค๊ณ/๊ตฌํ/์ด์ ์ ๋จ๊ณ๋ฅผ ์ปค๋ฒํ๋๋ก ๊ตฌ์ฑํ ์์ ์ ๋๋ค. ์ด๋ ํด๋ผ์ฐ๋ ๋ค์ดํฐ๋ธ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋ฐ์ ์๊ตฌ๋๋ ์ฒดํฌํฌ์ธํธ๋ค์ ํต๊ณผํ๊ธฐ ์ํ ์์ ๋ต์์ ํฌํจํฉ๋๋ค.
๋ฐฐ๋ฌ์ ๋ฏผ์กฑ ์ปค๋ฒํ๊ธฐ - https://1sung.tistory.com/106
๊ธฐ๋ฅ์ ์๊ตฌ์ฌํญ
๋น๊ธฐ๋ฅ์ ์๊ตฌ์ฌํญ
๋ถ์ ์ค๊ณ
์ด๋ฒคํธ์คํ ๋ฐ:
์คํฐ์ปค ์์๋ณ ๊ฐ์ฒด์ ์๋ฏธ๋ฅผ ์ ๋๋ก ์ดํดํ์ฌ ํฅ์ฌ๊ณ ๋ ์ํคํ ์ฒ์์ ์ฐ๊ณ ์ค๊ณ์ ์ ์ ํ ๋ฐ์ํ๊ณ ์๋๊ฐ?
๊ฐ ๋๋ฉ์ธ ์ด๋ฒคํธ๊ฐ ์๋ฏธ์๋ ์์ค์ผ๋ก ์ ์๋์๋๊ฐ?
์ด๊ทธ๋ฆฌ๊ฒ์: Command์ Event ๋ค์ ACID ํธ๋์ญ์ ๋จ์์ Aggregate ๋ก ์ ๋๋ก ๋ฌถ์๋๊ฐ?
๊ธฐ๋ฅ์ ์๊ตฌ์ฌํญ๊ณผ ๋น๊ธฐ๋ฅ์ ์๊ตฌ์ฌํญ์ ๋๋ฝ ์์ด ๋ฐ์ํ์๋๊ฐ?
์๋ธ ๋๋ฉ์ธ, ๋ฐ์ด๋๋ ์ปจํ ์คํธ ๋ถ๋ฆฌ
ํ๋ณ KPI ์ ๊ด์ฌ์ฌ, ์์ดํ ๋ฐฐํฌ์ฃผ๊ธฐ ๋ฑ์ ๋ฐ๋ฅธ ย Sub-domain ์ด๋ Bounded Context ๋ฅผ ์ ์ ํ ๋ถ๋ฆฌํ์๊ณ ๊ทธ ๋ถ๋ฆฌ ๊ธฐ์ค์ ํฉ๋ฆฌ์ฑ์ด ์ถฉ๋ถํ ์ค๋ช ๋๋๊ฐ?
ํด๋ฆฌ๊ธ๋ ์ค๊ณ: ๊ฐ ๋ง์ดํฌ๋ก ์๋น์ค๋ค์ ๊ตฌํ ๋ชฉํ์ ๊ธฐ๋ฅ ํน์ฑ์ ๋ฐ๋ฅธ ๊ฐ์์ ๊ธฐ์ 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 ํ ํฐ ์ธ์ฆ์ ํตํ์ฌ ๋ง์ดํฌ๋ก์๋น์ค๋ค์ ๋ณดํธํ ์ ์๋๊ฐ?
์ด์
- ๊ณผ์ ์ค ๋์ถ๋ ์๋ชป๋ ๋๋ฉ์ธ ์ด๋ฒคํธ๋ค์ ๊ฑธ๋ฌ๋ด๋ ์์
์ ์ํํจ
- ์ฃผ๋ฌธ์>๋ฉ๋ด์นดํ
๊ณ ๋ฆฌ์ ํ๋จ, ์ฃผ๋ฌธ์>๋ฉ๋ด๊ฒ์๋จ : UI ์ ์ด๋ฒคํธ์ด์ง, ์
๋ฌด์ ์ธ ์๋ฏธ์ ์ด๋ฒคํธ๊ฐ ์๋๋ผ์ ์ ์ธ
- app์ Order, store ์ ์ฃผ๋ฌธ์ฒ๋ฆฌ, ๊ฒฐ์ ์ ๊ฒฐ์ ์ด๋ ฅ์ ๊ทธ์ ์ฐ๊ฒฐ๋ command ์ event ๋ค์ ์ํ์ฌ ํธ๋์ญ์
์ด ์ ์ง๋์ด์ผ ํ๋ ๋จ์๋ก ๊ทธ๋ค ๋ผ๋ฆฌ ๋ฌถ์ด์ค
- ๋๋ฉ์ธ ์์ด ๋ถ๋ฆฌ
- Core Domain: app(front), store : ์์ด์๋ ์๋ ํต์ฌ ์๋น์ค์ด๋ฉฐ, ์ฐ๊ฒฌ Up-time SLA ์์ค์ 99.999% ๋ชฉํ, ๋ฐฐํฌ์ฃผ๊ธฐ๋ app ์ ๊ฒฝ์ฐ 1์ฃผ์ผ 1ํ ๋ฏธ๋ง, store ์ ๊ฒฝ์ฐ 1๊ฐ์ 1ํ ๋ฏธ๋ง
- Supporting Domain: marketing, customer : ๊ฒฝ์๋ ฅ์ ๋ด๊ธฐ์ํ ์๋น์ค์ด๋ฉฐ, SLA ์์ค์ ์ฐ๊ฐ 60% ์ด์ uptime ๋ชฉํ, ๋ฐฐํฌ์ฃผ๊ธฐ๋ ๊ฐ ํ์ ์์จ์ด๋ ํ์ค ์คํ๋ฆฐํธ ์ฃผ๊ธฐ๊ฐ 1์ฃผ์ผ ์ด๋ฏ๋ก 1์ฃผ์ผ 1ํ ์ด์์ ๊ธฐ์ค์ผ๋ก ํจ.
- General Domain: pay : ๊ฒฐ์ ์๋น์ค๋ก 3rd Party ์ธ๋ถ ์๋น์ค๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ๊ฒฝ์๋ ฅ์ด ๋์ (ํํฌ์์ผ๋ก ์ดํ ์ ํํ ์์ )
- View Model ์ถ๊ฐ
- ๊ณ ๊ฐ์ด ๋ฉ๋ด๋ฅผ ์ ํํ์ฌ ์ฃผ๋ฌธํ๋ค (ok)
- ๊ณ ๊ฐ์ด ๊ฒฐ์ ํ๋ค (ok)
- ์ฃผ๋ฌธ์ด ๋๋ฉด ์ฃผ๋ฌธ ๋ด์ญ์ด ์
์ ์์ ์ฃผ์ธ์๊ฒ ์ ๋ฌ๋๋ค (ok)
- ์์ ์ฃผ์ธ์ด ํ์ธํ์ฌ ์๋ฆฌํด์ ๋ฐฐ๋ฌ ์ถ๋ฐํ๋ค (ok)
- ์์ ๋ ๋ชจ๋ธ์ ๋ชจ๋ ์๊ตฌ์ฌํญ์ ์ปค๋ฒํจ.
- ๋ง์ดํฌ๋ก ์๋น์ค๋ฅผ ๋๋๋๋ ์๋๋ฆฌ์ค์ ๋ํ ํธ๋์ญ์
์ฒ๋ฆฌ
- ๊ณ ๊ฐ ์ฃผ๋ฌธ์ ๊ฒฐ์ ์ฒ๋ฆฌ: ๊ฒฐ์ ๊ฐ ์๋ฃ๋์ง ์์ ์ฃผ๋ฌธ์ ์ ๋ ๋ฐ์ง ์๋๋ค๋ ๊ฒฝ์์์ ์ค๋ ์ ๋
(?) ์ ๋ฐ๋ผ, ACID ํธ๋์ญ์
์ ์ฉ. ์ฃผ๋ฌธ์๋ฃ์ ๊ฒฐ์ ์ฒ๋ฆฌ์ ๋ํด์๋ Request-Response ๋ฐฉ์ ์ฒ๋ฆฌ
- ๊ฒฐ์ ์๋ฃ์ ์ ์ฃผ์ฐ๊ฒฐ ๋ฐ ๋ฐฐ์ก์ฒ๋ฆฌ: App(front) ์์ Store ๋ง์ดํฌ๋ก์๋น์ค๋ก ์ฃผ๋ฌธ์์ฒญ์ด ์ ๋ฌ๋๋ ๊ณผ์ ์ ์์ด์ Store ๋ง์ดํฌ๋ก ์๋น์ค๊ฐ ๋ณ๋์ ๋ฐฐํฌ์ฃผ๊ธฐ๋ฅผ ๊ฐ์ง๊ธฐ ๋๋ฌธ์ Eventual Consistency ๋ฐฉ์์ผ๋ก ํธ๋์ญ์
์ฒ๋ฆฌํจ.
- ๋๋จธ์ง ๋ชจ๋ inter-microservice ํธ๋์ญ์
: ์ฃผ๋ฌธ์ํ, ๋ฐฐ๋ฌ์ํ ๋ฑ ๋ชจ๋ ์ด๋ฒคํธ์ ๋ํด ์นดํก์ ์ฒ๋ฆฌํ๋ ๋ฑ, ๋ฐ์ดํฐ ์ผ๊ด์ฑ์ ์์ ์ด ํฌ๋ฆฌํฐ์ปฌํ์ง ์์ ๋ชจ๋ ๊ฒฝ์ฐ๊ฐ ๋๋ถ๋ถ์ด๋ผ ํ๋จ, Eventual Consistency ๋ฅผ ๊ธฐ๋ณธ์ผ๋ก ์ฑํํจ.
- Chris Richardson, MSA Patterns ์ฐธ๊ณ ํ์ฌ Inbound adaptor์ Outbound adaptor๋ฅผ ๊ตฌ๋ถํจ
- ํธ์ถ๊ด๊ณ์์ PubSub ๊ณผ Req/Resp ๋ฅผ ๊ตฌ๋ถํจ
- ์๋ธ ๋๋ฉ์ธ๊ณผ ๋ฐ์ด๋๋ ์ปจํ
์คํธ์ ๋ถ๋ฆฌ: ๊ฐ ํ์ KPI ๋ณ๋ก ์๋์ ๊ฐ์ด ๊ด์ฌ ๊ตฌํ ์คํ ๋ฆฌ๋ฅผ ๋๋ ๊ฐ์ง
๋ถ์/์ค๊ณ ๋จ๊ณ์์ ๋์ถ๋ ํฅ์ฌ๊ณ ๋ ์ํคํ ์ฒ์ ๋ฐ๋ผ, ๊ฐ BC๋ณ๋ก ๋๋ณ๋๋ ๋ง์ดํฌ๋ก ์๋น์ค๋ค์ ์คํ๋ง๋ถํธ์ ํ์ด์ ์ผ๋ก ๊ตฌํํ์๋ค. ๊ตฌํํ ๊ฐ ์๋น์ค๋ฅผ ๋ก์ปฌ์์ ์คํํ๋ ๋ฐฉ๋ฒ์ ์๋์ ๊ฐ๋ค (๊ฐ์์ ํฌํธ๋๋ฒ๋ 8081 ~ 808n ์ด๋ค)
cd app
mvn spring-boot:run
cd pay
mvn spring-boot:run
cd store
mvn spring-boot:run
cd customer
python policy-handler.py
package fooddelivery;
import javax.persistence.*;
import org.springframework.beans.BeanUtils;
import java.util.List;
@Entity
@Table(name="๊ฒฐ์ ์ด๋ ฅ_table")
public class ๊ฒฐ์ ์ด๋ ฅ {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String orderId;
private Double ๊ธ์ก;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public Double get๊ธ์ก() {
return ๊ธ์ก;
}
public void set๊ธ์ก(Double ๊ธ์ก) {
this.๊ธ์ก = ๊ธ์ก;
}
}
package fooddelivery;
import org.springframework.data.repository.PagingAndSortingRepository;
public interface ๊ฒฐ์ ์ด๋ ฅRepository extends PagingAndSortingRepository<๊ฒฐ์ ์ด๋ ฅ, Long>{ }
- ์ ์ฉ ํ REST API ์ ํ
์คํธ
http localhost:8081/orders item="ํต๋ญ"
http localhost:8083/์ฃผ๋ฌธ์ฒ๋ฆฌs orderId=1
http localhost:8081/orders/1
## ํด๋ฆฌ๊ธ๋ ํผ์์คํด์ค
์ฑํ๋ฐํธ (app) ๋ ์๋น์ค ํน์ฑ์ ๋ง์ ์ฌ์ฉ์์ ์ ์
๊ณผ ์ํ ์ ๋ณด์ ๋ค์ํ ์ฝํ
์ธ ๋ฅผ ์ ์ฅํด์ผ ํ๋ ํน์ง์ผ๋ก ์ธํด RDB ๋ณด๋ค๋ Document DB / NoSQL ๊ณ์ด์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ธ Mongo DB ๋ฅผ ์ฌ์ฉํ๊ธฐ๋ก ํ์๋ค. ์ด๋ฅผ ์ํด order ์ ์ ์ธ์๋ @Entity ๊ฐ ์๋ @Document ๋ก ๋งํน๋์์ผ๋ฉฐ, ๋ณ๋ค๋ฅธ ์์
์์ด ๊ธฐ์กด์ Entity Pattern ๊ณผ Repository Pattern ์ ์ฉ๊ณผ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ํ์ ์ค์ (application.yml) ๋ง์ผ๋ก MongoDB ์ ๋ถ์ฐฉ์์ผฐ๋ค
package fooddelivery;
@Document public class Order {
private String id; // mongo db ์ ์ฉ์์ id ๋ ๊ณ ์ ๊ฐ์ผ๋ก key๊ฐ ์๋ ๋ฐ๊ธ๋๋ ํ๋๊ธฐ ๋๋ฌธ์ @Id ๋ @GeneratedValue ๋ฅผ ์ฃผ์ง ์์๋ ๋๋ค.
private String item;
private Integer ์๋;
}
package fooddelivery;
public interface ์ฃผ๋ฌธRepository extends JpaRepository<Order, UUID>{ }
data: mongodb: host: mongodb.default.svc.cluster.local database: mongo-example
## ํด๋ฆฌ๊ธ๋ ํ๋ก๊ทธ๋๋ฐ
๊ณ ๊ฐ๊ด๋ฆฌ ์๋น์ค(customer)์ ์๋๋ฆฌ์ค์ธ ์ฃผ๋ฌธ์ํ, ๋ฐฐ๋ฌ์ํ ๋ณ๊ฒฝ์ ๋ฐ๋ผ ๊ณ ๊ฐ์๊ฒ ์นดํก๋ฉ์์ง ๋ณด๋ด๋ ๊ธฐ๋ฅ์ ๊ตฌํ ํํธ๋ ํด๋น ํ์ด python ์ ์ด์ฉํ์ฌ ๊ตฌํํ๊ธฐ๋ก ํ์๋ค. ํด๋น ํ์ด์ฌ ๊ตฌํ์ฒด๋ ๊ฐ ์ด๋ฒคํธ๋ฅผ ์์ ํ์ฌ ์ฒ๋ฆฌํ๋ Kafka consumer ๋ก ๊ตฌํ๋์๊ณ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ๋ค:
from flask import Flask from redis import Redis, RedisError from kafka import KafkaConsumer import os import socket
consumer = KafkaConsumer('fooddelivery', group_id='', bootstrap_servers=['localhost:9092']) for message in consumer: print ("%s:%d:%d: key=%s value=%s" % (message.topic, message.partition, message.offset, message.key, message.value))
# ์นดํกํธ์ถ API
ํ์ด์ ์ ํ๋ฆฌ์ผ์ด์
์ ์ปดํ์ผํ๊ณ ์คํํ๊ธฐ ์ํ ๋์ปคํ์ผ์ ์๋์ ๊ฐ๋ค (์ด์๋จ๊ณ์์ ํ ์ผ์ธ๊ฐ? ์๋๋ค ์ฌ๊ธฐ ๊น์ง๊ฐ ๊ฐ๋ฐ์๊ฐ ํ ์ผ์ด๋ค. Immutable Image):
FROM python:2.7-slim WORKDIR /app ADD . /app RUN pip install --trusted-host pypi.python.org -r requirements.txt ENV NAME World EXPOSE 8090 CMD ["python", "policy-handler.py"]
## ๋๊ธฐ์ ํธ์ถ ๊ณผ Fallback ์ฒ๋ฆฌ
๋ถ์๋จ๊ณ์์์ ์กฐ๊ฑด ์ค ํ๋๋ก ์ฃผ๋ฌธ(app)->๊ฒฐ์ (pay) ๊ฐ์ ํธ์ถ์ ๋๊ธฐ์ ์ผ๊ด์ฑ์ ์ ์งํ๋ ํธ๋์ญ์
์ผ๋ก ์ฒ๋ฆฌํ๊ธฐ๋ก ํ์๋ค. ํธ์ถ ํ๋กํ ์ฝ์ ์ด๋ฏธ ์์ Rest Repository ์ ์ํด ๋
ธ์ถ๋์ด์๋ REST ์๋น์ค๋ฅผ FeignClient ๋ฅผ ์ด์ฉํ์ฌ ํธ์ถํ๋๋ก ํ๋ค.
- ๊ฒฐ์ ์๋น์ค๋ฅผ ํธ์ถํ๊ธฐ ์ํ์ฌ Stub๊ณผ (FeignClient) ๋ฅผ ์ด์ฉํ์ฌ Service ๋ํ ์ธํฐํ์ด์ค (Proxy) ๋ฅผ ๊ตฌํ
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) ๊ฒฐ์ ๋ฅผ ์์ฒญํ๋๋ก ์ฒ๋ฆฌ
@PostPersist
public void onPostPersist(){
fooddelivery.external.๊ฒฐ์ ์ด๋ ฅ pay = new fooddelivery.external.๊ฒฐ์ ์ด๋ ฅ();
pay.setOrderId(getOrderId());
Application.applicationContext.getBean(fooddelivery.external.๊ฒฐ์ ์ด๋ ฅService.class)
.๊ฒฐ์ (pay);
}
- ๋๊ธฐ์ ํธ์ถ์์๋ ํธ์ถ ์๊ฐ์ ๋ฐ๋ฅธ ํ์ ์ปคํ๋ง์ด ๋ฐ์ํ๋ฉฐ, ๊ฒฐ์ ์์คํ
์ด ์ฅ์ ๊ฐ ๋๋ฉด ์ฃผ๋ฌธ๋ ๋ชป๋ฐ๋๋ค๋ ๊ฒ์ ํ์ธ:
http localhost:8081/orders item=ํต๋ญ storeId=1 #Fail http localhost:8081/orders item=ํผ์ storeId=2 #Fail
cd ๊ฒฐ์ mvn spring-boot:run
http localhost:8081/orders item=ํต๋ญ storeId=1 #Success http localhost:8081/orders item=ํผ์ storeId=2 #Success
- ๋ํ ๊ณผ๋ํ ์์ฒญ์์ ์๋น์ค ์ฅ์ ๊ฐ ๋๋ฏธ๋
ธ ์ฒ๋ผ ๋ฒ์ด์ง ์ ์๋ค. (์ํท๋ธ๋ ์ด์ปค, ํด๋ฐฑ ์ฒ๋ฆฌ๋ ์ด์๋จ๊ณ์์ ์ค๋ช
ํ๋ค.)
## ๋น๋๊ธฐ์ ํธ์ถ / ์๊ฐ์ ๋์ปคํ๋ง / ์ฅ์ ๊ฒฉ๋ฆฌ / ์ต์ข
(Eventual) ์ผ๊ด์ฑ ํ
์คํธ
๊ฒฐ์ ๊ฐ ์ด๋ฃจ์ด์ง ํ์ ์์ ์์คํ
์ผ๋ก ์ด๋ฅผ ์๋ ค์ฃผ๋ ํ์๋ ๋๊ธฐ์์ด ์๋๋ผ ๋น ๋๊ธฐ์์ผ๋ก ์ฒ๋ฆฌํ์ฌ ์์ ์์คํ
์ ์ฒ๋ฆฌ๋ฅผ ์ํ์ฌ ๊ฒฐ์ ์ฃผ๋ฌธ์ด ๋ธ๋กํน ๋์ง ์์๋๋ก ์ฒ๋ฆฌํ๋ค.
- ์ด๋ฅผ ์ํ์ฌ ๊ฒฐ์ ์ด๋ ฅ์ ๊ธฐ๋ก์ ๋จ๊ธด ํ์ ๊ณง๋ฐ๋ก ๊ฒฐ์ ์น์ธ์ด ๋์๋ค๋ ๋๋ฉ์ธ ์ด๋ฒคํธ๋ฅผ ์นดํ์นด๋ก ์ก์ถํ๋ค(Publish)
package fooddelivery;
@Entity @Table(name="๊ฒฐ์ ์ด๋ ฅ_table") public class ๊ฒฐ์ ์ด๋ ฅ {
... @PrePersist public void onPrePersist(){ ๊ฒฐ์ ์น์ธ๋จ ๊ฒฐ์ ์น์ธ๋จ = new ๊ฒฐ์ ์น์ธ๋จ(); BeanUtils.copyProperties(this, ๊ฒฐ์ ์น์ธ๋จ); ๊ฒฐ์ ์น์ธ๋จ.publish(); }
}
- ์์ ์๋น์ค์์๋ ๊ฒฐ์ ์น์ธ ์ด๋ฒคํธ์ ๋ํด์ ์ด๋ฅผ ์์ ํ์ฌ ์์ ์ ์ ์ฑ
์ ์ฒ๋ฆฌํ๋๋ก PolicyHandler ๋ฅผ ๊ตฌํํ๋ค:
package fooddelivery;
...
@Service public class PolicyHandler{
@StreamListener(KafkaProcessor.INPUT)
public void whenever๊ฒฐ์ ์น์ธ๋จ_์ฃผ๋ฌธ์ ๋ณด๋ฐ์(@Payload ๊ฒฐ์ ์น์ธ๋จ ๊ฒฐ์ ์น์ธ๋จ){
if(๊ฒฐ์ ์น์ธ๋จ.isMe()){
System.out.println("##### listener ์ฃผ๋ฌธ์ ๋ณด๋ฐ์ : " + ๊ฒฐ์ ์น์ธ๋จ.toJson());
// ์ฃผ๋ฌธ ์ ๋ณด๋ฅผ ๋ฐ์์ผ๋, ์๋ฆฌ๋ฅผ ์ฌ์ฌ ์์ํด์ผ์ง..
}
}
}
์ค์ ๊ตฌํ์ ํ์๋ฉด, ์นดํก ๋ฑ์ผ๋ก ์ ์ฃผ๋ ๋
ธํฐ๋ฅผ ๋ฐ๊ณ , ์๋ฆฌ๋ฅผ ๋ง์นํ, ์ฃผ๋ฌธ ์ํ๋ฅผ UI์ ์
๋ ฅํ ํ
๋, ์ฐ์ ์ฃผ๋ฌธ์ ๋ณด๋ฅผ DB์ ๋ฐ์๋์ ํ, ์ดํ ์ฒ๋ฆฌ๋ ํด๋น Aggregate ๋ด์์ ํ๋ฉด ๋๊ฒ ๋ค.:
@Autowired ์ฃผ๋ฌธ๊ด๋ฆฌRepository ์ฃผ๋ฌธ๊ด๋ฆฌRepository;
@StreamListener(KafkaProcessor.INPUT) public void whenever๊ฒฐ์ ์น์ธ๋จ_์ฃผ๋ฌธ์ ๋ณด๋ฐ์(@Payload ๊ฒฐ์ ์น์ธ๋จ ๊ฒฐ์ ์น์ธ๋จ){
if(๊ฒฐ์ ์น์ธ๋จ.isMe()){
์นดํก์ ์ก(" ์ฃผ๋ฌธ์ด ์์ด์! : " + ๊ฒฐ์ ์น์ธ๋จ.toString(), ์ฃผ๋ฌธ.getStoreId());
์ฃผ๋ฌธ๊ด๋ฆฌ ์ฃผ๋ฌธ = new ์ฃผ๋ฌธ๊ด๋ฆฌ();
์ฃผ๋ฌธ.setId(๊ฒฐ์ ์น์ธ๋จ.getOrderId());
์ฃผ๋ฌธ๊ด๋ฆฌRepository.save(์ฃผ๋ฌธ);
}
}
์์ ์์คํ
์ ์ฃผ๋ฌธ/๊ฒฐ์ ์ ์์ ํ ๋ถ๋ฆฌ๋์ด์์ผ๋ฉฐ, ์ด๋ฒคํธ ์์ ์ ๋ฐ๋ผ ์ฒ๋ฆฌ๋๊ธฐ ๋๋ฌธ์, ์์ ์์คํ
์ด ์ ์ง๋ณด์๋ก ์ธํด ์ ์ ๋ด๋ ค๊ฐ ์ํ๋ผ๋ ์ฃผ๋ฌธ์ ๋ฐ๋๋ฐ ๋ฌธ์ ๊ฐ ์๋ค:
http localhost:8081/orders item=ํต๋ญ storeId=1 #Success http localhost:8081/orders item=ํผ์ storeId=2 #Success
http localhost:8080/orders # ์ฃผ๋ฌธ์ํ ์๋ฐ๋ ํ์ธ
cd ์์ mvn spring-boot:run
http localhost:8080/orders # ๋ชจ๋ ์ฃผ๋ฌธ์ ์ํ๊ฐ "๋ฐฐ์ก๋จ"์ผ๋ก ํ์ธ
# ์ด์
## CI/CD ์ค์
๊ฐ ๊ตฌํ์ฒด๋ค์ ๊ฐ์์ source repository ์ ๊ตฌ์ฑ๋์๊ณ , ์ฌ์ฉํ CI/CD ํ๋ซํผ์ GCP๋ฅผ ์ฌ์ฉํ์์ผ๋ฉฐ, pipeline build script ๋ ๊ฐ ํ๋ก์ ํธ ํด๋ ์ดํ์ cloudbuild.yml ์ ํฌํจ๋์๋ค.
## ๋๊ธฐ์ ํธ์ถ / ์ํท ๋ธ๋ ์ดํน / ์ฅ์ ๊ฒฉ๋ฆฌ
* ์ํท ๋ธ๋ ์ดํน ํ๋ ์์ํฌ์ ์ ํ: Spring FeignClient + Hystrix ์ต์
์ ์ฌ์ฉํ์ฌ ๊ตฌํํจ
์๋๋ฆฌ์ค๋ ๋จ๋ง์ฑ(app)-->๊ฒฐ์ (pay) ์์ ์ฐ๊ฒฐ์ RESTful Request/Response ๋ก ์ฐ๋ํ์ฌ ๊ตฌํ์ด ๋์ด์๊ณ , ๊ฒฐ์ ์์ฒญ์ด ๊ณผ๋ํ ๊ฒฝ์ฐ CB ๋ฅผ ํตํ์ฌ ์ฅ์ ๊ฒฉ๋ฆฌ.
- Hystrix ๋ฅผ ์ค์ : ์์ฒญ์ฒ๋ฆฌ ์ฐ๋ ๋์์ ์ฒ๋ฆฌ์๊ฐ์ด 610 ๋ฐ๋ฆฌ๊ฐ ๋์ด์๊ธฐ ์์ํ์ฌ ์ด๋์ ๋ ์ ์ง๋๋ฉด CB ํ๋ก๊ฐ ๋ซํ๋๋ก (์์ฒญ์ ๋น ๋ฅด๊ฒ ์คํจ์ฒ๋ฆฌ, ์ฐจ๋จ) ์ค์
feign: hystrix: enabled: true
hystrix: command:
default:
execution.isolation.thread.timeoutInMilliseconds: 610
- ํผํธ์ถ ์๋น์ค(๊ฒฐ์ :pay) ์ ์์ ๋ถํ ์ฒ๋ฆฌ - 400 ๋ฐ๋ฆฌ์์ ์ฆ๊ฐ 220 ๋ฐ๋ฆฌ ์ ๋ ์๋ค๊ฐ๋ค ํ๊ฒ
@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
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
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
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
- ์ด์์์คํ
์ ์ฃฝ์ง ์๊ณ ์ง์์ ์ผ๋ก CB ์ ์ํ์ฌ ์ ์ ํ ํ๋ก๊ฐ ์ด๋ฆผ๊ณผ ๋ซํ์ด ๋ฒ์ด์ง๋ฉด์ ์์์ ๋ณดํธํ๊ณ ์์์ ๋ณด์ฌ์ค. ํ์ง๋ง, 63.55% ๊ฐ ์ฑ๊ณตํ์๊ณ , 46%๊ฐ ์คํจํ๋ค๋ ๊ฒ์ ๊ณ ๊ฐ ์ฌ์ฉ์ฑ์ ์์ด ์ข์ง ์๊ธฐ ๋๋ฌธ์ Retry ์ค์ ๊ณผ ๋์ Scale out (replica์ ์๋์ ์ถ๊ฐ,HPA) ์ ํตํ์ฌ ์์คํ
์ ํ์ฅ ํด์ฃผ๋ ํ์์ฒ๋ฆฌ๊ฐ ํ์.
- Retry ์ ์ค์ (istio)
- Availability ๊ฐ ๋์์ง ๊ฒ์ ํ์ธ (siege)
### ์คํ ์ค์ผ์ผ ์์
์์ CB ๋ ์์คํ
์ ์์ ๋๊ฒ ์ด์ํ ์ ์๊ฒ ํด์คฌ์ง๋ง ์ฌ์ฉ์์ ์์ฒญ์ 100% ๋ฐ์๋ค์ฌ์ฃผ์ง ๋ชปํ๊ธฐ ๋๋ฌธ์ ์ด์ ๋ํ ๋ณด์์ฑ
์ผ๋ก ์๋ํ๋ ํ์ฅ ๊ธฐ๋ฅ์ ์ ์ฉํ๊ณ ์ ํ๋ค.
- ๊ฒฐ์ ์๋น์ค์ ๋ํ replica ๋ฅผ ๋์ ์ผ๋ก ๋๋ ค์ฃผ๋๋ก HPA ๋ฅผ ์ค์ ํ๋ค. ์ค์ ์ CPU ์ฌ์ฉ๋์ด 15ํ๋ก๋ฅผ ๋์ด์๋ฉด replica ๋ฅผ 10๊ฐ๊น์ง ๋๋ ค์ค๋ค:
kubectl autoscale deploy pay --min=1 --max=10 --cpu-percent=15
- CB ์์ ํ๋ ๋ฐฉ์๋๋ก ์ํฌ๋ก๋๋ฅผ 2๋ถ ๋์ ๊ฑธ์ด์ค๋ค.
siege -c100 -t120S -r10 --content-type "application/json" 'http://localhost:8081/orders POST {"item": "chicken"}'
- ์คํ ์ค์ผ์ผ์ด ์ด๋ป๊ฒ ๋๊ณ ์๋์ง ๋ชจ๋ํฐ๋ง์ ๊ฑธ์ด๋๋ค:
kubectl get deploy pay -w
- ์ด๋์ ๋ ์๊ฐ์ด ํ๋ฅธ ํ (์ฝ 30์ด) ์ค์ผ์ผ ์์์ด ๋ฒ์ด์ง๋ ๊ฒ์ ํ์ธํ ์ ์๋ค:
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE pay 1 1 1 1 17s pay 1 2 1 1 45s pay 1 4 1 1 1m :
- siege ์ ๋ก๊ทธ๋ฅผ ๋ณด์๋ ์ ์ฒด์ ์ธ ์ฑ๊ณต๋ฅ ์ด ๋์์ง ๊ฒ์ ํ์ธ ํ ์ ์๋ค.
Transactions: 5078 hits Availability: 92.45 % Elapsed time: 120 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
## ๋ฌด์ ์ง ์ฌ๋ฐฐํฌ
* ๋จผ์ ๋ฌด์ ์ง ์ฌ๋ฐฐํฌ๊ฐ 100% ๋๋ ๊ฒ์ธ์ง ํ์ธํ๊ธฐ ์ํด์ Autoscaler ์ด๋ CB ์ค์ ์ ์ ๊ฑฐํจ
- seige ๋ก ๋ฐฐํฌ์์
์ง์ ์ ์ํฌ๋ก๋๋ฅผ ๋ชจ๋ํฐ๋ง ํจ.
siege -c100 -t120S -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 :
- ์๋ฒ์ ์ผ๋ก์ ๋ฐฐํฌ ์์
kubectl set image ...
- seige ์ ํ๋ฉด์ผ๋ก ๋์ด๊ฐ์ Availability ๊ฐ 100% ๋ฏธ๋ง์ผ๋ก ๋จ์ด์ก๋์ง ํ์ธ
Transactions: 3078 hits Availability: 70.45 % Elapsed time: 120 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
๋ฐฐํฌ๊ธฐ๊ฐ์ค Availability ๊ฐ ํ์ 100%์์ 70% ๋๋ก ๋จ์ด์ง๋ ๊ฒ์ ํ์ธ. ์์ธ์ ์ฟ ๋ฒ๋คํฐ์ค๊ฐ ์ฑ๊ธํ๊ฒ ์๋ก ์ฌ๋ ค์ง ์๋น์ค๋ฅผ READY ์ํ๋ก ์ธ์ํ์ฌ ์๋น์ค ์ ์
์ ์งํํ ๊ฒ์ด๊ธฐ ๋๋ฌธ. ์ด๋ฅผ ๋ง๊ธฐ์ํด Readiness Probe ๋ฅผ ์ค์ ํจ:
kubectl apply -f kubernetes/deployment.yaml
- ๋์ผํ ์๋๋ฆฌ์ค๋ก ์ฌ๋ฐฐํฌ ํ ํ Availability ํ์ธ:
Transactions: 3078 hits Availability: 100 % Elapsed time: 120 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
๋ฐฐํฌ๊ธฐ๊ฐ ๋์ Availability ๊ฐ ๋ณํ์๊ธฐ ๋๋ฌธ์ ๋ฌด์ ์ง ์ฌ๋ฐฐํฌ๊ฐ ์ฑ๊ณตํ ๊ฒ์ผ๋ก ํ์ธ๋จ.
# ์ ๊ท ๊ฐ๋ฐ ์กฐ์ง์ ์ถ๊ฐ
![image](https://user-images.githubusercontent.com/487999/79684133-1d6c4300-826a-11ea-94a2-602e61814ebf.png)
## ๋ง์ผํ
ํ์ ์ถ๊ฐ
- KPI: ์ ๊ท ๊ณ ๊ฐ์ ์ ์
๋ฅ ์ฆ๋์ ๊ธฐ์กด ๊ณ ๊ฐ์ ์ถฉ์ฑ๋ ํฅ์
- ๊ตฌํ๊ณํ ๋ง์ดํฌ๋ก ์๋น์ค: ๊ธฐ์กด customer ๋ง์ดํฌ๋ก ์๋น์ค๋ฅผ ์ธ์ํ๋ฉฐ, ๊ณ ๊ฐ์ ์์ ๋ฐ ๋ง์ง ์ถ์ฒ ์๋น์ค ๋ฑ์ ์ ๊ณตํ ์์
## ์ด๋ฒคํธ ์คํ ๋ฐ
![image](https://user-images.githubusercontent.com/487999/79685356-2b729180-8273-11ea-9361-a434065f2249.png)
## ํฅ์ฌ๊ณ ๋ ์ํคํ
์ฒ ๋ณํ
![image](https://user-images.githubusercontent.com/487999/79685243-1d704100-8272-11ea-8ef6-f4869c509996.png)
## ๊ตฌํ
๊ธฐ์กด์ ๋ง์ดํฌ๋ก ์๋น์ค์ ์์ ์ ๋ฐ์์ํค์ง ์๋๋ก Inbund ์์ฒญ์ REST ๊ฐ ์๋ Event ๋ฅผ Subscribe ํ๋ ๋ฐฉ์์ผ๋ก ๊ตฌํ. ๊ธฐ์กด ๋ง์ดํฌ๋ก ์๋น์ค์ ๋ํ์ฌ ์ํคํ
์ฒ๋ ๊ธฐ์กด ๋ง์ดํฌ๋ก ์๋น์ค๋ค์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ตฌ์กฐ์ ๊ด๊ณ์์ด ์ถ๊ฐ๋จ.
## ์ด์๊ณผ Retirement
Request/Response ๋ฐฉ์์ผ๋ก ๊ตฌํํ์ง ์์๊ธฐ ๋๋ฌธ์ ์๋น์ค๊ฐ ๋์ด์ ๋ถํ์ํด์ ธ๋ Deployment ์์ ์ ๊ฑฐ๋๋ฉด ๊ธฐ์กด ๋ง์ดํฌ๋ก ์๋น์ค์ ์ด๋ค ์ํฅ๋ ์ฃผ์ง ์์.
* [๋น๊ต] ๊ฒฐ์ (pay) ๋ง์ดํฌ๋ก์๋น์ค์ ๊ฒฝ์ฐ API ๋ณํ๋ Retire ์์ app(์ฃผ๋ฌธ) ๋ง์ดํฌ๋ก ์๋น์ค์ ๋ณ๊ฒฝ์ ์ด๋ํจ:
์) API ๋ณํ์
@PostPersist
public void onPostPersist(){
fooddelivery.external.๊ฒฐ์ ์ด๋ ฅ pay = new fooddelivery.external.๊ฒฐ์ ์ด๋ ฅ();
pay.setOrderId(getOrderId());
Application.applicationContext.getBean(fooddelivery.external.๊ฒฐ์ ์ด๋ ฅService.class)
.๊ฒฐ์ (pay);
-->
Application.applicationContext.getBean(fooddelivery.external.๊ฒฐ์ ์ด๋ ฅService.class)
.๊ฒฐ์ 2(pay);
}
์) Retire ์
@PostPersist
public void onPostPersist(){
/**
fooddelivery.external.๊ฒฐ์ ์ด๋ ฅ pay = new fooddelivery.external.๊ฒฐ์ ์ด๋ ฅ();
pay.setOrderId(getOrderId());
Application.applicationContext.getBean(fooddelivery.external.๊ฒฐ์ ์ด๋ ฅService.class)
.๊ฒฐ์ (pay);
**/
}
React-Native-Food-Delivery A food delivery app built with React Native, Pusher Channels, Chatkit, and Beams. It includes the following features:
Food ordering Real-time location tracking One on one chat Push notifications
Prerequisites React Native development environment Node.js Yarn Google Cloud Console account - enable Google Maps. Channels app instance Chatkit app instance Firebase app instance - one for each app (food delivery and driver app). Beams app instance - one for each app. ngrok account
Built with React Native Channels Chatkit Beams React Native Maps
https://www.youtube.com/watch?v=Ja9D0kpksxw&ab_channel=IOHK
IOHK | Cardano whiteboard; overview with Charles Hoskinson
About The food ordering & delivery application designed based on the real-life scenario. Anyone can use this code on their own purpose. If you are interested please make your contribution to the code. https://github.com/Tarikul711/flutter-food-delivery-app-ui โจ Requirements Any Operating System (ie. MacOS X, Linux, Windows) Any IDE with Flutter SDK installed (ie. Android Studio, VSCode, IntelliJ, etc) A little knowledge of Dart and Flutter A brain to think ๐ค๐ค