Ato amin'ity lahatsoratra ity, isika dia hijery ny ins sy ivelan'ny I/O reactor sy ny fomba fiasan'ny, manoratra fampiharana amin'ny latsaky ny 200 andalana code, ary manao dingana tsotra mpizara HTTP mihoatra ny 40 tapitrisa fangatahana/min.
sasin-teny
Ny lahatsoratra dia nosoratana mba hanampiana hahatakatra ny fiasan'ny I/O reactor, ary noho izany dia hahatakatra ny loza rehefa mampiasa azy io.
Ilaina ny fahalalana ny fototra mba hahatakarana ny lahatsoratra. C fiteny ary ny traikefa sasany amin'ny fampivoarana ny fampiharana tambajotra.
Ny kaody rehetra dia voasoratra amin'ny fiteny C araka ny fepetra (fampitandremana: PDF lava) ny C11 standard ho an'ny Linux ary azo alaina amin'ny GitHub.
Nahoana no ilaina izany?
Miaraka amin'ny fitomboan'ny lazan'ny Internet, ny mpizara tranonkala dia nanomboka nitaky fifandraisana marobe miaraka, ary noho izany dia fomba roa no nosedraina: fanakanana I/O amin'ny kofehy OS marobe ary tsy fanakanana I/O miaraka amin'ny rafitra fampandrenesana hetsika, antsoina koa hoe "selector system" (epoll/kqueue/IOCP/ sns).
Ny fomba voalohany dia ny famoronana kofehy OS vaovao ho an'ny fifandraisana miditra tsirairay. Ny fatiantoka dia ny scalability mahantra: ny rafitra fandidiana dia tsy maintsy mampihatra maro fiovan'ny contexte и antson'ny rafitra. Fampandehanana lafo izy ireo ary mety hitarika amin'ny tsy fahampian'ny RAM maimaim-poana miaraka amin'ny fifandraisana marobe.
Manasongadina ny dikan-teny novaina raikitra isan'ny kofehy (dobo filomanosana), amin'izay dia manakana ny rafitra tsy hanajanona ny famonoana, fa miaraka amin'izay koa dia miteraka olana vaovao: raha misy kofehy misy kofehy voasakana amin'izao fotoana izao noho ny famakiana lava be, dia tsy ho afaka ny sockets hafa izay efa afaka mandray data. ataovy izany.
Ny fomba faharoa dia mampiasa rafitra fampahafantarana hetsika (system selector) nomen'ny OS. Ity lahatsoratra ity dia miresaka momba ny karazana mpifidy rafitra mahazatra indrindra, mifototra amin'ny fanairana (hetsika, fampahafantarana) momba ny fahavononana amin'ny asa I/O, fa tsy amin'ny fampahafantarana momba ny fahavitan'izy ireo. Ny ohatra notsorina amin'ny fampiasana azy dia azo aseho amin'ny diagrama sakana manaraka:
Ny fahasamihafana eo amin'ireo fomba ireo dia toy izao manaraka izao:
Fanakanana ny asa I/O mampiato fikorianan'ny mpampiasa MANDRA-mandra-pahatongan'ny OS araka ny tokony ho izy defragments Narahi- fonosana IP to byte stream (TCP, mandray data) na tsy hisy toerana ampy ao amin'ny buffers fanoratana anatiny ho an'ny fandefasana manaraka amin'ny Nic (mandefa data).
Mpifidy rafitra rehefa ela mampahafantatra ny programa fa ny OS efa fonosana IP defragmented (TCP, fandraisana data) na habaka ampy amin'ny buffer fanoratana anatiny efa misy (mandefa data).
Raha fintinina dia fandaniam-poana ny herin'ny informatika ny famandrihana kofehy OS ho an'ny I/O tsirairay, satria raha ny tena izy dia tsy manao asa mahasoa ny kofehy (noho izany ny teny hoe "fanelingelenana rindrambaiko"). Mamaha ity olana ity ny mpifidy rafitra, mamela ny programa mpampiasa hampiasa loharanon-karena CPU ara-toekarena kokoa.
Modely reactor I/O
Ny reactor I/O dia miasa toy ny sosona eo anelanelan'ny mpifidy rafitra sy ny kaody mpampiasa. Ny fitsipiky ny fiasan'izy io dia voafaritra amin'ny alàlan'ny diagrama sakana manaraka:
Mamelà ahy hampahatsiahy anao fa ny hetsika dia fampandrenesana fa ny socket sasany dia afaka manao hetsika I/O tsy manakana.
Ny mpandrindra hetsika dia asa antsoin'ny reactor I/O rehefa voaray ny hetsika iray, izay manao hetsika I/O tsy manakana.
Zava-dehibe ny manamarika fa ny reactor I / O dia amin'ny famaritana tokana, saingy tsy misy na inona na inona manakana ny foto-kevitra tsy hampiasaina amin'ny tontolo misy kofehy maromaro amin'ny tahan'ny kofehy 1: 1 reactor, ka mamerina ny cores CPU rehetra.
fanatanterahana
Hapetrakay anaty rakitra ny interface public reactor.h, ary fampiharana - in reactor.c. reactor.h dia ahitana ireto fanambarana manaraka ireto:
Asehoy ny fanambarana ao amin'ny 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);
Ny rafitra reactor I/O dia ahitana famaritana rakitra mpifidy epoll и tabilao hashGHashTable, izay sarintany ny socket tsirairay CallbackData (firafitry ny mpitantana hetsika sy ny tohan-kevitry ny mpampiasa azy).
Azafady, mariho fa navelanay ny fahaizana mitantana karazana tsy feno araka ny fanondroana. IN reactor.h manambara ny rafitra izahay reactor, ary ao reactor.c mamaritra azy io izahay, ka manakana ny mpampiasa tsy hanova mazava ny sahany. Ity dia iray amin'ireo modely manafina angona, izay mifanentana amin'ny semantika C.
asa reactor_register, reactor_deregister и reactor_reregister manavao ny lisitr'ireo socket mahaliana sy ireo mpiandraikitra hetsika mifandraika amin'izany ao amin'ny lisitry ny rafitra sy ny latabatra hash.
/*
* Обработчик событий, который вызовется после того, как сокет будет
* готов принять новое соединение.
*/
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);
Famaritana ihany koa ny macro functional SAFE_CALL() ary voafaritra ny asa fail(). Ny macro dia mampitaha ny sandan'ny fitenenana amin'ny lesoka, ary raha marina ny fepetra dia miantso ny fiasa fail():
#define SAFE_CALL(call, error)
do {
if ((call) == error) {
fail("%s", #call);
}
} while (false)
asa fail() manonta ny tohan-kevitra nandalo tamin'ny terminal (toy ny printf()) ary mamarana ny programa amin'ny code EXIT_FAILURE:
Mariho fa ny socket dia noforonina tamin'ny fomba tsy fanakanana tamin'ny fampiasana ny saina SOCK_NONBLOCKka amin'ny asa on_accept() (vakio bebe kokoa) antso an-tariby accept() tsy nanakana ny famonoana kofehy.
raha reuse_port mitovy amin'ny true, dia ity fiasa ity dia hanitsy ny socket miaraka amin'ny safidy SO_REUSEPORT ny alalan ' setsockopt()hampiasa seranan-tsambo mitovy amin'ny tontolo misy kofehy maromaro (jereo ny fizarana "Server multi-threaded").
Event Handler on_accept() antsoina rehefa mamorona hetsika ny OS EPOLLIN, amin'ity tranga ity dia midika fa azo ekena ny fifandraisana vaovao. on_accept() manaiky fifandraisana vaovao, mamadika izany amin'ny fomba tsy manakana ary misoratra anarana amin'ny mpitantana hetsika on_recv() amin'ny reactor I/O.
Event Handler on_recv() antsoina rehefa mamorona hetsika ny OS EPOLLIN, amin'ity tranga ity dia midika fa nisoratra anarana ny fifandraisana on_accept(), vonona handray angona.
on_recv() mamaky angon-drakitra avy amin'ny fifandraisana mandra-pahazoana tanteraka ny fangatahana HTTP, avy eo dia manoratra mpandrindra on_send() handefa valiny HTTP. Raha tapaka ny fifandraisana ny mpanjifa dia nesorina ny socket ary mihidy amin'ny fampiasana azy close().
Asehoy ny fiasa 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);
}
}
Event Handler on_send() antsoina rehefa mamorona hetsika ny OS EPOLLOUT, midika izany fa nisoratra anarana ny fifandraisana on_recv(), vonona handefa angona. Ity fampiasa ity dia mandefa valinteny HTTP misy HTML miaraka amin'ny sary ho an'ny mpanjifa ary avy eo dia manova ny mpitantana hetsika on_recv().
Andeha hojerentsika fa mandeha araka ny nantenaina ny zava-drehetra. Manangona (chmod a+x compile.sh && ./compile.sh ao amin'ny fototry ny tetikasa) ary atombohy ny mpizara sora-tena, sokafy http://127.0.0.1:18470 ao amin'ny navigateur ary jereo izay nantenainay:
Nahavita nikarakara fangatahana mihoatra ny 11 tapitrisa isa-minitra avy amin'ny fifandraisana 100 ny mpizara misy kofehy tokana. Tsy vokatra ratsy, fa azo hatsaraina?
Mpizara maromaro
Araka ny voalaza etsy ambony, ny reactor I / O dia azo noforonina amin'ny kofehy misaraka, ka mampiasa ny CPU rehetra. Andao hampihatra ity fomba fiasa ity:
Ny vokatra tadiavina dia azo, ary miaraka amin'izany ny grafika mahaliana mampiseho ny fiankinan'ny isan'ny fangatahana voakarakara ao anatin'ny 1 minitra amin'ny isan'ny fifandraisana:
Hitantsika fa taorian'ny fifandraisana an-jatony roa dia nihena be ny fangatahana nokarakaraina ho an'ny mpizara roa (amin'ny dikan-teny maromaro misy kofehy io dia miharihary kokoa). Mifandray amin'ny fampiharana stack Linux TCP/IP ve izany? Aza misalasala manoratra ny eritreritrao momba ity fitondran-tenan'ny grafika ity sy ny fanatsarana ny safidy maromaro misy kofehy sy kofehy tokana ao amin'ny fanehoan-kevitra.
Ahoana no nanamarika Ao amin'ny fanehoan-kevitra, ity fitsapana fampisehoana ity dia tsy mampiseho ny fihetsiky ny reactor I/O eo ambanin'ny enta-mavesatra, satria saika matetika ny mpizara dia mifandray amin'ny angon-drakitra, mamoaka logs, mampiasa kriptografika amin'ny TLS sns., ka lasa tsy mitovy ny entana (dynamique). Ny fitsapana miaraka amin'ireo singa avy amin'ny antoko fahatelo dia hatao ao amin'ny lahatsoratra momba ny proactor I/O.
Ny tsy fahampian'ny reactor I/O
Tokony ho takatrao fa ny reactor I/O dia tsy misy tsy fahampiana, izany hoe:
Ny fampiasana reactor I/O amin'ny tontolo misy kofehy maromaro dia somary sarotra kokoa, satria tsy maintsy mitantana amin'ny tananao ny fikoriana ianao.
Ny fanazaran-tena dia mampiseho fa amin'ny ankamaroan'ny toe-javatra dia tsy mitovy ny entana, izay mety hitarika amin'ny fametahana kofehy iray raha ny iray kosa sahirana amin'ny asa.
Raha manakana kofehy iray ny mpitantana hetsika iray, dia ny mpifidy rafitra ihany koa no hisakana, izay mety hitarika amin'ny bibikely sarotra hita.
Mamaha ireo olana ireo I/O proactor, izay matetika manana fandaharam-potoana izay mizara mitovy ny entana amin'ny dobo kofehy, ary manana API mora kokoa. Hiresaka momba izany isika any aoriana, ao amin’ny lahatsoratro hafa.
famaranana
Eto no nifarana ny diantsika avy amin'ny teoria mankany amin'ny setroka profiler.
Tsy tokony hieritreritra an'izany ianao, satria misy fomba maro hafa mahaliana amin'ny fanoratana rindrambaiko amin'ny tambajotra miaraka amin'ny haavo sy hafainganam-pandeha samihafa. Mahaliana, raha ny hevitro, dia omena eto ambany ny rohy.