zoro9483 / Elmnue

research for building a delivery app frontend and backend
1 stars 0 forks source link

repos ideas #1

Open zoro9483 opened 3 years ago

zoro9483 commented 3 years ago

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 ๐Ÿค“๐Ÿค“

zoro9483 commented 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

zoro9483 commented 3 years ago

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


https://github.com/gmershad/FoodDelivery

About Design OO food delivery app with C# & Design Patterns

zoro9483 commented 3 years ago

About Swiggy Clone with React-Native

https://github.com/AndroConsis/Food-Delivery-App

zoro9483 commented 3 years ago

About food delivery App

https://github.com/Ashwinbicholiya/Food-delivery-app

zoro9483 commented 3 years ago

About iOS Food delivery app with custom components and layouts, Built entirely with swiftUI Built With SwfitUI - To build user interfaces across all Apple platforms. Xcode12-beta 2 - Latest beta version. Features On boarding page with custom animation Lazy Grid and Matched Geometry Effect Hero image with Parallax Scrolling SwiftUI Paged Scrolling with PageTabViewStyle TO DO Checkout view. Profile view.

https://github.com/AmeddahAchraf/Food-Delivery-SwiftUI

zoro9483 commented 3 years ago

About Its an Android app for delivery food. Client can order by website or this app. And a notification will be sent to food delivery boy who will be the 12 km radius of client place with order details. If he accept the order, another notification will be sent to client with delivery boy details. And if he reject the order then app will search anotherโ€ฆ rider and will send notification to another delivery boy who are in 12 km radius of client place. actually app makes a list of rider who are available within 12 km distance radius . If no delivery boy is found then client will get another notification that no delivery boy is found. This radius of distance is dynamic as it can be changed from cms.

https://github.com/tazimete/android-app-food-delivery-system

zoro9483 commented 3 years ago

์˜ˆ์ œ - ์Œ์‹๋ฐฐ๋‹ฌ ๋ณธ ์˜ˆ์ œ๋Š” MSA/DDD/Event Storming/EDA ๋ฅผ ํฌ๊ด„ํ•˜๋Š” ๋ถ„์„/์„ค๊ณ„/๊ตฌํ˜„/์šด์˜ ์ „๋‹จ๊ณ„๋ฅผ ์ปค๋ฒ„ํ•˜๋„๋ก ๊ตฌ์„ฑํ•œ ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค. ์ด๋Š” ํด๋ผ์šฐ๋“œ ๋„ค์ดํ‹ฐ๋ธŒ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๊ฐœ๋ฐœ์— ์š”๊ตฌ๋˜๋Š” ์ฒดํฌํฌ์ธํŠธ๋“ค์„ ํ†ต๊ณผํ•˜๊ธฐ ์œ„ํ•œ ์˜ˆ์‹œ ๋‹ต์•ˆ์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

์ฒดํฌํฌ์ธํŠธ : https://workflowy.com/s/assessment-check-po/T5YrzcMewfo4J6LW

https://1sung.tistory.com/106 https://workflowy.com/s/assessment-check-po/T5YrzcMewfo4J6LW

https://github.com/msa-ez/example-food-delivery

zoro9483 commented 3 years ago

image

์˜ˆ์ œ - ์Œ์‹๋ฐฐ๋‹ฌ

๋ณธ ์˜ˆ์ œ๋Š” MSA/DDD/Event Storming/EDA ๋ฅผ ํฌ๊ด„ํ•˜๋Š” ๋ถ„์„/์„ค๊ณ„/๊ตฌํ˜„/์šด์˜ ์ „๋‹จ๊ณ„๋ฅผ ์ปค๋ฒ„ํ•˜๋„๋ก ๊ตฌ์„ฑํ•œ ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค. ์ด๋Š” ํด๋ผ์šฐ๋“œ ๋„ค์ดํ‹ฐ๋ธŒ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๊ฐœ๋ฐœ์— ์š”๊ตฌ๋˜๋Š” ์ฒดํฌํฌ์ธํŠธ๋“ค์„ ํ†ต๊ณผํ•˜๊ธฐ ์œ„ํ•œ ์˜ˆ์‹œ ๋‹ต์•ˆ์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

Table of contents

์„œ๋น„์Šค ์‹œ๋‚˜๋ฆฌ์˜ค

๋ฐฐ๋‹ฌ์˜ ๋ฏผ์กฑ ์ปค๋ฒ„ํ•˜๊ธฐ - https://1sung.tistory.com/106

๊ธฐ๋Šฅ์  ์š”๊ตฌ์‚ฌํ•ญ

  1. ๊ณ ๊ฐ์ด ๋ฉ”๋‰ด๋ฅผ ์„ ํƒํ•˜์—ฌ ์ฃผ๋ฌธํ•œ๋‹ค
  2. ๊ณ ๊ฐ์ด ๊ฒฐ์ œํ•œ๋‹ค
  3. ์ฃผ๋ฌธ์ด ๋˜๋ฉด ์ฃผ๋ฌธ ๋‚ด์—ญ์ด ์ž…์ ์ƒ์ ์ฃผ์ธ์—๊ฒŒ ์ „๋‹ฌ๋œ๋‹ค
  4. ์ƒ์ ์ฃผ์ธ์ด ํ™•์ธํ•˜์—ฌ ์š”๋ฆฌํ•ด์„œ ๋ฐฐ๋‹ฌ ์ถœ๋ฐœํ•œ๋‹ค
  5. ๊ณ ๊ฐ์ด ์ฃผ๋ฌธ์„ ์ทจ์†Œํ•  ์ˆ˜ ์žˆ๋‹ค
  6. ์ฃผ๋ฌธ์ด ์ทจ์†Œ๋˜๋ฉด ๋ฐฐ๋‹ฌ์ด ์ทจ์†Œ๋œ๋‹ค
  7. ๊ณ ๊ฐ์ด ์ฃผ๋ฌธ์ƒํƒœ๋ฅผ ์ค‘๊ฐ„์ค‘๊ฐ„ ์กฐํšŒํ•œ๋‹ค
  8. ์ฃผ๋ฌธ์ƒํƒœ๊ฐ€ ๋ฐ”๋€” ๋•Œ ๋งˆ๋‹ค ์นดํ†ก์œผ๋กœ ์•Œ๋ฆผ์„ ๋ณด๋‚ธ๋‹ค

๋น„๊ธฐ๋Šฅ์  ์š”๊ตฌ์‚ฌํ•ญ

  1. ํŠธ๋žœ์žญ์…˜
    1. ๊ฒฐ์ œ๊ฐ€ ๋˜์ง€ ์•Š์€ ์ฃผ๋ฌธ๊ฑด์€ ์•„์˜ˆ ๊ฑฐ๋ž˜๊ฐ€ ์„ฑ๋ฆฝ๋˜์ง€ ์•Š์•„์•ผ ํ•œ๋‹ค Sync ํ˜ธ์ถœ
  2. ์žฅ์• ๊ฒฉ๋ฆฌ
    1. ์ƒ์ ๊ด€๋ฆฌ ๊ธฐ๋Šฅ์ด ์ˆ˜ํ–‰๋˜์ง€ ์•Š๋”๋ผ๋„ ์ฃผ๋ฌธ์€ 365์ผ 24์‹œ๊ฐ„ ๋ฐ›์„ ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค Async (event-driven), Eventual Consistency
    2. ๊ฒฐ์ œ์‹œ์Šคํ…œ์ด ๊ณผ์ค‘๋˜๋ฉด ์‚ฌ์šฉ์ž๋ฅผ ์ž ์‹œ๋™์•ˆ ๋ฐ›์ง€ ์•Š๊ณ  ๊ฒฐ์ œ๋ฅผ ์ž ์‹œํ›„์— ํ•˜๋„๋ก ์œ ๋„ํ•œ๋‹ค Circuit breaker, fallback
  3. ์„ฑ๋Šฅ
    1. ๊ณ ๊ฐ์ด ์ž์ฃผ ์ƒ์ ๊ด€๋ฆฌ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฐ๋‹ฌ์ƒํƒœ๋ฅผ ์ฃผ๋ฌธ์‹œ์Šคํ…œ(ํ”„๋ก ํŠธ์—”๋“œ)์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค CQRS
    2. ๋ฐฐ๋‹ฌ์ƒํƒœ๊ฐ€ ๋ฐ”๋€”๋•Œ๋งˆ๋‹ค ์นดํ†ก ๋“ฑ์œผ๋กœ ์•Œ๋ฆผ์„ ์ค„ ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค Event driven

์ฒดํฌํฌ์ธํŠธ

๋ถ„์„/์„ค๊ณ„

AS-IS ์กฐ์ง (Horizontally-Aligned)

image

TO-BE ์กฐ์ง (Vertically-Aligned)

image

Event Storming ๊ฒฐ๊ณผ

์ด๋ฒคํŠธ ๋„์ถœ

image

๋ถ€์ ๊ฒฉ ์ด๋ฒคํŠธ ํƒˆ๋ฝ

image

- ๊ณผ์ •์ค‘ ๋„์ถœ๋œ ์ž˜๋ชป๋œ ๋„๋ฉ”์ธ ์ด๋ฒคํŠธ๋“ค์„ ๊ฑธ๋Ÿฌ๋‚ด๋Š” ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•จ
    - ์ฃผ๋ฌธ์‹œ>๋ฉ”๋‰ด์นดํ…Œ๊ณ ๋ฆฌ์„ ํƒ๋จ, ์ฃผ๋ฌธ์‹œ>๋ฉ”๋‰ด๊ฒ€์ƒ‰๋จ :  UI ์˜ ์ด๋ฒคํŠธ์ด์ง€, ์—…๋ฌด์ ์ธ ์˜๋ฏธ์˜ ์ด๋ฒคํŠธ๊ฐ€ ์•„๋‹ˆ๋ผ์„œ ์ œ์™ธ

์•กํ„ฐ, ์ปค๋งจ๋“œ ๋ถ€์ฐฉํ•˜์—ฌ ์ฝ๊ธฐ ์ข‹๊ฒŒ

image

์–ด๊ทธ๋ฆฌ๊ฒŒ์ž‡์œผ๋กœ ๋ฌถ๊ธฐ

image

- app์˜ Order, store ์˜ ์ฃผ๋ฌธ์ฒ˜๋ฆฌ, ๊ฒฐ์ œ์˜ ๊ฒฐ์ œ์ด๋ ฅ์€ ๊ทธ์™€ ์—ฐ๊ฒฐ๋œ command ์™€ event ๋“ค์— ์˜ํ•˜์—ฌ ํŠธ๋žœ์žญ์…˜์ด ์œ ์ง€๋˜์–ด์•ผ ํ•˜๋Š” ๋‹จ์œ„๋กœ ๊ทธ๋“ค ๋ผ๋ฆฌ ๋ฌถ์–ด์คŒ

๋ฐ”์šด๋””๋“œ ์ปจํ…์ŠคํŠธ๋กœ ๋ฌถ๊ธฐ

image

- ๋„๋ฉ”์ธ ์„œ์—ด ๋ถ„๋ฆฌ 
    - 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 ์™ธ๋ถ€ ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๊ฒฝ์Ÿ๋ ฅ์ด ๋†’์Œ (ํ•‘ํฌ์ƒ‰์œผ๋กœ ์ดํ›„ ์ „ํ™˜ํ•  ์˜ˆ์ •)

ํด๋ฆฌ์‹œ ๋ถ€์ฐฉ (๊ด„ํ˜ธ๋Š” ์ˆ˜ํ–‰์ฃผ์ฒด, ํด๋ฆฌ์‹œ ๋ถ€์ฐฉ์„ ๋‘˜์งธ๋‹จ๊ณ„์—์„œ ํ•ด๋†”๋„ ์ƒ๊ด€ ์—†์Œ. ์ „์ฒด ์—ฐ๊ณ„๊ฐ€ ์ดˆ๊ธฐ์— ๋“œ๋Ÿฌ๋‚จ)

image

ํด๋ฆฌ์‹œ์˜ ์ด๋™๊ณผ ์ปจํ…์ŠคํŠธ ๋งคํ•‘ (์ ์„ ์€ Pub/Sub, ์‹ค์„ ์€ Req/Resp)

image

์™„์„ฑ๋œ 1์ฐจ ๋ชจํ˜•

image

- View Model ์ถ”๊ฐ€

1์ฐจ ์™„์„ฑ๋ณธ์— ๋Œ€ํ•œ ๊ธฐ๋Šฅ์ /๋น„๊ธฐ๋Šฅ์  ์š”๊ตฌ์‚ฌํ•ญ์„ ์ปค๋ฒ„ํ•˜๋Š”์ง€ ๊ฒ€์ฆ

image

- ๊ณ ๊ฐ์ด ๋ฉ”๋‰ด๋ฅผ ์„ ํƒํ•˜์—ฌ ์ฃผ๋ฌธํ•œ๋‹ค (ok)
- ๊ณ ๊ฐ์ด ๊ฒฐ์ œํ•œ๋‹ค (ok)
- ์ฃผ๋ฌธ์ด ๋˜๋ฉด ์ฃผ๋ฌธ ๋‚ด์—ญ์ด ์ž…์ ์ƒ์ ์ฃผ์ธ์—๊ฒŒ ์ „๋‹ฌ๋œ๋‹ค (ok)
- ์ƒ์ ์ฃผ์ธ์ด ํ™•์ธํ•˜์—ฌ ์š”๋ฆฌํ•ด์„œ ๋ฐฐ๋‹ฌ ์ถœ๋ฐœํ•œ๋‹ค (ok)

image

๋ชจ๋ธ ์ˆ˜์ •

image

- ์ˆ˜์ •๋œ ๋ชจ๋ธ์€ ๋ชจ๋“  ์š”๊ตฌ์‚ฌํ•ญ์„ ์ปค๋ฒ„ํ•จ.

๋น„๊ธฐ๋Šฅ ์š”๊ตฌ์‚ฌํ•ญ์— ๋Œ€ํ•œ ๊ฒ€์ฆ

image

- ๋งˆ์ดํฌ๋กœ ์„œ๋น„์Šค๋ฅผ ๋„˜๋‚˜๋“œ๋Š” ์‹œ๋‚˜๋ฆฌ์˜ค์— ๋Œ€ํ•œ ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ
    - ๊ณ ๊ฐ ์ฃผ๋ฌธ์‹œ ๊ฒฐ์ œ์ฒ˜๋ฆฌ:  ๊ฒฐ์ œ๊ฐ€ ์™„๋ฃŒ๋˜์ง€ ์•Š์€ ์ฃผ๋ฌธ์€ ์ ˆ๋Œ€ ๋ฐ›์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒฝ์˜์ž์˜ ์˜ค๋žœ ์‹ ๋…(?) ์— ๋”ฐ๋ผ, ACID ํŠธ๋žœ์žญ์…˜ ์ ์šฉ. ์ฃผ๋ฌธ์™€๋ฃŒ์‹œ ๊ฒฐ์ œ์ฒ˜๋ฆฌ์— ๋Œ€ํ•ด์„œ๋Š” Request-Response ๋ฐฉ์‹ ์ฒ˜๋ฆฌ
    - ๊ฒฐ์ œ ์™„๋ฃŒ์‹œ ์ ์ฃผ์—ฐ๊ฒฐ ๋ฐ ๋ฐฐ์†ก์ฒ˜๋ฆฌ:  App(front) ์—์„œ Store ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค๋กœ ์ฃผ๋ฌธ์š”์ฒญ์ด ์ „๋‹ฌ๋˜๋Š” ๊ณผ์ •์— ์žˆ์–ด์„œ Store ๋งˆ์ดํฌ๋กœ ์„œ๋น„์Šค๊ฐ€ ๋ณ„๋„์˜ ๋ฐฐํฌ์ฃผ๊ธฐ๋ฅผ ๊ฐ€์ง€๊ธฐ ๋•Œ๋ฌธ์— Eventual Consistency ๋ฐฉ์‹์œผ๋กœ ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌํ•จ.
    - ๋‚˜๋จธ์ง€ ๋ชจ๋“  inter-microservice ํŠธ๋žœ์žญ์…˜: ์ฃผ๋ฌธ์ƒํƒœ, ๋ฐฐ๋‹ฌ์ƒํƒœ ๋“ฑ ๋ชจ๋“  ์ด๋ฒคํŠธ์— ๋Œ€ํ•ด ์นดํ†ก์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋“ฑ, ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ์˜ ์‹œ์ ์ด ํฌ๋ฆฌํ‹ฐ์ปฌํ•˜์ง€ ์•Š์€ ๋ชจ๋“  ๊ฒฝ์šฐ๊ฐ€ ๋Œ€๋ถ€๋ถ„์ด๋ผ ํŒ๋‹จ, Eventual Consistency ๋ฅผ ๊ธฐ๋ณธ์œผ๋กœ ์ฑ„ํƒํ•จ.

ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ฒ˜ ๋‹ค์ด์–ด๊ทธ๋žจ ๋„์ถœ

image

- 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 

DDD ์˜ ์ ์šฉ

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.๊ธˆ์•ก = ๊ธˆ์•ก;
    }

}

import org.springframework.data.repository.PagingAndSortingRepository;

public interface ๊ฒฐ์ œ์ด๋ ฅRepository extends PagingAndSortingRepository<๊ฒฐ์ œ์ด๋ ฅ, Long>{ }

- ์ ์šฉ ํ›„ REST API ์˜ ํ…Œ์ŠคํŠธ

app ์„œ๋น„์Šค์˜ ์ฃผ๋ฌธ์ฒ˜๋ฆฌ

http localhost:8081/orders item="ํ†ต๋‹ญ"

store ์„œ๋น„์Šค์˜ ๋ฐฐ๋‹ฌ์ฒ˜๋ฆฌ

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 ์— ๋ถ€์ฐฉ์‹œ์ผฐ๋‹ค

Order.java

package fooddelivery;

@Document public class Order {

private String id; // mongo db ์ ์šฉ์‹œ์—” id ๋Š” ๊ณ ์ •๊ฐ’์œผ๋กœ key๊ฐ€ ์ž๋™ ๋ฐœ๊ธ‰๋˜๋Š” ํ•„๋“œ๊ธฐ ๋•Œ๋ฌธ์— @Id ๋‚˜ @GeneratedValue ๋ฅผ ์ฃผ์ง€ ์•Š์•„๋„ ๋œ๋‹ค.
private String item;
private Integer ์ˆ˜๋Ÿ‰;

}

์ฃผ๋ฌธRepository.java

package fooddelivery;

public interface ์ฃผ๋ฌธRepository extends JpaRepository<Order, UUID>{ }

application.yml

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

To consume latest messages and auto-commit offsets

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) ๋ฅผ ๊ตฌํ˜„ 

(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);
}

- ๋™๊ธฐ์‹ ํ˜ธ์ถœ์—์„œ๋Š” ํ˜ธ์ถœ ์‹œ๊ฐ„์— ๋”ฐ๋ฅธ ํƒ€์ž„ ์ปคํ”Œ๋ง์ด ๋ฐœ์ƒํ•˜๋ฉฐ, ๊ฒฐ์ œ ์‹œ์Šคํ…œ์ด ์žฅ์• ๊ฐ€ ๋‚˜๋ฉด ์ฃผ๋ฌธ๋„ ๋ชป๋ฐ›๋Š”๋‹ค๋Š” ๊ฒƒ์„ ํ™•์ธ:

๊ฒฐ์ œ (pay) ์„œ๋น„์Šค๋ฅผ ์ž ์‹œ ๋‚ด๋ ค๋†“์Œ (ctrl+c)

์ฃผ๋ฌธ์ฒ˜๋ฆฌ

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(์ฃผ๋ฌธ);
  }

}


์ƒ์  ์‹œ์Šคํ…œ์€ ์ฃผ๋ฌธ/๊ฒฐ์ œ์™€ ์™„์ „ํžˆ ๋ถ„๋ฆฌ๋˜์–ด์žˆ์œผ๋ฉฐ, ์ด๋ฒคํŠธ ์ˆ˜์‹ ์— ๋”ฐ๋ผ ์ฒ˜๋ฆฌ๋˜๊ธฐ ๋•Œ๋ฌธ์—, ์ƒ์ ์‹œ์Šคํ…œ์ด ์œ ์ง€๋ณด์ˆ˜๋กœ ์ธํ•ด ์ž ์‹œ ๋‚ด๋ ค๊ฐ„ ์ƒํƒœ๋ผ๋„ ์ฃผ๋ฌธ์„ ๋ฐ›๋Š”๋ฐ ๋ฌธ์ œ๊ฐ€ ์—†๋‹ค:

์ƒ์  ์„œ๋น„์Šค (store) ๋ฅผ ์ž ์‹œ ๋‚ด๋ ค๋†“์Œ (ctrl+c)

์ฃผ๋ฌธ์ฒ˜๋ฆฌ

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 ํšŒ๋กœ๊ฐ€ ๋‹ซํžˆ๋„๋ก (์š”์ฒญ์„ ๋น ๋ฅด๊ฒŒ ์‹คํŒจ์ฒ˜๋ฆฌ, ์ฐจ๋‹จ) ์„ค์ •

application.yml

feign: hystrix: enabled: true

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

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 ๋ฅผ ์„ค์ •ํ•จ:

deployment.yaml ์˜ 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 ๋ณ€ํ™”์‹œ

Order.java (Entity)

@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 ์‹œ

Order.java (Entity)

@PostPersist
public void onPostPersist(){

    /**
    fooddelivery.external.๊ฒฐ์ œ์ด๋ ฅ pay = new fooddelivery.external.๊ฒฐ์ œ์ด๋ ฅ();
    pay.setOrderId(getOrderId());

    Application.applicationContext.getBean(fooddelivery.external.๊ฒฐ์ œ์ด๋ ฅService.class)
            .๊ฒฐ์ œ(pay);

    **/
}
zoro9483 commented 3 years ago

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://github.com/anchetaWern/React-Native-Food-Delivery

https://github.com/search?p=2&q=food+delivery&type=Repositories

zoro9483 commented 3 years ago

https://www.youtube.com/watch?v=Ja9D0kpksxw&ab_channel=IOHK

IOHK | Cardano whiteboard; overview with Charles Hoskinson