๋Œ€๊ทœ๋ชจ ์‹œ์Šคํ…œ

  • ๋ฐฉ๋Œ€ํ•œ ์–‘์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  ์ˆ˜๋งŽ์€ ์‚ฌ์šฉ์ž์˜ ์š”์ฒญ์„ ๋™์‹œ์— ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ์‹œ์Šคํ…œ
  • RabbitMQ, Kafka์™€ ๊ฐ™์€ ๋ฉ”์‹œ์ง• ์‹œ์Šคํ…œ์„ ํ™œ์šฉํ•˜์—ฌ ๊ฐ ์„œ๋น„์Šค๊ฐ„์˜ ํšจ์œจ์ ์ธ ๋ฐ์ดํ„ฐ ํ†ต์‹ ๊ณผ ํ™•์žฅ์„ฑ ๋ณด์žฅ

๋ฉ”์‹œ์ง• ์‹œ์Šคํ…œ

  • Queue ํ˜•ํƒœ๋กœ ๋ฉ”์‹œ์ง€ ์ €์žฅ
  • Producer๊ฐ€ ์ „์†กํ•˜๊ณ ์ž ํ•˜๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ํ์— ๋„ฃ์œผ๋ฉด Consumer๋Š” ์ž์‹ ์˜ ์†๋„์— ๋งž์ถฐ Queue์—์„œ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ
  • Message Broker๋ฅผ ์‚ฌ์ด์— ๋‘๊ณ  Producer์™€ Consumer๊ฐ€ ๊ฐ„์ ‘์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ ๋ฐ›์Œ

๊ธฐ๋Šฅ

  • ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ
    • Producer๊ฐ€ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐœํ–‰ํ•œ ํ›„ consumer๊ฐ€ ๋ฉ”์‹œ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š์•„๋„ ๋จ
  • ๋ฐ์ดํ„ฐ ์†์‹ค ๋ฐฉ์ง€
    • ๋ฐ์ดํ„ฐ๋ฅผ ๋ฉ”์‹œ์ง€ ํ์— ์•ˆ์ „ํ•˜๊ฒŒ ๋ณด๊ด€
  • ๋ถ€ํ•˜ ๋ถ„์‚ฐ
    • ์—ฌ๋Ÿฌ consumer๊ฐ€ ํ์˜ ๋ฉ”์„ธ์ง€๋ฅผ ๊ฐ€์ ธ๊ฐ€์„œ ์ฒ˜๋ฆฌ โ†’ ์‹œ์Šคํ…œ์˜ ๋ถ€ํ•˜๋ฅผ ๋ถ„์‚ฐ
    • ํšจ์œจ์ ์ธ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ
  • ์Šค์ผ€์ผ๋ง
    • ์ˆ˜ํ‰์  ํ™•์žฅ ์šฉ์ด

RabbitMQ vs Kafka

์ฐจ์ด์ RabbitMQKafka
์ƒํ˜ธ์ž‘์šฉ๋ฐฉ์‹Producer๊ฐ€ ๋ฉ”์‹œ์ง€๋ฅผ ์ „์†กํ•˜๊ณ  ๋ฉ”์‹œ์ง€๊ฐ€ ์˜๋„ํ•œ Consumer์—๊ฒŒ ๋„์ฐฉํ–ˆ๋Š”์ง€ ๋ชจ๋‹ˆํ„ฐ๋งProducer๋Š” Consumer ์ˆ˜๋ น ์—ฌ๋ถ€์™€ ์ƒ๊ด€์—†์ด Queue์— ์ €์žฅ
์•„ํ‚คํ…์ณ- ๋ณต์žกํ•œ ๋ฉ”์‹œ์ง€ ๋ผ์šฐํŒ…
- ๋ฉ”์‹œ์ง€ ํ ๊ธฐ๋ฐ˜ ์„ค๊ณ„
- push ๋ชจ๋ธ ์‚ฌ์šฉ
- ๋” ๋ณต์žกํ•œ ์•„ํ‚คํ…์ณ(zookeeper)
- ํŒŒํ‹ฐ์…˜ ๊ธฐ๋ฐ˜ ์„ค๊ณ„ (ํ•˜๋‚˜์˜ ์ฃผ์ œ์•ˆ์— ์—ฌ๋Ÿฌ๊ฐ€์ง€ queue๋“ค์ด ๋“ค์–ด๊ฐ€์žˆ์Œ)
- pull ๋ชจ๋ธ ์‚ฌ์šฉ (์ฒ˜๋ฆฌ ๊ฐ€๋Šฅํ•  ๋•Œ ๊ฐ€์ ธ๊ฐ€๋Š” ๋ชจ๋ธ)
๋ฉ”์‹œ์ง•
์ฒ˜๋ฆฌ ๋ฐฉ์‹
- ์šฐ์„ ์ˆœ์œ„ ๋Œ€๊ธฐ์—ด ์ง€์›
- ์ˆœ์„œ๋Œ€๋กœ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ
- consumer ๊ฐ€ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ โ†’ ๋ธŒ๋กœ์ปค๊ฐ€ ํ™•์ธ ์‘๋‹ต (ack) ์ „์†ก โ†’ ๋ธŒ๋กœ์ปค๊ฐ€ ๋Œ€๊ธฐ์—ด์—์„œ ๋ฉ”์‹œ์ง€ ์‚ญ์ œ
- ์šฐ์„ ์ˆœ์œ„ ๋Œ€๊ธฐ์—ด X
- ํŒŒํ‹ฐ์…˜ ๋‚ด์—์„  ์ˆœ์„œ ๋ณด์žฅ (offset)
- ๋ฉ”์‹œ์ง€๋ฅผ ๋กœ๊ทธ ํŒŒ์ผ์— ์ถ”๊ฐ€
- ๋ณด์กด๊ธฐ๊ฐ„์ด ๋งŒ๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๋ณด๊ด€
์„ฑ๋Šฅ์ดˆ๋‹น ์ˆ˜์ฒœ๊ฐœ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ
์ง€์—ฐ์‹œ๊ฐ„ ์งง์Œ
์ดˆ๋‹น ์ˆ˜๋ฐฑ๋งŒ๊ฐœ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ
๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ
RabbitMQ์— ๋น„ํ•ด ์‹ค์‹œ๊ฐ„ ์ฒ˜๋ฆฌ๋Ÿ‰์€ ๋–จ์–ด์ง (consumeํ•  ์ˆ˜ ์žˆ๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ๊ฐ€์ ธ์˜ค๊ธฐ ๋•Œ๋ฌธ)

rabbit MQ๊ฐ€ ์ง€์—ฐ์‹œ๊ฐ„์ด ์งง์€๋ฐ Kafka๊ฐ€ ์ฒ˜๋ฆฌ๋Ÿ‰์ด ๋งŽ์€ ์ด์œ ?

rabbitMQ๋Š” ํ•˜๋‚˜์”ฉ ์ฒ˜๋ฆฌ, Kafka๋Š” ๋ฒŒํฌ๋กœ ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅ โ†’ ์ดˆ๋‹น ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐ์ดํ„ฐ ์–‘์ด ๋งŽ์Œ

MSA์—์„œ ์“ฐ์ด๋Š” ์ด์œ ?

  • ์„œ๋น„์Šค๊ฐ„ ๋น„๋™๊ธฐ ํ†ต์‹ 
    • ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์•ˆ์—์„œ ๋น„๋™๊ธฐ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๋ถ€๋ถ„์„ ๋ถ„๋ฆฌํ•˜๊ณ  kafka/rabbitMQ๋กœ ์ฒ˜๋ฆฌ โ†’ ์„ฑ๋Šฅ ํ–ฅ์ƒ
  • ํ™•์žฅ์„ฑ
    • ์ฆ๊ฐ€ํ•˜๋Š” ํŠธ๋ž˜ํ”ฝ์ด๋‚˜ ๋ฐ์ดํ„ฐ ์–‘์— ๋Œ€์‘ํ•˜์—ฌ ์„ฑ๋Šฅ์„ ์œ ์ง€์‹œํ‚ค๊ฑฐ๋‚˜ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š” ํ™˜๊ฒฝ์ด ์ค‘์š”
    • kafka โ†’ ํŒŒํ‹ฐ์…”๋‹, ํด๋Ÿฌ์Šคํ„ฐ์— ๋ธŒ๋กœ์ปค ์ถ”๊ฐ€
    • Rabbit MQ โ†’ ํด๋Ÿฌ์Šคํ„ฐ๋ง, ๋ถ„์‚ฐ ํ
  • ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ, ์‹ ๋ขฐ์„ฑ
    • ๊ฐ MSA๋Š” ๋…๋ฆฝ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ โ†’ ์„œ๋น„์Šค๊ฐ„ ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ ์ค‘์š”
    • Kafka โ†’ ๋กœ๊ทธ, ๋ฉ”์‹œ์ง€ ์ €์žฅ ๊ธฐ๋Šฅ์œผ๋กœ ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ ์œ ์ง€
    • RabbitMQ โ†’ ๋ฉ”์‹œ์ง€ ๋””์Šคํฌ์— ์ €์žฅ, ์‹คํŒจํ•œ ๋ฉ”์‹œ์ง€ ์žฌ์ฒ˜๋ฆฌ ์ง€์›
  • ์„œ๋น„์Šค ๊ฐ„ ๊ฒฐํ•ฉ๋„ โฌ‡๏ธ
    • Kafka โ†’ ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜ ์•„์ผ€ํ‹ฑ์ณ ์ง€์›, ์„œ๋น„์Šค ๊ฐ„ ๋…๋ฆฝ์  ๋™์ž‘ ๊ฐ€๋Šฅ
    • Rabbit MQ โ†’ ํ ๊ธฐ๋ฐ˜ ๋ฉ”์‹œ์ง•์„ ํ†ตํ•ด ์„œ๋น„์Šค ๊ฐ„ ๋ฉ”์‹œ์ง€ ์ „๋‹ฌ

Kafka๋ฅผ MSA์— ์ ์šฉํ•˜๊ธฐ

  • Event-Driven Architecture
    • ๊ฐ MSA๊ฐ„ ํ†ต์‹ ์„ ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜์œผ๋กœ ์ฒ˜๋ฆฌ, ํ†ต์‹  ์ฃผ์ฒด๋Š” Kafka
  • ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ
    • ๋Œ€๊ทœ๋ชจ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆฌ๋ฐ ์ฒ˜๋ฆฌ์— ์šฉ์ด
    • ํ–‰๋™ ๋กœ๊ทธ, ๋ชจ๋‹ˆํ„ฐ๋ง ๋ฐ์ดํ„ฐ ์‹ค์‹œ๊ฐ„ ๋ถ„์„
  • ๋ชจ๋‹ˆํ„ฐ๋ง๊ณผ ๋กœ๊น…
    • ์ค‘์•™ ์ง‘์ค‘์‹ ๋กœ๊ทธ ์ˆ˜์ง‘

Event-Driven Architecture

์ฝ”๋“œ

kafka: 
	bootstrap-servers: localhost:9092 
	consumer: # TODO kafka consumer, producer์— ๋Œ€ํ•œ ์„ค์ •๊ฐ’ ์ˆ˜์ •์€ ์—ฌ๊ธฐ์—์„œ 
		group-id: ${spring.application.name}-group 
		auto-offset-reset: earliest 
		key-deserializer: org.apache.kafka.common.serialization.StringDeserializer 
		value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer 
		properties: 
			spring.json.trusted.packages: '*' 
	producer: 
		key-serializer: org.apache.kafka.common.serialization.StringSerializer 
		value-serializer: org.springframework.kafka.support.serializer.JsonSerializer

์ด๋ฒคํŠธ ๋ฐœ์‹ 

  • KafkaTemplate์„ ์‚ฌ์šฉํ•ด ์ด๋ฒคํŠธ ๋ฐœ์‹ 
  • ํ† ํ”ฝ๊ณผ ๋ณด๋‚ด๊ณ ์ž ํ•˜๋Š” ์ด๋ฒคํŠธ ๋‚ด์šฉ์„ ๋‹ด์•„ ์ „์†ก
@Service
@RequiredArgsConstructor
public class DeliveryService {
    private final DeliveryRepository deliveryRepository;
		private final KafkaTemplate<String, Object> kafkaTemplate; // ์ž๋™ ์ฃผ์ž…
 
    @Transactional
    public void createDelivery(...) {
				Delivery delivery = new Delivery(...);
				deliveryRepository.save(delivery);
				
				DeliveryCreatedEvent event = new DeliveryCreatedEvent(...); //์ด๋ฒคํŠธ์— ํ•„์š”ํ•œ ๊ฐ’์„ ๋„ฃ์–ด์ฃผ์„ธ์š”
        kafkaTemplate.send("delivery-created", EventSerializer.serialize(event));
    }
}

์ด๋ฒคํŠธ ์ˆ˜์‹ 

  • @KafkaListener๋ฅผ ์‚ฌ์šฉํ•ด ๋ฉ”์‹œ์ง€ ์ˆ˜์‹ 
  • groupId = yml์— ์„ค์ •ํ•œ group id
@Transactional
@KafkaListener(topics = "delivery-created", groupId = "notification-group")
public void handleDeliveryCreatedEvent(String message) {
    DeliveryCreatedEvent event = EventSerializer.deserialize(message, DeliveryCreatedEvent.class);
    
    // ์ด๋ฒคํŠธ๋กœ ํ•ด์•ผํ•˜๋Š” ํ›„ ์ฒ˜๋ฆฌ ์ง„ํ–‰ (๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง)
    // e.g. ๋ฐฐ๋‹ฌ์™„๋ฃŒ ์ด๋ฒคํŠธ ๋ฐœํ–‰ 
}