๊ด€๋ฆฌ ๋ฉ”๋‰ด

Welcome! Everything is fine.

[STUDY] ๋™๊ธฐ vs ๋น„๋™๊ธฐ - RabbitMQ๋กœ ๋น„๋™๊ธฐ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌํ•˜๊ธฐ ๋ณธ๋ฌธ

์นดํ…Œ๊ณ ๋ฆฌ ์—†์Œ

[STUDY] ๋™๊ธฐ vs ๋น„๋™๊ธฐ - RabbitMQ๋กœ ๋น„๋™๊ธฐ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌํ•˜๊ธฐ

๊ฐœ๋ฐœ๊ณฐ๋ฐœ 2025. 6. 5.
728x90

๐Ÿง  ๋™๊ธฐ · ๋น„๋™๊ธฐ์™€ ๋ธ”๋กœํ‚น · ๋…ผ๋ธ”๋กœํ‚น

๋™๊ธฐ vs ๋น„๋™๊ธฐ

  • ๋™๊ธฐ(Synchronous) : ํ˜ธ์ถœ์ž์™€ ์š”์ฒญ์ž์˜ ๊ฒฐ๊ณผ ํ™•์ธ์ด ๋™์‹œ์— ์ผ์–ด๋‚จ
  • ๋น„๋™๊ธฐ(Asynchronous) : ํ˜ธ์ถœ์ž์™€ ์š”์ฒญ์ž์˜ ๊ฒฐ๊ณผ ํ™•์ธ์ด ๋™์‹œ์— ์ผ์–ด๋‚˜์ง€ ์•Š์•„๋„ ๋จ

๋ธ”๋กœํ‚น vs ๋…ผ๋ธ”๋กœํ‚น

  • ๋ธ”๋กœํ‚น(Blocking) : ํ˜ธ์ถœ์ž๊ฐ€ ์š”์ฒญ์— ๋Œ€ํ•œ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ฌ ๋•Œ๊นŒ์ง€ ์š”์ฒญ์ž์—๊ฒŒ ์ œ์–ด๊ถŒ์„ ๋Œ๋ ค์ฃผ์ง€ ์•Š๋Š” ๊ฒƒ
  • ๋…ผ๋ธ”๋กœํ‚น(Non-Blocking) : ํ˜ธ์ถœ์ž๊ฐ€ ์š”์ฒญ์— ๋Œ€ํ•œ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ค์ง€ ์•Š๋”๋ผ๋„ ์š”์ฒญ์ž์—๊ฒŒ ์ œ์–ด๊ถŒ์„ ๋Œ๋ ค์คŒ

๋™์‹œ์„ฑ vs ๋ณ‘๋ ฌ์„ฑ

  • ๋™์‹œ์„ฑ(Concurrency) : ์‹ฑ๊ธ€ ์ฝ”์–ด์—์„œ ์—ฌ๋Ÿฌ ์ž‘์—…์„ ๋ฒˆ๊ฐˆ์•„ ๊ฐ€๋ฉฐ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ์‹
  • ๋ณ‘๋ ฌ์„ฑ(Parallelism) : ๋ฉ€ํ‹ฐ ์ฝ”์–ด๊ฐ€ ๊ฐ ์ž‘์—…์„ ๋ฌผ๋ฆฌ์ ์œผ๋กœ ๋™์‹œ์— ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ์‹

์˜ˆ๋ฅผ ๋“ค์–ด, ์•„๋ž˜ ์ฝ”๋“œ๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ์ˆ˜์‹ ํ•  ๋•Œ ๋™์‹œ์— ์ตœ๋Œ€ 3๊ฐœ์˜ ๋ฉ”์‹œ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก concurrency = "3" ์„ค์ •์„ ํ•ด๋‘์—ˆ๋‹ค.

์ด๋•Œ Spring์€ ๋‚ด๋ถ€์ ์œผ๋กœ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ํ’€์„ ํ™œ์šฉํ•ด ๋ฉ”์‹œ์ง€๋ฅผ ๋ณ‘๋ ฌ๋กœ ์ฒ˜๋ฆฌํ•˜๋ ค๊ณ  ์‹œ๋„ํ•œ๋‹ค. ๋งŒ์•ฝ ์ด ์ฝ”๋“œ๊ฐ€ ๋‹จ์ผ CPU ํ™˜๊ฒฝ์—์„œ ์‹คํ–‰๋œ๋‹ค๋ฉด ์‹ค์ œ๋กœ๋Š” 3๊ฐœ์˜ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฐ˜๊ฐˆ์•„ ์‹คํ–‰๋˜๋ฉฐ ๊ฐ๊ฐ์˜ ๋ฉ”์‹œ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ฒŒ ๋˜๋Š”๋ฐ, ์ด ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์ด ๋™์‹œ์„ฑ ์ฒ˜๋ฆฌ๋‹ค.

๐Ÿ“จ ๋ฉ”์‹œ์ง• ์‹œ์Šคํ…œ์ด๋ž€?

๋ฉ”์‹œ์ง€ ํ์˜ ์—ญํ• ๊ณผ ๊ตฌ์กฐ

  • ๋ฉ”์‹œ์ง€ ํ(Message Queue, MQ) : ๋ฉ”์‹œ์ง€ ์ง€ํ–ฅ ๋ฏธ๋“ค์›จ์–ด(Message Oriented Middleware, MOM)์˜ ์ผ์ข…์œผ๋กœ, ์„œ๋กœ ๋…๋ฆฝ์ ์ธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐ„์— ๋ฉ”์‹œ์ง€๋ฅผ ์ฃผ๊ณ ๋ฐ›์œผ๋ฉฐ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ฃผ๋Š” ์‹œ์Šคํ…œ์ด๋‹ค.

๋ฉ”์‹œ์ง• ์‹œ์Šคํ…œ์˜ ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ๊ตฌ์กฐ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์ƒ์‚ฐ์ž(Producer)๊ฐ€ ๋ฉ”์‹œ์ง€๋ฅผ Message Queue์— ๋„ฃ๊ณ , ์†Œ๋น„์ž(Consumer) ๊ฐ€ ์ด๋ฅผ ๊บผ๋‚ด ์ฒ˜๋ฆฌํ•˜๋Š” ๊ตฌ์กฐ์ด๋‹ค. Producer์™€ Consumer๊ฐ€ ๋น„๋™๊ธฐ์ ์œผ๋กœ ๋ถ„๋ฆฌ๋˜์–ด ์žˆ๋‹ค๋Š” ๊ฒƒ์ด ํ•ต์‹ฌ์ด๋‹ค. 

  • ๋ฉ”์‹œ์ง€ ํ์˜ ํŠน์ง•
    • ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋กœ ์‘๋‹ต ์ง€์—ฐ ํ•ด์†Œ
      • Producer์™€ Consumer๊ฐ€ ์‹œ๊ฐ„์ ์œผ๋กœ ๋ถ„๋ฆฌ๋˜์–ด ์š”์ฒญ-์‘๋‹ต ๋ณ‘๋ชฉ ์—†์ด ๋น ๋ฅธ ์‚ฌ์šฉ์ž ์‘๋‹ต ๊ฐ€๋Šฅ
    • ๋ฒ„ํผ๋ง๊ณผ ๋ถ€ํ•˜ ์™„์ถฉ
      • ํ์— ๋ฉ”์‹œ์ง€๋ฅผ ์ผ์‹œ ์ €์žฅํ•จ์œผ๋กœ์จ, ํŠธ๋ž˜ํ”ฝ ํญ์ฃผ๋‚˜ ์ฒ˜๋ฆฌ ์ง€์—ฐ ์ƒํ™ฉ์—์„œ๋„ ์•ˆ์ •์ ์ธ ํ๋ฆ„ ์œ ์ง€ ๊ฐ€๋Šฅ
    • ์žฌ์ฒ˜๋ฆฌ ๋ฐ ์‹ ๋ขฐ์„ฑ ๋ณด์žฅ
      • ์ฒ˜๋ฆฌ ์‹คํŒจ ์‹œ ๋ฉ”์‹œ์ง€๋ฅผ DLQ ๋“ฑ์œผ๋กœ ์ด๋™ ํ›„ ์žฌ์‹œ๋„ ๊ฐ€๋Šฅ → ๋ฐ์ดํ„ฐ ์œ ์‹ค ์—†์ด ์•ˆ์ •์ ์ธ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ

๐Ÿšฆ ์™œ ๋ฉ”์‹œ์ง€ ๊ธฐ๋ฐ˜ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•œ๊ฐ€?

๋™๊ธฐ ์ฒ˜๋ฆฌ์˜ ํ•œ๊ณ„

์•„๋ž˜ ์ฝ”๋“œ๋Š” ๋ฉ”์‹œ์ง€ ํ๋ฅผ ๋„์ž…ํ•˜๊ธฐ ์ „, ์ด๋ฒคํŠธ ์˜คํ”ˆ ์‹œ ์•Œ๋ฆผ์„ ์ง์ ‘ ์ „์†กํ•˜๋˜ ๋ฐฉ์‹์ด๋‹ค. openEventsIfDueBefore() ๋ฉ”์„œ๋“œ๋Š” DB์—์„œ ์˜คํ”ˆ ๋Œ€์ƒ ์ด๋ฒคํŠธ๋ฅผ ์กฐํšŒํ•œ ๋’ค, ํ•ด๋‹น ์ด๋ฒคํŠธ์— ์ฐธ์—ฌํ•œ ์‚ฌ์šฉ์ž ๋ชฉ๋ก์„ ๊ฐ€์ ธ์™€ notificationService.createNotification()์„ ์ง์ ‘ ํ˜ธ์ถœํ•ด ์•Œ๋ฆผ์„ ์ „์†กํ•œ๋‹ค. ์ด๋•Œ ์•Œ๋ฆผ ์ „์†ก์€ ๋™๊ธฐ์ ์œผ๋กœ ์ฒ˜๋ฆฌ๋˜๋ฉฐ, ์œ ์ € ์ˆ˜๊ฐ€ ๋งŽ์•„์งˆ์ˆ˜๋ก for๋ฌธ ๋‚ด๋ถ€ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„์ด ๊ธธ์–ด์ง€๊ณ , ์ „์ฒด ํŠธ๋žœ์žญ์…˜์ด ์ง€์—ฐ๋  ๊ฐ€๋Šฅ์„ฑ์ด ํฌ๋‹ค.

 

๋™๊ธฐ ์ฒ˜๋ฆฌ์˜ ํ•œ๊ณ„๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ •๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

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

๋ฉ”์‹œ์ง€ ํ ๋„์ž… ์ „ํ›„ ๋น„๊ต

MQ๋ฅผ ์ ์šฉํ•˜์ง€ ์•Š์€ ๊ธฐ์กด ๊ตฌ์กฐ์˜ ํ๋ฆ„์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. ์•Œ๋ฆผ ์ „์†ก๊นŒ์ง€ ํ•œ ํŠธ๋žœ์žญ์…˜์—์„œ ์ฒ˜๋ฆฌ๋˜์–ด ์‘๋‹ต ์ง€์—ฐ์ด ๋ฐœ์ƒํ•  ํ™•๋ฅ ์ด ๋†’์•˜๊ณ , ์‹คํŒจ ์‹œ ์ „์ฒด ํ๋ฆ„์ด ์ค‘๋‹จ๋  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

[Scheduler ์‹คํ–‰ (์ฃผ๊ธฐ์ )]
       โ”‚
       โ–ผ
[DB์—์„œ READY ์ด๋ฒคํŠธ ์กฐํšŒ]
       โ”‚
       โ–ผ
[์ด๋ฒคํŠธ ์ƒํƒœ OPEN์œผ๋กœ ๋ณ€๊ฒฝ]
       โ”‚
       โ–ผ
[์œ ์ € ์กฐํšŒ → ์•Œ๋ฆผ ์ง์ ‘ ์ „์†ก (๋™๊ธฐ)]
       โ”‚
       โ–ผ
[์™„๋ฃŒ]

 

MQ ๋„์ž… ํ›„์—” ์•Œ๋ฆผ ๋กœ์ง์ด ๋ฉ”์‹œ์ง€ ํ๋ฅผ ํ†ตํ•ด ๋น„๋™๊ธฐ๋กœ ์ฒ˜๋ฆฌ๋˜๋ฉฐ, Scheduler๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐœํ–‰๋งŒ ํ•˜๊ณ , ์•Œ๋ฆผ ์ „์†ก์€ Consumer๊ฐ€ ๋ณ‘๋ ฌ๋กœ ์ฒ˜๋ฆฌํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค. ๊ตฌ์กฐ๋Š” ๋ณต์žกํ•ด์กŒ์ง€๋งŒ, ์‹œ์Šคํ…œ ํ™•์žฅ์„ฑ๊ณผ ์•ˆ์ •์„ฑ ์ธก๋ฉด์—์„  ๋ช…ํ™•ํ•œ ๊ฐœ์„ ์ด ์žˆ์—ˆ๋‹ค.

[์ด๋ฒคํŠธ ๋“ฑ๋ก ์‹œ]
       โ”‚
       โ–ผ
[Redis ZSET์— Event ๋ฉ”์‹œ์ง€ ์ €์žฅ]
       โ”‚
       โ–ผ
[Scheduler ์‹คํ–‰ (์ฃผ๊ธฐ์ )]
       โ”‚
       โ–ผ
[Redis ZSET์—์„œ ์˜คํ”ˆ ๋Œ€์ƒ ์กฐํšŒ]
       โ”‚
       โ–ผ
[DB ์ƒํƒœ OPEN์œผ๋กœ ๋ณ€๊ฒฝ]
       โ”‚
       โ–ผ
[RabbitMQ๋กœ ์˜คํ”ˆ ๋ฉ”์‹œ์ง€ ๋ฐœํ–‰]
       โ”‚
       โ–ผ
[Consumer๊ฐ€ ์•Œ๋ฆผ ๋น„๋™๊ธฐ ์ „์†ก]
       โ”‚
       โ–ผ
[์™„๋ฃŒ]

๐Ÿ‡ RabbitMQ ๊ธฐ๋ฐ˜ ์•Œ๋ฆผ ์ฒ˜๋ฆฌ ์ „๋žต

Exchange ํƒ€์ž…

๐Ÿ“Œ Exchange๋ž€?
RabbitMQ์—์„œ ๋ฉ”์‹œ์ง€๋ฅผ ์ˆ˜์‹ ํ•˜๊ณ , ์ ์ ˆํ•œ Queue๋กœ ๋ผ์šฐํŒ…ํ•˜๋Š” ์—ญํ• ์„ ํ•˜๋Š” ๊ตฌ์„ฑ ์š”์†Œ.
Producer๊ฐ€ ๋ณด๋‚ธ ๋ฉ”์‹œ์ง€๋Š” ๋จผ์ € Exchange๋กœ ๋„์ฐฉํ•˜๊ณ ,
Exchange๋Š” ๋ฉ”์‹œ์ง€์˜ RoutingKey๋‚˜ ์„ค์ •๋œ ๊ทœ์น™์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์–ด๋–ค Queue๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ผ์ง€ ๊ฒฐ์ •ํ•œ๋‹ค.
  • Direct : ์ •ํ™•ํžˆ ์ผ์น˜ํ•˜๋Š” RoutingKey๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ์ „๋‹ฌ
  • Fanout : RoutingKey ์—†์ด, ๋ฐ”์ธ๋”ฉ๋œ ๋ชจ๋“  Queue์— ๋ฉ”์‹œ์ง€๋ฅผ ์ „ํŒŒ
  • Topic : ํŒจํ„ด ๊ธฐ๋ฐ˜ RoutingKey ๋งค์นญ, ๋ผ์šฐํŒ… ํŒจํ„ด ๊ฐ„์˜ ์™€์ผ๋“œ์นด๋“œ(*) ํ˜น์€ ํ•ด์‹œ(#)๊ฐ€ ์ผ์น˜ํ•ด์•ผ ๋งค์นญ
  • Headers : ๋ฉ”์‹œ์ง€์˜ ํ—ค๋” ๊ฐ’์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ผ์šฐํŒ…

ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ์ด๋ฒคํŠธ ์˜คํ”ˆ ์‹œ, ์•Œ๋ฆผ์„ ์„ค์ •ํ•œ ๋ชจ๋“  ์œ ์ €์—๊ฒŒ ์•Œ๋ฆผ์„ ๋ณด๋‚ด์•ผ ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— Fanout ์œผ๋กœ ์„ค์ •ํ–ˆ๋‹ค.

DLQ  ·  DLX ๊ตฌ์กฐ

  • DLQ(Dead Letter Queue) : ๋ฉ”์‹œ์ง€ ์†Œ๋น„์— ์‹คํŒจํ•œ ๊ฒฝ์šฐ๋ฅผ ๋Œ€๋น„ํ•ด ๋ฉ”์‹œ์ง€๋ฅผ ๋ณ„๋„๋กœ ์ €์žฅํ•˜๊ณ  ์ถ”ํ›„ ์žฌ์ฒ˜๋ฆฌํ•˜๊ฑฐ๋‚˜ ๋กœ๊ทธ๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๊ตฌ์กฐ
  • ์‹คํŒจํ•œ ๋ฉ”์‹œ์ง€๋Š” DLX → DLQ๋กœ ์ด๋™, ์ดํ›„ ์ˆ˜๋™ ์žฌ์ฒ˜๋ฆฌ ๋˜๋Š” Retry Queue๋กœ์˜ ์ž๋™ ์žฌ์ฒ˜๋ฆฌ ๊ธฐ๋ฐ˜

TTL ๊ธฐ๋ฐ˜ Retry ์ฒ˜๋ฆฌ

  • DLQ์— ์Œ“์ธ ๋ฉ”์‹œ์ง€๋ฅผ ์ž๋™์œผ๋กœ ์žฌ์‹œ๋„ํ•˜๊ธฐ ์œ„ํ•ด TTL(Time To Live)๊ณผ Retry Queue๋ฅผ ํ™œ์šฉ
  • ์‹คํŒจํ•œ ๋ฉ”์‹œ์ง€๋Š” DLQ์—์„œ ๋‹ค์‹œ Retry Queue๋กœ ์ „์†ก
  • ํ•ด๋‹น Queue์—๋Š” TTL์ด ์„ค์ •๋˜์–ด ์žˆ์–ด ์ผ์ • ์‹œ๊ฐ„ ๋’ค ์›๋ž˜ Exchange๋กœ ์žฌ์ „์†ก