BackEnd/Spring Boot

[Spring] ๋ถ„์‚ฐ ์‹œ์Šคํ…œ์—์„œ ํšจ๊ณผ์ ์ธ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ ๋ฐฉ๋ฒ• (1) - RDB

ddonghyeo 2023. 11. 4. 16:39

์ตœ๊ทผ ๋งŽ์€ ๊ธฐ์—…๋“ค์ด ๋ถ„์‚ฐ ์‹œ์Šคํ…œ์œผ๋กœ ์„œ๋ฒ„๋ฅผ ๊ตฌ์ถ•ํ•˜๊ณ  ์žˆ๊ณ , ๋‚˜ ๋˜ํ•œ ๋ถ„์‚ฐ ์‹œ์Šคํ…œ์— ๊ด€์‹ฌ์ด ๋งŽ๋‹ค.

 

์ด๋ฒˆ์—” ๋ถ„์‚ฐ ์‹œ์Šคํ…œ ์†์—์„œ, ๊ฐ ์ปดํฌ๋„ŒํŠธ๋“ค ์‚ฌ์ด์— ์–ด๋–ป๊ฒŒ ์ •๋ณด๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์ด ์ข‹์„์ง€ ๊ณต๋ถ€ํ•ด๋ณด๊ฒ ๋‹ค.

 

0.  ๋ถ„์‚ฐ์‹œ์Šคํ…œ์ด๋ž€?

๋ถ„์‚ฐ ์‹œ์Šคํ…œ์€ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ปดํ“จํ„ฐ ๋ฆฌ์†Œ์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‘ ๊ฐœ ์ด์ƒ์˜ ์ปดํฌ๋„ŒํŠธ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ๋Š” ์‹œ์Šคํ…œ์ด๋‹ค.

 

๊ฐ ์ปดํฌ๋„ŒํŠธ๋“ค์€ ํ•˜๋‚˜์˜ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.

 

ํฌ๊ฒŒ ์„ธ ๊ฐ€์ง€ ์œ ํ˜•์ด ์žˆ์„ ์ˆ˜ ์žˆ๋‹ค.

  • ์—”ํ„ฐํ”„๋ผ์ด์ฆˆ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ Enterprise-application
  • ๋งˆ์ดํฌ๋กœ ์„œ๋น„์Šค ์•„ํ‚คํ…์ณ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ MSA(Micro Service Architecture)
  • ๋ชจ๋†€๋ฆฌ์‹ ์•„ํ‚คํ…์ณ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ + ๊ฒ€์ƒ‰์—”์ง„  Monolithic Architecture + Search Engine

 

1. ๋ฐ์ดํ„ฐ ์ „๋‹ฌ ๋ฐฉ๋ฒ•

๊ทธ๋ ‡๋‹ค๋ฉด ์ด ๋ถ„์‚ฐ ์‹œ์Šคํ…œ์—์„œ, ๊ฐ ์ปดํฌ๋„ŒํŠธ๋“ค์˜ ๋ฐ์ดํ„ฐ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์–ด๋–ค ๋ฐฉ์‹์ด ์žˆ์„๊นŒ.

 

 

1-1. Remote API

์ตœ๊ทผ ๊ฐœ๋ฐœํ•˜๋Š” ๋ฐฉ๋ฒ• ์ค‘ ๊ฐ€์žฅ ์นœ์ˆ™ํ•œ ๋ฐฉ๋ฒ•์ด๋‹ค.

 

์„œ๋ฒ„-ํด๋ผ์ด์–ธํŠธ ๊ตฌ์กฐ๋กœ ์ด๋ฃจ์–ด์ ธ ์žˆ๊ณ , ์„œ๋ฒ„๋Š” CRUD API๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

 

 

1-2. Message Queue

Publisher-Consumer๊ตฌ์กฐ๋กœ ์ด๋ฃจ์–ด์ ธ ์žˆ๋‹ค.

 

Publisher๊ฐ€ Message Queue์— ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†กํ•˜๊ณ , Consumer๊ฐ€ ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค.

 

๋ณดํ†ต ๋ฐฐ์น˜ ์ž‘์—…, ๋น„๋™๊ธฐ ์ž‘์—…์—์„œ ์ฃผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.

 

์ตœ๊ทผ ๊ณต๋ถ€ํ–ˆ๋˜ kafka๊ฐ€ ์ด์— ํ•ด๋‹นํ•œ๋‹ค.

 

 

1-3. ํšจ์œจ์ ์œผ๋กœ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ๋ฒ•

๋ถ„์‚ฐ ์‹œ์Šคํ…œ์˜ ๊ฐ ์ปดํฌ๋„ŒํŠธ๋“ค์€ ๋„คํŠธ์›Œํฌ๋กœ ์—ฐ๊ฒฐ๋˜์–ด์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ์•„๋ž˜ ์ƒํ™ฉ๋“ค์„ ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค.

  • ํŒจํ‚ท ์†์‹ค Packet Loss
  • ๋„คํŠธ์›Œํฌ ์ง€์—ฐ Latency
  • ๋„คํŠธ์›Œํฌ ๋‹ค์šด Network Down

์ค‘์š”ํ•œ ์‹œ์Šคํ…œ์ผ ์ˆ˜๋ก, ํ•ญ์ƒ ๋ฐ์ดํ„ฐ๋‚˜ ์„œ๋ฒ„์— ์žฅ์• ๊ฐ€ ์ƒ๊ฒผ์„ ๋•Œ๋ฅผ ๋Œ€๋น„ํ•ด์•ผ ํ•œ๋‹ค.

 


๋ฐ์ดํ„ฐ ์ „๋‹ฌ ๋ฐฉ๋ฒ•๋ก 

1. At-most-once Delivery (์ตœ๋Œ€ ํ•œ ๋ฒˆ ์ „๋‹ฌ)

 

์‹ ๋ขฐ์„ฑ์ด ๊ฐ€์žฅ ๋‚ฎ์€ ์ „๋‹ฌ ๋ฐฉ๋ฒ•์ด๋‹ค.


Producer๋Š” ์ตœ๋Œ€ ํ•œ ๋ฒˆ๋งŒ ๋ฉ”์„ธ์ง€๋ฅผ ์ „๋‹ฌํ•˜๊ณ , ๋ฉ”์„ธ์ง€๋ฅผ ๋ฐ›์ง€ ๋ชปํ–ˆ๋Š”์ง€ ํ™•์ธํ•˜์ง€ ์•Š๋Š”๋‹ค.

 

๊ฐ„๋‹จํ•œ ๊ตฌ์กฐ๋กœ ์ด๋ฃจ์–ด์ ธ ์žˆ์–ด ๋Œ€๊ทœ๋ชจ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์‰ฝ์ง€๋งŒ ๋ฉ”์„ธ์ง€ ์œ ์‹ค์„ ์œ ๋ฐœํ•  ์ˆ˜ ์žˆ๋‹ค.

 

2. At-least-once Delivery (์ตœ์†Œ ํ•œ ๋ฒˆ ์ „๋‹ฌ)

 

Producer๋Š” ์ตœ์†Œ ํ•œ ๋ฒˆ ๋ฉ”์„ธ์ง€๋ฅผ ์ „๋‹ฌํ•˜๊ณ , Consumer๋Š” ์ตœ์†Œ ํ•œ ๋ฒˆ ๋ฉ”์„ธ์ง€๋ฅผ ์ˆ˜์‹ ํ•œ๋‹ค.

 

๋„คํŠธ์›Œํฌ ํŒจํ‚ท์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ ์ฒ˜๋Ÿผ, ACK(Acknowledgement)๋ฅผ ์ด์šฉํ•˜์—ฌ Consumer๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์‹ ํ–ˆ๋‹ค๋Š” ์ •๋ณด๋ฅผ ๊ณต์œ ํ•œ๋‹ค.

 

3. Exactly-once Delivery (์ •ํ™•ํžˆ ํ•œ ๋ฒˆ ์ „๋‹ฌ)

 

๊ฐ€์žฅ ์ด์ƒ์ ์ธ ํ™˜๊ฒฝ์œผ๋กœ, ๋ฉ”์„ธ์ง€๋ฅผ ์ •ํ™•ํ•˜๊ฒŒ ํ•œ ๋ฒˆ๋งŒ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

 

๋ˆ„๋ฝ๊ณผ ์ค‘๋ณต์ด ์—†๋Š” ๋งŒํผ, ์‹œ์Šคํ…œ์˜ ๊ฐœ๋ฐœ ๋‚œ์ด๋„๊ฐ€ ๋†’๋‹ค.

 

Producer, Message Queue, Consumer ๋ชจ๋“  ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•ด์•ผ ํ•œ๋‹ค.

 


 

ํ•ด๋‹น ๋ฐฉ๋ฒ• ์ค‘์—์„œ, ๋ฐ์ดํ„ฐ์˜ ์ค‘์š”๋„์— ๋”ฐ๋ผ ์–ด๋–ค ๋ฐฉ์‹์„ ์„ ํƒํ•˜๋Š”์ง€์— ๋”ฐ๋ผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์‹ ๋ขฐ์„ฑ์ด ์ฆ๊ฐ€ํ•œ๋‹ค.

 

 

 

 

 

 

 

2. RDB๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ „๋‹ฌ ๋ฐฉ๋ฒ•

 

2-1. ์„œ๋น„์Šค๋ณ„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํŒจํ„ด (Database per Service Pattern)

 

https://medium.com/design-microservices-architecture-with-patterns/the-database-per-service-pattern-9d511b882425


MSA๊ตฌ์กฐ์—์„œ ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ํŒจํ„ด์ด๋‹ค.

 

๊ฐ ์„œ๋น„์Šค๋“ค์ด ๊ฐ ๋…๋ฆฝ๋œ DB๋ฅผ ๊ฐ–๊ณ ์žˆ๊ณ , ์„œ๋น„์Šค๋ณ„๋กœ DBํŠธ๋žœ์žญ์…˜์ด ๋ฐœ์ƒํ•œ๋‹ค.

 

 

์—ฌ๊ธฐ์„œ, ๋ฌธ์ œ์ ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ์•„๋ž˜ ์˜ˆ์‹œ๋ฅผ ๋ณด์ž.

 

์•„๋ž˜ ์ฝ”๋“œ๋Š” ํŠธ๋žœ์žญ์…˜์ด ๋ฐœ์ƒํ•˜๋Š” ์ฝ”๋“œ์˜ ์˜ˆ์ด๋‹ค.

@Service
public class CreateTaskService implments CreateTaskUserCase {

    @Transactional
    public CreateTaskResponse createTask(CreateTaksCommand createTaksCommand) {
    
    	Task task = createTaskCommand.toTask();
        taskRepository.save(task); // Save task Entity
        eventHandler.propagate(CreateTaskEvent.of(task)); // REST-API (To Another Component)
        return CreateTaskResponse.of(task);
    }
}

 

Task์˜ Entity๋ฅผ DB์— ์ €์žฅํ•˜๊ณ , MSA๊ตฌ์กฐ ๋‚ด์— ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์—๊ฒŒ REST API ํ˜ธ์ถœ์„ ํ•˜๋Š” ๊ตฌ์กฐ์ด๋‹ค.

 

Spring์˜ Transactional ์–ด๋…ธํ…Œ์ด์…˜์€ AOP๋ฅผ ์ƒ์„ฑํ•˜์—ฌ Proxy๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

์ฆ‰, ์•„๋ž˜ ์„ธ ๊ฐ€์ง€ ๋‹จ๊ณ„๋กœ ์‹คํ–‰์ด ๋œ๋‹ค.

 

๋ฐ์ดํ„ฐ ์ €์žฅ → ์ด๋ฒคํŠธ ์ „๋‹ฌ → ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹ (ํ˜น์€ Rollback)

 

๋งŒ์•ฝ, DBํŠธ๋žœ์žญ์…˜ ๊ณผ์ • ์ค‘์— Exception์ด ๋ฐœ์ƒํ•˜์—ฌ DB๊ฐ€ ๋กค๋ฐฑ ๋๋‹ค๋ฉด,

DB์— ๋ฐ์ดํ„ฐ๊ฐ€ ์—†๋Š”๋ฐ APIํ˜ธ์ถœ์ด ์ด๋ฃจ์–ด์ง€๋Š” ๊ฒƒ์ด๋‹ค.

 

ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

 

 

1.  ํŠธ๋žœ์žญ์…˜ Commit ์ด๋ฒคํŠธ ์‚ฌ์šฉ

  • @TransactionalEventListner + @Retryable

Spring ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์ด๋ฒคํŠธ๋ฅผ ์ด์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

 

@Service
public class EventHandler {

    @Retryable(maxAttempts = 3, backoff = @Backoff(delay = 100L))
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void propagate(CreateTaskEvent event) {
        //์ด๋ฒคํŠธ ๋ฐœ์ƒ ๋กœ์ง
        System.out.println("์ด๋ฒคํŠธ ๋ฐœ์ƒ");
        // restTemplate.execute(...);
        // rabbitTemplate.send(...);
    }
}

 

REST API๋‚˜ Message Queue๋ฅผ ์ด์šฉํ•˜์—ฌ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์—๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๊ฒฝ์šฐ,

 

TransactionalEventListener๋ฅผ propagate์—๊ฒŒ ์ ์šฉ์‹œํ‚จ๋‹ค.

 

 

์–ด๋…ธํ…Œ์ด์…˜ ์˜ต์…˜์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • @TransactionalEventHandler(phase = TransactionPhase.AFTER_COMMIT)
    • deafult๊ฐ’์ด๋‹ค. ํŠธ๋žœ์žญ์…˜์ด commit๋  ๋•Œ ์ด๋ฒคํŠธ๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.
  • @TransactionalEventHandler(phase = TransactionPhase.ROLLBACK)
    • ํŠธ๋žœ์žญ์…˜์ด rollback๋  ๋•Œ ์ด๋ฒคํŠธ๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.
  • @TransactionalEventHandler(phase = TransactionPhase.AFTER_COMPLETION)
    • ํŠธ๋žœ์žญ์…˜์ด completion( commit ๋˜๋Š” rollback)๋  ๋•Œ ์ด๋ฒคํŠธ๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.
  • @TransactionalEventHandler(phase = TransactionPhase.BEFORE_COMMIT)
    • ํŠธ๋žœ์žญ์…˜์ด commit๋˜๊ธฐ ์ „ ์ด๋ฒคํŠธ๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.

๋„ค ์–ด๋…ธํ…Œ์ด์…˜์„ ๋ชจ๋‘ ์‹คํ–‰์‹œํ‚ค๋ฉด, ์ˆœ์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‹คํ–‰๋œ๋‹ค.

 

- ํŠธ๋žœ์žญ์…˜ ์„ฑ๊ณต ์‹œ

"BEFORE_COMMIT" → "AFTER_COMMIT" → "AFTER_COMPLETION"

 

- ํŠธ๋žœ์žญ์…˜ ์‹คํŒจ ์‹œ

"ROLLBACK" → "AFTER_COMPLETION"

 

 

์—ฌ๊ธฐ๊นŒ์ง€ ํ™˜๊ฒฝ์„ ๊ตฌ์ถ•ํ–ˆ๋‹ค๋ฉด, DBํŠธ๋žœ์žญ์…˜ → REST API ์ˆœ์„œ๊ฐ€ ๋ณด์žฅ๋œ๋‹ค.

 

ํ•˜์ง€๋งŒ, DBํŠธ๋žœ์žญ์…˜์„ ์„ฑ๊ณตํ–ˆ์ง€๋งŒ REST API๊ฐ€ ์‹คํŒจํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ์–ด๋…ธํ…Œ์ด์…˜ @Retryable์„ ์‚ฌ์šฉํ•œ๋‹ค.

@Retryable(
    maxAttempts = 3, 
    backoff = @Backoff(delay = 100L)
)

 

์œ„ ์˜ต์…˜์€ ์ตœ๋Œ€ 3๋ฒˆ ๋‹ค์‹œ ์‹œ๋„ํ•˜๊ณ , backoff delay๋ฅผ 100์œผ๋กœ ์„ค์ •ํ•ด๋‘” ๊ฐ’์ด๋‹ค.

 

 

 

 

 

 

 

ํ•˜์ง€๋งŒ ๋งŒ์•ฝ ์žฌ์‹œ๋„๋„ ๋ชจ๋‘ ์‹คํŒจํ•œ๋‹ค๋ฉด, MSA๊ตฌ์กฐ์—์„œ ๋‹ค์Œ ํŒจํ„ด์„ ์‹œ๋„ํ•  ์ˆ˜ ์žˆ๊ฒ ๋‹ค.

 

 

2. Transaction Outbox Pattern & Polling Publisher Pattern

 

- Transaction Outbox Pattern

Transaction Outbox Pattern

 

RDB๋ฅผ Message Queue๋กœ ์ด์šฉํ•˜๋Š” ํŒจํ„ด์ด๋‹ค.

 

์ฆ‰, ํ•œ ํŠธ๋žœ์žญ์…˜์— ์ด๋ฒคํŠธ๋‚˜ ๋ฉ”์„ธ์ง€๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด RDB์— ์ €์žฅํ•œ๋‹ค.

 

RDB์—๋Š” ["์ด๋ฒคํŠธ", "๋ฐ์ดํ„ฐ"]๊ฐ€ ์ €์žฅ๋˜๊ฒ ๋‹ค.

 

์ด ํŒจํ„ด์„ ์ด์šฉํ•œ๋‹ค๋ฉด ์ด๋ฒคํŠธ, ํŠธ๋žœ์žญ์…˜ ๋ชจ๋‘ ์„ฑ๊ณตํ•˜๊ฑฐ๋‚˜ ๋กค๋ฐฑ์„ ํ•˜๊ฒ ๋‹ค.

 

์—ฌ๊ธฐ์„œ Publish๋Š” Polling Publisher ํŒจํ„ด์„ ์ด์šฉํ•œ๋‹ค.

 

 

 

 

- Polling Publisher Pattern

Polling Publisher Pattern

์ด ํŒจํ„ด์€ Publisher๊ฐ€ ์ฃผ๊ธฐ์ ์œผ๋กœ DB ๋ ˆ์ฝ”๋“œ๋ฅผ ์กฐํšŒํ•˜์—ฌ Publishํ•  ๋ฉ”์„ธ์ง€๋ฅผ Pollingํ•˜์—ฌ Publishํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค.

 

์ตœ์ข…์ ์œผ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํŒจํ„ด์ด ์™„์„ฑ๋œ๋‹ค.

 

Transaction Outobx + Polling Publisher

 

1. ๋ฐ์ดํ„ฐ์™€ ์ด๋ฒคํŠธ๊ฐ€ ํ•œ ํŠธ๋žœ์žญ์…˜ ๋‚ด์— ์‹คํ–‰๋œ๋‹ค.

2. ๋ฐ์ดํ„ฐ + ์ด๋ฒคํŠธ๊ฐ€ DB์— ์ €์žฅ๋œ๋‹ค.

3. Polling Publisher๊ฐ€ DB๋ฅผ ์ฃผ๊ธฐ์ ์œผ๋กœ ์กฐํšŒํ•œ๋‹ค.

4. DB์— Publish ํ•  ๋ฉ”์„ธ์ง€๊ฐ€ ์กฐํšŒ๋˜๋ฉด, ๋ฉ”์„ธ์ง€๋ฅผ ์ „๋‹ฌํ•œ๋‹ค.

 

์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์™„์„ฑ๋œ๋‹ค.

 

- Service

@Service
public class CreateTaskService {

    @Transactional
    public CreateTaskResponse createTask(CreateTaskCommand createTaskCommand) {

        Task task = createTaskCommand.toTask();
        taskRepository.save(task); // Save task Entity
        eventRepository.save(CreateTaskEvent.of(task)); // Save Event
        return CreateTaskResponse.of(task);
    }

}

 

 

- Publihser

@Service
public class MessagePublisher {

    @Scheduled(cron = "0/5 * * * * *")
    @Transactional
    public void publish() {

        LocalDateTime now = LocalDateTime.now();
        eventRepository.findByCreatedAtBefore(now, EventStatus.READY)
                .stream()
                .map(event -> restTemplate.execute(event))
                .map(event -> event.done())
                .forEach(eventRepository::save);

    }
}

 

ํ•ด๋‹น Publisher๋Š” ์–ด๋…ธํ…Œ์ด์…˜ @Scheduled๋ฅผ ์ด์šฉํ•ด์„œ 5์ดˆ๋งˆ๋‹ค Publsih๋ฅผ ํ•œ๋‹ค.

 

 

1. Ready ์ƒํƒœ์ธ Event๋ฅผ ๋ชจ๋‘ ์กฐํšŒํ•˜๊ณ , RestTemplate์„ ์ด์šฉํ•˜์—ฌ Event๋ฅผ ๋ฐœํ–‰ํ•œ๋‹ค.

2. ์ •์ƒ์ ์œผ๋กœ ๋ฐœํ–‰๋œ ์ด๋ฒคํŠธ๋Š” Done์œผ๋กœ ๋ฐ”๊พผ๋‹ค.

3. ์ด๋ฒคํŠธ๋ฅผ ๋‹ค์‹œ ์ €์žฅํ•œ๋‹ค.

 

์ด ๋ชจ๋“  ํ–‰๋™์€ ํ•œ ํŠธ๋žœ์žญ์…˜ ๋‚ด์— ์žˆ๊ธฐ ๋•Œ๋ฌธ์— RestTemplate, DB์ €์žฅ ๋“ฑ ์–ด๋–ค ํ–‰๋™์— Exception์ด ์ผ์–ด๋‚˜๋ฉด DB ๋กค๋ฐฑ์ด ์ผ์–ด๋‚˜๊ธฐ ๋•Œ๋ฌธ์— ์•ˆ์ „ํ•˜๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

 

์•„๋ž˜๋Š” DB์— ์ €์žฅ๋  ๋ ˆ์ฝ”๋“œ์˜ ํ…Œ์ด๋ธ”์— ํ•„์š”ํ•  ๋‚ด์šฉ๋“ค์ด๋‹ค.

ํ•„๋“œ๋ช… ๋ฐ์ดํ„ฐ ํƒ€์ž… ์„ค๋ช…
event_id BIGINT ์ด๋ฒคํŠธ ์ˆœ์„œ ๋ณด์žฅ
created_at Datetime ์ด๋ฒคํŠธ ๋ฐœ์ƒ ์‹œ๊ฐ„
status smallint Ready(0) / Done(1)
payload jsonb JSON ํƒ€์ž…์˜ Message payload
... ... ...

 

 

 

 

 

์žฅ์ 

  • REST API ํ™˜๊ฒฝ์—์„œ At-least-once๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋‹จ์ 

  • Polling, Publisher ๊ณผ์ •์—์„œ ์ง€์—ฐ ์ฒ˜๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.
  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ถ€ํ•˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค์— ๋น„๋ก€ํ•œ ์ฒ˜๋ฆฌ์†๋„๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

์ง€์—ฐ ์ฒ˜๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ , DB์„ฑ๋Šฅ์— ๋”ฐ๋ผ ์ฒ˜๋ฆฌ์†๋„๊ฐ€ ๋‹ฌ๋ผ์ง€๊ธฐ ๋•Œ๋ฌธ์— ์‹ค์‹œ๊ฐ„์„ฑ์ด ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋Š” ๊ณ ๋ คํ•ด๋ณผ ํ•„์š”๊ฐ€ ์žˆ๊ฒ ๋‹ค.

 

ํ•ด๋‹น ์ฝ”๋“œ๋Š” ๊นƒํ—ˆ๋ธŒ์— ๊ตฌํ˜„ํ•ด ๋†“์•˜๋‹ค. 

https://github.com/DDonghyeo/EffectiveDataTransfer

๋‚ด์šฉ์ด ๊ธธ์–ด์ ธ์„œ ์ถ”๊ฐ€๋กœ RabbitMQ, Kafka๋Š” ๋‹ค์Œ ํฌ์ŠคํŒ…๋•Œ ๋‹ค๋ค„๋ณด๋„๋ก ํ•˜๊ฒ ๋‹ค.

 

์ฐธ๊ณ  / ์ถœ์ฒ˜
https://medium.com/design-microservices-architecture-with-patterns/the-database-per-service-pattern-9d511b882425
https://www.youtube.com/watch?v=uk5fRLUsBfk&ab_channel=NHNCloud
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/transaction/event/TransactionalEventListener.html
https://microservices.io/patterns/data/transactional-outbox.html
https://microservices.io/patterns/data/polling-publisher.html
https://www.up-2date.com/post/transactional-messaging-polling-publisher
https://medium.com/batc/outbox-polling-reliable-message-broker-publisher-61f46ae65cdd