๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

ํ”„๋กœ์ ํŠธ/Airbnb Clone

@Async๋กœ ์ด๋ฉ”์ผ ์ „์†ก์„ Async-NonBlocking ์ฒ˜๋ฆฌํ•˜๊ธฐ: ์“ฐ๋ ˆ๋“œ ํ’€ ์„ค์ •๊ณผ API ์‘๋‹ต ์†๋„ 99% ๊ฐœ์„ 

@Async ๋ฅผ ๋„์ž…ํ•˜๋ฉฐ ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ๊ณผ ์“ฐ๋ ˆ๋“œ ๋ฉ”๋ชจ๋ฆฌ ๊ตฌ์กฐ ์ง€์‹์„ ๋ฐ”ํƒ•์œผ๋กœ ์“ฐ๋ ˆ๋“œ ํ’€ ์„ค์ •์„ ํ•™์Šตํ•˜๊ณ  ์ ์šฉํ•˜๋Š” ๊ณผ์ •์„ ์†Œ๊ฐœํ•˜๋ฉฐ Jmeter ๋ฅผ ํ†ตํ•ด API ์‘๋‹ต ์†๋„๊ฐ€ ๊ฐœ์„ ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๊ณผ์ •์„ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ชฉ์ฐจ๋กœ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค.

์ด๋ฉ”์ผ ์ „์†ก์„ ๋น„๋™๊ธฐ๋กœ ๊ฒฐ์ •ํ•œ ์ด์œ 

์“ฐ๋ ˆ๋“œ ํ’€ ์‚ฌ์šฉ ์—ฌ๋ถ€ ๊ณ ๋ ค

์“ฐ๋ ˆ๋“œ ํ’€ ์„ค์ • ๊ณ ๋ ค ์‚ฌํ•ญ

์“ฐ๋ ˆ๋“œ ํ’€ ์„ค์ •

ThreadPoolTaskExecutor ๋™์ž‘ ๋ฐฉ์‹

์“ฐ๋ ˆ๋“œ ํ’€ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ ์„ค์ •

Jmeter ๋กœ @Async ๋„์ž… ์ ์šฉ ์ „๊ณผ ํ›„์˜ API ์‘๋‹ต ์‹œ๊ฐ„์„ ๋น„๊ต

@Async ์‚ฌ์šฉ ์‹œ ์ฃผ์˜์ 

 

์ด๋ฉ”์ผ ์ „์†ก์„ ๋น„๋™๊ธฐ๋กœ ๊ฒฐ์ •ํ•œ ์ด์œ 

์—์–ด๋น„์•ค๋น„ ํด๋ก  ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ์ด๋ฉ”์ผ ์ธ์ฆ ๊ธฐ๋ฐ˜์˜ ํšŒ์›๊ฐ€์ž…/๋กœ๊ทธ์ธ์„ ์ง„ํ–‰ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

 

์ด๋ฉ”์ผ ์ „์†ก์—๋Š” Spring Mail์˜ JavaMailSender๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, ์ด๋Š” ๋™๊ธฐ API์ž…๋‹ˆ๋‹ค.

์ฆ‰, ๋ฉ”์ผ์„ ๋ณด๋‚ด๊ธฐ ์œ„ํ•ด ์„œ๋น„์Šค๊ฐ€ ๋ฉ”์ผ ์„œ๋ฒ„์™€ TCP/IP ์—ฐ๊ฒฐ์„ ์„ค์ •ํ•˜๊ณ  SMTP ํ”„๋กœํ† ์ฝœ๋กœ ์š”์ฒญ์„ ๋ณด๋‚ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋ฉ”์ผ์„ ๋ณด๋‚ด์ง€ ๋ชปํ–ˆ๋‹ค๋ฉด ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ "๋ฉ”์ผ์„ ๋ณด๋‚ด์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค" ๋ผ๊ณ  ์‘๋‹ต์„ ๋ณด๋‚ผ ํ•„์š”๊ฐ€ ์—†๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ, ๋ฉ”์ผ ์„œ๋ฒ„์˜ ์‘๋‹ต์„ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ  ๋น„๋™๊ธฐ๋กœ ์ฒ˜๋ฆฌํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋™๊ธฐ ๋ฐฉ์‹์˜ API ์‘๋‹ต ๊ณผ์ •

 

๊ทธ๋ž˜์„œ, ๋ฉ”์ผ ์ „์†ก์—๋Š” @Async๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ์ ์šฉํ•˜๊ณ , ๋ฉ”์ผ ์„œ๋ฒ„์˜ API ์‘๋‹ต์„ Non-blocking ๋ฐฉ์‹์œผ๋กœ ์ฒ˜๋ฆฌํ•˜์—ฌ ์‘๋‹ต ์†๋„๋ฅผ ๊ฐœ์„ ํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

@Async ๊ด€๋ จ ๋‚ด์šฉ์€ Baeldung์—์„œ ํ•™์Šตํ–ˆ์Šต๋‹ˆ๋‹ค.

์ธ์ฆ ๋ฉ”์ผ ์ „์†ก์€ ๋น„๋™๊ธฐ ์“ฐ๋ ˆ๋“œ์—์„œ ์‹คํ–‰, 200 OK ์‘๋‹ต์€ Non-Blocking ์ฒ˜๋ฆฌํ•˜๋Š” ๋„์‹ํ™”

 

์“ฐ๋ ˆ๋“œ ํ’€ ์‚ฌ์šฉ ์—ฌ๋ถ€ ๊ณ ๋ ค

Spring์˜ @Async๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ SimpleAsyncTaskExecutor๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์ด๋Š” ์š”์ฒญ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ์Šค๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•ด ์ž‘์—…์„ ํ• ๋‹นํ•˜๋ฏ€๋กœ, 10๊ฐœ์˜ ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด 10๊ฐœ์˜ ์Šค๋ ˆ๋“œ๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.

 

ํ•˜์ง€๋งŒ ์„œ๋น„์Šค ํŠน์„ฑ์ƒ ๋ฉ”์ผ ์ „์†ก ์ž‘์—…์€ ์ฃผ๋กœ ํšŒ์›๊ฐ€์ž… ์‹œ์—๋งŒ ๋ฐœ์ƒํ•˜๋ฉฐ, ๋นˆ๋ฒˆํ•˜๊ฒŒ ์ผ์–ด๋‚˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ด ๋•Œ๋ฌธ์—, ์“ฐ๋ ˆ๋“œ ํ’€์„ ๋งŒ๋“ค์–ด ์œ ํœด ์ƒํƒœ์˜ ์Šค๋ ˆ๋“œ๋ฅผ ๋ฏธ๋ฆฌ ์œ ์ง€ํ•˜๋Š” ๊ฒƒ์€ ์˜คํžˆ๋ ค ๋ฉ”๋ชจ๋ฆฌ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค.(์“ฐ๋ ˆ๋“œ๋งˆ๋‹ค ๊ณ ์œ ์˜ ์Šคํƒ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ฐจ์ง€ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค)

 

๋ฐ˜๋ฉด, ์“ฐ๋ ˆ๋“œ ํ’€์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด ์ตœ์•…์˜ ๊ฒฝ์šฐ 100๊ฐœ์˜ ์š”์ฒญ์ด ๋™์‹œ์— ๋“ค์–ด์™€ 100๊ฐœ์˜ ์Šค๋ ˆ๋“œ๊ฐ€ ์ƒ์„ฑ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋Š” ๋ฉ”๋ชจ๋ฆฌ ์˜ค๋ฒ„ํ—ค๋“œ๋ฅผ ์ดˆ๋ž˜ํ•  ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, ์Šค๋ ˆ๋“œ ์ˆ˜๊ฐ€ ๋งŽ์•„์ง์— ๋”ฐ๋ผ ๊ณผ๋„ํ•œ ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ์œผ๋กœ ์ธํ•ด CPU ์˜ค๋ฒ„ํ—ค๋“œ๋„ ๋ฐœ์ƒํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

๋”ฐ๋ผ์„œ, ์ตœ์•…์˜ ๊ฒฝ์šฐ๋ฅผ ๋Œ€๋น„ํ•ด ๋ฌด๋ฆฌํ•˜๊ฒŒ ์Šค๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ๋ณด๋‹ค๋Š”, ์“ฐ๋ ˆ๋“œ ํ’€์„ ์‚ฌ์šฉํ•˜์—ฌ ์Šค๋ ˆ๋“œ ์ˆ˜๋ฅผ ์ ์ • ์ˆ˜์ค€์œผ๋กœ ์ œํ•œํ•˜๋Š” ๋ฐฉ์•ˆ์„ ์„ ํƒํ–ˆ์Šต๋‹ˆ๋‹ค.

 

์“ฐ๋ ˆ๋“œ ํ’€ ์„ค์ • ๊ณ ๋ ค ์‚ฌํ•ญ

ํ•ด๋‹น ๋ถ€๋ถ„์„  ์‰ฌ์šด์ฝ”๋“œ - ์“ฐ๋ ˆ๋“œ ํ’€ ์˜์ƒ์„ ๋ณด๋ฉฐ ์ •๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.

 

1. ์“ฐ๋ ˆ๋“œ ํ’€์˜ ์“ฐ๋ ˆ๋“œ ๊ฐœ์ˆ˜ ์„ค์ •

์“ฐ๋ ˆ๋“œ ํ’€์—์„œ ์‚ฌ์šฉํ•  ์“ฐ๋ ˆ๋“œ ๊ฐœ์ˆ˜๋Š” CPU์˜ ๊ฐœ์ˆ˜์™€ ์ž‘์—…(task)์˜ ์œ ํ˜•์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง‘๋‹ˆ๋‹ค.

  • CPU-bound ์ž‘์—…
    • CPU ์ฝ”์–ด ์ˆ˜์™€ ๊ฐ™๊ฑฐ๋‚˜ 1~2๊ฐœ ๋งŽ์€ ์ •๋„๋กœ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ด ์ผ๋ฐ˜์ ์ž…๋‹ˆ๋‹ค.
  • I/O-bound ์ž‘์—…
    • I/O ์ž‘์—…์€ CPU์— ๋œ ์˜์กดํ•˜๋ฏ€๋กœ, ์ฝ”์–ด ์ˆ˜์˜ 1.5๋ฐฐ, 2๋ฐฐ, ํ˜น์€ 3๋ฐฐ ์ •๋„๋กœ ์„ค์ •ํ•ด๋„ ๋ฌด๋ฐฉํ•ฉ๋‹ˆ๋‹ค.
    • ์ด ์ˆ˜์น˜๋Š” ๊ฒฝํ—˜์ ์œผ๋กœ ์ตœ์ ์˜ ์„ฑ๋Šฅ์„ ์ฐพ๊ธฐ ์œ„ํ•ด ์กฐ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

2. ์“ฐ๋ ˆ๋“œ ํ’€์˜ Queue ์‚ฌ์ด์ฆˆ ์„ค์ •

Queue์˜ ์‚ฌ์ด์ฆˆ๊ฐ€ ๋ฌดํ•œ๋Œ€์ธ ๊ฒฝ์šฐ Queue์— ์š”์ฒญ์ด ์ง€์†ํ•ด์„œ ์Œ“์ด๊ฒŒ ๋˜๋ฉฐ Queue์˜ ์‚ฌ์ด์ฆˆ๊ฐ€ ๋งค์šฐ ์ปค์ ธ OOME๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์ž ์žฌ ์š”์ธ์ž…๋‹ˆ๋‹ค.

Queue์˜ ์‚ฌ์ด์ฆˆ์— ์ œํ•œ์„ ๋‘์–ด, ํŠน์ • ์š”์ฒญ์ด ์ดˆ๊ณผ๋˜๋”๋ผ๋„ ์„œ๋ฒ„ ์ „์ฒด์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

 

์ž๋ฐ”์˜ Executors.newFixedThreadPool()๋„ ๊ธฐ๋ณธ Queue ํฌ๊ธฐ๋Š” Integer.MAX_VALUE(์•ฝ 20์–ต)๋กœ, ์ œํ•œ ์—†์ด ์š”์ฒญ์ด ๋ˆ„์ ๋  ์ˆ˜ ์žˆ์–ด ๋ฉ”๋ชจ๋ฆฌ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์“ฐ๋ ˆ๋“œ ํ’€ ์„ค์ •

์ด์ œ 2๊ฐ€์ง€ ๊ณ ๋ ค์‚ฌํ•ญ์„ ๋ฐ”ํƒ•์œผ๋กœ ์ด๋ฉ”์ผ ์ „์†ก ์ž‘์—…์„ ์œ„ํ•œ ์“ฐ๋ ˆ๋“œ ํ’€์„ ์„ค์ •ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

1. ์“ฐ๋ ˆ๋“œ ๊ฐœ์ˆ˜ ์„ค์ •

linux์˜ lscpu ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•ด CPU ์•„ํ‚คํ…์ฒ˜๋ฅผ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.

Socket(s): 1

  • ๋ฌผ๋ฆฌ์ ์ธ CPU ๊ฐœ์ˆ˜๊ฐ€ 1๊ฐœ์ž…๋‹ˆ๋‹ค.

Core(s) per socket: 8

  • ๋ฌผ๋ฆฌ์ ์ธ CPU 1๊ฐœ์— 8๊ฐœ์˜ ์ฝ”์–ด๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

Thread(s) per core: 2

  • ์ฝ”์–ด 1๊ฐœ๋‹น 2๊ฐœ์˜ ์“ฐ๋ ˆ๋“œ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

CPU(s): 16

  • ๋…ผ๋ฆฌ์ ์ธ CPU ๊ฐœ์ˆ˜๊ฐ€ 16๊ฐœ์ž…๋‹ˆ๋‹ค.
  • 1๊ฐœ์˜ CPU๊ฐ€ 8๊ฐœ์˜ ์ฝ”์–ด๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๊ณ , ๊ฐ๊ฐ์˜ ์ฝ”์–ด๊ฐ€ 2๊ฐœ์˜ ์“ฐ๋ ˆ๋“œ๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ, I/O-bound ์ž‘์—…์ธ ๋ฉ”์ผ ์ „์†ก ์ž‘์—…์„ ์œ„ํ•ด ์“ฐ๋ ˆ๋“œ ํ’€์˜ ์ตœ๋Œ€ ์“ฐ๋ ˆ๋“œ ์ˆ˜๋Š” ๋…ผ๋ฆฌ CPU ๊ฐœ์ˆ˜์ธ 16์œผ๋กœ, ๊ธฐ๋ณธ ์“ฐ๋ ˆ๋“œ ์ˆ˜๋Š” 8๊ฐœ์˜ ์ฝ”์–ด ์˜ 1.5๋ฐฐ์ธ 12๋กœ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

 

2. Queue์‚ฌ์ด์ฆˆ ์ œํ•œํ•˜๊ธฐ

Spring ์˜ ์“ฐ๋ ˆ๋“œํ’€์ธ ThreadPoolTaskExecutor ๋ฅผ ์‚ฌ์šฉํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

ํ•ด๋‹น ์ฝ”๋“œ๋ฅผ ๊นŒ๋ณด๋ฉด Queue ์‚ฌ์ด์ฆˆ๊ฐ€ ์ œํ•œ์ด ์—†์œผ๋ฏ€๋กœ(int queueCapacity = Integer.MAX_VALUE), Queue ์‚ฌ์ด์ฆˆ๋ฅผ ์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

์ด๋ฉ”์ผ ์ธ์ฆ ๋ณด๋‚ด๋Š” ์ž‘์—…์ด ๋™์‹œ์— ๋งŽ์ง„ ์•Š๋‹ค๊ณ  ์ƒ๊ฐํ•ด์„œ ์ž„์˜์˜ ์ˆซ์ž์ธ 50์œผ๋กœ ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

 

์“ฐ๋ ˆ๋“œ ํ’€ ์„ค์ • ์ „์ฒด ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

@EnableAsync
@Configuration
public class MailConfig implements AsyncConfigurer {

    @Override
    @Bean
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

        executor.setCorePoolSize(12); // ๊ธฐ๋ณธ ์“ฐ๋ ˆ๋“œ ๊ฐœ์ˆ˜
        executor.setMaxPoolSize(16);  // ์ตœ๋Œ€ ์“ฐ๋ ˆ๋“œ ํ’€ ๊ฐœ์ˆ˜  
        executor.setQueueCapacity(50);  // ์ตœ๋Œ€ Queue Size
        executor.setThreadNamePrefix("MailExecutor-"); // ์“ฐ๋ ˆ๋“œ ํ’€์˜ ์“ฐ๋ ˆ๋“œ ์ด๋ฆ„
        executor.initialize();          // custom ํ•œ ์„ค์ •๋“ค์„ ์“ฐ๋ ˆ๋“œ ํ’€์— ๋ฐ˜์˜
        
        return executor;
    }
}

 

ThreadPoolTaskExecutor ๋™์ž‘ ๋ฐฉ์‹

์Šคํ”„๋ง์ด ์ง€์›ํ•˜๋Š” ThreadPool ์ธ ThreadPoolTaskExecutor ๋Š” ์–ด๋–ป๊ฒŒ ์ž‘๋™์›๋ฆฌ์— ๋Œ€ํ•ด ์•Œ์•„๋ด…๋‹ˆ๋‹ค.

ํ•ด๋‹น ๋ธ”๋กœ๊ทธ๋Š” ThreadPoolExecutor ๋™์ž‘ ๋ฐฉ์‹ ๋ฐ ์ฃผ์˜ ์‚ฌํ•ญ ์—์„œ ์„ค๋ช…ํ•˜์ง€๋งŒ Spring ์˜ ThreadPoolTaskExecutor ์€ ๋‚ด๋ถ€ ๊ตฌํ˜„์œผ๋กœ Java์˜ ThreadPoolExecutor ์„ ์‚ฌ์šฉํ•˜๋ฉฐ, ๋‹จ์ง€ Spring ์— ์‚ฌ์šฉํ•˜๊ธฐ ์‰ฝ๋„๋ก ์ถ”์ƒํ™”๋œ ๊ฒƒ์œผ๋กœ ์ž‘๋™ ๋ฉ”์ปค๋‹ˆ์ฆ˜์€ ๋™์ผํ•ฉ๋‹ˆ๋‹ค.

(์ถœ์ฒ˜ : Difference and suggest ThreadPoolTaskExecutor and ThreadPoolExecutor)

 

ThreadPoolExecutor์˜ ์ž‘๋™ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์š”์•ฝํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค

  • ๊ธฐ๋ณธ ์Šค๋ ˆ๋“œ ์ƒ์„ฑ: setCorePoolSize(12)๋กœ ์„ค์ •๋œ ๊ธฐ๋ณธ ์Šค๋ ˆ๋“œ ์ˆ˜(12๊ฐœ)๋งŒํผ ์Šค๋ ˆ๋“œ๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.
  • ํ์— ์ž‘์—… ์ถ”๊ฐ€: ๋งŒ์•ฝ ๋™์‹œ์— 12๊ฐœ ์ด์ƒ์˜ ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด, ์ถ”๊ฐ€ ์ž‘์—…์€ ๋Œ€๊ธฐ์—ด(Queue)์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.
  • ์Šค๋ ˆ๋“œ ํ’€ ํ™•์žฅ: ๋Œ€๊ธฐ์—ด์ด ๊ฐ€๋“ ์ฐจ๋ฉด, setMaxPoolSize()๋กœ ์„ค์ •๋œ ์ตœ๋Œ€ ์Šค๋ ˆ๋“œ ์ˆ˜๊นŒ์ง€ ์Šค๋ ˆ๋“œ ํ’€์„ ํ™•์žฅํ•˜์—ฌ ์ถ”๊ฐ€ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
  • ์œ ํœด ์Šค๋ ˆ๋“œ ์ œ๊ฑฐ: ์ตœ๋Œ€ ์Šค๋ ˆ๋“œ ์ˆ˜๋งŒํผ ์Šค๋ ˆ๋“œ๊ฐ€ ์ƒ์„ฑ๋˜์—ˆ๋‹ค๊ฐ€ ์œ ํœด ์ƒํƒœ๊ฐ€ ๋˜๋ฉด, 60์ดˆ(๊ธฐ๋ณธ ์„ค์ •) ํ›„์— ์œ ํœด ์Šค๋ ˆ๋“œ๊ฐ€ ์ ์ฐจ ์ œ๊ฑฐ๋˜์–ด, ์Šค๋ ˆ๋“œ ํ’€์€ setCorePoolSize()๋กœ ์„ค์ •๋œ ๊ธฐ๋ณธ ์Šค๋ ˆ๋“œ ์ˆ˜๋กœ ๋Œ์•„๊ฐ‘๋‹ˆ๋‹ค.
  • ์ž‘์—… ๊ฑฐ๋ถ€: ๋Œ€๊ธฐ์—ด์ด ๊ฐ€๋“ ์ฐจ๊ณ  ์Šค๋ ˆ๋“œ ์ˆ˜๊ฐ€ MaxPoolSize์— ๋„๋‹ฌํ•œ ํ›„์—๋„ ์ƒˆ๋กœ์šด ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด RejectedExecutionException ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉฐ ํ•ด๋‹น ์ž‘์—…์€ ๊ฑฐ์ ˆ๋ฉ๋‹ˆ๋‹ค.

RejectedExecutionException ์— ๋Œ€ํ•œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋Š” ๋ฉ”์ผ ๋ณด๋‚ด๋Š” ๊ธฐ๋Šฅ์— ์ค‘์š”ํ•˜์ง€ ์•Š๋‹ค๊ณ  ์ƒ๊ฐํ•˜์—ฌ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š๊ณ , ๋’ค ์ด์–ด ์†Œ๊ฐœํ•   AsyncUncaughtExceptionHandler ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋กœ๊ทธ๋งŒ ๋‚จ๊ธฐ๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.

 

์“ฐ๋ ˆ๋“œ ํ’€ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ ์„ค์ •

@Async(๋น„๋™๊ธฐ)๋กœ ์ฒ˜๋ฆฌ๋˜๋Š” ๋ฉ”์„œ๋“œ์—์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์˜ค๋ฅ˜๋Š” ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ• ๊นŒ์š”?

Baeldung์— ๋”ฐ๋ฅด๋ฉด, ๋น„๋™๊ธฐ ๋ฉ”์„œ๋“œ์˜ ๋ฐ˜ํ™˜ ํƒ€์ž…์ด Future์ธ ๊ฒฝ์šฐ Future.get()์„ ํ†ตํ•ด ๋Ÿฐํƒ€์ž„ ์˜ˆ์™ธ๋ฅผ ์žก์•„ ํ•ธ๋“ค๋งํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ, ๋น„๋™๊ธฐ ๋ฉ”์„œ๋“œ๊ฐ€ void ๋ฐ˜ํ™˜ ํƒ€์ž…์ธ ๊ฒฝ์šฐ์—๋Š” ๋Ÿฐํƒ€์ž„ ์˜ˆ์™ธ๊ฐ€ ๋ฌด์‹œ๋ฉ๋‹ˆ๋‹ค.

@Async๊ฐ€ ์žˆ๋Š” ๋ฉ”์„œ๋“œ ๋ฐ˜ํ™˜ ํƒ€์ž…์ด void ๋ผ ์˜ˆ์™ธ ์ „ํŒŒ X

 

์ €์˜ ๋น„๋™๊ธฐ๊ฐ€ ์ ์šฉ๋  ๋ฉ”์„œ๋“œ๋Š” void ๋ฐ˜ํ™˜ํƒ€์ž… ์ด๊ธฐ ๋•Œ๋ฌธ์—, ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ ์ค‘ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ ์ด๋ฅผ ์ธ์ง€ํ•  ์ˆ˜ ์žˆ๋„๋ก AsyncUncaughtExceptionHandler ๋ฉ”์„œ๋“œ๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋“œํ•˜์—ฌ log.error๋กœ ์˜ˆ์™ธ๋ฅผ ๊ธฐ๋กํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

 

์˜ค๋ฒ„๋ผ์ด๋”ฉ์„ ํ•˜์ง€ ์•Š์œผ๋ฉด, ์•„๋ž˜์™€ ๊ฐ™์€ ๋‹ค์–‘ํ•œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์ด๋Ÿฌํ•œ ์˜ˆ์™ธ๋Š” AsyncUncaughtExceptionHandler๋ฅผ ํ†ตํ•ด ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

org.springframework.mail.MailSendException: Mail server connection failed. Failed messages: jakarta.mail.MessagingException: Could not connect to SMTP host: smtp.naver.com, port: 465;
Caused by: jakarta.mail.MessagingException: Could not connect to SMTP host: smtp.naver.com, port: 465
Caused by: javax.net.ssl.SSLHandshakeException: Remote host terminated the handshake
Caused by: java.io.EOFException: SSL peer shut down incorrectly

 

AsyncUncaughtExceptionHandler ๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋”ฉํ•ฉ๋‹ˆ๋‹ค.

@EnableAsync
@Configuration
public class MailConfig implements AsyncConfigurer {

    ...์“ฐ๋ ˆ๋“œํ’€ ์„ค์ •...
    
    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return (ex, method, params) -> log.error("[๋ฉ”์ผ ์“ฐ๋ ˆ๋“œ ํ’€ ์—๋Ÿฌ] ๋ฐœ์ƒ ์œ„์น˜={}", method.toGenericString());
    }
}

 

Jmeter ๋กœ @Async ๋„์ž… ์ ์šฉ ์ „๊ณผ ํ›„์˜ API ์‘๋‹ต ์‹œ๊ฐ„์„ ๋น„๊ต

์ด์ œ, Jmeter ๋กœ ๋น„๋™๊ธฐ๋กœ ์ฒ˜๋ฆฌ ๋์„ ๋•Œ์˜, ์ด๋ฉ”์ผ์„ ๋ณด๋‚ด๋Š” API ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉฐ ์„ฑ๊ณต์ ์ธ์ง€ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

๋™๊ธฐ ๋ฐฉ์‹

  • ์“ฐ๋ ˆ๋“œ ํ’€์˜ ํฌ๊ธฐ์— ๋งž์ถฐ 12๊ฐœ์˜ ์š”์ฒญ(Request)์„ ๋ณด๋‚ธ ๊ฒฐ๊ณผ, ํ‰๊ท  ์‘๋‹ต ์‹œ๊ฐ„์€ 0.7์ดˆ, ์ตœ๋Œ€ ์‘๋‹ต ์‹œ๊ฐ„์€ 1.4์ดˆ์˜€์œผ๋ฉฐ, ์ดˆ๋‹น ์ฒ˜๋ฆฌ๋Ÿ‰(TPS)์€ 6์œผ๋กœ ์ธก์ •๋˜์—ˆ์Šต๋‹ˆ๋‹ค

 

๋น„๋™๊ธฐ ๋ฐฉ์‹

  • ๋™์ผํ•œ ์š”์ฒญ ์ˆ˜์—์„œ ํ‰๊ท  ์‘๋‹ต ์‹œ๊ฐ„์ด 0.007์ดˆ, ์ตœ๋Œ€ ์‘๋‹ต ์‹œ๊ฐ„์ด 0.01์ดˆ๋กœ ์ค„์—ˆ์œผ๋ฉฐ, ์ดˆ๋‹น ์ฒ˜๋ฆฌ๋Ÿ‰(TPS)์€ 13์œผ๋กœ ์ฆ๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค

 

์ด ๊ฒฐ๊ณผ๋กœ, ๋น„๋™๊ธฐ ๋ฐฉ์‹์œผ๋กœ ์ „ํ™˜ํ•จ์œผ๋กœ์จ ํ‰๊ท  ์‘๋‹ต ์‹œ๊ฐ„์ด ์•ฝ 99% ๊ฐ์†Œํ–ˆ์œผ๋ฉฐ, ์ตœ๋Œ€ ์‘๋‹ต ์‹œ๊ฐ„ ์—ญ์‹œ 99% ์ด์ƒ ๋‹จ์ถ•๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

 

@Async ์‚ฌ์šฉ ์‹œ ์ฃผ์˜์ 

1. ์“ฐ๋ ˆ๋“œ ํ’€์˜ ์“ฐ๋ ˆ๋“œ ๊ฐœ์ˆ˜์™€ Queue ์‚ฌ์ด์ฆˆ ์„ค์ •ํ•˜๊ธฐ

2. @Async ๋Š” Spring ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ Spring AOP ํ”„๋ก์‹œ ๊ธฐ๋ฐ˜์œผ๋กœ ์ž‘๋™ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, AOP ์‚ฌ์šฉ ์‹œ ์ฃผ์˜์ ์ธ self-invocation(์ž๊ธฐ ํ˜ธ์ถœ) ๊ณผ private ๋ฉ”์„œ๋“œ์ผ ๋•Œ, ์ ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

 

 

 

....

youtube ๋ฅผ ๋ณด๋ฉฐ ์žฌ๋ฐŒ์—ˆ๋˜ ์“ฐ๋ ˆ๋“œ ํ’€ ์‚ฌ์ง„์œผ๋กœ ๋งˆ๋ฌด๋ฆฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. 

์ด๋ฏธ์ง€ ์ถœ์ฒ˜: https://www.youtube.com/watch?v=YiTm0Pxvq30

์ถœ์ฒ˜

ํ…Œ์ฝ”๋ธ” - ์ด๋ฉ”์ผ ๋น„๋™๊ธฐ