Notice
Recent Posts
Recent Comments
Link
ยซ   2025/06   ยป
์ผ ์›” ํ™” ์ˆ˜ ๋ชฉ ๊ธˆ ํ† 
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
Tags
more
Archives
Today
Total
๊ด€๋ฆฌ ๋ฉ”๋‰ด

Mini

[์„ฑ๋Šฅ๊ฐœ์„ ] ์‹ค์‹œ๊ฐ„ ๊ฒŒ์ž„ ์„œ๋ฒ„ ์„ฑ๋Šฅ ์ตœ์ ํ™” - ๋ฉ€ํ‹ฐํ”„๋กœ์„ธ์„œ ๋ฐ ๋ฐฐ์น˜์ฒ˜๋ฆฌ๋ฅผ ํ†ตํ•ด 90% ์ด์ƒ์˜ ์„ฑ๋Šฅ ํ–ฅ์ƒ ๋ณธ๋ฌธ

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

[์„ฑ๋Šฅ๊ฐœ์„ ] ์‹ค์‹œ๊ฐ„ ๊ฒŒ์ž„ ์„œ๋ฒ„ ์„ฑ๋Šฅ ์ตœ์ ํ™” - ๋ฉ€ํ‹ฐํ”„๋กœ์„ธ์„œ ๋ฐ ๋ฐฐ์น˜์ฒ˜๋ฆฌ๋ฅผ ํ†ตํ•ด 90% ์ด์ƒ์˜ ์„ฑ๋Šฅ ํ–ฅ์ƒ

Mini_96 2025. 1. 14. 00:03

๐Ÿ‘€ ์„œ๋น„์Šค ํ•œ๋ˆˆ์— ๋ณด๊ธฐ

์‹ค์‹œ๊ฐ„์œผ๋กœ ํ€ด์ฆˆ ๋งž์ถ”๊ธฐ!

 

์‹ค์‹œ๊ฐ„ ํ€ด์ฆˆ ๊ฒŒ์ž„ ํ”Œ๋žซํผ ํ”„๋กœ์ ํŠธ์—์„œ

์„ฑ๋Šฅ ์ตœ์ ํ™”ํ•œ ๊ฒฝํ—˜์„ ๊ณต์œ ํ•˜๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค!

 

๋ถ€ํ•˜ํ…Œ์ŠคํŠธ๋ฅผ ํ†ตํ•ด ๋ฉ”ํŠธ๋ฆญ์„ ์ˆ˜์ง‘ํ–ˆ๊ณ , 

๋ณ‘๋ชฉ์˜ ์›์ธ์„ ๋ถ„์„ํ–ˆ์Šต๋‹ˆ๋‹ค. 

 

๊ทธ๋ฆฌ๊ณ  ์ตœ์ ํ™”ํ•œ ๋ฐฉ๋ฒ•๊นŒ์ง€

์ž์„ธํ•˜๊ฒŒ ๋‚จ๊ฒจ๋ณด๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค!

 


๐Ÿ“ ํ•œ ๊ฒŒ์ž„๋ฐฉ์— 200๋ช…์„ ์ง€์›ํ•˜๊ณ ์ž ํ•œ ์ด์œ  

ํ•œ ๊ฒŒ์ž„๋ฐฉ์— 200๋ช…์„ ์ง€์›ํ•˜๊ณ ์ž ํ•œ ์ด์œ ๋Š”..! ํŒ€์˜ ๋ชฉํ‘œ์˜€๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค!

ํ•œ ๊ฒŒ์ž„๋ฐฉ ๋‚ด 200๋ช…์˜ ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ์›ํ™œํžˆ ํ”Œ๋ ˆ์ดํ•  ์ˆ˜ ์žˆ๊ฒŒ ๊ตฌํ˜„

๊ฐ•์—ฐ ํ˜„์žฅ

๊ทธ๋Ÿฐ๋ฐ ์™œ 200๋ช…์ผ๊นŒ์š”? ์ €ํฌ์˜ ์„œ๋น„์Šค๊ฐ€ ์‚ฌ์šฉ๋˜๊ธธ ๊ธฐ๋Œ€ํ•˜๋Š” ์ƒํ™ฉ์€ ์œ„์™€ ๊ฐ™์•˜์Šต๋‹ˆ๋‹ค.

 

๋Œ€๊ทœ๋ชจ์˜ ์ธ์›์ด ํ•œ ์žฅ์†Œ์—์„œ ์ฆ๊ฒ๊ฒŒ ์•„์ด์Šค๋ธŒ๋ ˆ์ดํ‚น, ๋ ˆํฌ๋ฆฌ์—์ด์…˜ ๋“ฑ์˜ ์šฉ๋„๋กœ ์‚ฌ์šฉ๋˜๊ธธ ์›ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ์ง€๋ฃจํ•  ์ˆ˜๋„ ์žˆ๋Š” ๊ฐ•์—ฐ์ด๋‚˜ ํšŒ์˜์žฅ์—์„œ๋„ ์ฆ๊ฑฐ์›€์„ ๋”ํ•ด์ค„ ์ˆ˜ ์žˆ์œผ๋‹ˆ๊นŒ์š”! ๊ทธ๋ฆฌ๊ณ  ๋„ค์ด๋ฒ„ ๋ถ€์ŠคํŠธ์บ ํ”„ ์ธ์› ์ „์ฒด๋ฅผ ์ˆ˜์šฉํ•˜์ž๋Š” ์˜๋ฏธ์—์„œ 200๋ช…์ด๋ผ๋Š” ๊ตฌ์ฒด์ ์ธ ์ˆ˜์น˜๋ฅผ ์žก์•˜์Šต๋‹ˆ๋‹ค. 

 

๋ถ€ํ•˜ํ…Œ์ŠคํŠธ๋ฅผ ํ†ตํ•ด ํ•œ ๊ฒŒ์ž„๋ฐฉ ๋‚ด 200๋ช…์˜ ํŠธ๋ž˜ํ”ฝ์„ ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์›ํ•˜๋Š” ์„ฑ๋Šฅ์ด ๋‚˜์˜ค๋Š”์ง€ ์ฒดํฌํ•˜๊ณ , ๋ณ‘๋ชฉ์„ ๋ถ„์„ํ•˜์—ฌ ๊ฐœ์„ ํ•œ ๊ฒฝํ—˜์„ ์ด์•ผ๊ธฐํ•ด๋ณด๋ ค ํ•ฉ๋‹ˆ๋‹ค. 

 


๐Ÿš€ ๋ถ€ํ•˜ํ…Œ์ŠคํŠธ ์‹คํ–‰

๋จผ์ € ๋ถ€ํ•˜ํ…Œ์ŠคํŠธ๋ฅผ ํ†ตํ•ด ํ•œ ๊ฒŒ์ž„๋ฐฉ ๋‚ด 200๋ช…์˜ ํŠธ๋ž˜ํ”ฝ์„ ๋ฐ›์•„๋ณด๊ณ ์ž ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์›ํ™œํžˆ ๊ฒŒ์ž„์ด ๋˜๋Š”์ง€ ํ™•์ธํ•˜๊ณ ์ž ํ–ˆ๊ณ , ์ž˜ ์•ˆ ๋œ๋‹ค๋ฉด ๋ณ‘๋ชฉ ์ง€์ ์„ ๋ถ„์„ํ•˜์—ฌ ์ตœ์ ํ™”ํ•˜๊ณ ์ž ํ–ˆ์Šต๋‹ˆ๋‹ค. 

 

์‹œ๋‚˜๋ฆฌ์˜ค

๋ถ€ํ•˜ํ…Œ์ŠคํŠธ์˜ ์‹œ๋‚˜๋ฆฌ์˜ค๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์‹ค์ œ์™€ ๋น„์Šทํ•˜๊ฒŒ ๋ถ€ํ•˜๋ฅผ ์ฃผ๋ ค๊ณ  ๋…ธ๋ ฅํ–ˆ์Šต๋‹ˆ๋‹ค. 

  1. ํ•œ ๊ฒŒ์ž„๋ฐฉ์— 200๋ช…์˜ vuser๊ฐ€ ์ ‘์†
  2. ๊ฐ๊ฐ์˜ vuesr๋Š” 1์ดˆ์— 2๋ฒˆ ๊ผด๋กœ ์ด๋ฒคํŠธ(์œ„์น˜ ๋ณ€๊ฒฝ ํ˜น์€ ์ฑ„ํŒ… ๋ฉ”์‹œ์ง€ ์š”์ฒญ)๋ฅผ ์ „์†ก
    1. ์ด๋ฒคํŠธ์—๋Š” ์œ„์น˜ ๋ณ€๊ฒฝ ํ˜น์€ ์ฑ„ํŒ… ๋ฉ”์‹œ์ง€๊ฐ€ ์žˆ์Œ
    2. ์œ„์น˜ ๋ณ€๊ฒฝ(9):์ฑ„ํŒ… ๋ฉ”์‹œ์ง€(1)์˜ ๋น„์ค‘์œผ๋กœ ์š”์ฒญ
  3. ์ด 60์ดˆ ๋™์•ˆ ์ง€์† 

 

์ฒซ ๋ฒˆ์งธ ๋ถ€ํ•˜ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ

๋ถ€ํ•˜ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•œ ๊ฒฐ๊ณผ ์ฃผ์š” ์ด๋ฒคํŠธ์—์„œ ์•„๋ž˜์™€ ๊ฐ™์€ ์‘๋‹ต์‹œ๊ฐ„์ด ์ธก์ •๋˜์—ˆ์Šต๋‹ˆ๋‹ค. 

์—ฌ๊ธฐ์„œ์˜ ์‘๋‹ต์‹œ๊ฐ„์€ ๋„คํŠธ์›Œํฌ๋ฅผ ์ œ์™ธํ•œ ์„œ๋ฒ„ ์ž์ฒด์˜ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„์„ ์ธก์ •ํ•œ ๊ฐ’์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. 

์ฒซ ๋ฒˆ์งธ ๋ถ€ํ•˜ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ

 

ํ•˜๋Š˜์ƒ‰์ด '์บ๋ฆญํ„ฐ ์œ„์น˜ ๋ณ€๊ฒฝ', ์ฃผํ™ฉ์ƒ‰์ด '์ฑ„ํŒ… ๋ฉ”์‹œ์ง€ ์ „๋‹ฌ' ์‘๋‹ต์‹œ๊ฐ„์ž…๋‹ˆ๋‹ค! ์ง€ํ‘œ๋ฅผ ๋ณด๋ฉด 5์ดˆ, 8์ดˆ๊ฐ€ ๋‚˜์˜ค๋„ค์š”. 

 

์„ฑ๋Šฅ ์ธก์ • ๊ฒฐ๊ณผ, ์ฐธ๋‹ดํ•œ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์™”์Šต๋‹ˆ๋‹ค ใ…Žใ…Ž.. ์ด๋Ÿฐ ๊ฒŒ์ž„์„ ํ•˜๊ณ  ์‹ถ์€ ์‚ฌ์šฉ์ž๋Š” ์•„๋งˆ ์—†์„ ๊ฒ๋‹ˆ๋‹ค. ๊ฐœ์„ ์ด ์‹œ๊ธ‰ํ–ˆ์Šต๋‹ˆ๋‹ค!

 

์ฐธ๊ณ ๋กœ p95๋Š” ๋ฐฑ๋ถ„์œ„์ˆ˜๋กœ 95%์˜ ์š”์ฒญ์€ ํ•ด๋‹น ์‹œ๊ฐ„ ๋‚ด์— ์ฒ˜๋ฆฌ๋˜์—ˆ๋‹ค๋Š” ๊ฑธ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์‰ฝ๊ฒŒ ๋งํ•˜๋ฉด 100๋ช…์„ ์„ฑ์ ์ˆœ์œผ๋กœ ์ค„์„ ์„ธ์› ์„ ๋•Œ 95๋“ฑ์˜ ์„ฑ์ ์„ ๋œปํ•ฉ๋‹ˆ๋‹ค. 

 


๐Ÿ™Œ ์†Œ์ผ“ ์„œ๋ฒ„๋ฅผ ๋ฉ€ํ‹ฐ ํ”„๋กœ์„ธ์Šค๋กœ!

Why Multi Process?

์œ„ ์ธก์ • ๊ฒฐ๊ณผ๋ฅผ ๋ณด์‹œ๋ฉด ์•Œ๊ฒ ์ง€๋งŒ, ๊ฒŒ์ž„ ์ง„ํ–‰ ์ž์ฒด๊ฐ€ ํž˜๋“  ์ˆ˜์ค€์ž…๋‹ˆ๋‹ค. ๊ฒŒ์ž„์—์„œ ์บ๋ฆญํ„ฐ๋ฅผ ์ด๋™์‹œ์ผฐ๋Š”๋ฐ 5์ดˆ ๋’ค์— ์ด๋™ํ•œ๋‹ค๋ฉด ๋ˆ„๊ฐ€ ๊ทธ ๊ฒŒ์ž„์„ ํ• ๊นŒ์š”? ๊ทธ๋ž˜์„œ ๋„ˆ๋ฌด ๊ธด ์‘๋‹ต ์‹œ๊ฐ„์— ๋ฌธ์ œ์˜์‹์„ ์‚ผ๊ณ  ๊ฐœ์„ ํ•˜๊ณ ์ž ํ–ˆ์Šต๋‹ˆ๋‹ค. 

 

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

 

ํ”„๋กœ์ ํŠธ ๊ธฐ๊ฐ„์ด ์–ผ๋งˆ ๋‚จ์ง€ ์•Š์€ ์ƒํ™ฉ์ด๋ผ ์šฐ์„  ํ•ด๋ณผ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ๋ถ€ํ„ฐ ๋น ๋ฅด๊ฒŒ ์‹œ๋„ํ•˜๊ณ ์ž ํ–ˆ์Šต๋‹ˆ๋‹ค. 

 

๊ทธ๋ž˜์„œ ๋‚ธ ์†”๋ฃจ์…˜์€์š”!

 

๊ธฐ์กด์—๋Š” ๋‹จ์ผ ํ”„๋กœ์„ธ์Šค๋กœ ์„œ๋ฒ„๊ฐ€ ๋Œ์•„๊ฐ”๋Š”๋ฐ์š”.

 

๋ฐฐํฌ๋œ ์„œ๋ฒ„์˜ CPU๊ฐ€ 2๊ฐœ์ธ ์ ์„ ์ด์šฉํ•˜์—ฌ ๋‹จ์ผ ํ”„๋กœ์„ธ์Šค์—์„œ ๋ฉ€ํ‹ฐ ํ”„๋กœ์„ธ์Šค๋กœ ์ „ํ™˜ํ•˜๊ณ ์ž ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋™์‹œ์— ๋งŽ์€ ์š”์ฒญ์ด ๋“ค์–ด์™€๋„ ๋กœ๋“œ๋ฐธ๋Ÿฐ์‹ฑ์„ ํ†ตํ•ด ๋‘ ๊ฐœ์˜ ํ”„๋กœ์„ธ์Šค๊ฐ€ ๋‚˜๋ˆ ์„œ ์ผํ•˜๋„๋ก ํ•˜๋Š” ๊ฑธ ์œ ๋„ํ–ˆ์–ด์š”. 

 

๋ฉ€ํ‹ฐ ํ”„๋กœ์„ธ์Šค๋กœ ๋ฐ”๊พผ ํ›„ ๋‹ค์‹œ ๋ถ€ํ•˜ํ…Œ์ŠคํŠธํ•˜์—ฌ ์ถ”๊ฐ€๋กœ ๋ณ‘๋ชฉ์„ ์ฐพ์•„๋ณด๊ธฐ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค. 

 

๋ฉ€ํ‹ฐ ํ”„๋กœ์„ธ์Šค๋กœ ๋ฐ”๊พธ๋Š” ์ฃผ์š” ๊ณผ์ • 3๊ฐ€์ง€

1) ๊ฒŒ์ž„ ์„œ๋ฒ„๋ฅผ ๋ฉ€ํ‹ฐ ํ”„๋กœ์„ธ์Šค๋กœ ์‹คํ–‰

  • pm2๋ฅผ ํ†ตํ•ด 3000๋ฒˆ, 3001๋ฒˆ ํฌํŠธ๋กœ ํ”„๋กœ์„ธ์Šค 2๊ฐœ ์‹คํ–‰
  • ํด๋ผ์šฐ๋“œ ์„œ๋ฒ„ vCPU 2๊ฐœ์— ๋งž์ถฐ ํ”„๋กœ์„ธ์Šค 2๊ฐœ๋กœ ๊ฒฐ์ •

 

2) NginX๋กœ ๋กœ๋“œ ๋ฐธ๋Ÿฐ์‹ฑ

upstream backend-socket { 
    ip_hash;
    server localhost:3000;
    server localhost:3001;
}
  • NginX์—์„œ 3333๋ฒˆ ํฌํŠธ๋กœ ์˜ค๋Š” ๊ฑด ์†Œ์ผ“ ์—ฐ๊ฒฐ๋กœ 3000๋ฒˆ, 3001๋ฒˆ์œผ๋กœ ๋กœ๋“œ๋ฐธ๋Ÿฐ์‹ฑ
  • ip_hash๋กœ ํ•˜์—ฌ ์†Œ์ผ“ ์—ฐ๊ฒฐ์ด ์ง€์†์ ์œผ๋กœ ์ด๋ฃจ์–ด์งˆ ์ˆ˜ ์žˆ๊ฒŒ ํ•จ
  • ๋„ค์ด๋ฒ„ ํด๋ผ์šฐ๋“œ ์ฝ˜์†”์—์„œ ACG Inbound์— 3333๋ฒˆ ํฌํŠธ ์—ด๊ธฐ

 

 

์ดํ›„ ์•„ํ‚คํ…์ฒ˜

์ดํ›„์—๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ๋ฉ€ํ‹ฐ ํ”„๋กœ์„ธ์Šค๋กœ ์šด์˜ํ•  ์ˆ˜ ์žˆ๋Š” ๊ตฌ์กฐ๋กœ ๋ฐ”๋€Œ์—ˆ์Šต๋‹ˆ๋‹ค. 

์ดํ›„ ์•„ํ‚คํ…์ฒ˜

 

NginX๊ฐ€ 3333๋ฒˆ์„ ํ†ตํ•ด ์†Œ์ผ“ ์š”์ฒญ์„ ๋ฐ›์•„์ฃผ๊ณ ,

์ด๋ฅผ ๋‘ ๊ฐœ์˜ ํ”„๋กœ์„ธ์Šค 3000, 3001๋ฒˆ์œผ๋กœ ๋กœ๋“œ ๋ฐธ๋Ÿฐ์‹ฑํ•ด์ฃผ๋Š” ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค!

 


๐Ÿš€ ๋ฉ€ํ‹ฐํ”„๋กœ์„ธ์Šค๋กœ ๋ฐ”๊พผ ์ดํ›„, ๋ถ€ํ•˜ํ…Œ์ŠคํŠธ ์‹คํ–‰

์ดํ›„ ๊ฐ™์€ ํ™˜๊ฒฝ์—์„œ ๋‹ค์‹œ ๋ถ€ํ•˜ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ–ˆ์–ด์š”! ๊ฒฐ๊ณผ๋ฅผ ๋ณด์‹œ์ฃ !

 

์บ๋ฆญํ„ฐ ์œ„์น˜ ๋ณ€๊ฒฝ ์‘๋‹ต์‹œ๊ฐ„ - ์ด์ „๊ณผ ์ดํ›„ ๊ฒฐ๊ณผ ๋น„๊ต

๋‘ ๋ฒˆ์งธ ๋ถ€ํ•˜ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ

 

์ฑ„ํŒ…์˜ ์‘๋‹ต์‹œ๊ฐ„ - ์ด์ „๊ณผ ์ดํ›„ ๊ฒฐ๊ณผ ๋น„๊ต

๋‘ ๋ฒˆ์งธ ๋ถ€ํ•˜ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ

 

ํ•ด์„

์œ„์น˜ ๋ณ€๊ฒฝ์— ๋Œ€ํ•œ ์‘๋‹ต ์‹œ๊ฐ„์ด ํ‰๊ท , p95, max ์ง€ํ‘œ์—์„œ ๋Œ€๋ถ€๋ถ„ ์ ˆ๋ฐ˜ ์ด์ƒ ์ค„์–ด๋“ค์—ˆ์Šต๋‹ˆ๋‹ค! ์ฑ„ํŒ…์— ๋Œ€ํ•œ ์„œ๋ฒ„ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„์€ 4 ~ 6๋ฐฐ ์ •๋„ ์ค„์–ด๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. 

 

ํ•˜์ง€๋งŒ, ์—ฌ์ „ํžˆ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๊ฒŒ์ž„์„ ํ”Œ๋ ˆ์ดํ•˜๊ธฐ ํž˜๋“  ์ˆ˜์น˜์ž…๋‹ˆ๋‹ค..!! ๊ทธ๋ž˜์„œ ์ถ”๊ฐ€๋กœ ๋ณ‘๋ชฉ ์›์ธ์„ ์ฐพ์•„๋ณด๊ณ ์ž ํ–ˆ์Šต๋‹ˆ๋‹ค. 

 


๐Ÿง ๋„๋Œ€์ฒด ๋ฌด์—‡์ด ๋ณ‘๋ชฉ์ผ๊นŒ?

๋„๋Œ€์ฒด ๋ฌด์—‡์ด ๋ณ‘๋ชฉ์ผ์ง€ ๊ณ ๋ฏผํ–ˆ์Šต๋‹ˆ๋‹ค!

์ตœ๋Œ€ํ•œ ๊ฐ€๋Šฅ์„ฑ์„ ์—ด์–ด๋‘๊ณ  ํƒ์ƒ‰ํ•œ ๋’ค, ๊ฐ€์žฅ ์˜์‹ฌ์ด ๋˜๋Š” ๋ถ€๋ถ„์„ ๊ณจ๋ž์–ด์š”!

 

1) Redis์— ๋Œ€ํ•œ ๋งŽ์€ ์กฐํšŒ

์ฒซ ๋ฒˆ์งธ ํ›„๋ณด๋Š” Redis์ž…๋‹ˆ๋‹ค! Redis์— ๋งŽ์€ ์กฐํšŒ๊ฐ€ ๋ฐœ์ƒํ•ด์„œ ๋А๋ ค์ง€๋Š” ๊ฑฐ๋ผ ์ถ”์ธกํ•ด๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 

 

์„œ๋ฒ„ ๋‚ด ์ฝ”๋“œ์—์„œ๋Š” ์ฑ„ํŒ…์„ ๋ธŒ๋กœ๋“œ์บ์ŠคํŒ…ํ•˜๊ธฐ ์ „์— ํƒˆ๋ฝํ•œ ์‚ฌ๋žŒ์ด ๋ณด๋‚ธ ์ฑ„ํŒ…์ธ์ง€๋ฅผ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค. ๊ฒŒ์ž„์—์„œ ํƒˆ๋ฝํ•œ ํ”Œ๋ ˆ์ด์–ด๋ผ๋ฉด ์ƒ์กดํ•œ ํ”Œ๋ ˆ์ด์–ด์—๊ฒŒ ์ฑ„ํŒ…์ด ๊ฐ€์ง€ ์•Š๋„๋ก ํ•˜์—ฌ ๊ฒŒ์ž„ ํ”Œ๋ ˆ์ด์— ๋ฐฉํ•ด๊ฐ€ ๋˜์ง€ ์•Š๋„๋ก ํ•˜๊ธฐ ์œ„ํ•จ์ž…๋‹ˆ๋‹ค. 

 

์œ ๋ น์ด ํƒˆ๋ฝํ•œ ํ”Œ๋ ˆ์ด์–ด

 

ํƒˆ๋ฝํ•œ ์‚ฌ๋žŒ์ด ๋ณด๋‚ธ ์ฑ„ํŒ…์ธ์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด redis์—์„œ ํ•ด๋‹น ํ”Œ๋ ˆ์ด์–ด์˜ isAlive๋ฅผ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค. ์บ๋ฆญํ„ฐ์˜ ๋ณ€๊ฒฝ๋œ ์œ„์น˜๋ฅผ ์ „์†กํ•˜๋Š” ๊ฒƒ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค. ๋ถ€ํ•˜ํ…Œ์ŠคํŠธ ์Šคํฌ๋ฆฝํŠธ์ƒ 1์ดˆ์— ์บ๋ฆญํ„ฐ ์œ„์น˜ ๋ณ€๊ฒฝ, ์ฑ„ํŒ… ์ „์†ก ์ดํ•ฉ 400๊ฐœ์˜ ์š”์ฒญ์ด ์˜ค๊ธฐ์— 1์ดˆ์— 400๋ฒˆ Redis์—์„œ ํŠน์ • ํ”Œ๋ ˆ์ด์–ด์˜ isAlive๋ฅผ ์กฐํšŒํ•ด์•ผ ํ–ˆ์–ด์š”. ๋˜ํ•œ, Redis๋Š” Private Subnet์— ๋ณ„๋„์˜ ์„œ๋ฒ„์—์„œ ์‹คํ–‰๋˜๊ณ  ์žˆ์—ˆ๊ธฐ์— ์ถ”๊ฐ€์ ์ธ ๋„คํŠธ์›Œํฌ ๋น„์šฉ์ด ๋ฐœ์ƒํ–ˆ์„ ๊ฑฐ๋ผ ์ถ”์ธกํ–ˆ์Šต๋‹ˆ๋‹ค. 

 

๊ทธ๋Ÿฌ๋‚˜ Redis๊ฐ€ 1์ดˆ์— 400๋ฒˆ ์กฐํšŒ๋กœ ๋ถ€ํ•˜๊ฐ€ ์˜ค๊ธฐ๋Š” ํž˜๋“ค ๊ฒƒ ๊ฐ™๋‹ค๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค. ์ผ๋‹จ ๋А๋‚Œ์ ์œผ๋กœ Redis๊ฐ€ 1์ดˆ์— 400๋ฒˆ ์ •๋„๋ฅผ ๋ชป ๋ฒ„ํ‹ด๋‹ค๋ฉด ์ด๋ ‡๊ฒŒ ์ธ๊ธฐ ์žˆ๋Š” ๋น ๋ฅธ ์ธ๋ฉ”๋ชจ๋ฆฌ DB๊ฐ€ ๋  ์ˆ˜๋Š” ์—†์—ˆ๊ฒ ์ฃ . ๊ทธ๋ฆฌ๊ณ  ์•Œ๋ ค์ง€๊ธฐ๋ฅผ Redis์—์„œ๋Š” ๋Œ€๊ฒŒ ์ดˆ๋‹น 100,000 QPS๋Š” ๋œ๋‹ค๊ณ  ํ•ด์š”. 

 

๊ฒฐ๋ก ์€ Redis ์„ฑ๋Šฅ ์ƒ 1์ดˆ์— 400๋ฒˆ ์กฐํšŒ๋Š” ๋ฌด๋ฆฌ๊ฐ€ ์—†์„ ๊ฑฐ๋ผ ํŒ๋‹จํ•˜์—ฌ ๋‹ค์Œ ์›์ธ์„ ๋ถ„์„ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. 

 

+) ํ•ด๋‹น isAlive๋ฅผ ์กฐํšŒํ•˜๋Š” ๋กœ์ง์€ ๊ฐœ์„  ์ž์ฒด๊ฐ€ ํ•„์š”ํ•œ ๊ฑด ๋งž์Šต๋‹ˆ๋‹ค. isAlive๋Š” ์ž์ฃผ ๋ฐ”๋€Œ๋Š” ๊ฒŒ ์•„๋‹ˆ๊ธฐ์— WAS ๋‚ด์—์„œ ๊ฐ„๋‹จํžˆ ์„ธ์…˜์œผ๋กœ ๊ด€๋ฆฌํ•ด๋„ ๋˜๋Š” ๋ฐ์ดํ„ฐ์ž…๋‹ˆ๋‹ค. 

 

2) NginX ๊ณผ๋ถ€ํ•˜

๊ทธ๋‹ค์Œ์€ NginX์ธ๋ฐ์š”!

 

NginX์—์„œ๋Š” 1์ดˆ์— 400๋ฒˆ ์š”์ฒญ์„ ๋ฐ›์•„์•ผ ํ•˜๊ณ , 1์ดˆ์— 80000๋ฒˆ ์‘๋‹ต์„ ํ•ด์ค˜์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค. 80000๋ฒˆ ์‘๋‹ต์„ ํ•ด์ค˜์•ผ ํ•˜๋Š” ์ด์œ ๋Š” ํ•œ ๊ฒŒ์ž„๋ฐฉ์— 200๋ช…์˜ ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ์žˆ๊ณ  1์ดˆ๋‹น 400๊ฐœ์˜ ์š”์ฒญ์ด ์˜ค๋ฉด, ์ด๋ฅผ ๋ธŒ๋กœ๋“œ์บ์ŠคํŒ…ํ•˜๊ธฐ ์œ„ํ•ด์„œ 200๋ช…์—๊ฒŒ 400๊ฐœ์˜ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด์•ผ ํ•˜๊ธฐ์— 200 * 400 = 80000๊ฐœ์˜ ์‘๋‹ต์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. 

 

400๊ฐœ์˜ ์š”์ฒญ์„ ๊ดœ์ฐฎ์ง€๋งŒ, 80000๊ฐœ์˜ ์‘๋‹ต์€ ์‚ด์ง ๋ถ€๋‹ด์ด ๋  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋ณ‘๋ชฉ์˜ ์›์ธ์ผ ํ™•๋ฅ ์ด Redis๋ณด๋‹ค๋Š” ๋†’์•„๋ณด์ž…๋‹ˆ๋‹ค. 

 

๊ทธ๋Ÿฌ๋‚˜ NginX์—์„œ ์‘๋‹ต์„ ์ฃผ๋Š” ๊ณผ์ •์—์„œ ์ •ํ™•ํžˆ ๋ฌด์—‡ ๋•Œ๋ฌธ์— ๋ถ€ํ•˜๊ฐ€ ์ƒ๊ธธ์ง€ ๊ฐ€๋Š ์ด ์•ˆ ๋์Šต๋‹ˆ๋‹ค. ์ผ๋‹จ ๋ณด๋ฅ˜ํ•˜๊ณ  80000๊ฐœ์˜ ๋„คํŠธ์›Œํฌ ์‘๋‹ต์— ์ง‘์ค‘ํ•ด๋ณด๊ธฐ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค. 

 

3) ์ œํ•œ๋œ ๋„คํŠธ์›Œํฌ ๋Œ€์—ญํญ

๋„คํŠธ์›Œํฌ ๋Œ€์—ญํญ์ด 1์ดˆ์— 80000๊ฐœ ์‘๋‹ต์„ ๊ฒฌ๋””์ง€ ๋ชปํ–ˆ์„ ๊ฒƒ์ด๋ผ ์ถ”์ธกํ•ด๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด๋Š” ์•„๋‹ˆ์—ˆ์Šต๋‹ˆ๋‹ค. 

 

๊ทธ ์ด์œ ๋Š” ์„œ๋ฒ„๋ฅผ Ncloud๋กœ ์šด์˜ํ•˜๊ณ  ์žˆ์—ˆ๊ณ , Ncloud ์„œ๋ฒ„์˜ ๊ธฐ๋ณธ ๋„คํŠธ์›Œํฌ ๋Œ€์—ญํญ์€ 1Gbps์ž…๋‹ˆ๋‹ค. 1์ดˆ์— 80000๊ฐœ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ๋Œ€์—ญํญ์€ 122.24Mbps ์ •๋„์ž…๋‹ˆ๋‹ค. 1๊ฐœ์˜ ๋ฉ”์‹œ์ง€๋ฅผ ๋Œ€๋žต 191bytes ์ •๋„๋กœ ์žก๊ณ  ๊ณ„์‚ฐํ–ˆ์Šต๋‹ˆ๋‹ค. 

 

`1Gbps = 1000Mbps > 122.24Mbps`์ด๊ธฐ์— ๋Œ€์—ญํญ ์ž์ฒด์˜ ๋ฌธ์ œ๋Š” ์•„๋‹ ๊ฒƒ์œผ๋กœ ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค. 

 

4) Node.js event loop ๊ณผ๋ถ€ํ•˜ - Network I/O๋กœ ์ธํ•œ

๋“œ๋””์–ด ๋งˆ์ง€๋ง‰์ž…๋‹ˆ๋‹ค! ๋งˆ์ง€๋ง‰์— ๋‘” ์ด์œ ๊ฐ€ ์žˆ๊ฒ ์ฃ ? ใ…Žใ…Ž

 

Node.js event loop๊ฐ€ ์ฒ˜๋ฆฌํ•ด์•ผ ๋  ๊ฒŒ ๋งŽ์•„ ๊ณผ๋ถ€ํ•˜๋˜์–ด ๋‹ค๋ฅธ ์š”์ฒญ์ด ์™€๋„ ๊ณ„์†ํ•ด์„œ ๋Œ€๊ธฐํ•˜๊ณ  ์ง€์—ฐ๋œ๋‹ค๊ณ  ์ถ”์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. 

 

๊ทธ๋ ‡๋‹ค๋ฉด Node.js event loop๋Š” ์–ด๋–ค ๊ฒƒ ๋•Œ๋ฌธ์— ๊ณผ๋ถ€ํ•˜๋˜์—ˆ์„๊นŒ์š”? ์ด์™€ ๊ด€๋ จํ•ด ๊ณ ๋ฏผํ•˜๋˜ ์ค‘ ์ธํ”„๋Ÿฐ์—์„œ ์ˆ˜๊ฐ•ํ–ˆ๋˜ ํŒŒ์ผ์ž…์ถœ๋ ฅ ๊ด€๋ จ ํ•™์Šต๋‚ด์šฉ์ด ๋– ์˜ฌ๋ž์Šต๋‹ˆ๋‹ค.

ํŒŒ์ผ ์ž…์ถœ๋ ฅ์—์„œ ๋ฌธ์ œ ์ƒํ™ฉ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์•˜์Šต๋‹ˆ๋‹ค.

public static void main(String[] args) throws IOException {
   FileOutputStream fos = new FileOutputStream(FILE_NAME);
   
   long startTime = System.currentTimeMillis();
   
   for (int i = 0; i < FILE_SIZE; i++) {
       fos.write(1);
   }
   
   fos.close();
   
   long endTime = System.currentTimeMillis();
   
   System.out.println("File created: " + FILE_NAME);
   System.out.println("File size: " + FILE_SIZE / 1024 / 1024 + "MB");
   System.out.println("Time taken: " + (endTime - startTime) + "ms");
}

ํŒŒ์ผ์— ์“ฐ๊ธฐ๋ฅผ ํ•˜๋Š” ์ƒํ™ฉ์ธ๋ฐ, 1byte์”ฉ ๋””์Šคํฌ์— ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜๋Š” ์ƒํ™ฉ์ž…๋‹ˆ๋‹ค.

  • ์„ฑ๋Šฅ ์ €ํ•˜ ํ˜„์ƒ: 10MB ํŒŒ์ผ ์“ฐ๊ธฐ์— 14์ดˆ ์†Œ์š”
  • ๊ทผ๋ณธ ์›์ธ: 1byte์”ฉ ๋””์Šคํฌ์— ๋ฐ์ดํ„ฐ ์ „๋‹ฌ
  • ๋ฐ˜๋ณต ํšŸ์ˆ˜: ๋ฌด๋ ค 1,000๋งŒ ๋ฒˆ์˜ ๋ฐ˜๋ณต ์ž‘์—…

์‹œ์Šคํ…œ ์ฝœ ์˜ค๋ฒ„ํ—ค๋“œ ๋ฐœ์ƒ

  • ๋งค write(), read() ํ˜ธ์ถœ ์‹œ OS ์‹œ์Šคํ…œ ์ฝœ ๋ฐœ์ƒ
  • ์‹œ์Šคํ…œ ์ฝœ์€ ์ƒ๋Œ€์ ์œผ๋กœ ๋ฌด๊ฑฐ์šด ์ž‘์—…
  • ์ปจํ…์ŠคํŠธ ์ „ํ™˜ ๋น„์šฉ ๋ฐœ์ƒ

 

๋งˆ์น˜ ํ™”๋ฌผ์ฐจ์— ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์˜ ๋ฌผ๊ฑด๋งŒ ์‹ค์–ด ๋‚˜๋ฅด๋Š” ๊ฒƒ์ฒ˜๋Ÿผ, 1byte์”ฉ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ์‹์€ ๊ทน๋„๋กœ ๋น„ํšจ์œจ์ ์ž…๋‹ˆ๋‹ค. 1,000๋งŒ ๋ฒˆ์˜ ์™•๋ณต ์ž‘์—…์€ ์‹œ์Šคํ…œ ์ž์›์„ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ์†Œ๋ชจํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ํŒŒ์ผ I/O ํ•™์Šต ๊ฒฝํ—˜์„ ์‚ด๋ ค Network I/O ์ƒํ™ฉ์— ์œ ์ถ”ํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ณ ์ž ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ €ํฌ ํ”„๋กœ์ ํŠธ์—์„œ ๊ธฐ์กด ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์•˜์Šต๋‹ˆ๋‹ค.

private async handlePlayerPosition(playerId: string, playerData: any, server: Namespace) {
    const { gameId, positionX, positionY } = playerData;
    const playerPosition = [parseFloat(positionX), parseFloat(positionY)];
    const updateData = { playerId, playerPosition };
    const isAlivePlayer = await this.redis.hget(REDIS_KEY.PLAYER(playerId), 'isAlive');

    if (isAlivePlayer === SurvivalStatus.ALIVE) {
      server.to(gameId).emit(SocketEvents.UPDATE_POSITION, updateData);
}

player๊ฐ€ ์ด๋™ํ• ๋•Œ, ๋‹ค๋ฅธ ํ”Œ๋ ˆ์ด์–ด๋“ค์—๊ฒŒ ์ƒˆ ์œ„์น˜๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๋กœ์ง์ž…๋‹ˆ๋‹ค.

์ด๋•Œ, 1๋ช…์ด ์ด๋™ํ•˜๋ฉด 200๋ฒˆ(๋ฐฉ์˜ ํ”Œ๋ ˆ์ด์–ด ์ˆ˜)์˜ emit์ด ๋ฐœ์ƒํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋งˆ์น˜ 1byte ์”ฉ ํŒŒ์ผ์— ์“ฐ๊ณ  ์žˆ๋Š” ์ƒํ™ฉ๊ณผ ๋งค์šฐ ์œ ์‚ฌํ–ˆ์Šต๋‹ˆ๋‹ค!!

 

๋น„ํšจ์œจ์ ์ธ I/O๊ฐ€ ์ผ์–ด๋‚˜๋Š” ์ƒํ™ฉ์ด ์ €ํฌ ํ”„๋กœ์ ํŠธ์—์„œ๋„ ๋ฐœ์ƒํ•œ๊ฒƒ ์ž…๋‹ˆ๋‹ค.ํŒŒ์ผ I/O ์ž์ฒด๊ฐ€ ๋น„์šฉ์ด ๋น„์‹ผ ์ž‘์—…์ด๋ฉฐ Network I/O ์—ญ์‹œ ๋งˆ์ฐฌ๊ฐ€์ง€ ์ผ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. 1์ดˆ์— 80000๋ฒˆ Network Ouput์„ ํ•˜๋Š” ๊ฒƒ์ด๋‹ˆ ์—ฌ๊ธฐ์„œ ๋ณ‘๋ชฉ์ด ๋ฐœ์ƒํ–ˆ์„ ๊ฑธ๋กœ ์ถ”์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. 

 

ํŒŒ์ผ I/O์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ Network Output์„ ์œ„ํ•ด์„œ๋„ OS์—์„œ ์‹œ์Šคํ…œ ์ฝœ์„ ํ†ตํ•ด ์œ ์ €๋ชจ๋“œ์™€ ์ปค๋„๋ชจ๋“œ ์‚ฌ์ด๋ฅผ ์ „ํ™˜ํ•˜๋ฉด์„œ ์ƒ๊ธด ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ์žˆ์„ ๊ฑธ๋กœ ์ถ”์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋˜, Network Output์„ ์ฒ˜๋ฆฌํ•˜๋ฉด์„œ๋„ ์ƒˆ๋กœ์šด ์š”์ฒญ๋„ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•˜๊ธฐ์— ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ์ด ๋‹ค์†Œ ์ผ์–ด๋‚ฌ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. 

 

์ˆ˜๋งŽ์€ Network I/O๊ฐ€ ์œ ๋ ฅํ•œ ์›์ธ์ผ ๊ฒƒ์œผ๋กœ ํŒŒ์•…ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ Network I/O๋กœ ์ธํ•œ ๋ถ€ํ•˜๊ฐ€ Node.js event loop์—๋„ ์˜ํ–ฅ์„ ์ฃผ์—ˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. 

 

๊ณผ๊ฐํžˆ ๊ฐœ์„ ํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค!

 


๐ŸŽฏ ์ˆ˜๋งŽ์€ Network Output์„ ์–ด๋–ป๊ฒŒ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ์„๊นŒ?

1) ๋ฐ์ดํ„ฐ ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ

  • ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ(Batch Processing): ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•œ ๋ฐ˜๋ณต์ ์ธ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋Š” ๋Œ€์‹ , ๋ฐ์ดํ„ฐ๋ฅผ ๋ชจ์•„์„œ ์ผ์ • ์ฃผ๊ธฐ๋กœ ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌ
  • ์ด๋ฒคํŠธ๋ฅผ ๋ฐ”๋กœ ๋ธŒ๋กœ๋“œ์บ์ŠคํŒ…ํ•˜์ง€ ์•Š๊ณ  ํ์— ์Œ“์•„๋‘๊ธฐ
    • (์ž๋ฐ”์˜ ํŒŒ์ผ ์ž…์ถœ๋ ฅ Buffered ์ŠคํŠธ๋ฆผ์ฒ˜๋Ÿผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ์—ˆ์ง€๋งŒ, ์ด๊ฒŒ ํ™•์‹คํ•œ ์›์ธ์ธ์ง€ ๋ชจ๋ฅด๋Š” ์ƒํ™ฉ์ด์—ˆ๊ณ  ํŽ˜์–ดํ”„๋กœ๊ทธ๋ž˜๋ฐ ํ™˜๊ฒฝ์—์„œ ๋‹ค๊ฐ™์ด ์ดํ•ดํ• ์ˆ˜ ์žˆ๋Š” ์ž๋ฃŒ๊ตฌ์กฐ์ธ ํ๋กœ ๊ตฌํ˜„ํ•˜๊ณ ์ž ํ–ˆ์Šต๋‹ˆ๋‹ค.)
  • ์ •ํ•ด์ง„ ์ฃผ๊ธฐ๋กœ ํ์— ์žˆ๋Š” ๊ฑธ ํ•œ ๋ฒˆ์— ์ „์†กํ•จ์œผ๋กœ์จ Network Output ๋ถ€๋‹ด ์ค„์ด๊ธฐ
  • ๊ธฐ์กด์—๋Š” 1์ดˆ์— 80000๋ฒˆ Network Output์„ ํ•ด์•ผ ํ–ˆ์ง€๋งŒ,
    0.1์ดˆ์— 1๋ฒˆ์”ฉ ์Œ“์ธ ๋ฐฐ์น˜๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค๋ฉด ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ๋ฅผ ํ†ตํ•ด 1์ดˆ์— 10๋ฒˆ Network Outputํ•˜๊ฒŒ ๋จ

 

2) ๋ฐ์ดํ„ฐ ์••์ถ•

  • ๋ฐ์ดํ„ฐ ํฌ๊ธฐ๋ฅผ ์ค„์—ฌ ๋„คํŠธ์›Œํฌ ๋ถ€๋‹ด์„ ์ค„์ด๊ธฐ
  • Network I/O ํšŸ์ˆ˜ ์ž์ฒด๋Š” ๋ณ€๋™์ด ์—†๊ธฐ์— ์ง€๊ธˆ ๋ฌธ์ œ์— ์ ์ ˆํ•œ ํ•ด๊ฒฐ ๋ฐฉ์•ˆ์€ ์•„๋‹˜

 

3) ์ˆ˜ํ‰์  ํ™•์žฅ ๋˜๋Š” ์ˆ˜์ง์  ํ™•์žฅ

  • ์„œ๋ฒ„๋ฅผ ๋” ๋Š˜๋ฆฌ๊ฑฐ๋‚˜ ์‚ฌ์–‘์„ ๋†’์ด๋Š” ๊ฑด ๋ณธ์งˆ์ ์ธ ํ•ด๊ฒฐ ๋ฐฉ์•ˆ์ด ์•„๋‹˜ (์šฐ์„  pass)

 

๋ฐฐ์น˜ ์ฒ˜๋ฆฌ๋กœ ์„ ํƒ

์ง€๊ธˆ ๋‹น์žฅ ์‹œ๋„ํ•ด๋ณผ ๋ฒ•ํ•œ ๊ฑด ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ๋ผ๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค. Network Output ํšŸ์ˆ˜ ์ž์ฒด๋ฅผ ์ค„์ด๋Š” ๊ฒŒ ๋ณ‘๋ชฉ์˜ ์›์ธ์„ ์ œ๊ฑฐํ•˜๋Š” ๋ฐ ํ•ต์‹ฌ์ด๊ธฐ ๋•Œ๋ฌธ์ด์ฃ . ๊ทธ๋ฆฌ๊ณ  ๋ฐฐ์น˜์ฒ˜๋ฆฌ๋Š” ํšŸ์ˆ˜๋ฅผ ์ค„์—ฌ์ค„ ์ˆ˜ ์žˆ๋Š” ์•„์ฃผ ์ข‹์€ ์†”๋ฃจ์…˜์ž…๋‹ˆ๋‹ค.

 


โœˆ๏ธ ๋ฐ์ดํ„ฐ ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ ๋„์ž…ํ•˜๊ธฐ

๊ตฌํ˜„ ๊ณ„ํš

  1. ๋ฉ”๋ชจ๋ฆฌ ๋ณ€์ˆ˜์— ํ๋ฅผ ๋‘๊ธฐ
  2. ์ฑ„ํŒ…, ์œ„์น˜ ๋ณ€๊ฒฝ ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด ๋ฐ”๋กœ ๋ธŒ๋กœ๋“œ์บ์ŠคํŒ…ํ•˜์ง€ ์•Š๊ณ , ํ์— ๋ฐ์ดํ„ฐ๋ฅผ ์ผ๋‹จ ์Œ“์•„๋‘๊ธฐ
  3. 0.1์ดˆ์— ํ•œ ๋ฒˆ์”ฉ ํ ๋ฐฐ์น˜ ์ฒ˜๋ฆฌํ•˜๊ธฐ

 

๊ตฌํ˜„ ์ฝ”๋“œ

์œ„์น˜ ๋ณ€๊ฒฝ๊ณผ ์ฑ„ํŒ…์— ๋Œ€ํ•ด์„œ ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜๋Š” ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ๋ฅผ ์ง์ ‘ ๊ตฌํ˜„ํ•œ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค! ๋ฐฐ์น˜ ํ”„๋กœ์„ธ์„œ์™€ ์œ„์น˜ ๋ณ€๊ฒฝ ํ•จ์ˆ˜์— ๋Œ€ํ•ด ์‚ดํŽด๋ณด์‹œ์ฃ !

 

์ฐธ๊ณ ๋กœ ์œ„์น˜ ๋ณ€๊ฒฝ์˜ ๋ฐฐ์น˜์ฒ˜๋ฆฌ ์ฃผ๊ธฐ๋Š” 0.1์ดˆ๋กœ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ฑ„ํŒ…์€ ๋” ๋น ๋ฅธ ์‹ค์‹œ๊ฐ„ ๋™๊ธฐํ™”๋ฅผ ์œ„ํ•ด 0.05์ดˆ๋กœ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

 

1) batch.processor.ts (๋ฐฐ์น˜ ์ฒ˜๋ฆฌ๋ฅผ ์ง€์›ํ•˜๋Š” ๋ชจ๋“ˆ)

// ๋ฐฐ์น˜์ฒ˜๋ฆฌ ๋ชจ๋“ˆ
export class BatchProcessor implements OnApplicationShutdown, OnModuleDestroy {
  private batchMap: Map<BatchProcessorType, Map<string, any[]>> = new Map();
  private isProcessing = false;
  private intervalId: NodeJS.Timeout;

  private server: Namespace;
  private eventName: string;

  constructor(
    @InjectRedis() private readonly redis: Redis
  ) {}

  // ์–ด๋–ค ์„œ๋ฒ„, ์ด๋ฒคํŠธ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฐ์น˜์ฒ˜๋ฆฌ์ธ์ง€ ์ดˆ๊ธฐํ™”
  initialize(server: Namespace, eventName: string) {
    this.server = server;
    this.eventName = eventName;
    for (const type of Object.values(BatchProcessorType)) {
      this.batchMap.set(type, new Map());
    }
  }

  // ํŠน์ • ๊ฒŒ์ž„๋ฐฉ์— ๋ณด๋‚ด์•ผ ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐฐ์น˜ ํ์— ์‚ฝ์ž…
  pushData(type: BatchProcessorType, gameId: string, data: any): void {
    const batchMap = this.batchMap.get(type);
    if (!batchMap.has(gameId)) {
      batchMap.set(gameId, []);
    }
    batchMap.get(gameId).push(data);
  }
  
  // ์ฃผ๊ธฐ์ ์ธ ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ ์‹œ์Šคํ…œ ์‹œ์ž‘ 
  startProcessing(interval: number = 100): void {
    this.intervalId = setInterval(() => this.processBatch(), interval);
  }
  
  // ์ฃผ๊ธฐ์ ์œผ๋กœ ๋ฐฐ์น˜ ์ฒ˜๋ฆฌํ•˜๋ฉฐ ๊ฐ ๊ฒŒ์ž„๋ฐฉ์— ๋ธŒ๋กœ๋“œ์บ์ŠคํŒ…
  private async processBatch(): Promise<void> {
    // isProcessing์„ ํ†ตํ•ด lock์„ ๊ฑธ์–ด ํ•œ ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ ๋ฒˆ์˜ ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ ๋ฐฉ์ง€
    if (this.isProcessing) {
      return;
    }

    this.isProcessing = true;

    // BatchProcessorType์— ๋”ฐ๋ผ ์ฒ˜๋ฆฌ (ex. ์ฑ„ํŒ…์„ ๋ชจ๋‘์—๊ฒŒ ๋ณด๋‚ผ์ง€, ํŠน์ •์ธ์—๊ฒŒ ๋ณด๋‚ผ์ง€)
    for (const type of Object.values(BatchProcessorType)) {
      const batchMap = this.batchMap.get(type);
      const processingTasks = Array.from(batchMap.entries()).map(async ([gameId, queue]) => {
        if (queue.length > 0) {
          const batch = queue.splice(0, queue.length);
          const handler = this.batchProcessHandlers[type];
          await handler(gameId, batch);
        }
      });

      // isProcessing ๋ณ€์ˆ˜ ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•ด await 
      // ์‹œ์Šคํ…œ ์ƒ์œผ๋กœ ๋ธ”๋ก์ด ๋˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋ฉฐ ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ ์ดํ›„ ์‹คํ–‰ํ•ด์•ผ ํ•  ์ฃผ์š” ํ•จ์ˆ˜๋Š” ์—†๊ธฐ์— ์„ฑ๋Šฅ ์˜ํ–ฅx
      await Promise.all([...processingTasks]);
    }

    this.isProcessing = false;
  }

  // ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ ํ•ธ๋“ค๋Ÿฌ 
  private batchProcessHandlers: Record<
    BatchProcessorType,
    (gameId: string, batch: any[]) => Promise<void>
  > = {
    [BatchProcessorType.DEFAULT]: // (์ƒ๋žต) ๋””ํดํŠธ ๋ธŒ๋กœ๋“œ์บ์ŠคํŒ… ํ•ด์ฃผ๋Š” ํ•จ์ˆ˜,
    [BatchProcessorType.ONLY_DEAD]: // (์ƒ๋žต) ๊ฒŒ์ž„์—์„œ ์ฃฝ์€ ์‚ฌ๋žŒ๋ผ๋ฆฌ ๋ธŒ๋กœ๋“œ์บ์ŠคํŒ…ํ•˜๋Š” ํ•จ์ˆ˜
  };
}

 

 

2) player.subscriber.ts (์บ๋ฆญํ„ฐ์˜ ๋ณ€๊ฒฝ๋œ ์œ„์น˜๋ฅผ ๋ธŒ๋กœ๋“œ์บ์ŠคํŒ…ํ•˜๋Š” ๋ชจ๋“ˆ)

@Injectable()
export class PlayerSubscriber extends RedisSubscriber {
  constructor(
    @InjectRedis() redis: Redis,
    private positionProcessor: BatchProcessor
  ) {
    super(redis);
  }

  // ๋ฐฐ์น˜์ฒ˜๋ฆฌ ๊ฐ์ฒด ์ดˆ๊ธฐํ™” ๋ฐ ์‹œ์ž‘
  async subscribe(server: Namespace): Promise<void> {
    this.positionProcessor.initialize(server, SocketEvents.UPDATE_POSITION);
    this.positionProcessor.startProcessing(POSITION_BATCH_TIME);
    
    ...
  }

  // ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ ํ์— ๋ฐ์ดํ„ฐ ์Œ“์•„๋‘๊ธฐ  
  private async handlePlayerPosition(playerId: string, playerData: any) {
    const { gameId, positionX, positionY } = playerData;
    const playerPosition = [parseFloat(positionX), parseFloat(positionY)];
    const updateData = { playerId, playerPosition };

    const isAlivePlayer = await this.redis.hget(REDIS_KEY.PLAYER(playerId), 'isAlive');
		const processorType = isAlivePlayer === SurvivalStatus.ALIVE 
		 ? BatchProcessorType.DEFAULT 
		 : BatchProcessorType.ONLY_DEAD;
		 
		this.positionProcessor.pushData(processorType, gameId, updateData);
  }

 

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

 


๐Ÿš€ ๋ถ€ํ•˜ํ…Œ์ŠคํŠธ ์‹คํ–‰ํ•˜์—ฌ ํ™•์ธํ•ด๋ณด์ž!

๊ฐœ์š”

  • ์ด์ „ ๋ถ€ํ•˜ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ๊ณผ ๋™์ผ

 

์œ„์น˜ ๋ณ€๊ฒฝ์˜ ์‘๋‹ต์‹œ๊ฐ„ - ์ด์ „๊ณผ ์ดํ›„ ๊ฒฐ๊ณผ ๋น„๊ต

์„ธ ๋ฒˆ์งธ ๋ถ€ํ•˜ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ

 

์ฑ„ํŒ…์˜ ์‘๋‹ต์‹œ๊ฐ„ - ์ด์ „๊ณผ ์ดํ›„ ๊ฒฐ๊ณผ ๋น„๊ต

์„ธ ๋ฒˆ์งธ ๋ถ€ํ•˜ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ

 

๊ฒฐ๊ณผ ํ•ด์„

์ฑ„ํŒ…, ์œ„์น˜ ๋ณ€๊ฒฝ์˜ ์‘๋‹ต์‹œ๊ฐ„์ด ๋น„์•ฝ์ ์œผ๋กœ ๋‹จ์ถ•๋˜์—ˆ์Šต๋‹ˆ๋‹ค!! E2E ์ธก์ • ์‹œ๊ฐ„์„ ๋ณด๋”๋ผ๋„ ์ตœ๋Œ€ 0.12์ดˆ ์•ˆ์— ๋ณ€๊ฒฝ๋œ ์œ„์น˜ ์‘๋‹ต์„ ๋ฐ›๋Š” ๋ชจ์Šต์„ ๋ณด์˜€์Šต๋‹ˆ๋‹ค. ์ฆ‰, ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์ด๋ฃจ์–ด์กŒ๋‹ค๋Š” ๊ฑธ ์•Œ ์ˆ˜ ์žˆ์–ด์š”!

 

๊ธฐ์กด์—๋Š” Network Output์ด 1์ดˆ์— 80000๋ฒˆ์ด์—ˆ๊ณ , ์ด์ œ๋Š” 1์ดˆ์— 30๋ฒˆ ์ดํ•˜์ž…๋‹ˆ๋‹ค. ์—ญ์‹œ ์ˆ˜๋งŽ์€ Network I/O๋กœ ์ธํ•œ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ๋ณ‘๋ชฉ์˜ ์›์ธ์ด์—ˆ์„ ๊ฑธ๋กœ ์ถ”์ •๋˜๋„ค์š”. 

 


๐Ÿ“ ์ถ”๊ฐ€ ๋ถ„์„

'๋ฉ€ํ‹ฐ ํ”„๋กœ์„ธ์Šค + ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ' ๋ชจ๋‹ˆํ„ฐ๋ง ๊ฒฐ๊ณผ

Ncloud์˜ cloud insight ํ™œ์šฉ

 

  • Memory: ์•ฝ 500MB ์ƒ์Šน
  • Network Output: 100Mbps
  • CPU: 50% ์ƒ์Šน (์šฉ๋Ÿ‰์ด ํฐ ๋ฐ์ดํ„ฐ์˜ ์ง๋ ฌํ™”, ํŒจํ‚ท ๋“ฑ ๋„คํŠธ์›Œํฌ ์Šคํƒ์—์„œ ๋ถ€ํ•˜๊ฐ€ ์ƒ๊ฒผ์„ ๊ฑธ๋กœ ๋ณด์ž„)

 

ํ–ฅํ›„ ๊ฐœ์„  ์‚ฌํ•ญ

  1. ํ•œ ๊ฒŒ์ž„๋ฐฉ 200๋ช…์ด ์žˆ์„ ๋•Œ ๋ช‡ ๊ฐœ์˜ ๊ฒŒ์ž„๋ฐฉ๊นŒ์ง€ ๋ฒ„ํ‹ธ ์ˆ˜ ์žˆ๋Š”์ง€ ์ฒดํฌ ํ›„, ๋ณ‘๋ชฉ ์žฌ๋ถ„์„
  2. ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ ์–‘์ด ๋งŽ์•„์งˆ ๋•Œ๋ฅผ ๊ฐ์•ˆํ•˜์—ฌ ๋ฉ”์‹œ์ง€ ์••์ถ• ๊ณ ๋ ค
  3. ๊ธ‰๊ฒฉํžˆ ํŠธ๋ž˜ํ”ฝ ๋งŽ์•„์ง€๋Š” ๊ฑธ ๋Œ€๋น„ํ•ด ์˜คํ†  ์Šค์ผ€์ผ๋ง ์กฐ๊ฑด์„ ์„ธ์šฐ๊ณ  ํด๋ผ์šฐ๋“œ์—์„œ ์„ค์ •
  4. ๋ชจ๋‹ˆํ„ฐ๋ง ๋„๊ตฌ๋ฅผ ๋„์ž…ํ•˜์—ฌ ๋” ๋งŽ์€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ๋ณ‘๋ชฉ ๋ถ„์„
  5. ์ปค์Šคํ…€ ๋ฉ”ํŠธ๋ฆญ ์ธก์ • ์ฝ”๋“œ๊ฐ€ ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ ๋กœ์ง๊ณผ ์„ž์—ฌ์žˆ๋Š” ๊ฒƒ ๋ถ„๋ฆฌ

 


โญ๏ธ ๊ฒฐ๋ก 

์š”์•ฝ

  1. ํ•œ ๊ฒŒ์ž„๋ฐฉ 200๋ช… ์›ํ™œํ•œ ํ”Œ๋ ˆ์ด ์ง€์›ํ•˜๊ณ ์ž ๋ถ€ํ•˜ํ…Œ์ŠคํŠธ ์ง„ํ–‰
  2. '๋ฉ€ํ‹ฐ ํ”„๋กœ์„ธ์Šค + ๋ฐฐ์น˜์ฒ˜๋ฆฌ'๋ฅผ ๋„์ž…ํ•ด (ํ‰๊ท  5.8์ดˆ -> 3.3์ดˆ -> 0.06์ดˆ๋กœ) ๋Œ€๋ถ€๋ถ„ 0.1์ดˆ ์•ˆ์— ์‘๋‹ต์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๊ฒŒ ๋˜๋ฉฐ ๋น„์•ฝ์ ์ธ ์„ฑ๋Šฅ ํ–ฅ์ƒ
  3. ํ•œ ๊ฒŒ์ž„๋ฐฉ 200๋ช… ๋ชฉํ‘œ๋ฅผ ๋‹ฌ์„ฑํ–ˆ์œผ๋ฉฐ, ๋” ๋งŽ์€ ํŠธ๋ž˜ํ”ฝ์„ ๊ฐ๋‹นํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ถ”๊ฐ€ ๊ฐœ์„ ์ด ํ•„์š”

 

๋ ˆํผ๋Ÿฐ์Šค