๋ชจ๋“  ๊ธฐ๋Šฅ์„ ๊ฐ–์ถ˜ ๋ฒ ์–ด C I/O ๋ฆฌ์•กํ„ฐ

๋ชจ๋“  ๊ธฐ๋Šฅ์„ ๊ฐ–์ถ˜ ๋ฒ ์–ด C I/O ๋ฆฌ์•กํ„ฐ

์†Œ๊ฐœ

I/O ๋ฆฌ์•กํ„ฐ (๋‹จ์ผ ์Šค๋ ˆ๋“œ ์ด๋ฒคํŠธ ๋ฃจํ”„)๋Š” ๋งŽ์€ ์ธ๊ธฐ ์žˆ๋Š” ์†”๋ฃจ์…˜์— ์‚ฌ์šฉ๋˜๋Š” ๊ณ ๋ถ€ํ•˜ ์†Œํ”„ํŠธ์›จ์–ด ์ž‘์„ฑ์„ ์œ„ํ•œ ํŒจํ„ด์ž…๋‹ˆ๋‹ค.

์ด ๊ธฐ์‚ฌ์—์„œ๋Š” I/O ๋ฆฌ์•กํ„ฐ์˜ ๋ชจ๋“  ๋ฉด๊ณผ ์ž‘๋™ ๋ฐฉ์‹์„ ์‚ดํŽด๋ณด๊ณ , 200์ค„ ๋ฏธ๋งŒ์˜ ์ฝ”๋“œ๋กœ ๊ตฌํ˜„์„ ์ž‘์„ฑํ•˜๊ณ , ๋ถ„๋‹น 40์ฒœ๋งŒ ๊ฐœ๊ฐ€ ๋„˜๋Š” ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฐ„๋‹จํ•œ HTTP ์„œ๋ฒ„ ํ”„๋กœ์„ธ์Šค๋ฅผ ๋งŒ๋“ค ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋จธ๋ฆฌ๋ง

  • ์ด ๊ธฐ์‚ฌ๋Š” I/O ๋ฆฌ์•กํ„ฐ์˜ ๊ธฐ๋Šฅ์„ ์ดํ•ดํ•˜๊ณ  ์ด๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ์˜ ์œ„ํ—˜์„ ์ดํ•ดํ•˜๋Š” ๋ฐ ๋„์›€์„ ์ฃผ๊ธฐ ์œ„ํ•ด ์ž‘์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
  • ๊ธฐ์‚ฌ๋ฅผ ์ดํ•ดํ•˜๋ ค๋ฉด ๊ธฐ๋ณธ ์ง€์‹์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. C ์–ธ์–ด ๊ทธ๋ฆฌ๊ณ  ๋„คํŠธ์›Œํฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœ ๊ฒฝํ—˜์ด ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋ชจ๋“  ์ฝ”๋“œ๋Š” (์ฃผ์˜: ๊ธด PDF) C11 ํ‘œ์ค€์— Linux์šฉ์ด๋ฉฐ ๋‹ค์Œ์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅ GitHub์˜.

์™œํ•ฉ๋‹ˆ๊นŒ?

์ธํ„ฐ๋„ท์˜ ์ธ๊ธฐ๊ฐ€ ๋†’์•„์ง์— ๋”ฐ๋ผ ์›น ์„œ๋ฒ„๋Š” ๋งŽ์€ ์ˆ˜์˜ ์—ฐ๊ฒฐ์„ ๋™์‹œ์— ์ฒ˜๋ฆฌํ•ด์•ผ ํ•˜๋ฏ€๋กœ ๋‘ ๊ฐ€์ง€ ์ ‘๊ทผ ๋ฐฉ์‹์ด ์‹œ๋„๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ฆ‰, ๋‹ค์ˆ˜์˜ OS ์Šค๋ ˆ๋“œ์—์„œ I/O๋ฅผ ์ฐจ๋‹จํ•˜๋Š” ๋ฐฉ๋ฒ•๊ณผ "์‹œ์Šคํ…œ ์„ ํƒ๊ธฐ"๋ผ๊ณ ๋„ ํ•˜๋Š” ์ด๋ฒคํŠธ ์•Œ๋ฆผ ์‹œ์Šคํ…œ(Epoll/ํ/IOCP/๊ธฐํƒ€).

์ฒซ ๋ฒˆ์งธ ์ ‘๊ทผ ๋ฐฉ์‹์€ ๋“ค์–ด์˜ค๋Š” ์—ฐ๊ฒฐ๋งˆ๋‹ค ์ƒˆ๋กœ์šด OS ์Šค๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ด์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹จ์ ์€ ํ™•์žฅ์„ฑ์ด ์ข‹์ง€ ์•Š๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์šด์˜ ์ฒด์ œ๋Š” ๋งŽ์€ ๊ฒƒ์„ ๊ตฌํ˜„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ปจํ…์ŠคํŠธ ์ „ํ™˜ ะธ ์‹œ์Šคํ…œ ํ˜ธ์ถœ. ์ด๋Š” ๋น„์šฉ์ด ๋งŽ์ด ๋“œ๋Š” ์ž‘์—…์ด๋ฉฐ ์—ฐ๊ฒฐ ์ˆ˜๊ฐ€ ๋งŽ์•„ ์—ฌ์œ  RAM์ด ๋ถ€์กฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ˆ˜์ •๋œ ๋ฒ„์ „์ด ๊ฐ•์กฐ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ๊ณ ์ •๋œ ์Šค๋ ˆ๋“œ ์ˆ˜ (์Šค๋ ˆ๋“œ ํ’€) ์ด๋ฅผ ํ†ตํ•ด ์‹œ์Šคํ…œ ์ถฉ๋Œ์„ ๋ฐฉ์ง€ํ•˜์ง€๋งŒ ๋™์‹œ์— ์ƒˆ๋กœ์šด ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์Šค๋ ˆ๋“œ ํ’€์ด ํ˜„์žฌ ๊ธด ์ฝ๊ธฐ ์ž‘์—…์œผ๋กœ ์ฐจ๋‹จ๋œ ๊ฒฝ์šฐ ์ด๋ฏธ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์‹ ํ•  ์ˆ˜ ์žˆ๋Š” ๋‹ค๋ฅธ ์†Œ์ผ“์€ ์ด๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ.

๋‘ ๋ฒˆ์งธ ์ ‘๊ทผ ๋ฐฉ์‹์€ ์ด๋ฒคํŠธ ์•Œ๋ฆผ ์‹œ์Šคํ…œ (์‹œ์Šคํ…œ ์„ ํƒ๊ธฐ)๋Š” OS์—์„œ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฌธ์„œ์—์„œ๋Š” I/O ์ž‘์—… ์ค€๋น„ ์ƒํƒœ์— ๋Œ€ํ•œ ๊ฒฝ๊ณ (์ด๋ฒคํŠธ, ์•Œ๋ฆผ)๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๋Š” ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ธ ์œ ํ˜•์˜ ์‹œ์Šคํ…œ ์„ ํƒ๊ธฐ์— ๋Œ€ํ•ด ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. ์™„๋ฃŒ์— ๋Œ€ํ•œ ์•Œ๋ฆผ. ๊ทธ ์‚ฌ์šฉ์˜ ๊ฐ„๋‹จํ•œ ์˜ˆ๋Š” ๋‹ค์Œ ๋ธ”๋ก ๋‹ค์ด์–ด๊ทธ๋žจ์œผ๋กœ ํ‘œํ˜„๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

๋ชจ๋“  ๊ธฐ๋Šฅ์„ ๊ฐ–์ถ˜ ๋ฒ ์–ด C I/O ๋ฆฌ์•กํ„ฐ

์ด๋Ÿฌํ•œ ์ ‘๊ทผ ๋ฐฉ์‹์˜ ์ฐจ์ด์ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • I/O ์ž‘์—… ์ฐจ๋‹จ ์œ ์˜ˆํ•˜๋‹ค ์‚ฌ์šฉ์ž ํ๋ฆ„ ๊ทธ๋•Œ๊นŒ์ง€OS๊ฐ€ ์ œ๋Œ€๋กœ ์ž‘๋™ํ•  ๋•Œ๊นŒ์ง€ ์กฐ๊ฐ ๋ชจ์Œ ๋“ค์–ด์˜ค๋Š” IP ํŒจํ‚ท ๋ฐ”์ดํŠธ ์ŠคํŠธ๋ฆผ์œผ๋กœ(TCP, ๋ฐ์ดํ„ฐ ์ˆ˜์‹ ) ๋˜๋Š” ๋‹ค์Œ์„ ํ†ตํ•œ ํ›„์† ์ „์†ก์„ ์œ„ํ•ด ๋‚ด๋ถ€ ์“ฐ๊ธฐ ๋ฒ„ํผ์— ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๊ณต๊ฐ„์ด ์ถฉ๋ถ„ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. NIC (๋ฐ์ดํ„ฐ ์ „์†ก).
  • ์‹œ์Šคํ…œ ์„ ํƒ๊ธฐ ์‹œ๊ฐ„์ด ์ง€๋‚จ์— ๋”ฐ๋ผ OS๊ฐ€ ํ”„๋กœ๊ทธ๋žจ์— ์•Œ๋ฆฝ๋‹ˆ๋‹ค. ์ด๋ฏธ ์กฐ๊ฐ ๋ชจ์Œ๋œ IP ํŒจํ‚ท(TCP, ๋ฐ์ดํ„ฐ ์ˆ˜์‹ ) ๋˜๋Š” ๋‚ด๋ถ€ ์“ฐ๊ธฐ ๋ฒ„ํผ์— ์ถฉ๋ถ„ํ•œ ๊ณต๊ฐ„ ์ด๋ฏธ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค(๋ฐ์ดํ„ฐ ์ „์†ก).

์š”์•ฝํ•˜์ž๋ฉด, ๊ฐ I/O์— ๋Œ€ํ•ด OS ์Šค๋ ˆ๋“œ๋ฅผ ์˜ˆ์•ฝํ•˜๋Š” ๊ฒƒ์€ ์ปดํ“จํŒ… ์„ฑ๋Šฅ์„ ๋‚ญ๋น„ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ ์Šค๋ ˆ๋“œ๋Š” ์œ ์šฉํ•œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. "์†Œํ”„ํŠธ์›จ์–ด ์ธํ„ฐ๋ŸฝํŠธ"). ์‹œ์Šคํ…œ ์„ ํƒ๊ธฐ๋Š” ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜์—ฌ ์‚ฌ์šฉ์ž ํ”„๋กœ๊ทธ๋žจ์ด CPU ๋ฆฌ์†Œ์Šค๋ฅผ ํ›จ์”ฌ ๋” ๊ฒฝ์ œ์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

I/O ๋ฆฌ์•กํ„ฐ ๋ชจ๋ธ

I/O ๋ฆฌ์•กํ„ฐ๋Š” ์‹œ์Šคํ…œ ์„ ํƒ๊ธฐ์™€ ์‚ฌ์šฉ์ž ์ฝ”๋“œ ์‚ฌ์ด์˜ ๊ณ„์ธต ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. ์ž‘๋™ ์›๋ฆฌ๋Š” ๋‹ค์Œ ๋ธ”๋ก ๋‹ค์ด์–ด๊ทธ๋žจ์œผ๋กœ ์„ค๋ช…๋ฉ๋‹ˆ๋‹ค.

๋ชจ๋“  ๊ธฐ๋Šฅ์„ ๊ฐ–์ถ˜ ๋ฒ ์–ด C I/O ๋ฆฌ์•กํ„ฐ

  • ์ด๋ฒคํŠธ๋Š” ํŠน์ • ์†Œ์ผ“์ด ๋น„์ฐจ๋‹จ I/O ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์•Œ๋ฆผ์ด๋ผ๋Š” ์ ์„ ์ƒ๊ธฐ์‹œ์ผœ ๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.
  • ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋Š” ์ด๋ฒคํŠธ๊ฐ€ ์ˆ˜์‹ ๋  ๋•Œ I/O ๋ฆฌ์•กํ„ฐ์— ์˜ํ•ด ํ˜ธ์ถœ๋˜์–ด ๋น„์ฐจ๋‹จ I/O ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

I/O ๋ฆฌ์•กํ„ฐ๋Š” ์ •์˜์ƒ ๋‹จ์ผ ์Šค๋ ˆ๋“œ์ด์ง€๋งŒ ์ด ๊ฐœ๋…์ด 1 ์Šค๋ ˆ๋“œ: 1 ๋ฆฌ์•กํ„ฐ ๋น„์œจ๋กœ ๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ ์‚ฌ์šฉ๋˜์–ด ๋ชจ๋“  CPU ์ฝ”์–ด๋ฅผ ์žฌํ™œ์šฉํ•˜๋Š” ๊ฒƒ์„ ๋ง‰์„ ๋ฐฉ๋ฒ•์ด ์—†๋‹ค๋Š” ์ ์— ์œ ์˜ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

ะ ะตะฐะปะธะทะฐั†ะธั

๊ณต๊ฐœ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํŒŒ์ผ์— ๋ฐฐ์น˜ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. reactor.h๋ฐ ๊ตฌํ˜„ - reactor.c. reactor.h ๋‹ค์Œ ๊ณต์ง€์‚ฌํ•ญ์œผ๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค.

Reactor.h์— ์„ ์–ธ ํ‘œ์‹œ

typedef struct reactor Reactor;

/*
 * ะฃะบะฐะทะฐั‚ะตะปัŒ ะฝะฐ ั„ัƒะฝะบั†ะธัŽ, ะบะพั‚ะพั€ะฐั ะฑัƒะดะตั‚ ะฒั‹ะทั‹ะฒะฐั‚ัŒัั I/O ั€ะตะฐะบั‚ะพั€ะพะผ ะฟั€ะธ ะฟะพัั‚ัƒะฟะปะตะฝะธะธ
 * ัะพะฑั‹ั‚ะธั ะพั‚ ัะธัั‚ะตะผะฝะพะณะพ ัะตะปะตะบั‚ะพั€ะฐ.
 */
typedef void (*Callback)(void *arg, int fd, uint32_t events);

/*
 * ะ’ะพะทะฒั€ะฐั‰ะฐะตั‚ `NULL` ะฒ ัะปัƒั‡ะฐะต ะพัˆะธะฑะบะธ, ะฝะต-`NULL` ัƒะบะฐะทะฐั‚ะตะปัŒ ะฝะฐ `Reactor` ะฒ
 * ะฟั€ะพั‚ะธะฒะฝะพะผ ัะปัƒั‡ะฐะต.
 */
Reactor *reactor_new(void);

/*
 * ะžัะฒะพะฑะพะถะดะฐะตั‚ ัะธัั‚ะตะผะฝั‹ะน ัะตะปะตะบั‚ะพั€, ะฒัะต ะทะฐั€ะตะณะธัั‚ั€ะธั€ะพะฒะฐะฝะฝั‹ะต ัะพะบะตั‚ั‹ ะฒ ะดะฐะฝะฝั‹ะน ะผะพะผะตะฝั‚
 * ะฒั€ะตะผะตะฝะธ ะธ ัะฐะผ I/O ั€ะตะฐะบั‚ะพั€.
 *
 * ะกะปะตะดัƒัŽั‰ะธะต ั„ัƒะฝะบั†ะธะธ ะฒะพะทะฒั€ะฐั‰ะฐัŽั‚ -1 ะฒ ัะปัƒั‡ะฐะต ะพัˆะธะฑะบะธ, 0 ะฒ ัะปัƒั‡ะฐะต ัƒัะฟะตั…ะฐ.
 */
int reactor_destroy(Reactor *reactor);

int reactor_register(const Reactor *reactor, int fd, uint32_t interest,
                     Callback callback, void *callback_arg);
int reactor_deregister(const Reactor *reactor, int fd);
int reactor_reregister(const Reactor *reactor, int fd, uint32_t interest,
                       Callback callback, void *callback_arg);

/*
 * ะ—ะฐะฟัƒัะบะฐะตั‚ ั†ะธะบะป ัะพะฑั‹ั‚ะธะน ั ั‚ะฐะนะผ-ะฐัƒั‚ะพะผ `timeout`.
 *
 * ะญั‚ะฐ ั„ัƒะฝะบั†ะธั ะฟะตั€ะตะดะฐัั‚ ัƒะฟั€ะฐะฒะปะตะฝะธะต ะฒั‹ะทั‹ะฒะฐัŽั‰ะตะผัƒ ะบะพะดัƒ ะตัะปะธ ะพั‚ะฒะตะดั‘ะฝะฝะพะต ะฒั€ะตะผั ะฒั‹ัˆะปะพ
 * ะธะปะธ/ะธ ะฟั€ะธ ะพั‚ััƒั‚ัั‚ะฒะธะธ ะทะฐั€ะตะณะธัั‚ั€ะธั€ะพะฒะฐะฝะฝั‹ั… ัะพะบะตั‚ะพะฒ.
 */
int reactor_run(const Reactor *reactor, time_t timeout);

I/O ๋ฆฌ์•กํ„ฐ ๊ตฌ์กฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค. ํŒŒ์ผ ์„ค๋ช…์ž ์„ ํƒ์ž Epoll ะธ ํ•ด์‹œ ํ…Œ์ด๋ธ” GHashTable, ๊ฐ ์†Œ์ผ“์„ ๋‹ค์Œ์œผ๋กœ ๋งคํ•‘ํ•ฉ๋‹ˆ๋‹ค. CallbackData (์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์˜ ๊ตฌ์กฐ ๋ฐ ์ด์— ๋Œ€ํ•œ ์‚ฌ์šฉ์ž ์ธ์ˆ˜)

Reactor ๋ฐ CallbackData ํ‘œ์‹œ

struct reactor {
    int epoll_fd;
    GHashTable *table; // (int, CallbackData)
};

typedef struct {
    Callback callback;
    void *arg;
} CallbackData;

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

๊ธฐ๋Šฅ reactor_register, reactor_deregister ะธ reactor_reregister ์‹œ์Šคํ…œ ์„ ํƒ๊ธฐ์™€ ํ•ด์‹œ ํ…Œ์ด๋ธ”์—์„œ ๊ด€์‹ฌ ์žˆ๋Š” ์†Œ์ผ“ ๋ชฉ๋ก๊ณผ ํ•ด๋‹น ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.

๋“ฑ๋ก ๊ธฐ๋Šฅ ํ‘œ์‹œ

#define REACTOR_CTL(reactor, op, fd, interest)                                 
    if (epoll_ctl(reactor->epoll_fd, op, fd,                                   
                  &(struct epoll_event){.events = interest,                    
                                        .data = {.fd = fd}}) == -1) {          
        perror("epoll_ctl");                                                   
        return -1;                                                             
    }

int reactor_register(const Reactor *reactor, int fd, uint32_t interest,
                     Callback callback, void *callback_arg) {
    REACTOR_CTL(reactor, EPOLL_CTL_ADD, fd, interest)
    g_hash_table_insert(reactor->table, int_in_heap(fd),
                        callback_data_new(callback, callback_arg));
    return 0;
}

int reactor_deregister(const Reactor *reactor, int fd) {
    REACTOR_CTL(reactor, EPOLL_CTL_DEL, fd, 0)
    g_hash_table_remove(reactor->table, &fd);
    return 0;
}

int reactor_reregister(const Reactor *reactor, int fd, uint32_t interest,
                       Callback callback, void *callback_arg) {
    REACTOR_CTL(reactor, EPOLL_CTL_MOD, fd, interest)
    g_hash_table_insert(reactor->table, int_in_heap(fd),
                        callback_data_new(callback, callback_arg));
    return 0;
}

I/O ๋ฆฌ์•กํ„ฐ๊ฐ€ ์„ค๋ช…์ž๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋ฒคํŠธ๋ฅผ ์ฐจ๋‹จํ•œ ํ›„ fd, ํ•ด๋‹น ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ์ด๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. fd, ๋น„ํŠธ ๋งˆ์Šคํฌ ์ƒ์„ฑ๋œ ์ด๋ฒคํŠธ ๋ฐ ์‚ฌ์šฉ์ž ํฌ์ธํ„ฐ void.

Reactor_run() ํ•จ์ˆ˜ ํ‘œ์‹œ

int reactor_run(const Reactor *reactor, time_t timeout) {
    int result;
    struct epoll_event *events;
    if ((events = calloc(MAX_EVENTS, sizeof(*events))) == NULL)
        abort();

    time_t start = time(NULL);

    while (true) {
        time_t passed = time(NULL) - start;
        int nfds =
            epoll_wait(reactor->epoll_fd, events, MAX_EVENTS, timeout - passed);

        switch (nfds) {
        // ะžัˆะธะฑะบะฐ
        case -1:
            perror("epoll_wait");
            result = -1;
            goto cleanup;
        // ะ’ั€ะตะผั ะฒั‹ัˆะปะพ
        case 0:
            result = 0;
            goto cleanup;
        // ะฃัะฟะตัˆะฝะฐั ะพะฟะตั€ะฐั†ะธั
        default:
            // ะ’ั‹ะทะฒะฐั‚ัŒ ะพะฑั€ะฐะฑะพั‚ั‡ะธะบะพะฒ ัะพะฑั‹ั‚ะธะน
            for (int i = 0; i < nfds; i++) {
                int fd = events[i].data.fd;

                CallbackData *callback =
                    g_hash_table_lookup(reactor->table, &fd);
                callback->callback(callback->arg, fd, events[i].events);
            }
        }
    }

cleanup:
    free(events);
    return result;
}

์š”์•ฝํ•˜์ž๋ฉด, ์‚ฌ์šฉ์ž ์ฝ”๋“œ์˜ ํ•จ์ˆ˜ ํ˜ธ์ถœ ์ฒด์ธ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ˜•์‹์„ ์ทจํ•ฉ๋‹ˆ๋‹ค.

๋ชจ๋“  ๊ธฐ๋Šฅ์„ ๊ฐ–์ถ˜ ๋ฒ ์–ด C I/O ๋ฆฌ์•กํ„ฐ

๋‹จ์ผ ์Šค๋ ˆ๋“œ ์„œ๋ฒ„

๋†’์€ ๋ถ€ํ•˜์—์„œ I/O ๋ฆฌ์•กํ„ฐ๋ฅผ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์œ„ํ•ด ์ด๋ฏธ์ง€๋กœ ๋ชจ๋“  ์š”์ฒญ์— โ€‹โ€‹์‘๋‹ตํ•˜๋Š” ๊ฐ„๋‹จํ•œ HTTP ์›น ์„œ๋ฒ„๋ฅผ ์ž‘์„ฑํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

HTTP ํ”„๋กœํ† ์ฝœ์— ๋Œ€ํ•œ ๋น ๋ฅธ ์ฐธ์กฐ

HTTP - ์ด๊ฒƒ์ด ํ”„๋กœํ† ์ฝœ์ด๋‹ค ์‘์šฉ ์ˆ˜์ค€, ์ฃผ๋กœ ์„œ๋ฒ„-๋ธŒ๋ผ์šฐ์ € ์ƒํ˜ธ ์ž‘์šฉ์— ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

HTTP๋ฅผ ํ†ตํ•ด ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ˆ˜์†ก ๊ทœ์•ฝ TCP, ์ง€์ •๋œ ํ˜•์‹์œผ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด๊ณ  ๋ฐ›์Šต๋‹ˆ๋‹ค. ์‚ฌ์–‘.

์š”์ฒญ ํ˜•์‹

<ะšะžะœะะะ”ะ> <URI> <ะ’ะ•ะ ะกะ˜ะฏ HTTP>CRLF
<ะ—ะะ“ะžะ›ะžะ’ะžะš 1>CRLF
<ะ—ะะ“ะžะ›ะžะ’ะžะš 2>CRLF
<ะ—ะะ“ะžะ›ะžะ’ะžะš N>CRLF CRLF
<ะ”ะะะะซะ•>

  • CRLF ๋‘ ๋ฌธ์ž์˜ ์‹œํ€€์Šค์ž…๋‹ˆ๋‹ค. r ะธ n, ์š”์ฒญ์˜ ์ฒซ ๋ฒˆ์งธ ์ค„, ํ—ค๋” ๋ฐ ๋ฐ์ดํ„ฐ๋ฅผ ๊ตฌ๋ถ„ํ•ฉ๋‹ˆ๋‹ค.
  • <ะšะžะœะะะ”ะ> - ์ค‘ ํ•˜๋‚˜ CONNECT, DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT, TRACE. ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์„œ๋ฒ„์— ๋ช…๋ น์„ ๋ณด๋ƒ…๋‹ˆ๋‹ค. GET, "ํŒŒ์ผ ๋‚ด์šฉ์„ ๋ณด๋‚ด์ฃผ์„ธ์š”."๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.
  • <URI> - ๊ท ์ผ ์ž์› ์‹๋ณ„์ž. ์˜ˆ๋ฅผ ๋“ค์–ด URI =์ธ ๊ฒฝ์šฐ /index.html, ํด๋ผ์ด์–ธํŠธ๋Š” ์‚ฌ์ดํŠธ์˜ ๋ฉ”์ธ ํŽ˜์ด์ง€๋ฅผ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค.
  • <ะ’ะ•ะ ะกะ˜ะฏ HTTP> โ€” ํ˜•์‹์˜ HTTP ํ”„๋กœํ† ์ฝœ ๋ฒ„์ „ HTTP/X.Y. ์˜ค๋Š˜๋‚  ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๋ฒ„์ „์€ HTTP/1.1.
  • <ะ—ะะ“ะžะ›ะžะ’ะžะš N> ํ˜•์‹์˜ ํ‚ค-๊ฐ’ ์Œ์ž…๋‹ˆ๋‹ค. <ะšะ›ะฎะง>: <ะ—ะะะงะ•ะะ˜ะ•>, ์ถ”๊ฐ€ ๋ถ„์„์„ ์œ„ํ•ด ์„œ๋ฒ„๋กœ ์ „์†ก๋ฉ๋‹ˆ๋‹ค.
  • <ะ”ะะะะซะ•> โ€” ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ์„œ๋ฒ„์— ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ์ž…๋‹ˆ๋‹ค. ๊ฐ„๋‹จํ•œ ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค JSON ๋˜๋Š” ๋‹ค๋ฅธ ํ˜•์‹.

์‘๋‹ต ํ˜•์‹

<ะ’ะ•ะ ะกะ˜ะฏ HTTP> <ะšะžะ” ะกะขะะขะฃะกะ> <ะžะŸะ˜ะกะะะ˜ะ• ะกะขะะขะฃะกะ>CRLF
<ะ—ะะ“ะžะ›ะžะ’ะžะš 1>CRLF
<ะ—ะะ“ะžะ›ะžะ’ะžะš 2>CRLF
<ะ—ะะ“ะžะ›ะžะ’ะžะš N>CRLF CRLF
<ะ”ะะะะซะ•>

  • <ะšะžะ” ะกะขะะขะฃะกะ> ์—ฐ์‚ฐ ๊ฒฐ๊ณผ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ์ˆซ์ž์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ ์„œ๋ฒ„๋Š” ํ•ญ์ƒ ์ƒํƒœ 200(์„ฑ๊ณต์ ์ธ ์ž‘์—…)์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  • <ะžะŸะ˜ะกะะะ˜ะ• ะกะขะะขะฃะกะ> โ€” ์ƒํƒœ ์ฝ”๋“œ์˜ ๋ฌธ์ž์—ด ํ‘œํ˜„์ž…๋‹ˆ๋‹ค. ์ƒํƒœ ์ฝ”๋“œ 200์˜ ๊ฒฝ์šฐ ์ด๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. OK.
  • <ะ—ะะ“ะžะ›ะžะ’ะžะš N> โ€” ์š”์ฒญ๊ณผ ๋™์ผํ•œ ํ˜•์‹์˜ ํ—ค๋”์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ œ๋ชฉ์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค Content-Length (ํŒŒ์ผ ํฌ๊ธฐ) ๋ฐ Content-Type: text/html (๋ฐ˜ํ™˜ ๋ฐ์ดํ„ฐ ์œ ํ˜•).
  • <ะ”ะะะะซะ•> โ€” ์‚ฌ์šฉ์ž๊ฐ€ ์š”์ฒญํ•œ ๋ฐ์ดํ„ฐ. ์šฐ๋ฆฌ์˜ ๊ฒฝ์šฐ ์ด๊ฒƒ์€ ์ด๋ฏธ์ง€์˜ ๊ฒฝ๋กœ์ž…๋‹ˆ๋‹ค. HTML.

ํŒŒ์ผ http_server.c (๋‹จ์ผ ์Šค๋ ˆ๋“œ ์„œ๋ฒ„) ํŒŒ์ผ ํฌํ•จ common.h์—๋Š” ๋‹ค์Œ ํ•จ์ˆ˜ ํ”„๋กœํ† ํƒ€์ž…์ด ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

common.h์— ํ•จ์ˆ˜ ํ”„๋กœํ† ํƒ€์ž… ํ‘œ์‹œ

/*
 * ะžะฑั€ะฐะฑะพั‚ั‡ะธะบ ัะพะฑั‹ั‚ะธะน, ะบะพั‚ะพั€ั‹ะน ะฒั‹ะทะพะฒะตั‚ัั ะฟะพัะปะต ั‚ะพะณะพ, ะบะฐะบ ัะพะบะตั‚ ะฑัƒะดะตั‚
 * ะณะพั‚ะพะฒ ะฟั€ะธะฝัั‚ัŒ ะฝะพะฒะพะต ัะพะตะดะธะฝะตะฝะธะต.
 */
static void on_accept(void *arg, int fd, uint32_t events);

/*
 * ะžะฑั€ะฐะฑะพั‚ั‡ะธะบ ัะพะฑั‹ั‚ะธะน, ะบะพั‚ะพั€ั‹ะน ะฒั‹ะทะพะฒะตั‚ัั ะฟะพัะปะต ั‚ะพะณะพ, ะบะฐะบ ัะพะบะตั‚ ะฑัƒะดะตั‚
 * ะณะพั‚ะพะฒ ะพั‚ะฟั€ะฐะฒะธั‚ัŒ HTTP ะพั‚ะฒะตั‚.
 */
static void on_send(void *arg, int fd, uint32_t events);

/*
 * ะžะฑั€ะฐะฑะพั‚ั‡ะธะบ ัะพะฑั‹ั‚ะธะน, ะบะพั‚ะพั€ั‹ะน ะฒั‹ะทะพะฒะตั‚ัั ะฟะพัะปะต ั‚ะพะณะพ, ะบะฐะบ ัะพะบะตั‚ ะฑัƒะดะตั‚
 * ะณะพั‚ะพะฒ ะฟั€ะธะฝัั‚ัŒ ั‡ะฐัั‚ัŒ HTTP ะทะฐะฟั€ะพัะฐ.
 */
static void on_recv(void *arg, int fd, uint32_t events);

/*
 * ะŸะตั€ะตะฒะพะดะธั‚ ะฒั…ะพะดัั‰ะตะต ัะพะตะดะธะฝะตะฝะธะต ะฒ ะฝะตะฑะปะพะบะธั€ัƒัŽั‰ะธะน ั€ะตะถะธะผ.
 */
static void set_nonblocking(int fd);

/*
 * ะŸะตั‡ะฐั‚ะฐะตั‚ ะฟะตั€ะตะดะฐะฝะฝั‹ะต ะฐั€ะณัƒะผะตะฝั‚ั‹ ะฒ stderr ะธ ะฒั‹ั…ะพะดะธั‚ ะธะท ะฟั€ะพั†ะตััะฐ ั
 * ะบะพะดะพะผ `EXIT_FAILURE`.
 */
static noreturn void fail(const char *format, ...);

/*
 * ะ’ะพะทะฒั€ะฐั‰ะฐะตั‚ ั„ะฐะนะปะพะฒั‹ะน ะดะตัะบั€ะธะฟั‚ะพั€ ัะพะบะตั‚ะฐ, ัะฟะพัะพะฑะฝะพะณะพ ะฟั€ะธะฝะธะผะฐั‚ัŒ ะฝะพะฒั‹ะต
 * TCP ัะพะตะดะธะฝะตะฝะธั.
 */
static int new_server(bool reuse_port);

๊ธฐ๋Šฅ ๋งคํฌ๋กœ๋„ ์„ค๋ช…๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. SAFE_CALL() ๊ทธ๋ฆฌ๊ณ  ํ•จ์ˆ˜๊ฐ€ ์ •์˜๋˜์—ˆ์Šต๋‹ˆ๋‹ค fail(). ๋งคํฌ๋กœ๋Š” ํ‘œํ˜„์‹์˜ ๊ฐ’์„ ์˜ค๋ฅ˜์™€ ๋น„๊ตํ•˜๊ณ  ์กฐ๊ฑด์ด true์ธ ๊ฒฝ์šฐ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. fail():

#define SAFE_CALL(call, error)                                                 
    do {                                                                       
        if ((call) == error) {                                                   
            fail("%s", #call);                                                 
        }                                                                      
    } while (false)

๊ธฐ๋Šฅ fail() ์ „๋‹ฌ๋œ ์ธ์ˆ˜๋ฅผ ํ„ฐ๋ฏธ๋„์— ์ธ์‡„ํ•ฉ๋‹ˆ๋‹ค(์˜ˆ: printf()) ์ฝ”๋“œ๋กœ ํ”„๋กœ๊ทธ๋žจ์„ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค. EXIT_FAILURE:

static noreturn void fail(const char *format, ...) {
    va_list args;
    va_start(args, format);
    vfprintf(stderr, format, args);
    va_end(args);
    fprintf(stderr, ": %sn", strerror(errno));
    exit(EXIT_FAILURE);
}

๊ธฐ๋Šฅ new_server() ์‹œ์Šคํ…œ ํ˜ธ์ถœ์— ์˜ํ•ด ์ƒ์„ฑ๋œ "์„œ๋ฒ„" ์†Œ์ผ“์˜ ํŒŒ์ผ ์„ค๋ช…์ž๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. socket(), bind() ะธ listen() ๋น„์ฐจ๋‹จ ๋ชจ๋“œ์—์„œ ๋“ค์–ด์˜ค๋Š” ์—ฐ๊ฒฐ์„ ์ˆ˜๋ฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

new_server() ํ•จ์ˆ˜ ํ‘œ์‹œ

static int new_server(bool reuse_port) {
    int fd;
    SAFE_CALL((fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP)),
              -1);

    if (reuse_port) {
        SAFE_CALL(
            setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &(int){1}, sizeof(int)),
            -1);
    }

    struct sockaddr_in addr = {.sin_family = AF_INET,
                               .sin_port = htons(SERVER_PORT),
                               .sin_addr = {.s_addr = inet_addr(SERVER_IPV4)},
                               .sin_zero = {0}};

    SAFE_CALL(bind(fd, (struct sockaddr *)&addr, sizeof(addr)), -1);
    SAFE_CALL(listen(fd, SERVER_BACKLOG), -1);
    return fd;
}

  • ์†Œ์ผ“์€ ์ฒ˜์Œ์— ํ”Œ๋ž˜๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋น„์ฐจ๋‹จ ๋ชจ๋“œ๋กœ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. SOCK_NONBLOCK๊ทธ๋ž˜์„œ ํ•จ์ˆ˜์—์„œ on_accept() (์ž์„ธํžˆ ๋ณด๊ธฐ) ์‹œ์Šคํ…œ ํ˜ธ์ถœ accept() ์Šค๋ ˆ๋“œ ์‹คํ–‰์„ ์ค‘์ง€ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.
  • ๋ฉด reuse_port ๊ฐ™์Œ true, ์ด ํ•จ์ˆ˜๋Š” ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ์†Œ์ผ“์„ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค. SO_REUSEPORT ๋ฅผ ํ†ตํ•ด setsockopt()๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ ๋™์ผํ•œ ํฌํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด("๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ์„œ๋ฒ„" ์„น์…˜ ์ฐธ์กฐ)

์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ on_accept() OS๊ฐ€ ์ด๋ฒคํŠธ๋ฅผ ์ƒ์„ฑํ•œ ํ›„์— ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค. EPOLLIN, ์ด ๊ฒฝ์šฐ ์ƒˆ ์—ฐ๊ฒฐ์ด ํ—ˆ์šฉ๋  ์ˆ˜ ์žˆ์Œ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. on_accept() ์ƒˆ๋กœ์šด ์—ฐ๊ฒฐ์„ ์ˆ˜๋ฝํ•˜๊ณ  ์ด๋ฅผ ๋น„์ฐจ๋‹จ ๋ชจ๋“œ๋กœ ์ „ํ™˜ํ•œ ํ›„ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์— ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค. on_recv() I/O ๋ฆฌ์•กํ„ฐ์—์„œ.

on_accept() ํ•จ์ˆ˜ ํ‘œ์‹œ

static void on_accept(void *arg, int fd, uint32_t events) {
    int incoming_conn;
    SAFE_CALL((incoming_conn = accept(fd, NULL, NULL)), -1);
    set_nonblocking(incoming_conn);
    SAFE_CALL(reactor_register(reactor, incoming_conn, EPOLLIN, on_recv,
                               request_buffer_new()),
              -1);
}

์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ on_recv() OS๊ฐ€ ์ด๋ฒคํŠธ๋ฅผ ์ƒ์„ฑํ•œ ํ›„์— ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค. EPOLLIN, ์ด ๊ฒฝ์šฐ ์—ฐ๊ฒฐ์ด ๋“ฑ๋ก๋˜์—ˆ์Œ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. on_accept(), ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์‹ ํ•  ์ค€๋น„๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

on_recv() HTTP ์š”์ฒญ์ด ์™„์ „ํžˆ ์ˆ˜์‹ ๋  ๋•Œ๊นŒ์ง€ ์—ฐ๊ฒฐ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์€ ๋‹ค์Œ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค. on_send() HTTP ์‘๋‹ต์„ ๋ณด๋ƒ…๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์—ฐ๊ฒฐ์„ ๋Š์œผ๋ฉด ์†Œ์ผ“์€ ๋“ฑ๋ก ์ทจ์†Œ๋˜๊ณ  ๋‹ค์Œ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ซํž™๋‹ˆ๋‹ค. close().

on_recv() ํ•จ์ˆ˜ ํ‘œ์‹œ

static void on_recv(void *arg, int fd, uint32_t events) {
    RequestBuffer *buffer = arg;

    // ะŸั€ะธะฝะธะผะฐะตะผ ะฒั…ะพะดะฝั‹ะต ะดะฐะฝะฝั‹ะต ะดะพ ั‚ะตั… ะฟะพั€, ั‡ั‚ะพ recv ะฒะพะทะฒั€ะฐั‚ะธั‚ 0 ะธะปะธ ะพัˆะธะฑะบัƒ
    ssize_t nread;
    while ((nread = recv(fd, buffer->data + buffer->size,
                         REQUEST_BUFFER_CAPACITY - buffer->size, 0)) > 0)
        buffer->size += nread;

    // ะšะปะธะตะฝั‚ ะพะฑะพั€ะฒะฐะป ัะพะตะดะธะฝะตะฝะธะต
    if (nread == 0) {
        SAFE_CALL(reactor_deregister(reactor, fd), -1);
        SAFE_CALL(close(fd), -1);
        request_buffer_destroy(buffer);
        return;
    }

    // read ะฒะตั€ะฝัƒะป ะพัˆะธะฑะบัƒ, ะพั‚ะปะธั‡ะฝัƒัŽ ะพั‚ ะพัˆะธะฑะบะธ, ะฟั€ะธ ะบะพั‚ะพั€ะพะน ะฒั‹ะทะพะฒ ะทะฐะฑะปะพะบะธั€ัƒะตั‚
    // ะฟะพั‚ะพะบ
    if (errno != EAGAIN && errno != EWOULDBLOCK) {
        request_buffer_destroy(buffer);
        fail("read");
    }

    // ะŸะพะปัƒั‡ะตะฝ ะฟะพะปะฝั‹ะน HTTP ะทะฐะฟั€ะพั ะพั‚ ะบะปะธะตะฝั‚ะฐ. ะขะตะฟะตั€ัŒ ั€ะตะณะธัั‚ั€ะธั€ัƒะตะผ ะพะฑั€ะฐะฑะพั‚ั‡ะธะบะฐ
    // ัะพะฑั‹ั‚ะธะน ะดะปั ะพั‚ะฟั€ะฐะฒะบะธ ะดะฐะฝะฝั‹ั…
    if (request_buffer_is_complete(buffer)) {
        request_buffer_clear(buffer);
        SAFE_CALL(reactor_reregister(reactor, fd, EPOLLOUT, on_send, buffer),
                  -1);
    }
}

์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ on_send() OS๊ฐ€ ์ด๋ฒคํŠธ๋ฅผ ์ƒ์„ฑํ•œ ํ›„์— ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค. EPOLLOUT, ์ด๋Š” ์—ฐ๊ฒฐ์ด ๋“ฑ๋ก๋˜์—ˆ์Œ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. on_recv(), ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ผ ์ค€๋น„๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” ์ด๋ฏธ์ง€์™€ ํ•จ๊ป˜ HTML์ด ํฌํ•จ๋œ HTTP ์‘๋‹ต์„ ํด๋ผ์ด์–ธํŠธ์— ๋ณด๋‚ธ ๋‹ค์Œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋‹ค์‹œ ๋‹ค์Œ์œผ๋กœ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค. on_recv().

on_send() ํ•จ์ˆ˜ ํ‘œ์‹œ

static void on_send(void *arg, int fd, uint32_t events) {
    const char *content = "<img "
                          "src="https://habrastorage.org/webt/oh/wl/23/"
                          "ohwl23va3b-dioerobq_mbx4xaw.jpeg">";
    char response[1024];
    sprintf(response,
            "HTTP/1.1 200 OK" CRLF "Content-Length: %zd" CRLF "Content-Type: "
            "text/html" DOUBLE_CRLF "%s",
            strlen(content), content);

    SAFE_CALL(send(fd, response, strlen(response), 0), -1);
    SAFE_CALL(reactor_reregister(reactor, fd, EPOLLIN, on_recv, arg), -1);
}

๊ทธ๋ฆฌ๊ณ  ๋งˆ์ง€๋ง‰์œผ๋กœ ํŒŒ์ผ์—์„œ http_server.c, ๊ธฐ๋Šฅ ์ค‘ main() ์šฐ๋ฆฌ๋Š” ๋‹ค์Œ์„ ์‚ฌ์šฉํ•˜์—ฌ I/O ๋ฆฌ์•กํ„ฐ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. reactor_new(), ์„œ๋ฒ„ ์†Œ์ผ“์„ ์ƒ์„ฑํ•˜๊ณ  ๋“ฑ๋กํ•˜๊ณ  ๋‹ค์Œ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฆฌ์•กํ„ฐ๋ฅผ ์‹œ์ž‘ํ•˜์‹ญ์‹œ์˜ค. reactor_run() ์ •ํ™•ํžˆ XNUMX๋ถ„ ๋™์•ˆ ๋ฆฌ์†Œ์Šค๋ฅผ ํ•ด์ œํ•˜๊ณ  ํ”„๋กœ๊ทธ๋žจ์„ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค.

http_server.c ํ‘œ์‹œ

#include "reactor.h"

static Reactor *reactor;

#include "common.h"

int main(void) {
    SAFE_CALL((reactor = reactor_new()), NULL);
    SAFE_CALL(
        reactor_register(reactor, new_server(false), EPOLLIN, on_accept, NULL),
        -1);
    SAFE_CALL(reactor_run(reactor, SERVER_TIMEOUT_MILLIS), -1);
    SAFE_CALL(reactor_destroy(reactor), -1);
}

๋ชจ๋“  ๊ฒƒ์ด ์˜ˆ์ƒ๋Œ€๋กœ ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ปดํŒŒ์ผ(chmod a+x compile.sh && ./compile.sh ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ์—์„œ) ์ž์ฒด ์ž‘์„ฑ๋œ ์„œ๋ฒ„๋ฅผ ์‹œ์ž‘ํ•˜๊ณ  http://127.0.0.1:18470 ๋ธŒ๋ผ์šฐ์ €์—์„œ ์šฐ๋ฆฌ๊ฐ€ ๊ธฐ๋Œ€ํ–ˆ๋˜ ๊ฒƒ์„ ํ™•์ธํ•˜์„ธ์š”:

๋ชจ๋“  ๊ธฐ๋Šฅ์„ ๊ฐ–์ถ˜ ๋ฒ ์–ด C I/O ๋ฆฌ์•กํ„ฐ

์„ฑ๋Šฅ ์ธก์ •

๋‚ด ์ฐจ ์‚ฌ์–‘์„ ๋ณด์—ฌ์ฃผ์„ธ์š”

$ screenfetch
 MMMMMMMMMMMMMMMMMMMMMMMMMmds+.        OS: Mint 19.1 tessa
 MMm----::-://////////////oymNMd+`     Kernel: x86_64 Linux 4.15.0-20-generic
 MMd      /++                -sNMd:    Uptime: 2h 34m
 MMNso/`  dMM    `.::-. .-::.` .hMN:   Packages: 2217
 ddddMMh  dMM   :hNMNMNhNMNMNh: `NMm   Shell: bash 4.4.20
     NMm  dMM  .NMN/-+MMM+-/NMN` dMM   Resolution: 1920x1080
     NMm  dMM  -MMm  `MMM   dMM. dMM   DE: Cinnamon 4.0.10
     NMm  dMM  -MMm  `MMM   dMM. dMM   WM: Muffin
     NMm  dMM  .mmd  `mmm   yMM. dMM   WM Theme: Mint-Y-Dark (Mint-Y)
     NMm  dMM`  ..`   ...   ydm. dMM   GTK Theme: Mint-Y [GTK2/3]
     hMM- +MMd/-------...-:sdds  dMM   Icon Theme: Mint-Y
     -NMm- :hNMNNNmdddddddddy/`  dMM   Font: Noto Sans 9
      -dMNs-``-::::-------.``    dMM   CPU: Intel Core i7-6700 @ 8x 4GHz [52.0ยฐC]
       `/dMNmy+/:-------------:/yMMM   GPU: NV136
          ./ydNMMMMMMMMMMMMMMMMMMMMM   RAM: 2544MiB / 7926MiB
             .MMMMMMMMMMMMMMMMMMM

๋‹จ์ผ ์Šค๋ ˆ๋“œ ์„œ๋ฒ„์˜ ์„ฑ๋Šฅ์„ ์ธก์ •ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋‘ ๊ฐœ์˜ ํ„ฐ๋ฏธ๋„์„ ์—ด์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ํ•˜๋‚˜์—์„œ๋Š” ๋‹ค์Œ์„ ์‹คํ–‰ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ./http_server, ๋‹ค๋ฅธ - ์ผํ•˜๋‹ค. ์ž ์‹œ ํ›„ ๋‘ ๋ฒˆ์งธ ํ„ฐ๋ฏธ๋„์— ๋‹ค์Œ ํ†ต๊ณ„๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

$ wrk -c100 -d1m -t8 http://127.0.0.1:18470 -H "Host: 127.0.0.1:18470" -H "Accept-Language: en-US,en;q=0.5" -H "Connection: keep-alive"
Running 1m test @ http://127.0.0.1:18470
  8 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   493.52us   76.70us  17.31ms   89.57%
    Req/Sec    24.37k     1.81k   29.34k    68.13%
  11657769 requests in 1.00m, 1.60GB read
Requests/sec: 193974.70
Transfer/sec:     27.19MB

์šฐ๋ฆฌ์˜ ๋‹จ์ผ ์Šค๋ ˆ๋“œ ์„œ๋ฒ„๋Š” 11๊ฐœ์˜ ์—ฐ๊ฒฐ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๋ถ„๋‹น 100๋งŒ ๊ฐœ ์ด์ƒ์˜ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋‚˜์˜์ง€ ์•Š์€ ๊ฒฐ๊ณผ์ธ๋ฐ ๊ฐœ์„ ๋  ์ˆ˜ ์žˆ์„๊นŒ์š”?

๋‹ค์ค‘ ์Šค๋ ˆ๋“œ ์„œ๋ฒ„

์œ„์—์„œ ์–ธ๊ธ‰ํ–ˆ๋“ฏ์ด I/O ๋ฆฌ์•กํ„ฐ๋Š” ๋ณ„๋„์˜ ์Šค๋ ˆ๋“œ๋กœ ์ƒ์„ฑ๋  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๋ชจ๋“  CPU ์ฝ”์–ด๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์ ‘๊ทผ ๋ฐฉ์‹์„ ์‹ค์ œ๋กœ ์ ์šฉํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

http_server_multithreaded.c ํ‘œ์‹œ

#include "reactor.h"

static Reactor *reactor;
#pragma omp threadprivate(reactor)

#include "common.h"

int main(void) {
#pragma omp parallel
    {
        SAFE_CALL((reactor = reactor_new()), NULL);
        SAFE_CALL(reactor_register(reactor, new_server(true), EPOLLIN,
                                   on_accept, NULL),
                  -1);
        SAFE_CALL(reactor_run(reactor, SERVER_TIMEOUT_MILLIS), -1);
        SAFE_CALL(reactor_destroy(reactor), -1);
    }
}

์ด์ œ ๋ชจ๋“  ์Šค๋ ˆ๋“œ ์ž์‹ ์˜ ์†Œ์œ  ๋ฐ˜์‘๊ธฐ:

static Reactor *reactor;
#pragma omp threadprivate(reactor)

ํ•จ์ˆ˜ ์ธ์ˆ˜์— ์œ ์˜ํ•˜์„ธ์š”. new_server() ์˜นํ˜ธ์ž true. ์ด๋Š” ์„œ๋ฒ„ ์†Œ์ผ“์— ์˜ต์…˜์„ ํ• ๋‹นํ•œ๋‹ค๋Š” ์˜๋ฏธ์ž…๋‹ˆ๋‹ค. SO_REUSEPORT๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์ž์„ธํ•œ ๋‚ด์šฉ์„ ์ฝ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—.

๋‘ ๋ฒˆ์งธ ์‹คํ–‰

์ด์ œ ๋‹ค์ค‘ ์Šค๋ ˆ๋“œ ์„œ๋ฒ„์˜ ์„ฑ๋Šฅ์„ ์ธก์ •ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

$ wrk -c100 -d1m -t8 http://127.0.0.1:18470 -H "Host: 127.0.0.1:18470" -H "Accept-Language: en-US,en;q=0.5" -H "Connection: keep-alive"
Running 1m test @ http://127.0.0.1:18470
  8 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.14ms    2.53ms  40.73ms   89.98%
    Req/Sec    79.98k    18.07k  154.64k    78.65%
  38208400 requests in 1.00m, 5.23GB read
Requests/sec: 635876.41
Transfer/sec:     89.14MB

1๋ถ„ ๋™์•ˆ ์ฒ˜๋ฆฌ๋˜๋Š” ์š”์ฒญ ์ˆ˜๊ฐ€ ~3.28๋ฐฐ ์ฆ๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค! ํ•˜์ง€๋งŒ ์šฐ๋ฆฌ๋Š” ๋ผ์šด๋“œ ์ˆ˜๋ณด๋‹ค XNUMX๋งŒ ์ •๋„ ๋ถ€์กฑํ–ˆ์œผ๋ฏ€๋กœ ์ด๋ฅผ ๊ณ ์น˜๋„๋ก ๋…ธ๋ ฅํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋จผ์ € ์ƒ์„ฑ๋œ ํ†ต๊ณ„๋ฅผ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋ฐ˜ํ™˜ ํ•œ:

$ sudo perf stat -B -e task-clock,context-switches,cpu-migrations,page-faults,cycles,instructions,branches,branch-misses,cache-misses ./http_server_multithreaded

 Performance counter stats for './http_server_multithreaded':

     242446,314933      task-clock (msec)         #    4,000 CPUs utilized          
         1โ€ฏ813โ€ฏ074      context-switches          #    0,007 M/sec                  
             4โ€ฏ689      cpu-migrations            #    0,019 K/sec                  
               254      page-faults               #    0,001 K/sec                  
   895โ€ฏ324โ€ฏ830โ€ฏ170      cycles                    #    3,693 GHz                    
   621โ€ฏ378โ€ฏ066โ€ฏ808      instructions              #    0,69  insn per cycle         
   119โ€ฏ926โ€ฏ709โ€ฏ370      branches                  #  494,653 M/sec                  
     3โ€ฏ227โ€ฏ095โ€ฏ669      branch-misses             #    2,69% of all branches        
           808โ€ฏ664      cache-misses                                                

      60,604330670 seconds time elapsed

CPU ์„ ํ˜ธ๋„ ์‚ฌ์šฉ, ๋‹ค์Œ์œผ๋กœ ์ปดํŒŒ์ผ -march=native, PGO, ์ ์ค‘ ํšŸ์ˆ˜ ์ฆ๊ฐ€ ์บ์‹œ, ์ฆ๊ฐ€ํ•˜๋‹ค MAX_EVENTS ์‚ฌ์šฉํ•˜๋‹ค EPOLLET ์„ฑ๋Šฅ์ด ํฌ๊ฒŒ ํ–ฅ์ƒ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๋™์‹œ ์—ฐ๊ฒฐ ์ˆ˜๋ฅผ ๋Š˜๋ฆฌ๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š”?

352๊ฐœ์˜ ๋™์‹œ ์—ฐ๊ฒฐ์— ๋Œ€ํ•œ ํ†ต๊ณ„:

$ wrk -c352 -d1m -t8 http://127.0.0.1:18470 -H "Host: 127.0.0.1:18470" -H "Accept-Language: en-US,en;q=0.5" -H "Connection: keep-alive"
Running 1m test @ http://127.0.0.1:18470
  8 threads and 352 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     2.12ms    3.79ms  68.23ms   87.49%
    Req/Sec    83.78k    12.69k  169.81k    83.59%
  40006142 requests in 1.00m, 5.48GB read
Requests/sec: 665789.26
Transfer/sec:     93.34MB

์›ํ•˜๋Š” ๊ฒฐ๊ณผ๋ฅผ ์–ป์—ˆ์œผ๋ฉฐ 1๋ถ„ ๋™์•ˆ ์ฒ˜๋ฆฌ๋œ ์š”์ฒญ ์ˆ˜๊ฐ€ ์—ฐ๊ฒฐ ์ˆ˜์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง€๋Š” ๊ฒƒ์„ ๋ณด์—ฌ์ฃผ๋Š” ํฅ๋ฏธ๋กœ์šด ๊ทธ๋ž˜ํ”„๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

๋ชจ๋“  ๊ธฐ๋Šฅ์„ ๊ฐ–์ถ˜ ๋ฒ ์–ด C I/O ๋ฆฌ์•กํ„ฐ

์ˆ˜๋ฐฑ ๊ฐœ์˜ ์—ฐ๊ฒฐ ํ›„์— ๋‘ ์„œ๋ฒ„ ๋ชจ๋‘์— ๋Œ€ํ•ด ์ฒ˜๋ฆฌ๋œ ์š”์ฒญ ์ˆ˜๊ฐ€ ๊ธ‰๊ฒฉํžˆ ๊ฐ์†Œํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ ๋ฒ„์ „์—์„œ๋Š” ์ด๊ฒƒ์ด ๋” ๋ˆˆ์— ๋•๋‹ˆ๋‹ค). ์ด๊ฒƒ์ด Linux TCP/IP ์Šคํƒ ๊ตฌํ˜„๊ณผ ๊ด€๋ จ์ด ์žˆ์Šต๋‹ˆ๊นŒ? ๊ทธ๋ž˜ํ”„์˜ ์ด๋Ÿฌํ•œ ๋™์ž‘๊ณผ ๋‹ค์ค‘ ์Šค๋ ˆ๋“œ ๋ฐ ๋‹จ์ผ ์Šค๋ ˆ๋“œ ์˜ต์…˜์— ๋Œ€ํ•œ ์ตœ์ ํ™”์— ๋Œ€ํ•œ ๊ฐ€์ •์„ ๋Œ“๊ธ€์— ์ž์œ ๋กญ๊ฒŒ ์ ์–ด์ฃผ์„ธ์š”.

๋ฐฉ๋ฒ• ์œ ๋ช…ํ•œ ์„ค๋ช…์—์„œ ์ด ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ๋Š” ์‹ค์ œ ๋กœ๋“œ ํ•˜์—์„œ I/O ๋ฆฌ์•กํ„ฐ์˜ ๋™์ž‘์„ ๋ณด์—ฌ์ฃผ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ๊ฑฐ์˜ ํ•ญ์ƒ ์„œ๋ฒ„๊ฐ€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์ƒํ˜ธ ์ž‘์šฉํ•˜๊ณ , ๋กœ๊ทธ๋ฅผ ์ถœ๋ ฅํ•˜๊ณ , ์•”ํ˜ธํ™”๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. TLS ๋“ฑ์œผ๋กœ ์ธํ•ด ํ•˜์ค‘์ด ๋ถˆ๊ท ์ผํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค(๋™์ ). ํƒ€์‚ฌ ๊ตฌ์„ฑ ์š”์†Œ์™€ ํ•จ๊ป˜ ํ…Œ์ŠคํŠธํ•˜๋Š” ๊ฒƒ์€ I/O proactor์— ๋Œ€ํ•œ ๋ฌธ์„œ์—์„œ ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค.

I/O ๋ฆฌ์•กํ„ฐ์˜ ๋‹จ์ 

I/O ๋ฆฌ์•กํ„ฐ์—๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋‹จ์ ์ด ์žˆ๋‹ค๋Š” ์ ์„ ์ดํ•ดํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • ๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ I/O ๋ฆฌ์•กํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ๋‹ค์†Œ ์–ด๋ ต์Šต๋‹ˆ๋‹ค. ํ๋ฆ„์„ ์ˆ˜๋™์œผ๋กœ ๊ด€๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ์‹ค์Šต์— ๋”ฐ๋ฅด๋ฉด ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ ๋กœ๋“œ๊ฐ€ ๊ท ์ผํ•˜์ง€ ์•Š์•„ ํ•œ ์Šค๋ ˆ๋“œ๊ฐ€ ์ž‘์—…์— ๋ฐ”์œ ๋™์•ˆ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ ๋กœ๊น…๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ํ•˜๋‚˜์˜ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ์Šค๋ ˆ๋“œ๋ฅผ ์ฐจ๋‹จํ•˜๋ฉด ์‹œ์Šคํ…œ ์„ ํƒ๊ธฐ ์ž์ฒด๋„ ์ฐจ๋‹จ๋˜๋ฏ€๋กœ ์ฐพ๊ธฐ ์–ด๋ ค์šด ๋ฒ„๊ทธ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค. I/O ํ”„๋กœ์•กํ„ฐ, ์Šค๋ ˆ๋“œ ํ’€์— ๋กœ๋“œ๋ฅผ ๊ท ๋“ฑํ•˜๊ฒŒ ๋ถ„๋ฐฐํ•˜๋Š” ์Šค์ผ€์ค„๋Ÿฌ๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์œผ๋ฉฐ ๋” ํŽธ๋ฆฌํ•œ API๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚˜์ค‘์— ๋‹ค๋ฅธ ๊ธฐ์‚ฌ์—์„œ ์ด์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๊ฒฐ๋ก 

์ด๋ก ์—์„œ ๋ฐ”๋กœ ํ”„๋กœํŒŒ์ผ๋Ÿฌ ๋ฐฐ๊ธฐ ์žฅ์น˜๋กœ์˜ ์—ฌ์ •์ด ๋๋‚˜๋Š” ๊ณณ์ž…๋‹ˆ๋‹ค.

๋‹ค์–‘ํ•œ ์ˆ˜์ค€์˜ ํŽธ๋ฆฌํ•จ๊ณผ ์†๋„๋ฅผ ๊ฐ–์ถ˜ ๋„คํŠธ์›Œํฌ ์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋ฐ ์žˆ์–ด ๋˜‘๊ฐ™์ด ํฅ๋ฏธ๋กœ์šด ์ ‘๊ทผ ๋ฐฉ์‹์ด ๋งŽ์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒƒ์— ๋Œ€ํ•ด ๋„ˆ๋ฌด ๊นŠ์ด ์ƒ๊ฐํ•ด์„œ๋Š” ์•ˆ ๋ฉ๋‹ˆ๋‹ค. ์ œ ์ƒ๊ฐ์—๋Š” ํฅ๋ฏธ๋กœ์šด ๋งํฌ๊ฐ€ ์•„๋ž˜์— ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค.

๋•Œ๊นŒ์ง€ ์šฐ๋ฆฌ๋Š” ๋‹ค์‹œ ๋งŒ๋‚ !

ํฅ๋ฏธ๋กœ์šด ํ”„๋กœ์ ํŠธ

๋˜ ๋ฌด์—‡์„ ์ฝ์–ด์•ผ ํ•˜๋‚˜์š”?

์ถœ์ฒ˜ : habr.com

์ฝ”๋ฉ˜ํŠธ๋ฅผ ์ถ”๊ฐ€