Sehloohong sena, re tla sheba ins and outs ea I / O reactor le kamoo e sebetsang kateng, ngola ts'ebetsong ka tlase ho mela ea 200 ea khoutu, 'me u etse mokhoa o bonolo oa seva sa HTTP ho feta likopo tse limilione tse 40 / min.
Tlhaloso
Sengoliloeng se ngotsoe ho thusa ho utloisisa ts'ebetso ea mochini oa I / O, ka hona ho utloisisa likotsi ha u se sebelisa.
Tsebo ea metheo ea hlokahala ho utloisisa sehlooho. C puo le boiphihlelo bo itseng ho nts'etsopele ea ts'ebeliso ea marang-rang.
Khoutu eohle e ngotsoe ka puo ea C ho latela (tlhokomeliso: PDF e telele) ho ea ho C11 e tloaelehileng bakeng sa Linux mme e fumaneha ho GitHub.
Ke hobane'ng ha see se hlokahala?
Ka botumo bo ntseng bo eketseha ba Marang-rang, li-server tsa marang-rang li ile tsa qala ho hloka ho sebetsana le palo e kholo ea likhokahano ka nako e le 'ngoe, ka hona ho ile ha lekoa mekhoa e' meli: ho thibela I / O ka palo e kholo ea likhoele tsa OS le I / O e sa thibeleng hammoho le sistimi ea tsebiso ea ketsahalo, eo hape e bitsoang "system selectctor" (epoll/kqueue/IOCP/ joalo-joalo).
Mokhoa oa pele o ne o kenyelletsa ho theha khoele e ncha ea OS bakeng sa khokahano ka 'ngoe e kenang. Bothata ba eona ke bofokoli bo fokolang: sistimi e sebetsang e tla tlameha ho kenya ts'ebetsong tse ngata phetoho ya maemo и mehala ea tsamaiso. Ke ts'ebetso e theko e boima 'me e ka lebisa ho haelloe ke RAM ea mahala e nang le palo e tsotehang ea likhokahano.
Phetolelo e fetotsoeng e totobatsa palo e tsitsitseng ea likhoele (letamo la khoele), ka hona ho thibela tsamaiso hore e se ke ea bolaoa, empa ka nako e ts'oanang e hlahisa bothata bo bocha: haeba letamo la likhoele le koetsoe ke ts'ebetso ea nako e telele, joale li-sockets tse ling tse seng li khona ho fumana data li ke ke tsa khona ho etsa joalo.
Mokhoa oa bobeli o sebelisoa sistimi ea tsebiso ea ketsahalo (sekhetho sa sistimi) se fanoeng ke OS. Sengoliloeng sena se bua ka mofuta o atileng haholo oa khetho ea sistimi, e ipapisitseng le litlhokomeliso (liketsahalo, litsebiso) mabapi le ho itokisetsa ts'ebetso ea I/O, ho fapana le litsebiso mabapi le ho phethoa ha tsona. Mohlala o nolofalitsoeng oa ts'ebeliso ea ona o ka emeloa ke setšoantšo se latelang sa block:
Phapang pakeng tsa mekhoa ena ke e latelang:
Ho thibela ts'ebetso ea I/O emisa phallo ya mosebedisi ho fihlelaho fihlela OS e nepahetse defragments tse kenang Lipakete tsa IP ho phalla (TCP, ho amohela data) kapa ho ke ke ha e-ba le sebaka se lekaneng se fumanehang ka har'a li-buffers tsa ka hare bakeng sa ho romella ka mor'a moo. NIC (ho romela lintlha).
Mokhethi oa tsamaiso ka mor'a nako e itseng e tsebisa lenaneo leo OS se lipakete tsa IP tse senyehileng (TCP, kamohelo ea data) kapa sebaka se lekaneng ho li-buffers tsa ka hare tsa ho ngola se e fumaneha (ho romela data).
Ho e akaretsa, ho boloka khoele ea OS bakeng sa I/O ka 'ngoe ke tšenyo ea matla a komporo, hobane ha e le hantle, likhoele ha li etse mosebetsi oa bohlokoa (ka hona poleloana e reng " "software e sitisa"). Mokhethi oa tsamaiso o rarolla bothata bona, a lumella lenaneo la mosebedisi ho sebelisa lisebelisoa tsa CPU haholo moruong.
Moetso oa actor oa I/O
I/O reactor e sebetsa e le lera lipakeng tsa mokhethoa oa sistimi le khoutu ea mosebelisi. Molao-motheo oa ts'ebetso ea ona o hlalosoa ke setšoantšo se latelang sa block:
E-re ke u hopotse hore ketsahalo ke tsebiso ea hore sokete e itseng e khona ho etsa ts'ebetso ea I / O e sa thibeleng.
Sebatli sa ketsahalo ke ts'ebetso e bitsoang "I/O reactor" ha ketsahalo e amoheloa, ebe e etsa ts'ebetso e sa thibeleng ea I/O.
Ke habohlokoa ho hlokomela hore mochine oa I / O ka tlhaloso o na le khoele e le 'ngoe, empa ha ho letho le thibelang khopolo ho sebelisoa sebakeng se nang le mefuta e mengata ka karolelano ea khoele ea 1: 1 reactor, kahoo e tsosolosa li-cores tsohle tsa CPU.
Ts'ebetsong
Re tla beha sebopeho sa sechaba faeleng reactor.h, le ho kenya tshebetsong - ho reactor.c. reactor.h e tla ba le litsebiso tse latelang:
Hlahisa liphatlalatso ho 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);
Sebopeho sa I/O reactor se na le tlhaloso ea faele mokhethi epoll и litafole tsa hashGHashTable, e etsang limmapa sokete ka 'ngoe ho CallbackData (sebopeho sa motshwari wa ketsahalo le phehisano ya mosebedisi bakeng sa yona).
Ka kopo hlokomela hore re nolofalitse bokhoni ba ho sebetsana mofuta o sa fellang ho latela index. IN reactor.h re phatlalatsa sebopeho reactorle ho reactor.c rea e hlalosa, ka hona re thibela mosebelisi ho fetola masimo a eona ka ho hlaka. Ena ke e 'ngoe ea mekhoa ho pata lintlha, e lumellanang hantle le semantiki ea C.
Mesebetsi reactor_register, reactor_deregister и reactor_reregister ntlafatsa lenane la li-sockets tsa thahasello le batšoasi ba liketsahalo tse tsamaellanang ho mokhethoa oa sistimi le tafole ea hash.
Kamora hore sebapali sa I/O se kenelle ketsahalo ka mohlalosi fd, e bitsa motshwari wa ketsahalo e tsamaisanang le yona, eo e fetelang ho yona fd, maske hanyane diketsahalo tse hlahisoang le sesupa sa mosebedisi ho void.
Hlahisa ts'ebetso ea 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;
}
Ho akaretsa, letoto la mehala ea ts'ebetso ho khoutu ea mosebelisi le tla nka ka mokhoa o latelang:
Seva ea khoele e le 'ngoe
E le ho leka mochine oa I / O tlas'a mojaro o phahameng, re tla ngola sebatli se bonolo sa HTTP se arabelang kopo leha e le efe e nang le setšoantšo.
Tlhahiso e potlakileng ho protocol ea HTTP
http - ena ke protocol boemo ba kopo, haholo-holo e sebelisetsoang ho sebelisana le seva le sebatli.
HTTP e ka sebelisoa habonolo sepalangoang melaoana TCP, ho romela le ho amohela melaetsa ka mokhoa o boletsoeng tlhaloso.
CRLF ke tatellano ea litlhaku tse peli: r и n, ho arola mohala oa pele oa kopo, lihlooho le data.
<КОМАНДА> - e mong oa CONNECT, DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT, TRACE. Sebatli se tla romela taelo ho seva sa rona GET, e bolelang "Nthomelle litaba tsa faele."
<КОД СТАТУСА> ke nomoro e emelang sephetho sa ts'ebetso. Seva ea rona e tla lula e khutlisetsa boemo ba 200 (ts'ebetso e atlehileng).
<ОПИСАНИЕ СТАТУСА> — kemiso ea khoele ea khoutu ea boemo. Bakeng sa khoutu ea boemo 200 sena ke OK.
<ЗАГОЛОВОК N> - sehlooho sa sebopeho se tšoanang le sa kopo. Re tla khutlisetsa litlotla Content-Length (boholo ba faele) le Content-Type: text/html (khutlisetsa mofuta oa data).
<ДАННЫЕ> - data e kopiloeng ke mosebelisi. Tabeng ea rona, ena ke tsela e lebisang setšoantšong HTML.
faele http_server.c (seva e le 'ngoe ea khoele) e kenyelletsa faele common.h, e nang le li-prototypes tse latelang tsa ts'ebetso:
Bontša li-prototypes tsa ts'ebetso tse tšoanang.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);
Macro e sebetsang e boetse e hlalosoa SAFE_CALL() mme mosebetsi o hlalositsoe fail(). Macro e bapisa boleng ba polelo le phoso, 'me haeba boemo e le' nete, e bitsa mosebetsi fail():
#define SAFE_CALL(call, error)
do {
if ((call) == error) {
fail("%s", #call);
}
} while (false)
Mosebetsi fail() e hatisa likhang tse fetisitsoeng ho terminal (joalo ka printf()) le ho felisa lenaneo ka khoutu EXIT_FAILURE:
Mosebetsi new_server() e khutlisa tlhaloso ea faele ea sokete ea "server" e entsoeng ke mehala ea sistimi socket(), bind() и listen() mme e kgona ho amohela dikgokelo tse kenang ka mokgwa o sa thibeleng.
Hlokomela hore sokete e qalile ka mokhoa o sa thibeleng ho sebelisoa folakha SOCK_NONBLOCKe le hore mosebetsing on_accept() (bala haholoanyane) mohala oa sistimi accept() ha ea ka ea emisa ts'ebetso ea khoele.
haeba reuse_port e lekana le true, joale mosebetsi ona o tla lokisa sokete ka khetho SO_REUSEPORT ka setsockopt()ho sebelisa boema-kepe bo le bong sebakeng se nang le likhoele tse ngata (sheba karolo ea "Multi-threaded server").
Mohlokomeli oa Ketsahalo on_accept() e bitsoa ka mor'a hore OS e hlahise ketsahalo EPOLLIN, tabeng ena ho bolela hore khokahano e ncha e ka amoheloa. on_accept() e amohela khokahanyo e ncha, e e fetolela ho mokhoa o sa thibeleng ebe e ngolisa le motho ea sebetsanang le liketsahalo on_recv() ka har'a sebapali sa I/O.
Mohlokomeli oa Ketsahalo on_recv() e bitsoa ka mor'a hore OS e hlahise ketsahalo EPOLLIN, tabeng ena ho bolela hore khokahano e ngolisitsoe on_accept(), e loketse ho amohela data.
on_recv() e bala lintlha tse tsoang ho khokahanyo ho fihlela kopo ea HTTP e amoheloa ka ho feletseng, ebe e ngolisa mohlokomeli on_send() ho romella karabo ea HTTP. Haeba moreki a roba khokahano, sokete e hlakotsoe mme e koetsoe ho sebelisoa close().
Hlahisa tšebetso ho_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);
}
}
Mohlokomeli oa Ketsahalo on_send() e bitsoa ka mor'a hore OS e hlahise ketsahalo EPOLLOUT, ho bolelang hore khokahano e ngolisitsoe on_recv(), e loketse ho romela data. Ts'ebetso ena e romela karabo ea HTTP e nang le HTML e nang le setšoantšo ho moreki ebe e fetola sebatli sa ketsahalo ho khutlela ho sona on_recv().
'Me qetellong, ka faele http_server.c, mosebetsing main() re theha mochini oa motlakase oa I/O re sebelisa reactor_new(), theha sokete ea seva 'me u e ngolise, qala mochini o sebelisang reactor_run() bakeng sa motsotso o le mong hantle, ebe re lokolla lisebelisoa ebe re tsoa lenaneong.
Ha re hlahlobeng hore na tsohle li sebetsa kamoo ho neng ho lebelletsoe. Ho bokella (chmod a+x compile.sh && ./compile.sh motsong oa morero) 'me u qale seva se ngotseng, bula http://127.0.0.1:18470 ho sebatli 'me u bone seo re se lebelletseng:
Ha re lekanye ts'ebetso ea seva ea khoele e le 'ngoe. Ha re bule li-terminals tse peli: ho e 'ngoe re tla matha ./http_server, ka tsela e fapaneng - wrk. Kamora motsotso, lipalo-palo tse latelang li tla hlahisoa setsing sa bobeli:
Seva ea rona e nang le khoele e le 'ngoe e khonne ho sebetsana le likopo tse fetang limilione tse 11 ka motsotso ho tsoa ho likhokahano tse 100. Ha se phello e mpe, empa na e ka ntlafatsoa?
Multithreaded server
Joalokaha ho boletsoe ka holimo, mochine oa I / O o ka etsoa ka likhoele tse arohaneng, kahoo o sebelisa li-cores tsohle tsa CPU. Ha re sebeliseng mokhoa ona ts'ebetsong:
Ka kopo hlokomela hore tlhaloso ea mosebetsi new_server() babuelli true. Sena se bolela hore re fana ka khetho ho sokete ea seva SO_REUSEPORTho e sebelisa sebakeng sa likhoele tse ngata. U ka bala lintlha tse ling mona.
Ho matha la bobeli
Joale ha re lekanye ts'ebetso ea seva e nang le likhoele tse ngata:
Palo ea likopo tse sebetsitsoeng ka motsotso o le 1 e eketsehile ka makhetlo a ~ 3.28! Empa re ne re haelloa ke ~ limilione tse XNUMX feela ho palo e potolohileng, kahoo ha re lekeng ho lokisa seo.
Pele a re shebeng lipalo-palo tse hlahisitsoeng sehlahisoa:
Ho sebelisa CPU Affinity, pokello le -march=native, PGO, ho eketseha ha palo ea ho otla polokelo, nyollelo MAX_EVENTS le tšebeliso EPOLLET ha ea ka ea fana ka keketseho e kholo ea ts'ebetso. Empa ho etsahala'ng haeba u eketsa palo ea likhokahano ka nako e le 'ngoe?
Lipalopalo tsa likhokahano tse 352 ka nako e le 'ngoe:
Sephetho se lakatsehang se ile sa fumanoa, 'me ka eona kerafo e khahlisang e bonts'a ho its'etleha ha palo ea likopo tse sebetsitsoeng ka motsotso o le mong ka palo ea likhokahano:
Rea bona hore ka mor'a likhokahano tse makholo a 'maloa, palo ea likopo tse sebetsitsoeng bakeng sa li-server ka bobeli e theoha haholo (ka mofuta oa mefuta e mengata sena se bonahala haholoanyane). Na see se amana le ts'ebetsong ea li-stack tsa Linux TCP/IP? Ikutloe u lokolohile ho ngola maikutlo a hau mabapi le boits'oaro bona ba kerafo le lintlafatso bakeng sa likhetho tse nang le likhoele tse ngata le tse nang le khoele e le 'ngoe maikutlong.
How hlokometsoe litlhalosong, tlhahlobo ena ea ts'ebetso ha e bontše boitšoaro ba mochine oa I / O tlas'a meroalo ea sebele, hobane hoo e batlang e le kamehla seva se sebelisana le database, se hlahisa lintlha, se sebelisa li-cryptography le TLS joalo-joalo, ka lebaka leo mojaro o fetohang o sa ts'oaneng (matla). Liteko hammoho le likarolo tsa mokha oa boraro li tla etsoa sengoloa se mabapi le I/O proactor.
Mefokolo ea I/O reactor
U hloka ho utloisisa hore mochini oa I/O ha o na mathata, e leng:
Ho sebelisa mochine oa I/O sebakeng se nang le likhoele tse ngata ho batla ho le thata, hobane o tla tlameha ho laola phallo ka letsoho.
Boikoetliso bo bontša hore hangata mojaro ha o tšoane, e leng se ka lebisang ho rengong ha khoele ha e mong a ntse a tšoarehile ka mosebetsi.
Haeba sebatli se le seng sa ketsahalo se thiba khoele, joale mokhethoa oa sistimi ka boeona o tla thibela, e leng se ka lebisang ho liphoso tse thata ho fumana.
E rarolla mathata ana Sebapali sa I/O, eo hangata e nang le kemiso e arolelanang mojaro ka mokhoa o lekanang letamong la likhoele, hape e na le API e bonolo haholoanyane. Re tla bua ka eona hamorao, sehloohong sa ka se seng.
fihlela qeto e
Mona ke moo leeto la rona ho tloha khopolo-taba ho ea ho profil exhaust le fihlile pheletsong.
Ha ua lokela ho lula u nahana ka sena, hobane ho na le mekhoa e meng e mengata e khahlisang ka mokhoa o ts'oanang ea ho ngola software ea marang-rang e nang le maemo a fapaneng a bonolo le lebelo. Ho khahlisang, ka maikutlo a ka, lihokelo li fanoe ka tlase.