๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
BackEnd๐ŸŒฑ/Spring

Kafka ๊ฐœ๋…๊ณผ Spring Boot + Kafka ๊ฐ„๋‹จํ•œ ์—ฐ๋™

by ์•ˆ์ฃผํ˜• 2023. 7. 19.

์„œ๋ก 

๊ธฐ์กด ๋ฐ์ดํ„ฐ ์‹œ์Šคํ…œ์˜ ๊ตฌ์กฐ๋Š” ๊ฐ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ end-to-end๋กœ ์ง์ ‘ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๊ตฌ์กฐ๋Š” ๊ฐ„๋‹จํ•˜์ง€๋งŒ ๊ฐ๊ฐ์˜ ๋ฐ์ดํ„ฐ ํŒŒ์ดํ”„๋ผ์ธ์ด ๋ถ„๋ฆฌ๋˜์–ด ์žˆ์–ด, ์š”๊ตฌ์‚ฌํ•ญ์ด ์ฆ๊ฐ€ํ•จ์— ๋”ฐ๋ผ ์‹œ์Šคํ…œ์˜ ๋ณต์žก๋„๋ฅผ ๋†’์ด๋Š” ๊ฒฐ๊ณผ๋ฅผ ๊ฐ€์ ธ์™”๊ณ , ํฌ๊ฒŒ ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฌธ์ œ์ ๋“ค์ด ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

์‹œ์Šคํ…œ ๋ณต์žก๋„์˜ ์ฆ๊ฐ€

  • ์ค‘์•™ํ™”๋œ ๋ฐ์ดํ„ฐ ์ „์†ก ์˜์—ญ์ด ์—†์–ด, ๋ฐ์ดํ„ฐ์˜ ํ๋ฆ„์„ ํŒŒ์•…ํ•˜๊ธฐ ์–ด๋ ต๊ณ , ์‹œ์Šคํ…œ ๊ด€๋ฆฌ๊ฐ€ ๋ณต์žกํ•จ.
  • ์‹œ์Šคํ…œ์˜ ์ผ๋ถ€๋ถ„์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด, ์—ฐ๊ฒฐ๋œ ๋ชจ๋“  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๋“ค์„ ํ™•์ธํ•ด์•ผ ํ•จ.

๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ ์œ ์ง€์˜ ์–ด๋ ค์›€

  • ๋ฐ์ดํ„ฐ๊ฐ€ ์—ฌ๋Ÿฌ ์‹œ์Šคํ…œ๊ณผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ถ„์‚ฐ๋˜์–ด ์žˆ๋Š” ๊ฒฝ์šฐ, ํ•œ ์‹œ์Šคํ…œ์—์„œ ๋ณ€๊ฒฝ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋‹ค๋ฅธ ์‹œ์Šคํ…œ์— ์ฆ‰์‹œ ๋ฐ˜์˜๋˜์ง€ ์•Š์•„ ๋ฐ์ดํ„ฐ์˜ ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•˜๊ธฐ ์–ด๋ ค์›€.

๋ฐ์ดํ„ฐ ์‹ค์‹œ๊ฐ„ ์ฒ˜๋ฆฌ์˜ ์–ด๋ ค์›€

  • ์ „ํ†ต์ ์ธ ๋ฉ”์‹œ์ง€ ํ ์‹œ์Šคํ…œ์ด๋‚˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” ๋Œ€๋ถ€๋ถ„ ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•จ.
  • ์ด๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์–ด๋ ต๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธ.

ํ™•์žฅ์„ฑ ์ œํ•œ

  • ๋Œ€๋ถ€๋ถ„์˜ ์ „ํ†ต์ ์ธ ๋ฉ”์‹œ์ง€ ํ ์‹œ์Šคํ…œ์€ ํ•œ์ •๋œ ๋ฆฌ์†Œ์Šค ๋‚ด์—์„œ ์ž‘๋™ํ•˜๋ฏ€๋กœ, ๋Œ€๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ ์ œํ•œ์ด ์žˆ์Œ.
  • ๋ฐ์ดํ„ฐ์˜ ์–‘์ด ์ฆ๊ฐ€ํ•˜๋ฉด์„œ ์‹œ์Šคํ…œ์„ ํ™•์žฅํ•ด์•ผ ํ•˜๋Š” ์ƒํ™ฉ์—์„œ ์ด๋Ÿฐ ์ œํ•œ์ด ํฐ ๋ฌธ์ œ๊ฐ€ ๋  ์ˆ˜ ์žˆ์Œ.

์•„ํŒŒ์น˜ ์นดํ”„์นด(Apache Kafka)๋Š” ์ด๋Ÿฐ ๋ฌธ์ œ์ ๋“ค์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋งํฌ๋“œ์ธ์—์„œ ๊ฐœ๋ฐœ๋˜์—ˆ๊ณ , ํ˜„์žฌ๋Š” Apache Software Foundation์˜ ์˜คํ”ˆ ์†Œ์Šค ํ”„๋กœ์ ํŠธ๋กœ ์œ ์ง€ ๊ด€๋ฆฌ๋˜๋Š” ๋ถ„์‚ฐ ์ŠคํŠธ๋ฆฌ๋ฐ ํ”Œ๋žซํผ์ž…๋‹ˆ๋‹ค. 

 

 

Kafka์˜ ๊ธฐ๋ณธ ๊ตฌ์กฐ

Kafka๋Š” ํฌ๊ฒŒ ์•„๋ž˜์˜ ๋„ค ๊ฐ€์ง€์˜ ํ•ต์‹ฌ ๊ตฌ์„ฑ ์š”์†Œ๋กœ ์ด๋ฃจ์–ด์ ธ ์žˆ์Šต๋‹ˆ๋‹ค.

1. ํ”„๋กœ๋“€์„œ(Producer)

ํ”„๋กœ๋“€์„œ๋Š” Kafka์— ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐœํ–‰ํ•˜๋Š” ์—ญํ• ์„ ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค.

ํ”„๋กœ๋“€์„œ๋Š” ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ ์†Œ์Šค๋กœ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€ Kafka์˜ ํŠน์ • ํ† ํ”ฝ์— ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐœํ–‰ํ•ฉ๋‹ˆ๋‹ค.

2. ๋ธŒ๋กœ์ปค(Broker)

๋ธŒ๋กœ์ปค๋Š” Kafka์˜ ํ•ต์‹ฌ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋กœ, ํ”„๋กœ๋“€์„œ๋กœ๋ถ€ํ„ฐ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›์•„์„œ ์ €์žฅํ•˜๊ณ , ์ปจ์Šˆ๋จธ์—๊ฒŒ ๋ฉ”์‹œ์ง€๋ฅผ ์ „๋‹ฌํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

Kafka ํด๋Ÿฌ์Šคํ„ฐ๋Š” ์—ฌ๋Ÿฌ ๋ธŒ๋กœ์ปค๋“ค๋กœ ๊ตฌ์„ฑ๋˜๋ฉฐ, ๊ฐ ๋ธŒ๋กœ์ปค๋Š” ํ•˜๋‚˜ ์ด์ƒ์˜ ํ† ํ”ฝ์˜ ๋ฉ”์‹œ์ง€๋ฅผ ์ €์žฅํ•˜๊ณ  ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

3. ํ† ํ”ฝ(Topic)

ํ† ํ”ฝ์€ Kafka์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„๋ฅ˜ํ•˜๋Š” ๋‹จ์œ„์ž…๋‹ˆ๋‹ค.

ํ”„๋กœ๋“€์„œ๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ํŠน์ • ํ† ํ”ฝ์— ๋ฐœํ–‰ํ•˜๊ณ , ์ปจ์Šˆ๋จธ๋Š” ํ† ํ”ฝ์„ ๊ตฌ๋…ํ•˜์—ฌ ๋ฉ”์‹œ์ง€๋ฅผ ์†Œ๋น„ํ•ฉ๋‹ˆ๋‹ค. ํ† ํ”ฝ์€ ์—ฌ๋Ÿฌ ํŒŒํ‹ฐ์…˜์œผ๋กœ ๋‚˜๋‰˜์–ด์งˆ ์ˆ˜ ์žˆ๊ณ , ์ด๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ‘๋ ฌ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ ํŒŒํ‹ฐ์…˜์€ ์ˆœ์„œ๊ฐ€ ๋ณด์žฅ๋œ ๋ฉ”์‹œ์ง€ ์ŠคํŠธ๋ฆผ์„ ์ œ๊ณตํ•˜๋ฉฐ, ๋ธŒ๋กœ์ปค๊ฐ€ ํด๋Ÿฌ์Šคํ„ฐ ๋‚ด์—์„œ ํŒŒํ‹ฐ์…˜์„ ๋ถ„์‚ฐํ•˜์—ฌ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

4. ์ปจ์Šˆ๋จธ(Consumer)

์ปจ์Šˆ๋จธ๋Š” Kafka์˜ ํŠน์ • ํ† ํ”ฝ์„ ๊ตฌ๋…ํ•˜๊ณ , ํ•ด๋‹น ํ† ํ”ฝ์˜ ๋ฉ”์‹œ์ง€๋ฅผ ์†Œ๋น„ํ•˜๋Š” ์—ญํ• ์„ ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค.

์ปจ์Šˆ๋จธ๋Š” ํ•˜๋‚˜ ์ด์ƒ์˜ ํ† ํ”ฝ์„ ๊ตฌ๋…ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ํ† ํ”ฝ์˜ ํŒŒํ‹ฐ์…˜์„ ๋™์‹œ์— ์†Œ๋น„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

Kafka์™€ Zookeeper์˜ ๊ด€๊ณ„

https://ifuwanna.tistory.com/487

Kafka๋Š” Apache Zookeeper๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํด๋Ÿฌ์Šคํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. Zookeeper๋Š” ๋ธŒ๋กœ์ปค, ํ† ํ”ฝ, ํŒŒํ‹ฐ์…˜ ๋“ฑ Kafka์˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ •๋ณด๋ฅผ ์ €์žฅํ•˜๊ณ  ๊ด€๋ฆฌํ•˜์—ฌ Kafka ํด๋Ÿฌ์Šคํ„ฐ์˜ ์ƒํƒœ๋ฅผ ์ผ๊ด€๋˜๊ฒŒ ์œ ์ง€ํ•˜๊ณ , ๋ธŒ๋กœ์ปค์˜ ์‹คํŒจ ๋ฐ ๋ณต๊ตฌ๋ฅผ ๊ฐ์ง€ํ•˜๋Š” ๋“ฑ์˜ ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ Kafka๋ฅผ ๋„์šฐ๊ธฐ ์œ„ํ•ด์„œ๋Š” Zookeeper๊ฐ€ ๋ฐ˜๋“œ์‹œ ์‹คํ–‰๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ํŒŒํ‹ฐ์…˜์ด 100,000๊ฐœ๊ฐ€ ๋„˜์—ˆ์„ ๋•Œ Kafka ํด๋Ÿฌ์Šคํ„ฐ์— ์Šค์ผ€์ผ๋ง ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ๊ณ , ์œ ์ง€๋ณด์ˆ˜์™€ ์„ค์ • ๊ทธ๋ฆฌ๊ณ  ์•ˆ์ •์„ฑ ๊ฐœ์„ ์„ ์œ„ํ•ด ์ตœ๊ทผ Kafka๋Š” Zookeeper์˜ ์˜์กด์„ฑ์„ ์ค„์ด๊ธฐ ์œ„ํ•œ ๋…ธ๋ ฅ์„ ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์‹ค์ œ๋กœ Kafka 3.x๋ถ€ํ„ฐ๋Š” Zookeeper ์—†์ด ์นดํ”„์นด๋ฅผ ๊ตฌ๋™ํ•  ์ˆ˜ ์žˆ๋Š” Kraft(Kafka Raft Metadata mode) ๋ชจ๋“œ๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ์œผ๋ฉฐ Kafka 4.0๋ถ€ํ„ฐ๋Š” Zookeeper์˜ ์˜์กด์„ฑ์„ ์™„์ „ํžˆ ์ง€์šด ์˜ค์ง KRaft๋งŒ ์ง€์›๋˜๋Š” ๋ฒ„์ „์œผ๋กœ ์ถœ์‹œ๋  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.(https://www.confluent.io/blog/kafka-without-zookeeper-a-sneak-peek/)

 

 

RabbitMQ์™€์˜ ์ฐจ์ด

RabbitMQ์™€ Kafka์˜ ๊ฐ€์žฅ ํฐ ์ฐจ์ด์ ์€ RabbitMQ๋Š” ์ „ํ†ต์ ์ธ ๋ฉ”์‹œ์ง€ ๋ธŒ๋กœ์ปค(Message Broker)์ด๊ณ , Kafka๋Š” ์ด๋ฒคํŠธ ์ŠคํŠธ๋ฆฌ๋ฐ ํ”Œ๋žซํผ(Event Streaming Platform)์ด๋ผ๋Š” ์ ์ž…๋‹ˆ๋‹ค.

์œ„์™€ ๊ฐ™์ด ๋ฐœํ–‰์ž(Publisher)๊ฐ€ Message Exchange์— ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด๋ฉด, ๊ตฌ๋…์ž(Subscriber)๊ฐ€ ๋ฉ”์‹œ์ง€๋ฅผ ๊ตฌ๋…ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ๊ฐœ์˜ Publisher๊ฐ€ Message Exchange์— ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด๋ฉด, ์ •ํ•ด์ง„ ๊ทœ์น™์— ๋”ฐ๋ผ์„œ ํ์— Routing์ด ๋˜๊ณ , Consumer๋“ค์ด ๋ฉ”์‹œ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.(์—ฌ๊ธฐ์„œ Kafka์™€ ๋‹ค๋ฅธ ์ ์€ Kafka๋Š” ํ๋ฅผ ๊ตฌํ˜„ํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค. Kafka๋Š” ์œ„์—์„œ ๋ดค๋“ฏ์ด ํ† ํ”ฝ(Topic)์œผ๋กœ ๋ถˆ๋ฆฌ๋Š” ์นดํ…Œ๊ณ ๋ฆฌ์— ๋ฐ์ดํ„ฐ ์ง‘ํ•ฉ์„ ์ €์žฅํ•˜๋ฉฐ, ๊ฐ๊ฐ์˜ ํ† ํ”ฝ์— ๋ถ„ํ• ๋œ ๋ฉ”์‹œ์ง€ ๋กœ๊ทธ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.)

๊ทธ๋ฆฌ๊ณ  ์ปจ์Šˆ๋จธ๊ฐ€ ๋ฉ”์‹œ์ง€๋ฅผ ๊ฐ€์ ธ๊ฐ€๋ฉด ํ์—๋Š” ๋” ์ด์ƒ ๋‚จ์ง€ ์•Š๊ณ  ์‚ฌ๋ผ์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ „ํ†ต์ ์ธ ๋ฉ”์‹œ์ง€ ๋ธŒ๋กœ์ปค์˜ ํ˜•ํƒœ๋Š” ์†Œ๋น„์ž์™€ ๋ฉ”์‹œ์ง€ ๋ธŒ๋กœ์ปค์˜ ๊ฒฐํ•ฉ๋ ฅ์ด ๋†’์•„์ง€๊ฒŒ ๋˜์–ด ํ›„์— ์„œ๋น„์Šค์˜ ํŠธ๋ž˜ํ”ฝ์ด ์ฆ๊ฐ€ํ•˜์˜€์„ ๋•Œ ์ˆ˜ํ‰์ ์œผ๋กœ ํ™•์žฅํ•˜๋Š” ๋ฐ์— ์–ด๋ ค์›€์ด ์žˆ์—ˆ์œผ๋ฉฐ, ์ด๋ฒคํŠธ ๋ฉ”์‹œ์ง€๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์ „๋‹ฌ๋˜์—ˆ๋‹ค๊ณ  ํŒ๋‹จ๋  ๊ฒฝ์šฐ ์ด ๋ฉ”์‹œ์ง€๋Š” ํ์—์„œ ์‚ญ์ œ๋˜์–ด ๋ฒ„๋ฆฌ๊ธฐ ๋•Œ๋ฌธ์— ํ›„์— ๋‹ค์‹œ ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›๊ธฐ๊ฐ€ ์–ด๋ ต๋‹ค๋Š” ๋‹จ์ ์ด ์กด์žฌํ–ˆ์Šต๋‹ˆ๋‹ค.

์นดํ”„์นด๋Š” ์ด๋Ÿฌํ•œ ๋‹จ์ ์„ ๊ทน๋ณตํ•˜๋ฉฐ ์ƒ๊ธด ์ด๋ฒคํŠธ ์ŠคํŠธ๋ฆฌ๋ฐ ํ”Œ๋žซํผ์œผ๋กœ์จ๋ฉ”์‹œ์ง€ ๋ธŒ๋กœ์ปค์™€ ์ด๋ฒคํŠธ ์ŠคํŠธ๋ฆฌ๋ฐ ํ”Œ๋žซํผ ๋ชจ๋‘ ์ด๋ฒคํŠธ๋ฅผ ์ˆ˜์‹ ํ•˜๊ณ , ์ด๊ฒƒ์„ ์†Œ๋น„์ž์—๊ฒŒ ์ „๋‹ฌํ•˜๋Š” ๋ฐ์— ๋ชฉ์ ์„ ๋‘๊ณ  ์žˆ์ง€๋งŒ ์ž‘๋™ ๋ฐฉ์‹์— ํฐ ์ฐจ์ด๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋ฒคํŠธ ์ŠคํŠธ๋ฆฌ๋ฐ ํ”Œ๋žซํผ์€ ๋ฉ”์‹œ์ง€ ๋ธŒ๋กœ์ปค์™€ ๋‹ค๋ฅด๊ฒŒ ํ† ํ”ฝ(Topic)์ด๋ผ๋Š” ๊ฒƒ์ด Event Streamer์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. Event producer๊ฐ€ ์ด๋ฒคํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋ฉด, ํ† ํ”ฝ์ด๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š” ์ด๋ฒคํŠธ์˜ ๋ ˆ์ฝ”๋“œ ๋กœ๊ทธ๋ฅผ streamer์— ์ˆœ์„œ๋Œ€๋กœ ๊ธฐ๋กํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๊ทธ ํ›„ ํ•ด๋‹น ํ† ํ”ฝ์„ ๊ตฌ๋…(subscribe)ํ•œ ์ปจ์Šˆ๋จธ์—๊ฒŒ ์ „๋‹ฌํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ์ด ํ† ํ”ฝ์„ ์ปจ์Šˆ๋จธ๊ฐ€ ๊ฐ€์ ธ๊ฐ„ ํ›„์—๋„ ์ด๋ฒคํŠธ ์ŠคํŠธ๋ฆผ์—์„œ ๊ณ„์† ํ† ํ”ฝ์„ ์œ ์ง€ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์žฅ์•  ๋ฐœ์ƒ ์ƒํ™ฉ์—์„œ๋„ ์ด๋ฒคํŠธ๋ฅผ ๋‹ค์‹œ ๋ฐœํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Kafka vs RabbitMQ RabbitMQ Kafka
์„ฑ๋Šฅ ์ดˆ๋‹น 4,000 ~ 10,000๊ฐœ์˜ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ ์ดˆ๋‹น 1,000,000 ๊ฐœ์˜ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ
๋ฉ”์‹œ์ง€ ์ƒ๋ช…์ฃผ๊ธฐ ์†Œ๋น„๋˜๋Š” ์ˆœ๊ฐ„ ์‚ญ์ œ ์ •์ฑ… ๊ธฐ๋ฐ˜(์„ค์ • ํ•œ ๊ธฐ๊ฐ„๋งŒํผ)
ํŠธ๋žœ์žญ์…˜ ๋ฐ์ดํ„ฐ ์ค‘๋ณต ๋ฉ”์‹œ์ง€ ๋ฐœ์†ก ๋ฐ ๋ฉ”์‹œ์ง€ ์†์‹ค ๊ฐ€๋Šฅ์„ฑ ์กด์žฌ ์ˆœ์„œ ๋ณด์žฅ ๋ฐ ๋ฉ”์‹œ์ง€ ์œ ์‹ค ๊ฐ€๋Šฅ์„ฑ์ด ์ ์Œ
์šด์˜ ๋ฐ์ดํ„ฐ ๋ผ์šฐํŒ…, ํด๋Ÿฌ์Šคํ„ฐ๋ง, ๋ฉ”์‹œ์ง€ ํ™•์ธ ๋“ฑ์˜ ์šด์˜ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ์— ๊ฐ•์  ๋Œ€๋Ÿ‰์˜ ์‹ค์‹œ๊ฐ„ ๋กœ๊ทธ ๋˜๋Š” ์ŠคํŠธ๋ฆผ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ์— ๊ฐ•์ 
ํด๋Ÿฌ์Šคํ„ฐ๋ง ํด๋Ÿฌ์Šคํ„ฐ๋ง ์ง€์›, ๋…ธ๋“œ ๊ฐ„ ๋ฉ”์‹œ์ง€ ๋ณต์ œ๋กœ ๊ณ ๊ฐ€์šฉ์„ฑ ์ œ๊ณต ํด๋Ÿฌ์Šคํ„ฐ๋ง ์ง€์›, ์ž๋™ ๋ณต์ œ์™€ ํŒŒํ‹ฐ์…”๋‹์„ ํ†ตํ•ด ๊ณ ๊ฐ€์šฉ์„ฑ ๋ฐ ์žฅ์•  ๋ณต๊ตฌ ๊ธฐ๋Šฅ ์ œ๊ณต
๋ฉ”์‹œ์ง€ ๋ชจ๋ธ Queue์™€ Topic ๋ชจ๋ธ ์ง€์› Topic ๋ชจ๋ธ๋งŒ ์ง€์›
์Šค์ผ€์ผ๋ง ์Šค์ผ€์ผ ์—…์— ์ ํ•ฉ ์Šค์ผ€์ผ ์•„์›ƒ์— ์ ํ•ฉ, ๋Œ€๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๋†’์€ ํ™•์žฅ์„ฑ

๋” ์ƒ์„ธํ•œ ์ฐจ์ด์ ์€ ๋งํฌ์—์„œ ํ™•์ธ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

 

 

Spring Boot + Kafka ์—ฐ๋™

ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • Spring Boot 2.7 + Java 11 + gradle
  • Docker kafka & Zookeeper

๊นƒํ—ˆ๋ธŒ: https://github.com/dkswnkk/Spring-Playground/tree/main/kafka-run-practice

Producer Application Project

1. ์˜์กด์„ฑ ์ถ”๊ฐ€

// kafka
implementation 'org.springframework.kafka:spring-kafka'

 

2. Producer Config ์„ค์ •

@Configuration
public class KafkaProducerConfig {

    @Bean
    public ProducerFactory<String, Object> producerFactory() {
        Map<String, Object> config = new HashMap<>();
        config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
        config.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
        config.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);

        return new DefaultKafkaProducerFactory<>(config);
    }

    @Bean
    public KafkaTemplate<String, Object> kafkaTemplate() {
        return new KafkaTemplate<>(producerFactory());
    }
}

์„ค์ •๋œ ์†์„ฑ์— ๋Œ€ํ•œ ์„ค๋ช…์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • BOOTSTRAP_SERVERS_CONFIG: Producer๊ฐ€ ์ฒ˜์Œ์œผ๋กœ ์—ฐ๊ฒฐํ•  Kafka ๋ธŒ๋กœ์ปค์˜ ์œ„์น˜๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ ์ฝ”๋“œ์˜ ๊ฒฝ์šฐ localhost์˜ 9092 ํฌํŠธ์— ์œ„์น˜ํ•˜๋„๋ก ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • KEY_SERIALIZER_CLASS_CONFIG & VALUE_SERIALIZER_CLASS_CONFIG: Producer๊ฐ€ Key์™€ Value ๊ฐ’์˜ ๋ฐ์ดํ„ฐ๋ฅผ Kafka ๋ธŒ๋กœ์ปค๋กœ ์ „์†กํ•˜๊ธฐ ์ „์— ๋ฐ์ดํ„ฐ๋ฅผ byte array๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•˜๋Š” ์ง๋ ฌํ™” ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. Kafka๋Š” ๋„คํŠธ์›Œํฌ๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†กํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ๊ฐ์ฒด๋ฅผ byte array๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์ง๋ ฌํ™” ๊ณผ์ •์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ, StringSerializer๋ฅผ ์‚ฌ์šฉํ•ด ์ง๋ ฌํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค.

KafkaTemplate๋Š” Spring Kafka์—์„œ ์ œ๊ณตํ•˜๋Š” Kafka Producer๋ฅผ Wrapping ํ•œ ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค. KafkaTemplate๋Š” Kafka์— ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด๋Š” ์—ฌ๋Ÿฌ ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•˜๋ฉฐ, ์ด ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ธŒ๋กœ์ปค๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด๊ธฐ ์œ„ํ•ด ์ง์ ‘ Kafka Producer API๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋Œ€์‹ , send์™€ ๊ฐ™์€ ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ๋” ํŽธ๋ฆฌํ•˜๊ณ  ๊ฐ„๊ฒฐํ•œ ์ฝ”๋“œ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

3. Producer ์ƒ์„ฑ

package com.example.kafkarunpractice.producer;

import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Component;

@Component
public class TestProducer {
    private final KafkaTemplate<String, Object> kafkaTemplate;

    public TestProducer(KafkaTemplate<String, Object> kafkaTemplate) {
        this.kafkaTemplate = kafkaTemplate;
    }

    public void create() {
        kafkaTemplate.send("topic", "say hello~");
    }
}

 

 

Consumer Application Project

1. ์˜์กด์„ฑ ์ถ”๊ฐ€

// kafka
implementation 'org.springframework.kafka:spring-kafka'

2. Consumer Config ์„ค์ •

@Configuration
public class KafkaConsumerConfig {

    @Bean
    public ConsumerFactory<String, Object> consumerFactory() {
        Map<String, Object> config = new HashMap<>();
        config.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
        config.put(ConsumerConfig.GROUP_ID_CONFIG, "group_1");
        config.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
        config.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);

        return new DefaultKafkaConsumerFactory<>(config);
    }

    @Bean
    public ConcurrentKafkaListenerContainerFactory<String, Object> kafkaListenerContainerFactory() {
        ConcurrentKafkaListenerContainerFactory<String, Object> factory = new ConcurrentKafkaListenerContainerFactory<>();
        factory.setConsumerFactory(consumerFactory());

        return factory;
    }
}

ConsumerConfig.GROUP_ID_CONFIG๋Š” Consumer๊ฐ€ ์†ํ•œ Consumer ๊ทธ๋ฃน์˜ ID๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. Consumer ๊ทธ๋ฃน์€ ๊ฐ™์€ ํ† ํ”ฝ์„ ์†Œ๋น„ํ•˜๋Š” Consumer๋“ค์˜ ๊ทธ๋ฃน์œผ๋กœ, ๊ทธ๋ฃน ๋‚ด์˜ ๋ชจ๋“  Consumer๋Š” ํ† ํ”ฝ์˜ ์„œ๋กœ ๋‹ค๋ฅธ ํŒŒํ‹ฐ์…˜์—์„œ ๋ฉ”์‹œ์ง€๋ฅผ ์ฝ์–ด ๋“ค์ž…๋‹ˆ๋‹ค.

์ด๋ฅผ ํ†ตํ•ด ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ๋ฅผ ๋ณ‘๋ ฌํ™” ํ•˜์—ฌ ์ฒ˜๋ฆฌ ์†๋„๋ฅผ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์œผ๋ฉฐ, Consumer๊ฐ€ ์‹คํŒจํ•  ๊ฒฝ์šฐ ๋‹ค๋ฅธ Consumer๊ฐ€ ํ•ด๋‹น Consumer์˜ ํŒŒํ‹ฐ์…˜์„ ์ฒ˜๋ฆฌํ•˜์—ฌ ๊ณ ๊ฐ€์šฉ์„ฑ์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ConcurrentKafkaListenerContainerFactory๋Š” Spring์˜ @KafkaListener ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ ๋ฉ”์„œ๋“œ์— ์ฃผ์ž…๋˜์–ด ์‚ฌ์šฉ๋˜๋ฉฐ, ๋ฉ”์‹œ์ง€๋ฅผ ๋™์‹œ์— ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๋ฉ”์‹œ์ง€ ๋ฆฌ์Šค๋„ˆ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

3. Consumer ์ƒ์„ฑ

@Component
public class TestConsumer {

    @KafkaListener(topics = "topic", groupId = "group_1")
    public void listener(Object data) {
        System.out.println(data);
    }
}

 

 

Kafka & Zookeeper ์‹คํ–‰

docker-compose.yml ์„ค์ •

version: '2'
services:
  zookeeper:
    image: wurstmeister/zookeeper
    container_name: zookeeper
    ports:
      - "2181:2181"
  kafka:
    image: wurstmeister/kafka:2.12-2.5.0
    container_name: kafka
    ports:
      - "9092:9092"
    environment:
      KAFKA_ADVERTISED_HOST_NAME: 127.0.0.1
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock

Docker Compose๋ฅผ ์ด์šฉํ•˜์—ฌ Kafka์™€ Zookeeper๋ฅผ ๋„์ปค ์ปจํ…Œ์ด๋„ˆ๋กœ ์‹คํ–‰์‹œํ‚ต๋‹ˆ๋‹ค. Zookeeper๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ 2181 ํฌํŠธ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

  • ์ปดํฌ์ฆˆ ์‹คํ–‰: docker-compose up -d
  • ์ปดํฌ์ฆˆ ์ข…๋ฃŒ: docker-compose down

 

ํ…Œ์ŠคํŠธ

Consumer Application Project๋ฅผ ์‹คํ–‰ํ•˜๊ณ , Produce Application Project์—์„œ ์ƒ์„ฑํ•œ ์•„๋ž˜์˜ ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค.

@SpringBootTest
class TestProducerTest {

    @Autowired
    private TestProducer testProducer;

    @Test
    void test() {
        testProducer.create();
    }
}

์ˆ˜ํ–‰๊ฒฐ๊ณผ

์ •์ƒ์ ์œผ๋กœ Producer Application์—์„œ ๋ฐœํ–‰ํ•œ ํ† ํ”ฝ์ด Consumer Application์—์„œ ์†Œ๋น„๋˜๋Š” ๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

๊ฐ™์ด ๋ณด๋ฉด ์ข‹์€ ๊ธ€

๋Œ“๊ธ€