Hōʻike piha piha i ka pahu-C I/O reactor

Hōʻike piha piha i ka pahu-C I/O reactor

Hōʻike

I/O reactor (hoʻokahi kaula hanana hanana) he kumu hoʻohālike no ke kākau ʻana i nā lako polokalamu haʻahaʻa kiʻekiʻe, i hoʻohana ʻia i nā hāʻina kaulana he nui:

Ma kēia ʻatikala, e nānā mākou i ka ins a me waho o kahi reactor I/O a pehea e hana ai, e kākau i kahi hoʻokō ma lalo o 200 laina o ke code, a hana i kahi kaʻina kikowaena HTTP maʻalahi ma luna o 40 miliona mau noi/min.

Kauwehe

  • Ua kākau ʻia ka ʻatikala e kōkua i ka hoʻomaopopo ʻana i ka hana ʻana o ka reactor I/O, a no laila e hoʻomaopopo i nā pilikia ke hoʻohana ʻia.
  • Pono ka ʻike o nā kumu no ka hoʻomaopopo ʻana i ka ʻatikala. ʻōlelo C a me kekahi ʻike i ka hoʻomohala ʻana i nā noi pūnaewele.
  • Ua kākau ʻia nā code āpau ma ka ʻōlelo C e like me (akahele: lōʻihi PDF) i ka maʻamau C11 no Linux a loaʻa ma GitHub.

No ke aha e pono ai kēia?

Me ka ulu ʻana o ka kaulana o ka Pūnaewele, ua hoʻomaka nā kikowaena pūnaewele e mālama i ka nui o nā pilina i ka manawa like, a no laila ua hoʻāʻo ʻia ʻelua ala: ke kāohi ʻana i ka I/O ma ka nui o nā loina OS a me ka pale ʻole I/O i hui pū me. he ʻōnaehana hoʻolaha hanana, i kapa ʻia ʻo "mea koho ʻōnaehana" (epoll/kqueue/IOCP/etc).

ʻO ke ala mua e pili ana i ka hana ʻana i kahi thread OS hou no kēlā me kēia pili e komo mai. ʻO kona hemahema ka scalability maikaʻi ʻole: pono e hoʻokō ka ʻōnaehana hana i nā mea he nui nā hoʻololi pōʻaiapili и kelepona ʻōnaehana. He mau hana koʻikoʻi lākou a hiki ke alakaʻi i ka nele o ka RAM manuahi me ka nui o nā pilina.

Hōʻike ka mana i hoʻololi ʻia helu paʻa o nā kaula (waiwai thread), no laila e pale ana i ka ʻōnaehana mai ka hoʻopau ʻana i ka hoʻokō ʻana, akā i ka manawa like e hoʻopuka ana i kahi pilikia hou: inā paʻa ʻia kahi kolamu thread e nā hana heluhelu lōʻihi, a laila ʻaʻole hiki i nā kumu ʻē aʻe ke loaʻa ka ʻikepili. e hana pela.

Hoʻohana ka ʻaoʻao ʻelua ʻōnaehana hoʻolaha hanana (mea koho pūnaewele) i hāʻawi ʻia e ka OS. Kūkākūkā kēia ʻatikala i ke ʻano maʻamau o ka mea koho ʻōnaehana, e pili ana i nā mākaʻikaʻi (nā hanana, nā hoʻolaha) e pili ana i ka mākaukau no nā hana I/O, ma mua o ka nā hoʻolaha e pili ana i ko lākou hoʻopau ʻana. Hiki ke hōʻike ʻia kahi hiʻohiʻona maʻalahi o kāna hoʻohana ʻana e ke kiʻi poloka aʻe:

Hōʻike piha piha i ka pahu-C I/O reactor

ʻO ka ʻokoʻa ma waena o kēia mau ala e like me kēia:

  • Kāohi i nā hana I/O hoʻopaneʻe kahe mea hoʻohana a hiki ia hiki i ka OS pono defragments e komo mai ana ʻO nā ʻeke IP i ke kahawai byte (TCP, ka loaʻa ʻana o ka ʻikepili) a i ʻole ʻaʻole lawa ka wahi i loaʻa i loko o nā pahu kākau kūloko no ka hoʻouna ʻana ma o NIC (hoʻouna ʻikepili).
  • Mea koho pūnaewele ua holo ʻoi aʻe ka manawa hōʻike i ka polokalamu i ka OS ua nā ʻeke IP defragmented (TCP, ka loaʻa ʻana o ka ʻikepili) a i ʻole ka nui o nā wahi i loko o nā pale kākau kūloko ua loaʻa (ka hoʻouna ʻana i ka ʻikepili).

No ka hōʻuluʻulu ʻana, ʻo ka mālama ʻana i kahi thread OS no kēlā me kēia I/O he mea ʻino ia o ka mana helu, no ka mea, ʻo ka ʻoiaʻiʻo, ʻaʻole e hana pono nā kaula (no laila ka huaʻōlelo. "hoʻopau polokalamu"). Hoʻoponopono ka mea koho ʻōnaehana i kēia pilikia, e ʻae ana i ka polokalamu mea hoʻohana e hoʻohana i nā kumuwaiwai CPU i ʻoi aku ka waiwai.

Ke kumu hoʻohālike I/O

Hana ka reactor I/O ma ke ʻano he papa ma waena o ka mea koho ʻōnaehana a me ka code mea hoʻohana. ʻO ke kumu o kāna hana i wehewehe ʻia e ke kiʻi poloka aʻe:

Hōʻike piha piha i ka pahu-C I/O reactor

  • E hoʻomanaʻo wau iā ʻoe he hanana kahi hōʻike e hiki ai i kahi kumu ke hana i kahi hana I/O ʻole paʻa ʻole.
  • ʻO ka mea lawelawe hanana kahi hana i kapa ʻia e ka reactor I/O i ka wā i loaʻa ai kahi hanana, a laila hana i kahi hana I/O pale ʻole.

He mea nui ia e hoʻomaopopo i ka I/O reactor ma ka wehewehe ʻana i hoʻokahi threaded, akā ʻaʻohe mea e kāpae i ka manaʻo mai ka hoʻohana ʻia ʻana i loko o kahi kaiapuni multi-threaded ma ka ratio o 1 thread: 1 reactor, a laila e hana hou i nā cores CPU āpau.

Ka hoʻokō

E kau mākou i ke kikowaena lehulehu i kahi faila reactor.h, a me ka hoʻokō - in reactor.c. reactor.h e komo i keia mau hoolaha:

Hōʻike i nā ʻōlelo hoʻolaha ma ka 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);

ʻO ka ʻōnaehana reactor I/O ka waihona wehewehe mea koho epoll и nā papa hash GHashTable, ka palapala ʻāina i kēlā me kēia kumu i CallbackData (ke ʻano o kahi mea hoʻoponopono hanana a me kahi hoʻopaʻapaʻa mea hoʻohana no ia).

Hōʻike i ka Reactor a me CallbackData

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

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

E ʻoluʻolu e hoʻomaopopo ua hiki iā mākou ke hoʻohana ʻano piha ʻole e like me ka papa kuhikuhi. IN reactor.h ke hai aku nei makou i ka hale reactor, a in reactor.c wehewehe mākou iā ia, no laila e pale aku i ka mea hoʻohana mai ka hoʻololi pono ʻana i kāna mau kahua. ʻO kēia kekahi o nā hiʻohiʻona huna ʻikepili, i kūpono i ka C semantics.

Nā hana reactor_register, reactor_deregister и reactor_reregister hōʻano hou i ka papa inoa o nā kumu hoihoi a me nā mea lawelawe hanana like i ka mea koho ʻōnaehana a me ka papa hash.

Hōʻike i nā hana hoʻopaʻa inoa

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

Ma hope o ka ʻae ʻana o ka reactor I/O i ka hanana me ka mea wehewehe fd, kahea ia i ka mea lawelawe hanana pili, kahi e hele ai fd, wahi huna huna hana ʻia nā hanana a me kahi kuhikuhi mea hoʻohana i void.

Hōʻike i ka hana 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;
}

No ka hōʻuluʻulu ʻana, e lawe ke kaulahao o nā kelepona hana i ka code mea hoʻohana i kēia ʻano:

Hōʻike piha piha i ka pahu-C I/O reactor

Hoʻokahi kikowaena kaula

No ka hoʻāʻo ʻana i ka reactor I/O ma lalo o ka haʻahaʻa kiʻekiʻe, e kākau mākou i kahi kikowaena pūnaewele HTTP maʻalahi e pane i kekahi noi me kahi kiʻi.

ʻO kahi kuhikuhi wikiwiki i ka protocol HTTP

HTTP - ʻo kēia ka protocol pae noi, hoʻohana nui ʻia no ka launa pū ʻana o ka server-browser.

Hiki ke hoʻohana maʻalahi iā HTTP halihali Hola Pūnaewele TCP, ka hoʻouna ʻana a me ka loaʻa ʻana o nā memo ma kahi ʻano i kuhikuhi ʻia kikoʻī.

Palapala Noi

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

  • CRLF he kaʻina o nā huaʻōlelo ʻelua: r и n, hoʻokaʻawale i ka laina mua o ka noi, nā poʻo a me nā ʻikepili.
  • <КОМАНДА> - kekahi o CONNECT, DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT, TRACE. E hoʻouna ka polokalamu kele i kahi kauoha i kā mākou kikowaena GET, ʻo ia hoʻi "E hoʻouna mai iaʻu i nā mea o ka faila."
  • <URI> - mea hōʻike waiwai like ʻole. No ka laʻana, inā URI = /index.html, a laila noi ka mea kūʻai aku i ka ʻaoʻao nui o ka pūnaewele.
  • <ВЕРСИЯ HTTP> - ka mana o ka protocol HTTP ma ke ʻano HTTP/X.Y. ʻO ka mana maʻamau i hoʻohana ʻia i kēia lā HTTP/1.1.
  • <ЗАГОЛОВОК N> he hui waiwai kī ma ke ʻano <КЛЮЧ>: <ЗНАЧЕНИЕ>, hoʻouna ʻia i ke kikowaena no ka nānā hou ʻana.
  • <ДАННЫЕ> - ka ʻikepili i koi ʻia e ke kikowaena e hana i ka hana. He maʻalahi pinepine JSON a i ʻole kekahi ʻano ʻē aʻe.

Hōʻike pane

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

  • <КОД СТАТУСА> he helu e hōʻike ana i ka hopena o ka hana. E hoʻihoʻi mau kā mākou kikowaena i ke kūlana 200 (hana holomua).
  • <ОПИСАНИЕ СТАТУСА> — hōʻike string o ke code kūlana. No ke code kūlana 200 kēia OK.
  • <ЗАГОЛОВОК N> - ke poʻomanaʻo o ke ʻano like me ka noi. E hoʻihoʻi mākou i nā poʻo inoa Content-Length (ka nui waihona) a Content-Type: text/html (hoʻihoʻi ʻano ʻikepili).
  • <ДАННЫЕ> - ʻikepili i noi ʻia e ka mea hoʻohana. I kā mākou hihia, ʻo ia ke ala i ke kiʻi i loko HTML.

waihona http_server.c (hoʻokahi kikowaena pūnaewele) me ka faila common.h, i loaʻa nā prototypes hana penei:

Hōʻike i nā prototypes hana maʻamau.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);

Ua wehewehe ʻia ka macro hana SAFE_CALL() a ua wehewehe ʻia ka hana fail(). Hoʻohālikelike ka macro i ka waiwai o ka ʻōlelo me ka hewa, a inā he ʻoiaʻiʻo ke kūlana, kāhea i ka hana fail():

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

kuleana pili i fail() paʻi i nā hoʻopaʻapaʻa i hala i ka pahu (e like me printf()) a hoʻopau i ka papahana me ke code 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);
}

kuleana pili i new_server() hoʻihoʻi i ka wehewehe faila o ke kumu "server" i hana ʻia e nā kelepona ʻōnaehana socket(), bind() и listen() a hiki ke ʻae i nā pilina e komo mai ana ma ke ʻano hoʻopaʻa ʻole.

Hōʻike i ka hana new_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 hoʻomaopopo i ka hana mua ʻana o ke kumu ma ke ʻano non-blocking me ka hoʻohana ʻana i ka hae SOCK_NONBLOCKpela i ka hana on_accept() (heluhelu hou aku) kelepona ʻōnaehana accept() ʻaʻole i hoʻōki i ka hoʻokō ʻana i ke kaula.
  • ina reuse_port e like me true, a laila e hoʻonohonoho kēia hana i ke kumu me ke koho SO_REUSEPORT ma o setsockopt()e hoʻohana i ka awa like i loko o kahi kaiapuni multi-threaded (e nānā i ka ʻāpana "Multi-threaded server").

Luna Hana Hana on_accept() kāhea ʻia ma hope o ka hana ʻana o ka OS i kahi hanana EPOLLIN, i kēia hihia ke manaʻo nei e hiki ke ʻae ʻia ka pilina hou. on_accept() ʻae i kahi pilina hou, hoʻololi iā ia i ke ʻano hoʻopaʻa ʻole a hoʻopaʻa inoa me kahi mea mālama hanana on_recv() i loko o kahi reactor I/O.

Hōʻike i ka hana 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);
}

Luna Hana Hana on_recv() kāhea ʻia ma hope o ka hana ʻana o ka OS i kahi hanana EPOLLIN, i kēia hihia ke manaʻo nei ua hoʻopaʻa inoa ʻia ka pilina on_accept(), mākaukau e loaʻa ka ʻikepili.

on_recv() Heluhelu i ka ʻikepili mai ka pilina a hiki i ka loaʻa ʻana o ka noi HTTP, a laila hoʻopaʻa inoa i kahi mea lawelawe on_send() e hoʻouna i kahi pane HTTP. Inā haki ka mea kūʻai aku i ka pilina, kāpae ʻia ke kumu a pani ʻia me ka hoʻohana ʻana close().

Hōʻike i ka hana ma_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);
    }
}

Luna Hana Hana on_send() kāhea ʻia ma hope o ka hana ʻana o ka OS i kahi hanana EPOLLOUT, 'o ia ho'i ua ho'opa'a 'ia ka pilina on_recv(), mākaukau e hoʻouna i ka ʻikepili. Hoʻouna kēia hana i kahi pane HTTP i loaʻa HTML me kahi kiʻi i ka mea kūʻai aku a laila hoʻololi i ka mea hoʻoponopono hanana on_recv().

Hōʻike i ka hana 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);
}

A ʻo ka hope, i ka faila http_server.c, ma ka hana main() hana mākou i kahi reactor I/O me ka hoʻohana ʻana reactor_new(), hana i kahi kikowaena kikowaena a hoʻopaʻa inoa iā ia, e hoʻomaka i ka reactor me ka hoʻohana ʻana reactor_run() no hoʻokahi minuke, a laila hoʻokuʻu mākou i nā kumuwaiwai a haʻalele i ka papahana.

Hōʻike 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);
}

E nānā kāua e hana ana nā mea a pau e like me ka mea i manaʻo ʻia. ka houluulu ana (chmod a+x compile.sh && ./compile.sh i ke kumu o ka papahana) a hoʻomaka i ke kikowaena kākau ponoʻī, wehe http://127.0.0.1:18470 i ka polokalamu kele pūnaewele a ʻike i ka mea a mākou i manaʻo ai:

Hōʻike piha piha i ka pahu-C I/O reactor

Ana hana

Hōʻike i kaʻu mau kikoʻī kaʻa

$ 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

E ana kāua i ka hana o kahi kikowaena kaula hoʻokahi. E wehe mākou i ʻelua mau pahu: i hoʻokahi e holo mākou ./http_server, ma kahi ʻokoʻa - ʻōpala. Ma hope o hoʻokahi minuke, e hōʻike ʻia nā helu helu ma ka pahu lua:

$ 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

Ua hiki i kā mākou kikowaena pūnaewele hoʻokahi ke hana ma luna o 11 miliona mau noi i kēlā me kēia minuke mai 100 mau pilina. ʻAʻole he hopena maikaʻi ʻole, akā hiki ke hoʻomaikaʻi?

Mea hoʻohana multithreaded

E like me ka mea i ʻōlelo ʻia ma luna, hiki ke hana ʻia ka reactor I/O i nā ʻāpana ʻokoʻa, a laila e hoʻohana ana i nā cores CPU āpau. E hoʻokō kākou i kēia ʻano hana:

Hōʻike 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);
    }
}

I kēia manawa i kēlā me kēia pae nona iho reactor:

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

E ʻoluʻolu e hoʻomaopopo i ka hoʻopaʻapaʻa hana new_server() nā mea noi true. ʻO ia ke ʻano o ka hāʻawi ʻana i ke koho i ke kumu kikowaena SO_REUSEPORTe hoʻohana iā ia i loko o kahi kaiapuni multi-threaded. Hiki iā ʻoe ke heluhelu i nā kikoʻī hou aku maanei.

Holo lua

I kēia manawa e ana kāua i ka hana o kahi kikowaena multi-threaded:

$ 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

Ua hoʻonui ʻia ka nui o nā noi i hoʻopaʻa ʻia i ka minuke 1 e ~3.28 manawa! Akā ʻo mākou wale nō ~ XNUMX miliona pōkole o ka helu pōʻai, no laila e hoʻāʻo mākou e hoʻoponopono i kēlā.

E nānā mua kākou i nā ʻikepili i hana ʻia ʻala:

$ 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

Ke hoʻohana nei i ka CPU Affinity, hui pu me -march=native, PGO, he hoʻonui i ka helu o nā hits huna huna, hoonui MAX_EVENTS a hoʻohana EPOLLET ʻaʻole i hāʻawi i ka piʻi nui o ka hana. Akā he aha ka hopena inā hoʻonui ʻoe i ka helu o nā pilina like?

Heluhelu no 352 pili like:

$ 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

Loaʻa ka hopena i makemake ʻia, a me ia kahi pakuhi hoihoi e hōʻike ana i ka hilinaʻi o ka helu o nā noi i hoʻoponopono ʻia i 1 mau minuke ma ka helu o nā pilina:

Hōʻike piha piha i ka pahu-C I/O reactor

ʻIke mākou ma hope o ʻelua mau haneli pili, ua hāʻule nui ka helu o nā noi i hoʻoponopono ʻia no nā kikowaena ʻelua (ma ka mana multi-threaded ʻoi aku ka ʻike). Pili kēia i ka hoʻokō ʻana i ka hoʻokō Linux TCP/IP? E ʻoluʻolu e kākau i kāu mau manaʻo e pili ana i kēia ʻano o ka pakuhi a me nā manaʻo no nā koho multi-threaded a me hoʻokahi-threaded i nā manaʻo.

Pehea ʻike ʻia i nā manaʻo, ʻaʻole hōʻike kēia hōʻike hoʻokō i ka ʻano o ka reactor I/O ma lalo o nā haʻahaʻa maoli, no ka mea, ʻaneʻane e launa pū ke kikowaena me ka waihona, hoʻopuka i nā logs, hoʻohana i ka cryptography me TLS etc., ma muli o ka lilo ʻana o ka ukana i mea like ʻole (dynamic). E hana ʻia nā hoʻāʻo me nā ʻāpana ʻaoʻao ʻekolu ma ka ʻatikala e pili ana i ka proactor I/O.

Nā hemahema o ka reactor I/O

Pono ʻoe e hoʻomaopopo ʻaʻole ʻaʻohe hemahema o ka reactor I/O, ʻo ia hoʻi:

  • ʻOi aku ka paʻakikī o ka hoʻohana ʻana i kahi reactor I/O i loko o kahi kaiapuni multi-threaded, no ka mea pono ʻoe e hoʻokele lima i nā kahe.
  • Hōʻike ka hoʻomaʻamaʻa ʻana i ka hapa nui o ka ukana ʻaʻole like ʻole, hiki ke alakaʻi i kahi logging thread a paʻa kekahi i ka hana.
  • Inā hoʻopaʻa kekahi mea hoʻokele hanana i kahi kaula, a laila e poloka pū ka mea koho ʻōnaehana ponoʻī, hiki ke alakaʻi i nā pōpoki paʻakikī.

Hoʻoholo i kēia mau pilikia I/O proactor, i loaʻa pinepine i kahi mea hoʻonohonoho e puʻunaue like i ka ukana i kahi wai o nā kaula, a loaʻa pū kekahi API maʻalahi. E kamaʻilio mākou e pili ana iā ia ma hope, ma kaʻu ʻatikala ʻē aʻe.

hopena

ʻO kēia kahi i pau ai kā mākou huakaʻi mai ke kumumanaʻo i ka exhaust profiler.

ʻAʻole pono ʻoe e noʻonoʻo i kēia, no ka mea he nui nā ala ʻē aʻe e like me ka hoihoi i ke kākau ʻana i nā polokalamu pūnaewele me nā pae like ʻole o ka maʻalahi a me ka wikiwiki. ʻO ka hoihoi, i koʻu manaʻo, hāʻawi ʻia nā loulou ma lalo nei.

E ʻike koke ʻoe!

Nā papahana hoihoi

He aha hou aʻe kaʻu e heluhelu ai?

Source: www.habr.com

Pākuʻi i ka manaʻo hoʻopuka