پېژندنه
په دې مقاله کې، موږ به د I/O ریکټور دننه او بهر وګورو او دا څنګه کار کوي، د کوډ له 200 څخه لږ لینونو کې پلي کول ولیکئ، او د 40 ملیون غوښتنو / دقیقو څخه ډیر ساده HTTP سرور پروسه جوړه کړئ.
وړاندیز
- مقاله د I/O ریکټور د فعالیت په پوهیدو کې د مرستې لپاره لیکل شوې وه ، او له همدې امله د کارولو پرمهال خطرونه درک کوي.
- د مقالې د پوهیدو لپاره د اساساتو پوهه اړینه ده.
سي ژبه او د شبکې غوښتنلیک پراختیا کې ځینې تجربه. - ټول کوډ په C ژبه کې په کلکه د (احتیاط: اوږد PDF)
د C11 معیار ته د لینکس لپاره او شتون لريGitHub .
دا ولې ضروري دی؟
د انټرنیټ د مخ په زیاتیدونکي شهرت سره، ویب سرورونو په ورته وخت کې د ډیرو اړیکو اداره کولو ته اړتیا پیل کړه، او له همدې امله دوه طریقې هڅه شوې: د I/O په ډیری OS تارونو کې بلاک کول او د I/O سره په ترکیب کې غیر بلاک کول. د پیښې خبرتیا سیسټم چې د "سیسټم ټاکونکی" په نوم هم یادیږي (
لومړۍ طریقه د هر راتلونکي پیوستون لپاره د نوي OS تار رامینځته کول شامل دي. د دې نیمګړتیا ضعیف پیمانه ده: عملیاتي سیسټم باید ډیری پلي کړي
تعدیل شوی نسخه روښانه کوي
دویمه طریقه کارول کیږي
د دې طریقو ترمنځ توپیر په لاندې ډول دی:
- د I/O عملیات بندول ځنډول د کارونکي جریان تر څوتر هغه چې OS په سمه توګه وي
تخریبونه راتلونکید IP کڅوړې د بایټ جریان ته (TCP ، د معلوماتو ترلاسه کول) یا به د داخلي لیکلو بفرونو کې د راتلونکي لیږلو لپاره کافي ځای شتون ونلري.NIC (د معلوماتو لیږل). - سیسټم ټاکونکی پس له وخته پروګرام ته خبر ورکوي چې OS لا د ډیفرګمینټ شوي IP پاکټونه (TCP، د معلوماتو رسیدنه) یا د داخلي لیکلو بفرونو کې کافي ځای لا د شتون لري (د معلوماتو لیږل).
د دې لنډیز لپاره ، د هر I/O لپاره د OS تار ساتل د کمپیوټري ځواک ضایع کول دي ، ځکه چې په حقیقت کې تارونه ګټور کار نه کوي (له همدې امله اصطلاح
د I/O ریکټور ماډل
I/O ریکټر د سیسټم انتخاب کونکي او د کارونکي کوډ تر مینځ د پرت په توګه کار کوي. د دې عملیاتو اصول د لاندې بلاک ډیاګرام لخوا تشریح شوي:
- اجازه راکړئ تاسو ته یادونه وکړم چې پیښه یو خبرتیا ده چې یو ځانګړی ساکټ د غیر بلاک کولو I/O عملیات ترسره کولو توان لري.
- د پیښې هینډلر یو فنکشن دی چې د I/O ریکټر لخوا ویل کیږي کله چې پیښه ترلاسه شي ، کوم چې بیا د I/O غیر بلاک کولو عملیات ترسره کوي.
دا مهمه ده چې په یاد ولرئ چې I/O ریکټور د تعریف له مخې واحد - تار شوی دی، مګر هیڅ شی شتون نلري چې مفهوم د 1 سلیډ: 1 ریکتور په تناسب په څو-تریډ شوي چاپیریال کې د کارولو مخه ونیسي، په دې توګه د CPU ټول کورونه ریسایکل کوي.
پلي کول
موږ به عامه انٹرفیس په فایل کې ځای په ځای کړو reactor.h
reactor.c
reactor.h
لاندې اعلانونه به ولري:
په 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);
د I/O ریکټور جوړښت عبارت دی له GHashTable
CallbackData
(د پیښې اداره کونکي جوړښت او د دې لپاره د کارونکي دلیل).
ریکټر او د کال بیک ډیټا وښایاست
struct reactor {
int epoll_fd;
GHashTable *table; // (int, CallbackData)
};
typedef struct {
Callback callback;
void *arg;
} CallbackData;
مهرباني وکړئ په یاد ولرئ چې موږ د سمبالولو وړتیا فعاله کړې ده reactor.h
موږ جوړښت اعلان کوو reactor
، او په reactor.c
موږ دا تعریف کوو، په دې توګه د کاروونکي مخه نیسي چې په ښکاره توګه د هغې ساحې بدل کړي. دا یو له نمونو څخه دی
دندې reactor_register
, reactor_deregister
и reactor_reregister
د سیسټم انتخاب کونکي او هش میز کې د ګټو ساکټونو لیست او اړونده پیښې اداره کونکي تازه کړئ.
د راجسټریشن دندې وښایاست
#define REACTOR_CTL(reactor, op, fd, interest)
if (epoll_ctl(reactor->epoll_fd, op, fd,
&(struct epoll_event){.events = interest,
.data = {.fd = fd}}) == -1) {
perror("epoll_ctl");
return -1;
}
int reactor_register(const Reactor *reactor, int fd, uint32_t interest,
Callback callback, void *callback_arg) {
REACTOR_CTL(reactor, EPOLL_CTL_ADD, fd, interest)
g_hash_table_insert(reactor->table, int_in_heap(fd),
callback_data_new(callback, callback_arg));
return 0;
}
int reactor_deregister(const Reactor *reactor, int fd) {
REACTOR_CTL(reactor, EPOLL_CTL_DEL, fd, 0)
g_hash_table_remove(reactor->table, &fd);
return 0;
}
int reactor_reregister(const Reactor *reactor, int fd, uint32_t interest,
Callback callback, void *callback_arg) {
REACTOR_CTL(reactor, EPOLL_CTL_MOD, fd, interest)
g_hash_table_insert(reactor->table, int_in_heap(fd),
callback_data_new(callback, callback_arg));
return 0;
}
وروسته له دې چې I/O ریکټر د توضیح کونکي سره پیښه مداخله وکړه fd
، دا د اړوندې پیښې اداره کونکي ته زنګ وهي ، کوم چې دا تیریږي fd
, void
.
د 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;
}
د لنډیز کولو لپاره، د کارن کوډ کې د فنکشن زنګونو سلسله به لاندې بڼه واخلي:
واحد تار شوی سرور
د لوړ بار لاندې د I/O ریکټر ازموینې لپاره ، موږ به یو ساده HTTP ویب سرور ولیکو چې د عکس سره هرې غوښتنې ته ځواب ووایی.
د HTTP پروتوکول ته چټک حواله
HTTP په اسانۍ سره کارول کیدی شي
د غوښتنې بڼه
<КОМАНДА> <URI> <ВЕРСИЯ HTTP>CRLF
<ЗАГОЛОВОК 1>CRLF
<ЗАГОЛОВОК 2>CRLF
<ЗАГОЛОВОК N>CRLF CRLF
<ДАННЫЕ>
CRLF
د دوو حروفونو ترتیب دی:r
иn
د غوښتنې لومړۍ کرښه جلا کول، سرلیکونه او ډاټا.<КОМАНДА>
- یو لهCONNECT
,DELETE
,GET
,HEAD
,OPTIONS
,PATCH
,POST
,PUT
,TRACE
. براوزر به زموږ سرور ته کمانډ واستويGET
، پدې معنی چې "ما ته د فایل مینځپانګې واستوئ."<URI>
-یونیفورم سرچینې پیژندونکی . د مثال په توګه، که URI =/index.html
، بیا پیرودونکي د سایټ اصلي پا pageې غوښتنه کوي.<ВЕРСИЯ HTTP>
- په بڼه کې د HTTP پروتوکول نسخهHTTP/X.Y
. نن ورځ ترټولو عام کارول شوی نسخه دهHTTP/1.1
.<ЗАГОЛОВОК N>
په شکل کې د کلیدي ارزښت جوړه ده<КЛЮЧ>: <ЗНАЧЕНИЕ>
د نورو تحلیلونو لپاره سرور ته لیږل شوی.<ДАННЫЕ>
- د سرور لخوا د عملیاتو ترسره کولو لپاره اړین معلومات. ډیری وختونه دا ساده ديJSON یا کوم بل شکل.
د غبرګون بڼه
<ВЕРСИЯ HTTP> <КОД СТАТУСА> <ОПИСАНИЕ СТАТУСА>CRLF
<ЗАГОЛОВОК 1>CRLF
<ЗАГОЛОВОК 2>CRLF
<ЗАГОЛОВОК N>CRLF CRLF
<ДАННЫЕ>
<КОД СТАТУСА>
یو شمیر دی چې د عملیاتو پایله څرګندوي. زموږ سرور به تل د 200 حالت بیرته راولي (بریالی عملیات).<ОПИСАНИЕ СТАТУСА>
- د حالت کوډ تار استازیتوب. د وضعیت کوډ 200 لپاره دا دیOK
.<ЗАГОЛОВОК N>
- د ورته فارمیټ سرلیک لکه څنګه چې په غوښتنه کې دی. موږ به سرلیکونه بیرته راوړوContent-Length
(د دوتنې اندازه) اوContent-Type: text/html
(د معلوماتو ډول بیرته راګرځي).<ДАННЫЕ>
- د کارونکي لخوا غوښتل شوي معلومات. زموږ په قضیه کې، دا د انځور لپاره لاره دهد HTML .
د دوتنې http_server.c
common.h
په common.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);
فعال میکرو هم تشریح شوی SAFE_CALL()
او فعالیت تعریف شوی fail()
. میکرو د بیان ارزښت له خطا سره پرتله کوي، او که حالت سم وي، فنکشن ته زنګ ووهي fail()
:
#define SAFE_CALL(call, error)
do {
if ((call) == error) {
fail("%s", #call);
}
} while (false)
دنده fail()
تیر شوي دلیلونه ترمینل ته چاپ کوي (لکه printf()
EXIT_FAILURE
:
static noreturn void fail(const char *format, ...) {
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
fprintf(stderr, ": %sn", strerror(errno));
exit(EXIT_FAILURE);
}
دنده new_server()
د سیسټم زنګونو لخوا رامینځته شوي د "سرور" ساکټ فایل توضیح کونکی بیرته راګرځوي socket()
bind()
listen()
نوی_سرور () فعالیت وښایاست
static int new_server(bool reuse_port) {
int fd;
SAFE_CALL((fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP)),
-1);
if (reuse_port) {
SAFE_CALL(
setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &(int){1}, sizeof(int)),
-1);
}
struct sockaddr_in addr = {.sin_family = AF_INET,
.sin_port = htons(SERVER_PORT),
.sin_addr = {.s_addr = inet_addr(SERVER_IPV4)},
.sin_zero = {0}};
SAFE_CALL(bind(fd, (struct sockaddr *)&addr, sizeof(addr)), -1);
SAFE_CALL(listen(fd, SERVER_BACKLOG), -1);
return fd;
}
- په یاد ولرئ چې ساکټ په پیل کې د بیرغ په کارولو سره په غیر بلاک کولو حالت کې رامینځته شوی
SOCK_NONBLOCK
نو په فعالیت کېon_accept()
(نور ولولئ) سیسټم کالaccept()
د تار اجرا کول بند نه کړل. - که
reuse_port
سره مساوي دهtrue
، بیا دا فنکشن به ساکټ د اختیار سره تنظیم کړي له لارېSO_REUSEPORT
په څو-تریډ شوي چاپیریال کې د ورته بندر کارولو لپاره (د "ملټي-تریډ شوي سرور" برخه وګورئ).setsockopt()
د پیښې سمبالونکی on_accept()
وروسته له هغه چې OS یوه پیښه رامینځته کوي غږ کیږي EPOLLIN
، پدې حالت کې پدې معنی چې نوې اړیکه منل کیدی شي. on_accept()
یو نوی پیوستون مني، د غیر بلاک کولو حالت ته یې بدلوي او د پیښې سمبالونکي سره راجستر کوي on_recv()
په I/O ریکټور کې.
on_accept() فنکشن وښایاست
static void on_accept(void *arg, int fd, uint32_t events) {
int incoming_conn;
SAFE_CALL((incoming_conn = accept(fd, NULL, NULL)), -1);
set_nonblocking(incoming_conn);
SAFE_CALL(reactor_register(reactor, incoming_conn, EPOLLIN, on_recv,
request_buffer_new()),
-1);
}
د پیښې سمبالونکی on_recv()
وروسته له هغه چې OS یوه پیښه رامینځته کوي غږ کیږي EPOLLIN
، پدې حالت کې پدې معنی چې اړیکه ثبت شوې on_accept()
د معلوماتو ترلاسه کولو لپاره چمتو دی.
on_recv()
د پیوستون څخه ډاټا لوستل تر هغه چې د HTTP غوښتنه په بشپړه توګه ترلاسه نشي، بیا دا یو سمبالونکی راجستر کوي on_send()
د HTTP ځواب لیږلو لپاره. که چیرې پیرودونکي پیوستون مات کړي، ساکټ یې راجستر شوی او په کارولو سره تړل شوی close()
په_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);
}
}
د پیښې سمبالونکی on_send()
وروسته له هغه چې OS یوه پیښه رامینځته کوي غږ کیږي EPOLLOUT
، پدې معنی چې اړیکه ثبت شوې on_recv()
د معلوماتو لیږلو ته چمتو دی. دا فنکشن پیرودونکي ته د عکس سره HTML لرونکی HTTP ځواب لیږي او بیا د پیښې اداره کونکي بیرته ته بدلوي on_recv()
.
on_send() فنکشن وښایاست
static void on_send(void *arg, int fd, uint32_t events) {
const char *content = "<img "
"src="https://habrastorage.org/webt/oh/wl/23/"
"ohwl23va3b-dioerobq_mbx4xaw.jpeg">";
char response[1024];
sprintf(response,
"HTTP/1.1 200 OK" CRLF "Content-Length: %zd" CRLF "Content-Type: "
"text/html" DOUBLE_CRLF "%s",
strlen(content), content);
SAFE_CALL(send(fd, response, strlen(response), 0), -1);
SAFE_CALL(reactor_reregister(reactor, fd, EPOLLIN, on_recv, arg), -1);
}
او بالاخره، په فایل کې http_server.c
په فعالیت کې main()
موږ د I/O ریکټور په کارولو سره رامینځته کوو reactor_new()
، د سرور ساکټ رامینځته کړئ او ثبت یې کړئ ، د ریکټور په کارولو سره پیل کړئ reactor_run()
د دقیقې یوې دقیقې لپاره، او بیا موږ سرچینې خوشې کوو او له پروګرام څخه ووځو.
ښکاره کړئ http_server.c
#include "reactor.h"
static Reactor *reactor;
#include "common.h"
int main(void) {
SAFE_CALL((reactor = reactor_new()), NULL);
SAFE_CALL(
reactor_register(reactor, new_server(false), EPOLLIN, on_accept, NULL),
-1);
SAFE_CALL(reactor_run(reactor, SERVER_TIMEOUT_MILLIS), -1);
SAFE_CALL(reactor_destroy(reactor), -1);
}
راځئ وګورو چې هرڅه د تمې سره سم کار کوي. تالیف کول (chmod a+x compile.sh && ./compile.sh
د پروژې په روټ کې) او پخپله لیکل شوی سرور پیل کړئ، خلاص کړئ
د فعالیت اندازه کول
زما د موټر مشخصات وښایاست
$ screenfetch
MMMMMMMMMMMMMMMMMMMMMMMMMmds+. OS: Mint 19.1 tessa
MMm----::-://////////////oymNMd+` Kernel: x86_64 Linux 4.15.0-20-generic
MMd /++ -sNMd: Uptime: 2h 34m
MMNso/` dMM `.::-. .-::.` .hMN: Packages: 2217
ddddMMh dMM :hNMNMNhNMNMNh: `NMm Shell: bash 4.4.20
NMm dMM .NMN/-+MMM+-/NMN` dMM Resolution: 1920x1080
NMm dMM -MMm `MMM dMM. dMM DE: Cinnamon 4.0.10
NMm dMM -MMm `MMM dMM. dMM WM: Muffin
NMm dMM .mmd `mmm yMM. dMM WM Theme: Mint-Y-Dark (Mint-Y)
NMm dMM` ..` ... ydm. dMM GTK Theme: Mint-Y [GTK2/3]
hMM- +MMd/-------...-:sdds dMM Icon Theme: Mint-Y
-NMm- :hNMNNNmdddddddddy/` dMM Font: Noto Sans 9
-dMNs-``-::::-------.`` dMM CPU: Intel Core i7-6700 @ 8x 4GHz [52.0°C]
`/dMNmy+/:-------------:/yMMM GPU: NV136
./ydNMMMMMMMMMMMMMMMMMMMMM RAM: 2544MiB / 7926MiB
.MMMMMMMMMMMMMMMMMMM
راځئ چې د یو واحد تار شوي سرور فعالیت اندازه کړو. راځئ چې دوه ټرمینلونه پرانیزو: په یو کې به یې وګرځوو ./http_server
په بل ډول -
$ wrk -c100 -d1m -t8 http://127.0.0.1:18470 -H "Host: 127.0.0.1:18470" -H "Accept-Language: en-US,en;q=0.5" -H "Connection: keep-alive"
Running 1m test @ http://127.0.0.1:18470
8 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 493.52us 76.70us 17.31ms 89.57%
Req/Sec 24.37k 1.81k 29.34k 68.13%
11657769 requests in 1.00m, 1.60GB read
Requests/sec: 193974.70
Transfer/sec: 27.19MB
زموږ یو واحد تاریډ سرور د 11 اړیکو څخه رامینځته شوي په هره دقیقه کې د 100 ملیون څخه ډیر غوښتنې پروسس کولو توان درلود. خرابه پایله نه ده، مګر ایا دا ښه کیدی شي؟
څو اړخیز سرور
لکه څنګه چې پورته یادونه وشوه، I/O ریکټر په جلا تارونو کې رامینځته کیدی شي، په دې توګه د CPU ټول کورونه کاروي. راځئ چې دا طریقه عملي کړو:
وښایاست http_server_multithreaded.c
#include "reactor.h"
static Reactor *reactor;
#pragma omp threadprivate(reactor)
#include "common.h"
int main(void) {
#pragma omp parallel
{
SAFE_CALL((reactor = reactor_new()), NULL);
SAFE_CALL(reactor_register(reactor, new_server(true), EPOLLIN,
on_accept, NULL),
-1);
SAFE_CALL(reactor_run(reactor, SERVER_TIMEOUT_MILLIS), -1);
SAFE_CALL(reactor_destroy(reactor), -1);
}
}
اوس هر تار
static Reactor *reactor;
#pragma omp threadprivate(reactor)
مهرباني وکړئ په یاد ولرئ چې د فعالیت دلیل new_server()
خوښول true
. دا پدې مانا ده چې موږ د سرور ساکټ ته اختیار ورکوو SO_REUSEPORT
دوهمه منډې
اوس راځئ چې د څو اړخیز سرور فعالیت اندازه کړو:
$ wrk -c100 -d1m -t8 http://127.0.0.1:18470 -H "Host: 127.0.0.1:18470" -H "Accept-Language: en-US,en;q=0.5" -H "Connection: keep-alive"
Running 1m test @ http://127.0.0.1:18470
8 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.14ms 2.53ms 40.73ms 89.98%
Req/Sec 79.98k 18.07k 154.64k 78.65%
38208400 requests in 1.00m, 5.23GB read
Requests/sec: 635876.41
Transfer/sec: 89.14MB
په 1 دقیقه کې د پروسس شویو غوښتنو شمیر ~ 3.28 ځله زیات شوی! مګر موږ د ګردي شمیرې څخه یوازې ~ XNUMX ملیون لنډ وو ، نو راځئ هڅه وکړو چې دا سم کړو.
لومړی راځئ چې تولید شوي احصایې وګورو
$ sudo perf stat -B -e task-clock,context-switches,cpu-migrations,page-faults,cycles,instructions,branches,branch-misses,cache-misses ./http_server_multithreaded
Performance counter stats for './http_server_multithreaded':
242446,314933 task-clock (msec) # 4,000 CPUs utilized
1 813 074 context-switches # 0,007 M/sec
4 689 cpu-migrations # 0,019 K/sec
254 page-faults # 0,001 K/sec
895 324 830 170 cycles # 3,693 GHz
621 378 066 808 instructions # 0,69 insn per cycle
119 926 709 370 branches # 494,653 M/sec
3 227 095 669 branch-misses # 2,69% of all branches
808 664 cache-misses
60,604330670 seconds time elapsed
-march=native
, MAX_EVENTS
او کارول EPOLLET
په فعالیت کې د پام وړ زیاتوالی ندی ورکړی. مګر څه پیښیږي که تاسو د ورته اړیکو شمیر زیات کړئ؟
د 352 یوځای اړیکو لپاره احصایې:
$ wrk -c352 -d1m -t8 http://127.0.0.1:18470 -H "Host: 127.0.0.1:18470" -H "Accept-Language: en-US,en;q=0.5" -H "Connection: keep-alive"
Running 1m test @ http://127.0.0.1:18470
8 threads and 352 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 2.12ms 3.79ms 68.23ms 87.49%
Req/Sec 83.78k 12.69k 169.81k 83.59%
40006142 requests in 1.00m, 5.48GB read
Requests/sec: 665789.26
Transfer/sec: 93.34MB
مطلوب پایله ترلاسه شوه، او د دې سره یو په زړه پورې ګراف د اړیکو شمیر په 1 دقیقو کې د پروسس شویو غوښتنو شمیر پورې تړاو ښیي:
موږ ګورو چې د څو سوو ارتباطاتو وروسته ، د دواړو سرورونو لپاره د پروسس شوي غوښتنو شمیر په چټکۍ سره راټیټیږي (په څو اړخیزه نسخه کې دا خورا د پام وړ دی). ایا دا د لینکس TCP/IP سټیک پلي کولو پورې اړه لري؟ د ګراف د دې چلند په اړه خپل انګیرنې ولیکئ او په نظرونو کې د څو تارونو او واحد تارونو اختیارونو لپاره اصلاح کولو لپاره وړیا احساس وکړئ.
څنګه
د I/O ریکټور زیانونه
تاسو اړتیا لرئ پوه شئ چې I/O ریکټر د دې نیمګړتیاو پرته نه دی، یعنې:
- په څو اړخیزه چاپیریال کې د I/O ریکټور کارول یو څه ډیر ستونزمن دی، ځکه چې تاسو باید په لاسي ډول جریان اداره کړئ.
- تمرین ښیي چې په ډیری مواردو کې بار غیر یونیفورم دی، کوم چې کولی شي د یوې تار د ننوتلو لامل شي پداسې حال کې چې بل په کار بوخت وي.
- که چیرې د پیښې یو سمبالونکی تار بند کړي ، د سیسټم انتخاب کونکی به پخپله هم بلاک کړي ، کوم چې کولی شي د موندلو سختو بګونو لامل شي.
دا ستونزې حل کوي
پایلې
دا هغه ځای دی چې زموږ له تیوري څخه مستقیم پروفایلر اخراج ته پای ته رسیدلی.
تاسو باید پدې اړه فکر ونه کړئ ، ځکه چې د اسانتیا او سرعت مختلف کچو سره د شبکې سافټویر لیکلو لپاره ډیری نور مساوي په زړه پوري لارې شتون لري. په زړه پورې، زما په نظر، لینکونه لاندې ورکړل شوي.
ژر به سره ګورو!
په زړه پورې پروژې
نور څه لوستل؟
https://linux.die.net/man/7/socket https://stackoverflow.com/questions/1050222/what-is-the-difference-between-concurrency-and-parallelism http://www.kegel.com/c10k.html https://kernel.dk/io_uring.pdf https://aturon.github.io/blog/2016/09/07/futures-design/ https://tokio.rs/blog/2019-10-scheduler/ https://www.artima.com/articles/io_design_patterns.html https://habr.com/en/post/183832/
سرچینه: www.habr.com