I/O reactor (hoʻokahi kaula hanana hanana) he kumu hoʻohālike no ke kākau ʻana i nā lako polokalamu haʻahaʻa kiʻekiʻe, i hoʻohana ʻia i nā hāʻina kaulana he nui:
Ma kēia ʻatikala, e nānā mākou i ka ins a me waho o kahi reactor I/O a pehea e hana ai, e kākau i kahi hoʻokō ma lalo o 200 laina o ke code, a hana i kahi kaʻina kikowaena HTTP maʻalahi ma luna o 40 miliona mau noi/min.
Kauwehe
Ua kākau ʻia ka ʻatikala e kōkua i ka hoʻomaopopo ʻana i ka hana ʻana o ka reactor I/O, a no laila e hoʻomaopopo i nā pilikia ke hoʻohana ʻia.
Pono ka ʻike o nā kumu no ka hoʻomaopopo ʻana i ka ʻatikala. ʻōlelo C a me kekahi ʻike i ka hoʻomohala ʻana i nā noi pūnaewele.
Ua kākau ʻia nā code āpau ma ka ʻōlelo C e like me (akahele: lōʻihi PDF) i ka maʻamau C11 no Linux a loaʻa ma GitHub.
No ke aha e pono ai kēia?
Me ka ulu ʻana o ka kaulana o ka Pūnaewele, ua hoʻomaka nā kikowaena pūnaewele e mālama i ka nui o nā pilina i ka manawa like, a no laila ua hoʻāʻo ʻia ʻelua ala: ke kāohi ʻana i ka I/O ma ka nui o nā loina OS a me ka pale ʻole I/O i hui pū me. he ʻōnaehana hoʻolaha hanana, i kapa ʻia ʻo "mea koho ʻōnaehana" (epoll/kqueue/IOCP/etc).
ʻO ke ala mua e pili ana i ka hana ʻana i kahi thread OS hou no kēlā me kēia pili e komo mai. ʻO kona hemahema ka scalability maikaʻi ʻole: pono e hoʻokō ka ʻōnaehana hana i nā mea he nui nā hoʻololi pōʻaiapili и kelepona ʻōnaehana. He mau hana koʻikoʻi lākou a hiki ke alakaʻi i ka nele o ka RAM manuahi me ka nui o nā pilina.
Hōʻike ka mana i hoʻololi ʻia helu paʻa o nā kaula (waiwai thread), no laila e pale ana i ka ʻōnaehana mai ka hoʻopau ʻana i ka hoʻokō ʻana, akā i ka manawa like e hoʻopuka ana i kahi pilikia hou: inā paʻa ʻia kahi kolamu thread e nā hana heluhelu lōʻihi, a laila ʻaʻole hiki i nā kumu ʻē aʻe ke loaʻa ka ʻikepili. e hana pela.
Hoʻohana ka ʻaoʻao ʻelua ʻōnaehana hoʻolaha hanana (mea koho pūnaewele) i hāʻawi ʻia e ka OS. Kūkākūkā kēia ʻatikala i ke ʻano maʻamau o ka mea koho ʻōnaehana, e pili ana i nā mākaʻikaʻi (nā hanana, nā hoʻolaha) e pili ana i ka mākaukau no nā hana I/O, ma mua o ka nā hoʻolaha e pili ana i ko lākou hoʻopau ʻana. Hiki ke hōʻike ʻia kahi hiʻohiʻona maʻalahi o kāna hoʻohana ʻana e ke kiʻi poloka aʻe:
ʻO ka ʻokoʻa ma waena o kēia mau ala e like me kēia:
Kāohi i nā hana I/O hoʻopaneʻe kahe mea hoʻohana a hiki ia hiki i ka OS pono defragments e komo mai ana ʻO nā ʻeke IP i ke kahawai byte (TCP, ka loaʻa ʻana o ka ʻikepili) a i ʻole ʻaʻole lawa ka wahi i loaʻa i loko o nā pahu kākau kūloko no ka hoʻouna ʻana ma o NIC (hoʻouna ʻikepili).
Mea koho pūnaewele ua holo ʻoi aʻe ka manawa hōʻike i ka polokalamu i ka OS ua nā ʻeke IP defragmented (TCP, ka loaʻa ʻana o ka ʻikepili) a i ʻole ka nui o nā wahi i loko o nā pale kākau kūloko ua loaʻa (ka hoʻouna ʻana i ka ʻikepili).
No ka hōʻuluʻulu ʻana, ʻo ka mālama ʻana i kahi thread OS no kēlā me kēia I/O he mea ʻino ia o ka mana helu, no ka mea, ʻo ka ʻoiaʻiʻo, ʻaʻole e hana pono nā kaula (no laila ka huaʻōlelo. "hoʻopau polokalamu"). Hoʻoponopono ka mea koho ʻōnaehana i kēia pilikia, e ʻae ana i ka polokalamu mea hoʻohana e hoʻohana i nā kumuwaiwai CPU i ʻoi aku ka waiwai.
Ke kumu hoʻohālike I/O
Hana ka reactor I/O ma ke ʻano he papa ma waena o ka mea koho ʻōnaehana a me ka code mea hoʻohana. ʻO ke kumu o kāna hana i wehewehe ʻia e ke kiʻi poloka aʻe:
E hoʻomanaʻo wau iā ʻoe he hanana kahi hōʻike e hiki ai i kahi kumu ke hana i kahi hana I/O ʻole paʻa ʻole.
ʻO ka mea lawelawe hanana kahi hana i kapa ʻia e ka reactor I/O i ka wā i loaʻa ai kahi hanana, a laila hana i kahi hana I/O pale ʻole.
He mea nui ia e hoʻomaopopo i ka I/O reactor ma ka wehewehe ʻana i hoʻokahi threaded, akā ʻaʻohe mea e kāpae i ka manaʻo mai ka hoʻohana ʻia ʻana i loko o kahi kaiapuni multi-threaded ma ka ratio o 1 thread: 1 reactor, a laila e hana hou i nā cores CPU āpau.
Ka hoʻokō
E kau mākou i ke kikowaena lehulehu i kahi faila reactor.h, a me ka hoʻokō - in reactor.c. reactor.h e komo i keia mau hoolaha:
Hōʻike i nā ʻōlelo hoʻolaha ma ka 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);
ʻO ka ʻōnaehana reactor I/O ka waihona wehewehe mea koho epoll и nā papa hashGHashTable, ka palapala ʻāina i kēlā me kēia kumu i CallbackData (ke ʻano o kahi mea hoʻoponopono hanana a me kahi hoʻopaʻapaʻa mea hoʻohana no ia).
E ʻoluʻolu e hoʻomaopopo ua hiki iā mākou ke hoʻohana ʻano piha ʻole e like me ka papa kuhikuhi. IN reactor.h ke hai aku nei makou i ka hale reactor, a in reactor.c wehewehe mākou iā ia, no laila e pale aku i ka mea hoʻohana mai ka hoʻololi pono ʻana i kāna mau kahua. ʻO kēia kekahi o nā hiʻohiʻona huna ʻikepili, i kūpono i ka C semantics.
Nā hana reactor_register, reactor_deregister и reactor_reregister hōʻano hou i ka papa inoa o nā kumu hoihoi a me nā mea lawelawe hanana like i ka mea koho ʻōnaehana a me ka papa hash.
Ma hope o ka ʻae ʻana o ka reactor I/O i ka hanana me ka mea wehewehe fd, kahea ia i ka mea lawelawe hanana pili, kahi e hele ai fd, wahi huna huna hana ʻia nā hanana a me kahi kuhikuhi mea hoʻohana i void.
Hōʻike i ka hana 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;
}
No ka hōʻuluʻulu ʻana, e lawe ke kaulahao o nā kelepona hana i ka code mea hoʻohana i kēia ʻano:
Hoʻokahi kikowaena kaula
No ka hoʻāʻo ʻana i ka reactor I/O ma lalo o ka haʻahaʻa kiʻekiʻe, e kākau mākou i kahi kikowaena pūnaewele HTTP maʻalahi e pane i kekahi noi me kahi kiʻi.
ʻO kahi kuhikuhi wikiwiki i ka protocol HTTP
HTTP - ʻo kēia ka protocol pae noi, hoʻohana nui ʻia no ka launa pū ʻana o ka server-browser.
Hiki ke hoʻohana maʻalahi iā HTTP halihali Hola Pūnaewele TCP, ka hoʻouna ʻana a me ka loaʻa ʻana o nā memo ma kahi ʻano i kuhikuhi ʻia kikoʻī.
CRLF he kaʻina o nā huaʻōlelo ʻelua: r и n, hoʻokaʻawale i ka laina mua o ka noi, nā poʻo a me nā ʻikepili.
<КОМАНДА> - kekahi o CONNECT, DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT, TRACE. E hoʻouna ka polokalamu kele i kahi kauoha i kā mākou kikowaena GET, ʻo ia hoʻi "E hoʻouna mai iaʻu i nā mea o ka faila."
<URI> - mea hōʻike waiwai like ʻole. No ka laʻana, inā URI = /index.html, a laila noi ka mea kūʻai aku i ka ʻaoʻao nui o ka pūnaewele.
<ВЕРСИЯ HTTP> - ka mana o ka protocol HTTP ma ke ʻano HTTP/X.Y. ʻO ka mana maʻamau i hoʻohana ʻia i kēia lā HTTP/1.1.
<ЗАГОЛОВОК N> he hui waiwai kī ma ke ʻano <КЛЮЧ>: <ЗНАЧЕНИЕ>, hoʻouna ʻia i ke kikowaena no ka nānā hou ʻana.
<ДАННЫЕ> - ka ʻikepili i koi ʻia e ke kikowaena e hana i ka hana. He maʻalahi pinepine JSON a i ʻole kekahi ʻano ʻē aʻe.
<КОД СТАТУСА> he helu e hōʻike ana i ka hopena o ka hana. E hoʻihoʻi mau kā mākou kikowaena i ke kūlana 200 (hana holomua).
<ОПИСАНИЕ СТАТУСА> — hōʻike string o ke code kūlana. No ke code kūlana 200 kēia OK.
<ЗАГОЛОВОК N> - ke poʻomanaʻo o ke ʻano like me ka noi. E hoʻihoʻi mākou i nā poʻo inoa Content-Length (ka nui waihona) a Content-Type: text/html (hoʻihoʻi ʻano ʻikepili).
<ДАННЫЕ> - ʻikepili i noi ʻia e ka mea hoʻohana. I kā mākou hihia, ʻo ia ke ala i ke kiʻi i loko HTML.
waihona http_server.c (hoʻokahi kikowaena pūnaewele) me ka faila common.h, i loaʻa nā prototypes hana penei:
Hōʻike i nā prototypes hana maʻamau.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);
Ua wehewehe ʻia ka macro hana SAFE_CALL() a ua wehewehe ʻia ka hana fail(). Hoʻohālikelike ka macro i ka waiwai o ka ʻōlelo me ka hewa, a inā he ʻoiaʻiʻo ke kūlana, kāhea i ka hana fail():
#define SAFE_CALL(call, error)
do {
if ((call) == error) {
fail("%s", #call);
}
} while (false)
kuleana pili i fail() paʻi i nā hoʻopaʻapaʻa i hala i ka pahu (e like me printf()) a hoʻopau i ka papahana me ke code EXIT_FAILURE:
kuleana pili i new_server() hoʻihoʻi i ka wehewehe faila o ke kumu "server" i hana ʻia e nā kelepona ʻōnaehana socket(), bind() и listen() a hiki ke ʻae i nā pilina e komo mai ana ma ke ʻano hoʻopaʻa ʻole.
E hoʻomaopopo i ka hana mua ʻana o ke kumu ma ke ʻano non-blocking me ka hoʻohana ʻana i ka hae SOCK_NONBLOCKpela i ka hana on_accept() (heluhelu hou aku) kelepona ʻōnaehana accept() ʻaʻole i hoʻōki i ka hoʻokō ʻana i ke kaula.
ina reuse_port e like me true, a laila e hoʻonohonoho kēia hana i ke kumu me ke koho SO_REUSEPORT ma o setsockopt()e hoʻohana i ka awa like i loko o kahi kaiapuni multi-threaded (e nānā i ka ʻāpana "Multi-threaded server").
Luna Hana Hana on_accept() kāhea ʻia ma hope o ka hana ʻana o ka OS i kahi hanana EPOLLIN, i kēia hihia ke manaʻo nei e hiki ke ʻae ʻia ka pilina hou. on_accept() ʻae i kahi pilina hou, hoʻololi iā ia i ke ʻano hoʻopaʻa ʻole a hoʻopaʻa inoa me kahi mea mālama hanana on_recv() i loko o kahi reactor I/O.
Luna Hana Hana on_recv() kāhea ʻia ma hope o ka hana ʻana o ka OS i kahi hanana EPOLLIN, i kēia hihia ke manaʻo nei ua hoʻopaʻa inoa ʻia ka pilina on_accept(), mākaukau e loaʻa ka ʻikepili.
on_recv() Heluhelu i ka ʻikepili mai ka pilina a hiki i ka loaʻa ʻana o ka noi HTTP, a laila hoʻopaʻa inoa i kahi mea lawelawe on_send() e hoʻouna i kahi pane HTTP. Inā haki ka mea kūʻai aku i ka pilina, kāpae ʻia ke kumu a pani ʻia me ka hoʻohana ʻana close().
Hōʻike i ka hana ma_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);
}
}
Luna Hana Hana on_send() kāhea ʻia ma hope o ka hana ʻana o ka OS i kahi hanana EPOLLOUT, 'o ia ho'i ua ho'opa'a 'ia ka pilina on_recv(), mākaukau e hoʻouna i ka ʻikepili. Hoʻouna kēia hana i kahi pane HTTP i loaʻa HTML me kahi kiʻi i ka mea kūʻai aku a laila hoʻololi i ka mea hoʻoponopono hanana on_recv().
A ʻo ka hope, i ka faila http_server.c, ma ka hana main() hana mākou i kahi reactor I/O me ka hoʻohana ʻana reactor_new(), hana i kahi kikowaena kikowaena a hoʻopaʻa inoa iā ia, e hoʻomaka i ka reactor me ka hoʻohana ʻana reactor_run() no hoʻokahi minuke, a laila hoʻokuʻu mākou i nā kumuwaiwai a haʻalele i ka papahana.
E nānā kāua e hana ana nā mea a pau e like me ka mea i manaʻo ʻia. ka houluulu ana (chmod a+x compile.sh && ./compile.sh i ke kumu o ka papahana) a hoʻomaka i ke kikowaena kākau ponoʻī, wehe http://127.0.0.1:18470 i ka polokalamu kele pūnaewele a ʻike i ka mea a mākou i manaʻo ai:
E ana kāua i ka hana o kahi kikowaena kaula hoʻokahi. E wehe mākou i ʻelua mau pahu: i hoʻokahi e holo mākou ./http_server, ma kahi ʻokoʻa - ʻōpala. Ma hope o hoʻokahi minuke, e hōʻike ʻia nā helu helu ma ka pahu lua:
Ua hiki i kā mākou kikowaena pūnaewele hoʻokahi ke hana ma luna o 11 miliona mau noi i kēlā me kēia minuke mai 100 mau pilina. ʻAʻole he hopena maikaʻi ʻole, akā hiki ke hoʻomaikaʻi?
Mea hoʻohana multithreaded
E like me ka mea i ʻōlelo ʻia ma luna, hiki ke hana ʻia ka reactor I/O i nā ʻāpana ʻokoʻa, a laila e hoʻohana ana i nā cores CPU āpau. E hoʻokō kākou i kēia ʻano hana:
E ʻoluʻolu e hoʻomaopopo i ka hoʻopaʻapaʻa hana new_server() nā mea noi true. ʻO ia ke ʻano o ka hāʻawi ʻana i ke koho i ke kumu kikowaena SO_REUSEPORTe hoʻohana iā ia i loko o kahi kaiapuni multi-threaded. Hiki iā ʻoe ke heluhelu i nā kikoʻī hou aku maanei.
Holo lua
I kēia manawa e ana kāua i ka hana o kahi kikowaena multi-threaded:
Ua hoʻonui ʻia ka nui o nā noi i hoʻopaʻa ʻia i ka minuke 1 e ~3.28 manawa! Akā ʻo mākou wale nō ~ XNUMX miliona pōkole o ka helu pōʻai, no laila e hoʻāʻo mākou e hoʻoponopono i kēlā.
Ke hoʻohana nei i ka CPU Affinity, hui pu me -march=native, PGO, he hoʻonui i ka helu o nā hits huna huna, hoonui MAX_EVENTS a hoʻohana EPOLLET ʻaʻole i hāʻawi i ka piʻi nui o ka hana. Akā he aha ka hopena inā hoʻonui ʻoe i ka helu o nā pilina like?
Loaʻa ka hopena i makemake ʻia, a me ia kahi pakuhi hoihoi e hōʻike ana i ka hilinaʻi o ka helu o nā noi i hoʻoponopono ʻia i 1 mau minuke ma ka helu o nā pilina:
ʻIke mākou ma hope o ʻelua mau haneli pili, ua hāʻule nui ka helu o nā noi i hoʻoponopono ʻia no nā kikowaena ʻelua (ma ka mana multi-threaded ʻoi aku ka ʻike). Pili kēia i ka hoʻokō ʻana i ka hoʻokō Linux TCP/IP? E ʻoluʻolu e kākau i kāu mau manaʻo e pili ana i kēia ʻano o ka pakuhi a me nā manaʻo no nā koho multi-threaded a me hoʻokahi-threaded i nā manaʻo.
Pehea ʻike ʻia i nā manaʻo, ʻaʻole hōʻike kēia hōʻike hoʻokō i ka ʻano o ka reactor I/O ma lalo o nā haʻahaʻa maoli, no ka mea, ʻaneʻane e launa pū ke kikowaena me ka waihona, hoʻopuka i nā logs, hoʻohana i ka cryptography me TLS etc., ma muli o ka lilo ʻana o ka ukana i mea like ʻole (dynamic). E hana ʻia nā hoʻāʻo me nā ʻāpana ʻaoʻao ʻekolu ma ka ʻatikala e pili ana i ka proactor I/O.
Nā hemahema o ka reactor I/O
Pono ʻoe e hoʻomaopopo ʻaʻole ʻaʻohe hemahema o ka reactor I/O, ʻo ia hoʻi:
ʻOi aku ka paʻakikī o ka hoʻohana ʻana i kahi reactor I/O i loko o kahi kaiapuni multi-threaded, no ka mea pono ʻoe e hoʻokele lima i nā kahe.
Hōʻike ka hoʻomaʻamaʻa ʻana i ka hapa nui o ka ukana ʻaʻole like ʻole, hiki ke alakaʻi i kahi logging thread a paʻa kekahi i ka hana.
Inā hoʻopaʻa kekahi mea hoʻokele hanana i kahi kaula, a laila e poloka pū ka mea koho ʻōnaehana ponoʻī, hiki ke alakaʻi i nā pōpoki paʻakikī.
Hoʻoholo i kēia mau pilikia I/O proactor, i loaʻa pinepine i kahi mea hoʻonohonoho e puʻunaue like i ka ukana i kahi wai o nā kaula, a loaʻa pū kekahi API maʻalahi. E kamaʻilio mākou e pili ana iā ia ma hope, ma kaʻu ʻatikala ʻē aʻe.
hopena
ʻO kēia kahi i pau ai kā mākou huakaʻi mai ke kumumanaʻo i ka exhaust profiler.
ʻAʻole pono ʻoe e noʻonoʻo i kēia, no ka mea he nui nā ala ʻē aʻe e like me ka hoihoi i ke kākau ʻana i nā polokalamu pūnaewele me nā pae like ʻole o ka maʻalahi a me ka wikiwiki. ʻO ka hoihoi, i koʻu manaʻo, hāʻawi ʻia nā loulou ma lalo nei.