Full-ifihan igboro-C I/O riakito

Full-ifihan igboro-C I/O riakito

Ifihan

I/O riakito (o tẹle ara nikan iṣẹlẹ lupu) jẹ apẹrẹ fun kikọ sọfitiwia fifuye giga, ti a lo ninu ọpọlọpọ awọn solusan olokiki:

Ninu àpilẹkọ yii, a yoo wo awọn ins ati awọn ita ti I/O reactor ati bii o ṣe n ṣiṣẹ, kọ imuse ni o kere ju awọn laini koodu 200, ati ṣe ilana olupin HTTP ti o rọrun lori awọn ibeere 40 million / min.

Ọrọ iṣaaju

  • A kọ nkan naa lati ṣe iranlọwọ lati loye iṣẹ ṣiṣe ti riakito I / O, ati nitorinaa loye awọn eewu nigba lilo rẹ.
  • Oye awọn ipilẹ ni a nilo lati ni oye nkan naa. C ede ati iriri diẹ ninu idagbasoke ohun elo nẹtiwọki.
  • Gbogbo koodu ti kọ ni ede C ni muna ni ibamu si (ṣọra: gun PDF) to C11 bošewa fun Linux ati ki o wa lori GitHub.

Kini idi ti eyi nilo?

Pẹlu olokiki ti Intanẹẹti ti ndagba, awọn olupin wẹẹbu bẹrẹ lati nilo lati mu nọmba nla ti awọn asopọ ni nigbakannaa, ati nitorinaa awọn ọna meji ni a gbiyanju: didi I / O lori nọmba nla ti awọn okun OS ati ti kii ṣe idilọwọ I / O ni apapo pẹlu eto ifitonileti iṣẹlẹ kan, ti a tun pe ni “oluyan eto” (epoll/kqueue/IOCP/ati be be lo).

Ọna akọkọ jẹ pẹlu ṣiṣẹda okun OS tuntun fun asopọ ti nwọle kọọkan. Alailanfani rẹ jẹ iwọn ti ko dara: ẹrọ ṣiṣe yoo ni lati ṣe ọpọlọpọ awọn iyipada ti o tọ и awọn ipe eto. Wọn jẹ awọn iṣẹ ṣiṣe gbowolori ati pe o le ja si aini ti Ramu ọfẹ pẹlu nọmba iwunilori ti awọn asopọ.

Awọn títúnṣe version ifojusi ti o wa titi nọmba ti awon (adagun odo okun), nitorinaa idilọwọ eto naa lati iṣẹyun ipaniyan, ṣugbọn ni akoko kanna ti n ṣafihan iṣoro tuntun kan: ti adagun okun kan ti dina lọwọlọwọ nipasẹ awọn iṣẹ ṣiṣe kika gigun, lẹhinna awọn iho miiran ti o ni anfani lati gba data tẹlẹ kii yoo ni anfani lati ṣe bẹ.

Ọna keji lo eto iwifunni iṣẹlẹ (oluyan eto) ti a pese nipasẹ OS. Nkan yii n jiroro lori iru yiyan eto ti o wọpọ julọ, ti o da lori awọn titaniji (awọn iṣẹlẹ, awọn iwifunni) nipa imurasilẹ fun awọn iṣẹ I / O, kuku ju lori awọn iwifunni nipa ipari wọn. Apeere irọrun ti lilo rẹ le jẹ aṣoju nipasẹ aworan atọka bulọọki atẹle:

Full-ifihan igboro-C I/O riakito

Iyatọ laarin awọn ọna wọnyi jẹ bi atẹle:

  • Ìdènà I/O mosi da duro olumulo sisan titititi OS jẹ daradara defragments ti nwọle IP awọn apo-iwe si ṣiṣan baiti (TCP, gbigba data) tabi kii yoo ni aaye to wa ninu awọn ifibu kikọ inu fun fifiranṣẹ atẹle nipasẹ NIC (firanṣẹ data).
  • Aṣayan eto asiko lehin asiko leti awọn eto ti awọn OS tẹlẹ defragmented IP awọn apo-iwe (TCP, data gbigba) tabi to aaye ninu ti abẹnu kikọ buffers tẹlẹ wa (fifiranṣẹ data).

Lati ṣe akopọ, fifipamọ okun OS kan fun I / O kọọkan jẹ egbin ti agbara iširo, nitori ni otitọ, awọn okun ko ṣe iṣẹ ti o wulo (eyi ni ibiti ọrọ naa ti wa. "Idaduro software"). Aṣayan eto yanju iṣoro yii, gbigba eto olumulo laaye lati lo awọn orisun Sipiyu pupọ diẹ sii ni ọrọ-aje.

Mo / Eyin riakito awoṣe

Awọn riakito I/O n ṣiṣẹ bi Layer laarin oluyan eto ati koodu olumulo. Ilana ti iṣiṣẹ rẹ jẹ apejuwe nipasẹ aworan atọka wọnyi:

Full-ifihan igboro-C I/O riakito

  • Jẹ ki n ran ọ leti pe iṣẹlẹ kan jẹ ifitonileti kan pe iho kan ni anfani lati ṣe iṣẹ I/O ti kii ṣe dina.
  • Olutọju iṣẹlẹ jẹ iṣẹ ti a npe ni nipasẹ I/O reactor nigbati iṣẹlẹ ba ti gba, eyiti o ṣe iṣẹ I/O ti kii ṣe idilọwọ.

O ṣe pataki lati ṣe akiyesi pe ohun riakito I / O jẹ nipasẹ asọye nikan-asapo, ṣugbọn ko si ohun ti o dẹkun ero naa lati lo ni agbegbe ti o ni asapo pupọ ni ipin ti okun 1: 1 reactor, nitorinaa atunlo gbogbo awọn ohun kohun Sipiyu.

Imuse

A yoo gbe wiwo gbogbo eniyan sinu faili kan reactor.h, ati imuse - ni reactor.c. reactor.h yoo ni awọn ikede wọnyi:

Ṣe afihan awọn ikede ni 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 / Eyin riakito be oriširiši faili apejuwe yiyan epoll и elile tabili GHashTable, eyi ti maapu kọọkan iho to CallbackData (ẹya ti oluṣakoso iṣẹlẹ ati ariyanjiyan olumulo fun rẹ).

Ṣe afihan Reactor ati CallbackData

struct reactor {
    int epoll_fd;
    GHashTable *table; // (int, CallbackData)
};

typedef struct {
    Callback callback;
    void *arg;
} CallbackData;

Jọwọ ṣe akiyesi pe a ti mu agbara lati mu ṣiṣẹ iru ti ko pe gẹgẹ bi atọka. IN reactor.h a kede awọn be reactorati ninu reactor.c a ṣe alaye rẹ, nitorinaa idilọwọ olumulo lati yi awọn aaye rẹ pada ni gbangba. Eyi jẹ ọkan ninu awọn awoṣe nọmbafoonu data, eyi ti o ni ṣoki ni ibamu si imọ-ọrọ C.

Awọn iṣẹ reactor_register, reactor_deregister и reactor_reregister ṣe imudojuiwọn atokọ ti awọn iho ti iwulo ati awọn olutọju iṣẹlẹ ti o baamu ni yiyan eto ati tabili hash.

Ṣe afihan awọn iṣẹ iforukọsilẹ

#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;
}

Lẹhin ti I/O riakito ti intercepted awọn iṣẹlẹ pẹlu awọn apejuwe fd, o pe oluṣakoso iṣẹlẹ ti o baamu, eyiti o kọja fd, bit boju ti ipilẹṣẹ iṣẹlẹ ati olumulo ijuboluwole si void.

Ṣe afihan iṣẹ 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;
}

Lati ṣe akopọ, ẹwọn awọn ipe iṣẹ ni koodu olumulo yoo gba fọọmu atẹle:

Full-ifihan igboro-C I/O riakito

Nikan asapo olupin

Lati le ṣe idanwo riakito I/O labẹ ẹru giga, a yoo kọ olupin wẹẹbu HTTP ti o rọrun ti o dahun si eyikeyi ibeere pẹlu aworan kan.

Itọkasi iyara si ilana HTTP

HTTP - Eyi ni ilana naa ipele ohun elo, ni akọkọ ti a lo fun ibaraenisepo olupin-kiri.

HTTP le ni irọrun lo lori gbigbe Ilana TCP, fifiranṣẹ ati gbigba awọn ifiranṣẹ ni ọna kika kan pato sipesifikesonu.

Ìbéèrè kika

<КОМАНДА> <URI> <ВЕРСИЯ HTTP>CRLF
<ЗАГОЛОВОК 1>CRLF
<ЗАГОЛОВОК 2>CRLF
<ЗАГОЛОВОК N>CRLF CRLF
<ДАННЫЕ>

  • CRLF jẹ ọkọọkan awọn ohun kikọ meji: r и n, yiya sọtọ laini akọkọ ti ibeere, awọn akọle ati data.
  • <КОМАНДА> - ọkan ninu CONNECT, DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT, TRACE. Ẹrọ aṣawakiri yoo fi aṣẹ ranṣẹ si olupin wa GET, itumo "Firanṣẹ awọn akoonu inu faili naa."
  • <URI> - aṣọ awọn oluşewadi idamo. Fun apẹẹrẹ, ti URI = /index.html, lẹhinna alabara beere oju-iwe akọkọ ti aaye naa.
  • <ВЕРСИЯ HTTP> - ẹya ti Ilana HTTP ni ọna kika HTTP/X.Y. Awọn julọ commonly lo version loni ni HTTP/1.1.
  • <ЗАГОЛОВОК N> jẹ bata-iye bọtini ni ọna kika <КЛЮЧ>: <ЗНАЧЕНИЕ>, ranṣẹ si olupin fun itupalẹ siwaju sii.
  • <ДАННЫЕ> - data ti o nilo nipasẹ olupin lati ṣe iṣẹ naa. Nigbagbogbo o rọrun JSON tabi eyikeyi miiran kika.

Ọna idahun

<ВЕРСИЯ HTTP> <КОД СТАТУСА> <ОПИСАНИЕ СТАТУСА>CRLF
<ЗАГОЛОВОК 1>CRLF
<ЗАГОЛОВОК 2>CRLF
<ЗАГОЛОВОК N>CRLF CRLF
<ДАННЫЕ>

  • <КОД СТАТУСА> jẹ nọmba ti o nsoju abajade iṣẹ naa. Olupin wa yoo da ipo 200 pada nigbagbogbo (isẹ aṣeyọri).
  • <ОПИСАНИЕ СТАТУСА> - oniduro okun ti koodu ipo. Fun koodu ipo 200 eyi ni OK.
  • <ЗАГОЛОВОК N> - akọsori ti ọna kika kanna bi ninu ibeere naa. A yoo da awọn akọle pada Content-Length (iwọn faili) ati Content-Type: text/html (pada data iru).
  • <ДАННЫЕ> - data ti olumulo beere. Ninu ọran wa, eyi ni ọna si aworan ni HTML.

Ọna http_server.c (Olupin ti o tẹle nikan) pẹlu faili common.h, eyiti o ni awọn apẹrẹ iṣẹ wọnyi:

Ṣe afihan awọn apẹrẹ iṣẹ ni wọpọ.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);

Makiro iṣẹ-ṣiṣe tun ṣe apejuwe SAFE_CALL() ati awọn iṣẹ ti wa ni telẹ fail(). Makiro ṣe afiwe iye ti ikosile pẹlu aṣiṣe, ati pe ti ipo naa ba jẹ otitọ, pe iṣẹ naa fail():

#define SAFE_CALL(call, error)                                                 
    do {                                                                       
        if ((call) == error) {                                                   
            fail("%s", #call);                                                 
        }                                                                      
    } while (false)

Išẹ fail() tẹjade awọn ariyanjiyan ti o kọja si ebute (bii printf()) ati ki o fopin si eto pẹlu koodu 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);
}

Išẹ new_server() da pada apejuwe faili ti iho "olupin" ti a ṣẹda nipasẹ awọn ipe eto socket(), bind() и listen() ati agbara lati gba awọn asopọ ti nwọle ni ipo ti kii ṣe idinamọ.

Ṣe afihan iṣẹ tuntun_server ().

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;
}

  • Ṣe akiyesi pe iho ti wa ni ipilẹṣẹ ni ibẹrẹ ti kii ṣe idinamọ nipa lilo asia SOCK_NONBLOCKnitorinaa ninu iṣẹ naa on_accept() (ka siwaju) eto ipe accept() ko da awọn o tẹle ipaniyan.
  • ti o ba ti reuse_port dogba true, lẹhinna iṣẹ yii yoo tunto iho pẹlu aṣayan SO_REUSEPORT nipasẹ setsockopt()lati lo ibudo kanna ni agbegbe olona-asapo (wo apakan "Olupin-asapo olupin").

Iṣẹlẹ Handler on_accept() ti a npe ni lẹhin ti awọn OS ipilẹṣẹ iṣẹlẹ EPOLLIN, ninu apere yi afipamo pe awọn titun asopọ le ti wa ni gba. on_accept() gba asopọ tuntun kan, yipada si ipo ti kii ṣe idinamọ ati forukọsilẹ pẹlu oluṣakoso iṣẹlẹ on_recv() ninu ohun I/O riakito.

Ṣe afihan iṣẹ 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);
}

Iṣẹlẹ Handler on_recv() ti a npe ni lẹhin ti awọn OS ipilẹṣẹ iṣẹlẹ EPOLLIN, ninu apere yi afipamo pe awọn asopọ ti forukọsilẹ on_accept(), setan lati gba data.

on_recv() ka data lati asopọ titi ti ibeere HTTP ti gba patapata, lẹhinna o forukọsilẹ oluṣakoso kan on_send() lati fi esi HTTP ranṣẹ. Ti alabara ba fọ asopọ naa, iho naa ti kọ silẹ ati pipade ni lilo close().

Ṣafihan iṣẹ lori_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);
    }
}

Iṣẹlẹ Handler on_send() ti a npe ni lẹhin ti awọn OS ipilẹṣẹ iṣẹlẹ EPOLLOUT, afipamo pe asopọ ti forukọsilẹ on_recv(), setan lati fi data. Iṣẹ yii nfi esi HTTP ranṣẹ ti o ni HTML pẹlu aworan si alabara lẹhinna yi oluṣakoso iṣẹlẹ pada si on_recv().

Ṣe afihan iṣẹ 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);
}

Ati nikẹhin, ninu faili naa http_server.c, ni iṣẹ main() a ṣẹda I / O riakito lilo reactor_new(), ṣẹda iho olupin ati forukọsilẹ, bẹrẹ riakito nipa lilo reactor_run() fun iṣẹju kan gangan, ati lẹhinna a tu awọn orisun silẹ ati jade kuro ni eto naa.

Ṣe afihan 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);
}

Jẹ ki a ṣayẹwo pe ohun gbogbo n ṣiṣẹ bi o ti ṣe yẹ. Iṣakojọpọ (chmod a+x compile.sh && ./compile.sh ninu root ise agbese) ki o si ṣe ifilọlẹ olupin ti ara ẹni, ṣii http://127.0.0.1:18470 ninu ẹrọ aṣawakiri ati wo ohun ti a nireti:

Full-ifihan igboro-C I/O riakito

Iwọn iṣẹ ṣiṣe

Ṣe afihan awọn pato ọkọ ayọkẹlẹ mi

$ 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

Jẹ ki a wọn iṣẹ ṣiṣe ti olupin ala-ẹyọkan. Jẹ ki a ṣii awọn ebute meji: ninu ọkan a yoo ṣiṣẹ ./http_server, ni oriṣiriṣi - wrk. Lẹhin iṣẹju kan, awọn iṣiro atẹle yoo han ni ebute keji:

$ 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

Olupin alasopo ẹyọkan ni anfani lati ṣiṣẹ lori awọn ibeere miliọnu 11 fun iṣẹju kan ti o bẹrẹ lati awọn asopọ 100. Kii ṣe abajade buburu, ṣugbọn ṣe o le ni ilọsiwaju bi?

Multithreaded olupin

Gẹgẹbi a ti sọ loke, riakito I / O le ṣẹda ni awọn okun lọtọ, nitorinaa lilo gbogbo awọn ohun kohun Sipiyu. Jẹ ki a fi ọna yii si iṣe:

Ṣe afihan 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);
    }
}

Bayi gbogbo okun ti ara rẹ riakito:

static Reactor *reactor;
#pragma omp threadprivate(reactor)

Jọwọ ṣe akiyesi pe ariyanjiyan iṣẹ new_server() alagbawi true. Eyi tumọ si pe a yan aṣayan si iho olupin naa SO_REUSEPORTlati lo ni agbegbe olona-asapo. O le ka diẹ ẹ sii nibi.

Ṣiṣe keji

Bayi jẹ ki a wọn iṣẹ ṣiṣe ti olupin ti o ni asapo pupọ:

$ 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

Nọmba awọn ibeere ti a ṣe ilana ni iṣẹju 1 pọ si nipasẹ ~ 3.28 igba! Ṣugbọn a jẹ ~ XNUMX milionu kukuru ti nọmba iyipo, nitorinaa jẹ ki a gbiyanju lati ṣatunṣe iyẹn.

Ni akọkọ jẹ ki a wo awọn iṣiro ti ipilẹṣẹ lofinda:

$ 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

Lilo Sipiyu Affinity, akopo pẹlu -march=native, PGO, ilosoke ninu awọn nọmba ti deba kaṣe, alekun MAX_EVENTS ati lilo EPOLLET ko fun a significant ilosoke ninu išẹ. Ṣugbọn kini yoo ṣẹlẹ ti o ba pọ si nọmba awọn asopọ nigbakanna?

Awọn iṣiro fun awọn asopọ igbakana 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

Abajade ti o fẹ ni a gba, ati pẹlu rẹ ayaworan ti o nifẹ ti n fihan igbẹkẹle ti nọmba awọn ibeere ti a ṣe ilana ni iṣẹju 1 lori nọmba awọn asopọ:

Full-ifihan igboro-C I/O riakito

A rii pe lẹhin tọkọtaya ọgọọgọrun awọn isopọ, nọmba awọn ibeere ti a ṣe ilana fun awọn olupin mejeeji ṣubu silẹ (ninu ẹya-ara-asapo pupọ eyi jẹ akiyesi diẹ sii). Ṣe eyi ni ibatan si imuse akopọ Linux TCP/IP? Lero ọfẹ lati kọ awọn arosinu rẹ nipa ihuwasi yiyaya ati awọn iṣapeye fun asapo-pupọ ati awọn aṣayan asapo ẹyọkan ninu awọn asọye.

Bawo ni woye ninu awọn asọye, idanwo iṣẹ ṣiṣe ko ṣe afihan ihuwasi ti riakito I / O labẹ awọn ẹru gidi, nitori pe nigbagbogbo olupin n ṣepọ pẹlu ibi ipamọ data, awọn iwejade ti n jade, lo cryptography pẹlu TLS ati be be lo, bi awọn kan abajade ti awọn fifuye di ti kii-aṣọ (ìmúdàgba). Awọn idanwo papọ pẹlu awọn paati ẹnikẹta yoo ṣee ṣe ninu nkan naa nipa olutọpa I/O.

Alailanfani ti mo ti / O riakito

O nilo lati ni oye pe reactor I/O kii ṣe laisi awọn apadabọ rẹ, eyun:

  • Lilo ohun I/O riakito ni a olona-asapo ayika ni itumo diẹ soro, nitori iwọ yoo ni lati ṣakoso awọn ṣiṣan pẹlu ọwọ.
  • Iwa ṣe fihan pe ni ọpọlọpọ igba fifuye kii ṣe aṣọ-aṣọ, eyiti o le ja si gige igi okun kan nigba ti omiiran n ṣiṣẹ pẹlu iṣẹ.
  • Ti oluṣakoso iṣẹlẹ kan ba di okun kan, lẹhinna yiyan eto funrararẹ yoo tun dina, eyiti o le ja si awọn idun lile-lati-wa.

Yanju awọn iṣoro wọnyi I/O proactor, eyiti o nigbagbogbo ni oluṣeto ti o pin kaakiri fifuye si adagun awọn okun, ati tun ni API ti o rọrun diẹ sii. A yoo sọrọ nipa rẹ nigbamii, ninu nkan mi miiran.

ipari

Eyi ni ibiti irin-ajo wa lati imọ-jinlẹ taara sinu eefi profaili ti de opin.

O yẹ ki o ko gbe lori eyi, nitori ọpọlọpọ awọn ọna iwunilori deede miiran wa si kikọ sọfitiwia nẹtiwọọki pẹlu awọn ipele oriṣiriṣi ti irọrun ati iyara. O yanilenu, ninu ero mi, awọn ọna asopọ ni a fun ni isalẹ.

Но новых встреч!

Awon ise agbese

Kini ohun miiran yẹ ki o Mo ka?

orisun: www.habr.com

Fi ọrọìwòye kun