์ต๊ทผ ๋ง์ ๊ธฐ์ ๋ค์ด ๋ถ์ฐ ์์คํ ์ผ๋ก ์๋ฒ๋ฅผ ๊ตฌ์ถํ๊ณ ์๊ณ , ๋ ๋ํ ๋ถ์ฐ ์์คํ ์ ๊ด์ฌ์ด ๋ง๋ค.
์ด๋ฒ์ ๋ถ์ฐ ์์คํ ์์์, ๊ฐ ์ปดํฌ๋ํธ๋ค ์ฌ์ด์ ์ด๋ป๊ฒ ์ ๋ณด๋ฅผ ํจ๊ณผ์ ์ผ๋ก ์ ๋ฌํ๋ ๊ฒ์ด ์ข์์ง ๊ณต๋ถํด๋ณด๊ฒ ๋ค.
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)
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
RDB๋ฅผ Message Queue๋ก ์ด์ฉํ๋ ํจํด์ด๋ค.
์ฆ, ํ ํธ๋์ญ์ ์ ์ด๋ฒคํธ๋ ๋ฉ์ธ์ง๊ฐ ๋ฐ์ํ๋ฉด RDB์ ์ ์ฅํ๋ค.
RDB์๋ ["์ด๋ฒคํธ", "๋ฐ์ดํฐ"]๊ฐ ์ ์ฅ๋๊ฒ ๋ค.
์ด ํจํด์ ์ด์ฉํ๋ค๋ฉด ์ด๋ฒคํธ, ํธ๋์ญ์ ๋ชจ๋ ์ฑ๊ณตํ๊ฑฐ๋ ๋กค๋ฐฑ์ ํ๊ฒ ๋ค.
์ฌ๊ธฐ์ Publish๋ Polling Publisher ํจํด์ ์ด์ฉํ๋ค.
- Polling Publisher Pattern
์ด ํจํด์ Publisher๊ฐ ์ฃผ๊ธฐ์ ์ผ๋ก DB ๋ ์ฝ๋๋ฅผ ์กฐํํ์ฌ Publishํ ๋ฉ์ธ์ง๋ฅผ Pollingํ์ฌ Publishํ๋ ๋ฐฉ์์ด๋ค.
์ต์ข ์ ์ผ๋ก ๋ค์๊ณผ ๊ฐ์ ํจํด์ด ์์ฑ๋๋ค.
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
'BackEnd > Spring Boot' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Spring] ๋ถ์ฐ ์์คํ ์์ ํจ๊ณผ์ ์ธ ๋ฐ์ดํฐ ์ ๋ฌ ๋ฐฉ๋ฒ (3) - Kafka (0) | 2023.11.11 |
---|---|
[Spring] ๋ถ์ฐ ์์คํ ์์ ํจ๊ณผ์ ์ธ ๋ฐ์ดํฐ ์ ๋ฌ ๋ฐฉ๋ฒ (2) - RabbitMQ (0) | 2023.11.10 |
[Spring] Docker์์ Static File ์ฒ๋ฆฌํ๊ธฐ (0) | 2023.09.02 |
[Spring] MongoDB ์ ์ฉ, ํ ์คํธ (0) | 2023.09.02 |
Spring ํ๊ฒฝ์์ Redis ์ฌ์ฉํ๊ธฐ (0) | 2023.09.01 |