Hauv tsab xov xwm no, peb yuav saib cov ins thiab outs ntawm I / O reactor thiab nws ua haujlwm li cas, sau ib qho kev siv tsawg dua 200 kab ntawm cov cai, thiab ua kom yooj yim HTTP server txheej txheem tshaj 40 lab thov / min.
Lus Qhia Tshab
Tsab ntawv tau sau los pab kom nkag siab txog kev ua haujlwm ntawm I / O reactor, thiab yog li nkag siab txog qhov txaus ntshai thaum siv nws.
Tag nrho cov cai sau ua lus C nruj me ntsis raws li (ceev faj: ntev PDF) rau C11 standard rau Linux thiab muaj nyob rau ntawm GitHub.
Vim li cas thiaj li tsim nyog?
Nrog rau kev loj hlob ntawm Internet, web servers pib xav tau los tswj ntau qhov kev sib txuas ib txhij, thiab yog li ob txoj hauv kev tau sim: thaiv I / O ntawm ntau OS threads thiab tsis thaiv I / O ua ke nrog. ib qho kev ceeb toom kev tshwm sim, kuj hu ua "system selector" (epol/kque ua/IOCP/etc).
Thawj txoj hauv kev koom nrog tsim cov xov tshiab OS rau txhua qhov kev sib txuas tuaj. Nws qhov tsis zoo yog qhov tsis zoo scalability: lub operating system yuav tsum tau siv ntau cov ntsiab lus hloov и hu xovtooj. Lawv yog cov haujlwm kim heev thiab tuaj yeem ua rau tsis muaj RAM dawb nrog cov kev sib txuas zoo heev.
Cov hloov kho version tseem ceeb taag naj npawb ntawm threads (xov pas dej), yog li tiv thaiv lub kaw lus los ntawm kev rho tawm kev tua, tab sis tib lub sij hawm qhia txog qhov teeb meem tshiab: yog tias lub pas dej yog tam sim no thaiv los ntawm kev nyeem ntawv ntev, ces lwm lub qhov (socket) uas twb tau txais cov ntaub ntawv yuav tsis muaj peev xwm. ua li ntawd.
Qhov thib ob txoj kev siv kev ceeb toom qhov system (system selector) muab los ntawm OS. Kab lus no tham txog hom kev xaiv ntau tshaj plaws, raws li kev ceeb toom (xws li, ceeb toom) txog kev npaj rau kev ua haujlwm I / O, es tsis yog ntawm cov ntawv ceeb toom txog lawv ua tiav. Ib qho piv txwv yooj yim ntawm nws siv tuaj yeem sawv cev los ntawm cov duab thaiv hauv qab no:
Qhov sib txawv ntawm cov txheej txheem no yog raws li nram no:
Thaiv kev ua haujlwm I/O ncua neeg siv flow mus txog thaummus txog rau thaum OS zoo defragments tuaj IP pob ntawv mus byte kwj (TCP, tau txais cov ntaub ntawv) los yog yuav tsis muaj chaw txaus nyob rau hauv lub internal sau buffers rau xa tom qab ntawm NIC (xa cov ntaub ntawv).
Qhov system selector dhau sijhawm ceeb toom rau qhov kev pab cuam uas OS twb defragmented IP pob ntawv (TCP, txais cov ntaub ntawv) lossis qhov chaw txaus hauv kev sau ntawv buffers twb muaj (xa cov ntaub ntawv).
Txhawm rau suav nws, khaws cov xov OS rau txhua I / O yog qhov pov tseg ntawm kev suav lub zog, vim tias qhov tseeb, cov xov tsis ua haujlwm tseem ceeb (qhov no yog lo lus los ntawm "software cuam tshuam"). Lub system selector daws qhov teeb meem no, tso cai rau cov neeg siv kev pab cuam siv CPU cov peev txheej ntau dua kev lag luam.
I/O reactor qauv
Lub I/O reactor ua raws li ib txheej ntawm qhov system selector thiab tus neeg siv code. Lub hauv paus ntsiab lus ntawm nws txoj haujlwm yog piav qhia los ntawm daim duab thaiv hauv qab no:
Cia kuv ceeb toom rau koj tias ib qho kev tshwm sim yog ib qho kev ceeb toom tias ib lub qhov (socket) muaj peev xwm ua tau qhov kev ua haujlwm tsis thaiv I / O.
Tus neeg saib xyuas kev tshwm sim yog ib qho haujlwm hu ua I / O reactor thaum tau txais qhov xwm txheej, uas tom qab ntawd ua haujlwm tsis thaiv I / O.
Nws yog ib qho tseem ceeb uas yuav tsum nco ntsoov tias I / O reactor yog los ntawm kev txhais ib leeg xov, tab sis tsis muaj ib yam dab tsi txwv tsis pub lub tswv yim los ntawm kev siv nyob rau hauv ib qho chaw sib txuas ntawm qhov sib piv ntawm 1 xov: 1 reactor, yog li rov ua dua tag nrho CPU cores.
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 reactor qauv muaj cov ntaub ntawv piav qhia xaiv epol и rooj roojGHashTable, uas maps txhua lub qhov (socket) rau CallbackData (Cov qauv ntawm tus tuav kev tshwm sim thiab tus neeg siv kev sib cav rau nws).
/*
* Обработчик событий, который вызовется после того, как сокет будет
* готов принять новое соединение.
*/
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);
Cov kev ua haujlwm macro kuj tau piav qhia SAFE_CALL() thiab muaj nuj nqi yog txhais fail(). Lub macro piv tus nqi ntawm qhov kev qhia nrog qhov yuam kev, thiab yog tias qhov xwm txheej muaj tseeb, hu rau qhov ua haujlwm fail():
#define SAFE_CALL(call, error)
do {
if ((call) == error) {
fail("%s", #call);
}
} while (false)
muaj nuj nqi fail() luam cov lus sib cav dhau mus rau lub davhlau ya nyob twg (xws li printf()) thiab terminates qhov kev pab cuam nrog cov code EXIT_FAILURE:
Nco ntsoov tias lub qhov (socket) tau pib tsim nyob rau hauv hom tsis thaiv kev siv tus chij SOCK_NONBLOCKyog li ntawd hauv kev ua haujlwm on_accept() (nyeem ntxiv) system hu accept() tsis tau tso tseg txoj xov execution.
Txheej xwm Handler on_recv() hu ua tom qab OS tsim ib qho kev tshwm sim EPOLLIN, nyob rau hauv cov ntaub ntawv no txhais tau hais tias cov kev twb kev txuas sau npe on_accept(), npaj tau txais cov ntaub ntawv.
on_recv() nyeem cov ntaub ntawv los ntawm kev sib txuas kom txog rau thaum qhov kev thov HTTP tau txais tag nrho, ces nws sau npe rau tus tuav on_send() xa HTTP teb. Yog tias tus neeg siv khoom tawg qhov kev sib txuas, lub qhov (socket) raug deregistered thiab kaw siv close().
Qhia ua haujlwm 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);
}
}
Txheej xwm Handler on_send() hu ua tom qab OS tsim ib qho kev tshwm sim EPOLLOUT, txhais tau hais tias kev sib txuas sau npe on_recv(), npaj xa cov ntaub ntawv. Qhov kev ua haujlwm no xa HTTP cov lus teb uas muaj HTML nrog cov duab rau tus neeg siv khoom thiab tom qab ntawd hloov cov xwm txheej tuav rov qab mus rau on_recv().
Cia peb ntsuas qhov ua tau zoo ntawm ib lub xov tooj server. Cia peb qhib ob lub terminals: hauv ib qho peb yuav khiav ./http_server, nyob rau hauv sib txawv - wb wrk. Tom qab ib feeb, cov kev txheeb cais hauv qab no yuav tshwm sim hauv lub davhlau ya nyob twg thib ob:
Raws li tau hais los saum no, I / O reactor tuaj yeem tsim hauv cov xov sib cais, yog li siv tag nrho CPU cores. Cia peb muab txoj hauv kev no rau kev xyaum:
Tus naj npawb ntawm kev thov ua tiav hauv 1 feeb tau nce los ntawm ~ 3.28 npaug! Tab sis peb tsuas yog ~ XNUMX lab luv ntawm tus lej puag ncig, yog li cia peb sim kho qhov ntawd.
Ua ntej cia peb saib cov txheeb cais tsim tawm zoo tag nrho:
Siv CPU Affinity, compilation nrog -march=native, PGO, nce tus naj npawb ntawm hits cache, nce MAX_EVENTS thiab siv EPOLLET tsis tau ua kom muaj txiaj ntsig zoo hauv kev ua haujlwm. Tab sis yuav ua li cas yog tias koj nce tus naj npawb ntawm kev sib txuas ib txhij?
Cov txiaj ntsig xav tau tau txais, thiab nrog nws cov duab nthuav qhia qhia qhov kev cia siab ntawm tus lej ntawm kev thov ua tiav hauv 1 feeb ntawm tus lej ntawm kev sib txuas:
Peb pom tias tom qab ob peb puas kev sib txuas, tus naj npawb ntawm cov kev thov ua tiav rau ob lub servers poob qis (hauv ntau txoj xov xov no yog qhov pom ntau dua). Qhov no puas cuam tshuam rau Linux TCP / IP pawg siv? Xav tias dawb sau koj cov kev xav txog tus cwj pwm no ntawm daim duab thiab kev ua kom zoo rau ntau txoj xov thiab ib leeg-xov xaiv hauv cov lus.
Yuav ua li cas sau tseg nyob rau hauv cov lus, qhov kev ntsuam xyuas kev ua tau zoo no tsis qhia tus cwj pwm ntawm I / O reactor nyob rau hauv cov loads tiag tiag, vim hais tias yuav luag txhua tus neeg rau zaub mov cuam tshuam nrog cov ntaub ntawv, tso tawm cov cav, siv cryptography nrog TLS thiab lwm yam, raws li qhov tshwm sim ntawm qhov load ua tsis zoo (dynamic). Kev sim ua ke nrog cov khoom thib peb yuav raug ua nyob rau hauv tsab xov xwm hais txog I / O proactor.
Disadvantages ntawm I / O reactor
Koj yuav tsum nkag siab tias I / O reactor tsis yog tsis muaj nws qhov tsis zoo, xws li:
Kev siv I / O reactor nyob rau hauv ib puag ncig ntau txoj xov yog qhov nyuaj dua, vim tias koj yuav tau manually tswj cov ntws.
Kev xyaum qhia tau hais tias nyob rau hauv feem ntau cov load yog tsis-uniform, uas yuav ua tau rau ib tug xov txiav thaum lwm tus neeg ua hauj lwm tsis khoom.
Yog hais tias ib qho kev tshwm sim handler blocks ib txoj xov, ces qhov system selector nws tus kheej kuj yuav thaiv, uas tuaj yeem ua rau nyuaj-rau-nrhiav kab.
daws cov teeb meem no I/O proactor, uas feem ntau muaj lub sijhawm teem sijhawm uas sib npaug faib cov khoom thauj mus rau lub pas dej ntawm cov xov, thiab tseem muaj API yooj yim dua. Peb yuav tham txog nws tom qab, hauv kuv tsab xov xwm.
xaus
Qhov no yog qhov uas peb txoj kev los ntawm txoj kev xav ncaj nraim mus rau hauv lub qhov hluav taws xob profiler tau los txog qhov kawg.
Koj yuav tsum tsis txhob nyob ntawm qhov no, vim tias muaj ntau lwm txoj hauv kev zoo sib xws los sau cov software network nrog ntau qib ntawm kev yooj yim thiab nrawm. Nthuav, hauv kuv lub tswv yim, cov ntawv txuas tau muab hauv qab no.