N'isiokwu a, anyị ga-eleba anya na ins na outs nke I / O reactor na otú o si arụ ọrụ, dee mmejuputa na-erughị 200 ahịrị nke koodu, na-eme ka ihe nkesa HTTP dị mfe karịa 40 nde arịrịọ / min.
Okwu mmalite
Edere akụkọ ahụ iji nyere aka ịghọta ọrụ nke reactor I/O, yabụ ghọta ihe egwu dị mgbe ị na-eji ya.
A chọrọ ihe ọmụma nke ihe ndị bụ isi iji ghọta isiokwu ahụ. C asụsụ na ụfọdụ ahụmahụ na mmepe ngwa netwọk.
Edere koodu niile n'asụsụ C nke ọma dịka (ịkpachara anya: ogologo PDF) ruo C11 ọkọlọtọ maka Linux ma dị na GitHub.
Gịnị mere nke a ji dị mkpa?
Site na mgbasawanye nke ịntanetị na-eto eto, sava weebụ malitere mkpa ijikwa ọnụ ọgụgụ dị ukwuu nke njikọ n'otu oge, ya mere a nwalere ụzọ abụọ: igbochi I / O na ọnụ ọgụgụ dị ukwuu nke eriri OS na ndị na-adịghị egbochi I / O jikọtara ya na ya. usoro ngosi mmemme, nke a na-akpọkwa “system selector” (epoll/kwue/IOCP/ wdg).
Ụzọ mbụ gụnyere ịmepụta eriri OS ọhụrụ maka njikọ ọ bụla na-abata. Ọdịmma ya bụ scalability na-adịghị mma: sistemụ arụmọrụ ga-emerịrị ọtụtụ ntụgharị ọnọdụ и oku usoro. Ha na-arụ ọrụ dị oke ọnụ ma nwee ike ibute enweghị RAM n'efu yana ọnụọgụ njikọ dị egwu.
Ụdị gbanwegharịrị na-egosipụta ọnụ ọgụgụ a kapịrị ọnụ nke eri (ọdọ mmiri nke eriri), si otú ahụ na-egbochi usoro ahụ site na nkwụsị nke igbu egbu, ma n'otu oge ahụ na-ebute nsogbu ọhụrụ: ọ bụrụ na a na-egbochi ọdọ mmiri eri ugbu a site na ogologo ọrụ na-agụ ogologo oge, mgbe ahụ, oghere ndị ọzọ nwere ike ịnweta data agaghị enwe ike. mee ya.
Usoro nke abụọ na-eji usoro ngosi omume (onye na-ahọpụta sistemụ) nke OS nyere. Isiokwu a na-atụle ụdị onye na-ahọrọ usoro a na-ahụkarị, dabere na ọkwa (ihe omume, ọkwa) gbasara njikere maka ọrụ I / O, kama na ozi gbasara mmecha ha. Enwere ike ịnọchite anya ihe atụ dị mfe nke iji ya site na eserese ngọngọ ndị a:
Ihe dị iche n'etiti ụzọ ndị a bụ ndị a:
Na-egbochi ọrụ I/O kwụsịtụ onye ọrụ eruba ruo mgberuo mgbe OS dị mma defragments na-abata IP ngwugwu na iyi byte (TCP, ịnata data) ma ọ bụ agaghị enwe ohere zuru oke na nchekwa ederede nke ime maka izipu na-esote site na Ọ DỊGHỊ (na-eziga data).
Nhọrọ sistemu oge n'aga na-agwa mmemme na OS ama Ihe ngwugwu IP defragmented (TCP, nnabata data) ma ọ bụ ohere zuru oke na nchekwa ederede ime ama dị (na-eziga data).
N'ịchịkọta ya, idobe eriri OS maka I / O ọ bụla bụ ihe efu nke ike kọmputa, n'ihi na n'eziokwu, eriri anaghị arụ ọrụ bara uru (ya mere okwu ahụ. "software nkwụsịtụ"). Onye na-ahọrọ sistemụ na-edozi nsogbu a, na-ekwe ka mmemme onye ọrụ jiri akụrụngwa CPU mee ihe n'ụzọ akụ na ụba.
I/O reactor nlereanya
The I/O reactor na-arụ ọrụ dị ka oyi akwa n'etiti onye na-ahọrọ sistemụ na koodu njirimara. A kọwara ụkpụrụ nke ọrụ ya site na eserese ngọngọ ndị a:
Ka m chetara gị na mmemme bụ ngosi na ụfọdụ oghere nwere ike ịrụ ọrụ I/O anaghị egbochi.
Onye na-ahụ maka mmemme bụ ọrụ nke I/O reactor na-akpọ mgbe anatara mmemme, nke na-arụ ọrụ I/O anaghị egbochi.
Ọ dị mkpa iburu n'obi na reactor I / O bụ site na nkọwa otu-threaded, mana ọ nweghị ihe na-egbochi echiche ahụ iji mee ihe na mpaghara multi-threaded na nha nke 1 eri: 1 reactor, si otú ahụ na-emegharị ihe niile CPU cores.
Mmejuputa iwu
Anyị ga-etinye interface ọha na faịlụ reactor.h, na mmejuputa iwu-na reactor.c. reactor.h ga-enwe ọkwa ndị a:
Gosi nkwupụta na 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);
The I/O reactor Ọdịdị mejupụtara onye na-akọwa faịlụ onye nhọpụta epoll и tebụl hashGHashTable, nke maapụ oghere ọ bụla na CallbackData (nhazi nke onye njikwa ihe omume na arụmụka onye ọrụ maka ya).
Biko mara na anyị enyela ikike ijikwa ya ụdị ezughị ezu dị ka index. N'ime reactor.h anyị na-ekwupụta nhazi ahụ reactor, na n’ime reactor.c anyị kọwapụtara ya, si otú a na-egbochi onye ọrụ ịgbanwe mpaghara ya n'ụzọ doro anya. Nke a bụ otu n'ime ụkpụrụ na-ezo data, nke dabara nkenke na semantics C.
Ọrụ reactor_register, reactor_deregister и reactor_reregister melite ndepụta oghere nke mmasị na ndị na-ahụ maka ihe omume kwekọrọ na onye na-ahọrọ sistemụ na tebụl hash.
Mgbe reactor I/O ejirila onye nkọwa mechie ihe omume ahụ fd, ọ na-akpọ onye njikwa ihe omume kwekọrọ, nke ọ na-agafe fd, bit nkpuchi emepụtara emume na ntụnye onye ọrụ ka void.
Gosi ọrụ 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;
}
CRLF bụ usoro mkpụrụedemede abụọ: r и n, na-ekewa akara mbụ nke arịrịọ, nkụnye eji isi mee na data.
<КОМАНДА> - otu n'ime CONNECT, DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT, TRACE. Ihe nchọgharị ahụ ga-ezigara sava anyị iwu GET, nke pụtara "Zitere m ọdịnaya nke faịlụ ahụ."
<URI> - edo akụrụngwa nchọpụta. Dịka ọmụmaatụ, ọ bụrụ URI = /index.html, mgbe ahụ onye ahịa rịọrọ isi peeji nke saịtị ahụ.
<ВЕРСИЯ HTTP> - ụdị nke protocol HTTP n'ụdị HTTP/X.Y. Ụdị nke a na-ejikarị eme ihe taa bụ HTTP/1.1.
<ЗАГОЛОВОК N> bụ ụzọ igodo-uru na usoro <КЛЮЧ>: <ЗНАЧЕНИЕ>, ezigara na ihe nkesa maka nyocha ọzọ.
<ДАННЫЕ> - data nke ihe nkesa chọrọ iji rụọ ọrụ ahụ. Ọtụtụ mgbe, ọ dị mfe JSON ma ọ bụ usoro ọ bụla ọzọ.
<КОД СТАТУСА> bụ nọmba na-anọchite anya nsonaazụ nke ọrụ ahụ. Ihe nkesa anyị ga-eweghachi ọkwa 200 mgbe niile (ọrụ na-aga nke ọma).
<ОПИСАНИЕ СТАТУСА> - eriri eriri nke koodu ọnọdụ. Maka koodu ọnọdụ 200 nke a bụ OK.
<ЗАГОЛОВОК N> - nkụnye eji isi mee nke otu usoro dị na arịrịọ ahụ. Anyị ga-eweghachite aha ndị ahụ Content-Length (nha faịlụ) na Content-Type: text/html (ụdị data nloghachi).
<ДАННЫЕ> - data onye ọrụ rịọrọ. N'ọnọdụ anyị, nke a bụ ụzọ nke ihe oyiyi na HTML.
file http_server.c (otu eriri ihe nkesa) gụnyere faịlụ common.h, nke nwere ụdị ọrụ ndị a:
Gosi ụdị ọrụ na-ahụkarị.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);
A kọwakwara nnukwu macro na-arụ ọrụ SAFE_CALL() a kọwakwara ọrụ ahụ fail(). Macro na-atụnyere uru nke okwu ahụ na njehie ahụ, ma ọ bụrụ na ọnọdụ ahụ bụ eziokwu, kpọọ ọrụ ahụ fail():
#define SAFE_CALL(call, error)
do {
if ((call) == error) {
fail("%s", #call);
}
} while (false)
ọrụ fail() na-ebipụta arụmụka agafere na njedebe (dịka printf()) ma kwụsị mmemme na koodu EXIT_FAILURE:
ọrụ new_server() weghachite nkọwa faịlụ nke oghere "ihe nkesa" nke oku sistemụ mepụtara socket(), bind() и listen() ma nwee ike ịnakwere njikọ mbata na ọnọdụ anaghị egbochi.
Rịba ama na a na-emepụta oghere ahụ na ọnọdụ anaghị egbochi ya site na iji ọkọlọtọ SOCK_NONBLOCKnke mere na na ọrụ on_accept() (gụkwuo) oku usoro accept() akwụsịghị ogbugbu eri.
ma ọ bụrụ na reuse_port ha nhata true, mgbe ahụ, ọrụ a ga-ahazi oghere na nhọrọ SO_REUSEPORT site na setsockopt()iji otu ọdụ ụgbọ mmiri ahụ na gburugburu ebe nwere ọtụtụ eriri (lee ngalaba "Ihe nkesa nwere ọtụtụ ihe").
Onye njikwa mmemme on_accept() a na-akpọ mgbe OS mepụtara ihe omume EPOLLIN, na nke a pụtara na njikọ ọhụrụ nwere ike ịnakwere. on_accept() na-anabata njikọ ọhụrụ, gbanwee ya na ọnọdụ anaghị egbochi ya wee debanye aha ya na onye njikwa mmemme on_recv() n'ime reactor I/O.
Onye njikwa mmemme on_recv() a na-akpọ mgbe OS mepụtara ihe omume EPOLLIN, na nke a pụtara na njikọ aha on_accept(), dị njikere ịnata data.
on_recv() na-agụ data sitere na njikọ ahụ ruo mgbe enwetara arịrịọ HTTP kpamkpam, wee debanye aha onye njikwa on_send() izipu nzaghachi HTTP. Ọ bụrụ na onye ahịa ahụ agbaji njikọ ahụ, ewepụrụ oghere ahụ wee mechie ya site na iji close().
Gosi ọrụ na_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);
}
}
Onye njikwa mmemme on_send() a na-akpọ mgbe OS mepụtara ihe omume EPOLLOUT, nke pụtara na njikọ ahụ edebanyere aha on_recv(), dị njikere izipu data. Ọrụ a na-eziga nzaghachi HTTP nwere HTML nwere onyonyo nye onye ahịa wee gbanwee onye njikwa ihe omume azụ on_recv().
Na n'ikpeazụ, na faịlụ http_server.c, na ọrụ main() anyị na-eji ihe na-emepụta I/O reactor reactor_new(), mepụta oghere nkesa wee debanye aha ya, malite reactor site na iji reactor_run() maka otu nkeji, wee hapụ akụrụngwa wee pụọ na mmemme ahụ.
Ka anyị lelee na ihe niile na-arụ ọrụ dịka a tụrụ anya ya. Na-achịkọta (chmod a+x compile.sh && ./compile.sh na mgbọrọgwụ oru ngo) wee malite ihe nkesa nke edere onwe ya, mepee http://127.0.0.1:18470 na ihe nchọgharị ma hụ ihe anyị tụrụ anya:
Ka anyị tụọ arụmọrụ nke ihe nkesa nwere otu eriri. Ka anyị mepee ọnụ ụzọ abụọ: na otu anyị ga-agba ọsọ ./http_server, n'ụzọ dị iche - ọrụ. Mgbe otu nkeji gachara, a ga-egosipụta ọnụ ọgụgụ ndị a na njedebe nke abụọ:
Ihe nkesa anyị nwere otu eriri nwere ike hazie arịrịọ karịrị nde 11 kwa nkeji sitere na njikọ 100. Ọ bụghị nsonaazụ ọjọọ, mana enwere ike imeziwanye ya?
Multithreaded nkesa
Dịka e kwuru n'elu, enwere ike ịmepụta reactor I/O na eriri dị iche iche, si otú a na-eji cores CPU niile. Ka anyị tinye usoro a n'ọrụ:
Biko mara na arụmụka ọrụ new_server() akwado true. Nke a pụtara na anyị na-ekenye nhọrọ na oghere nkesa SO_REUSEPORTiji ya mee ihe na gburugburu ọtụtụ eriri. Ị nwere ike ịgụ nkọwa ndị ọzọ ebe a.
Agba nke abụọ
Ugbu a, ka anyị tụọ arụmọrụ nke sava nwere ọtụtụ eriri:
Ọnụọgụ nke arịrịọ ahaziziri na nkeji 1 mụbara site ~ 3.28 ugboro! Mana anyị bụ naanị ~ XNUMX nde mkpụmkpụ nke ọnụọgụ gburugburu, yabụ ka anyị gbalịa idozi nke ahụ.
Ka anyị buru ụzọ leba anya na ọnụ ọgụgụ ewepụtara zuru oke:
Iji CPU Affinity, nchịkọta na -march=native, PGO, mmụba nke ọnụ ọgụgụ hits cache, mụbaa MAX_EVENTS na iji EPOLLET enyeghị mmụba dị ịrịba ama na arụmọrụ. Ma gịnị na-eme ma ọ bụrụ na ị na-abawanye ọnụ ọgụgụ nke njikọ oge?
Enwetara nsonaazụ achọrọ, yana ya eserese na-adọrọ mmasị na-egosi ndabere nke ọnụọgụ arịrịọ ahazi na nkeji 1 na ọnụọgụ njikọ:
Anyị na-ahụ na mgbe narị ole na ole njikọ gachara, ọnụọgụ nke arịrịọ ahazi maka sava abụọ ahụ na-adaba nke ọma (na ụdị multi-threaded nke a na-ahụkarị). Nke a ọ metụtara mmejuputa ngwugwu Linux TCP/IP? Na-enwere onwe gị ide echiche gị gbasara omume a nke eserese na njikarịcha maka nhọrọ multi-threaded na otu-threaded na nkwupụta.
Olee otú kwuru na nkwupụta, ule arụmọrụ a anaghị egosi omume nke reactor I / O n'okpuru ezigbo ibu, n'ihi na ọ fọrọ nke nta ka ọ bụrụ mgbe niile na ihe nkesa na-emekọ ihe na nchekwa data, na-emepụta ndekọ, na-eji cryptography na TLS wdg, n'ihi na ibu ahụ na-aghọ ihe na-adịghị mma (dị ike). A ga-eme ule yana akụrụngwa ndị ọzọ n'ime akụkọ gbasara I/O proactor.
Ọdịmma nke I/O reactor
Ịkwesịrị ịghọta na onye na-ahụ maka I/O enweghị ihe ndọghachi azụ ya, ya bụ:
Iji ihe I/O reactor na a multi-threaded gburugburu ebe obibi bụ ihe siri ike karị, n'ihi na ị ga-eji aka gị jikwaa ihe ndị na-asọ asọ.
Omume na-egosi na n'ọtụtụ ọnọdụ, ibu anaghị adị n'otu, nke nwere ike iduga n'otu eriri eri mgbe onye ọzọ na-arụsi ọrụ ike.
Ọ bụrụ na otu onye na-ahụ maka ihe omume gbochiri eri, onye na-ahọrọ sistemu n'onwe ya ga-egbochikwa, nke nwere ike ibute ahụhụ siri ike ịchọta.
Na-edozi nsogbu ndị a I/O proactor, nke na-enwekarị nhazi nhazi nke na-ekesa ibu ahụ n'otu n'otu na ọdọ mmiri nke eriri, ma nweekwa API dị mma karị. Anyị ga-ekwu maka ya ma emechaa, na edemede m ọzọ.
nkwubi
Nke a bụ ebe njem anyị site na tiori ozugbo banye na ikpochapụ profaịlụ abịala na njedebe.
I kwesịghị ilekwasị anya na nke a, n'ihi na e nwere ọtụtụ ụzọ ndị ọzọ na-adọrọ mmasị maka ide software netwọk na ọkwa dị iche iche nke ịdị mma na ọsọ. Na-akpali mmasị, n'echiche nke m, e nyere njikọ n'okpuru.