Choyatsira chathunthu chopanda-C I/O

Choyatsira chathunthu chopanda-C I/O

Mau oyamba

I/O riyakitala (single threaded chochitika chozungulira) ndi njira yolembera mapulogalamu olemetsa kwambiri, omwe amagwiritsidwa ntchito pamayankho ambiri otchuka:

M'nkhaniyi, tiwona ins and outs of the I / O reactor ndi momwe zimagwirira ntchito, lembani kukhazikitsa muzitsulo zosachepera 200, ndikupanga njira yosavuta ya seva ya HTTP pa zopempha za 40 miliyoni / min.

Maulosi

  • Nkhaniyi inalembedwa kuti ithandize kumvetsetsa momwe makina a I / O amagwirira ntchito, choncho kumvetsetsa kuopsa kwake pogwiritsira ntchito.
  • Kudziwa zoyambira ndikofunikira kuti mumvetsetse nkhaniyi. C chinenero ndi zina zambiri pakukula kwa ma network application.
  • Ma code onse amalembedwa m'chinenero cha C motsatira (Chenjezo: PDF yayitali) ku C11 standard kwa Linux komanso kupezeka pa GitHub.

Nchifukwa chiyani izi zili zofunika?

Ndi kutchuka kwa intaneti, ma seva a pa intaneti anayamba kufunikira kugwirizanitsa maulendo ambiri nthawi imodzi, choncho njira ziwiri zinayesedwa: kutsekereza I / O pa chiwerengero chachikulu cha ulusi wa OS ndi osatseka I / O kuphatikiza ndi dongosolo lodziwitsa zochitika, lomwe limatchedwanso "system selector" (epoll/ku/Mtengo wa IOCP/ etc).

Njira yoyamba idaphatikizapo kupanga ulusi watsopano wa OS pamalumikizidwe aliwonse omwe akubwera. Zoyipa zake ndizosakhazikika bwino: makina ogwiritsira ntchito ayenera kugwiritsa ntchito zambiri kusintha kwa nkhani и mafoni adongosolo. Ndi ntchito zodula ndipo zimatha kubweretsa kusowa kwa RAM yaulere yokhala ndi maulumikizidwe ambiri.

Mawonekedwe osinthidwa amawunikira chiwerengero chokhazikika cha ulusi (ulusi dziwe), potero kuteteza dongosolo kuchotsa mimba, koma pa nthawi yomweyo kuyambitsa vuto latsopano: ngati ulusi dziwe panopa otsekedwa ndi ntchito kuwerenga kwautali, ndiye sockets ena amene angathe kale kulandira deta sangathe chita chomwecho.

Njira yachiwiri imagwiritsa ntchito dongosolo zidziwitso zochitika (system selector) yoperekedwa ndi OS. Nkhaniyi ikufotokoza za mtundu wodziwika bwino wa osankha makina, kutengera zidziwitso (zochitika, zidziwitso) za kukonzekera kwa ntchito za I/O, osati zidziwitso zakukwaniritsidwa kwawo. Chitsanzo chosavuta chakugwiritsa ntchito kwake chitha kuyimiridwa ndi chithunzi chotsatirachi:

Choyatsira chathunthu chopanda-C I/O

Kusiyana pakati pa njirazi ndi motere:

  • Kuletsa ntchito za I/O kuyimitsa wosuta kuyenda mpakampaka Os ali bwino defragments obwera IP mapaketi kupita kumtsinje (TCP, kulandira zidziwitso) kapena sipadzakhala malo okwanira muzosunga zolembera zamkati kuti mutumize kudzera NIC (kutumiza deta).
  • Chosankha chadongosolo popita nthawi imadziwitsa pulogalamu yomwe OS kale mapaketi a IP a defragmented (TCP, kulandila kwa data) kapena malo okwanira muzolemba zamkati kale kupezeka (kutumiza deta).

Kuti tifotokoze mwachidule, kusunga ulusi wa OS pa I / O iliyonse ndikuwononga mphamvu yamakompyuta, chifukwa zenizeni, ulusiwo sukugwira ntchito yothandiza (chifukwa chake mawuwa amatanthauza "kuwononga" "kusokoneza mapulogalamu"). Wosankha dongosolo amathetsa vutoli, kulola pulogalamu ya ogwiritsa ntchito kugwiritsa ntchito zida za CPU mwachuma kwambiri.

Mtundu wa riyakitala wa I/O

The I/O reactor imakhala ngati wosanjikiza pakati pa chosankha chadongosolo ndi code ya ogwiritsa. Mfundo ya ntchito yake ikufotokozedwa ndi chithunzi chotsatirachi:

Choyatsira chathunthu chopanda-C I/O

  • Ndiroleni ndikukumbutseni kuti chochitika ndi chidziwitso kuti socket inayake imatha kuchita ntchito yosatsekereza ya I / O.
  • Wothandizira zochitika ndi ntchito yotchedwa I / O reactor pamene chochitika chalandiridwa, chomwe chimagwira ntchito yosatseketsa I / O.

Ndikofunika kuzindikira kuti riyakitala ya I / O ndi tanthawuzo la ulusi umodzi, koma palibe chomwe chimalepheretsa lingalirolo kuti ligwiritsidwe ntchito mu malo okhala ndi ulusi wambiri pa chiŵerengero cha 1 ulusi: 1 reactor, potero amabwezeretsanso ma CPU cores.

Реализация

Tiyika mawonekedwe agulu mu fayilo reactor.h, ndi kukhazikitsa - mu reactor.c. reactor.h izikhala ndi zolengeza izi:

Onetsani zolengeza mu 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);

Kapangidwe ka riyakitala ya I/O imakhala ndi fayilo yofotokozera wosankha epoll и matebulo a hashi GHashTable, yomwe imayika socket iliyonse CallbackData (mapangidwe a wosamalira zochitika ndi mkangano wogwiritsa ntchito).

Onetsani Reactor ndi CallbackData

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

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

Chonde dziwani kuti tapangitsa luso logwira mtundu wosakwanira malinga ndi index. MU reactor.h timalengeza dongosolo reactorndi reactor.c timatanthauzira, potero timalepheretsa wogwiritsa ntchito kusintha magawo ake. Ichi ndi chimodzi mwa machitidwe kubisa deta, zomwe zimagwirizana bwino ndi C semantics.

Ntchito reactor_register, reactor_deregister и reactor_reregister sinthani mndandanda wazokonda ndi osamalira zochitika muzosankha zamakina ndi tebulo la hashi.

Onetsani ntchito zolembetsa

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

Pambuyo pa riyakitala ya I / O idasokoneza chochitikacho ndi wofotokozera fd, imayitana woyendetsa zochitika wofananira, kumene amadutsa fd, pang'ono mask zochitika zopangidwa ndi cholozera cha ogwiritsa ntchito void.

Onetsani ntchito ya 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;
}

Kufotokozera mwachidule, mndandanda wa mafoni ogwiritsira ntchito mu code yogwiritsira ntchito utenga mawonekedwe awa:

Choyatsira chathunthu chopanda-C I/O

Seva yokhala ndi ulusi umodzi

Kuti tiyese choyatsira cha I / O pansi pa katundu wambiri, tidzalemba seva yosavuta ya HTTP yomwe imayankha pempho lililonse ndi chithunzi.

Kufotokozera mwachangu kwa protocol ya HTTP

HTTP - iyi ndiye protocol ntchito mlingo, yomwe imagwiritsidwa ntchito makamaka pakulumikizana ndi msakatuli wa seva.

HTTP itha kugwiritsidwa ntchito mosavuta transport ndondomeko TCP, kutumiza ndi kulandira mauthenga mumtundu wotchulidwa kufotokoza.

Fomu Yofunsira

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

  • CRLF ndi mndandanda wa zilembo ziwiri: r и n, kulekanitsa mzere woyamba wa pempho, mitu ndi deta.
  • <КОМАНДА> -mmodzi mwa CONNECT, DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT, TRACE. Msakatuli adzatumiza lamulo ku seva yathu GET, kutanthauza "Nditumizireni zomwe zili mufayiloyo."
  • <URI> - chizindikiritso cha zida zofananira. Mwachitsanzo, ngati URI = /index.html, ndiye kasitomala amapempha tsamba lalikulu la malowa.
  • <ВЕРСИЯ HTTP> - mtundu wa HTTP protocol mu mawonekedwe HTTP/X.Y. Chomwe chimagwiritsidwa ntchito kwambiri masiku ano ndi HTTP/1.1.
  • <ЗАГОЛОВОК N> ndi mtengo wamtengo wapatali awiri mumpangidwe <КЛЮЧ>: <ЗНАЧЕНИЕ>, yotumizidwa ku seva kuti ifufuzenso.
  • <ДАННЫЕ> - data yofunikira ndi seva kuti igwire ntchitoyi. Nthawi zambiri zimakhala zosavuta JSON kapena mtundu wina uliwonse.

Mayankho Format

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

  • <КОД СТАТУСА> ndi nambala yomwe ikuyimira zotsatira za ntchito. Seva yathu nthawi zonse imabweza mawonekedwe 200 (ntchito yopambana).
  • <ОПИСАНИЕ СТАТУСА> - chiwonetsero chazingwe cha code code. Kwa code code 200 iyi ndi OK.
  • <ЗАГОЛОВОК N> - mutu wamtundu womwewo wa pempho. Tidzabwezeranso maudindo Content-Length (kukula kwa fayilo) ndi Content-Type: text/html (mtundu wa data wobwerera).
  • <ДАННЫЕ> - zomwe zafunsidwa ndi wogwiritsa ntchito. Kwa ife, iyi ndi njira yopita ku chithunzi mu HTML.

file http_server.c (seva yokhala ndi ulusi umodzi) imaphatikizapo fayilo common.h, yomwe ili ndi ma prototypes otsatirawa:

Onetsani ntchito zofananira zofanana.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 yogwira ntchito ikufotokozedwanso SAFE_CALL() ndipo ntchitoyo imafotokozedwa fail(). The macro amafanizira mtengo wa mawuwo ndi cholakwika, ndipo ngati mkhalidwewo ndi wowona, imayitanitsa ntchitoyi fail():

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

ntchito fail() amasindikiza mfundo zomwe zadutsa ku terminal (monga printf()) ndikuyimitsa pulogalamuyo ndi 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);
}

ntchito new_server() imabweretsanso fayilo yofotokozera za socket ya "server" yopangidwa ndi mafoni amtundu socket(), bind() и listen() ndi wokhoza kuvomereza maulumikizidwe obwera m'njira yosatsekereza.

Onetsani new_server () ntchito

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

  • Zindikirani kuti socket idapangidwa poyambira mosatsekereza pogwiritsa ntchito mbendera SOCK_NONBLOCKkuti mu ntchito on_accept() (werengani zambiri) kuyitana kwadongosolo accept() sanayimitse kuphedwa kwa ulusi.
  • ngati reuse_port ndizofanana ndi true, ndiye ntchitoyi idzakonza socket ndi mwayi SO_REUSEPORT kudzera setsockopt()kugwiritsa ntchito doko lomwelo pamalo okhala ndi ulusi wambiri (onani gawo la "Multi-threaded server").

Wothandizira Zochitika on_accept() kuyitanidwa pambuyo pa OS kupanga chochitika EPOLLIN, pamenepa kutanthauza kuti kugwirizana kwatsopano kungavomerezedwe. on_accept() amavomereza kulumikizidwa kwatsopano, kuyisintha kukhala yosatsekereza ndikulembetsa ndi wosamalira zochitika on_recv() mu riyakitala ya I/O.

Onetsani pa_accept() ntchito

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

Wothandizira Zochitika on_recv() kuyitanidwa pambuyo pa OS kupanga chochitika EPOLLIN, mu nkhani iyi kutanthauza kuti kugwirizana analembetsa on_accept(), okonzeka kulandira deta.

on_recv() amawerenga deta kuchokera ku kugwirizana mpaka pempho la HTTP litalandiridwa kwathunthu, ndiye limalembetsa wothandizira on_send() kutumiza yankho la HTTP. Ngati kasitomala aphwanya kulumikizana, socket imachotsedwa ndikutsekedwa pogwiritsa ntchito close().

Onetsani ntchito pa_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);
    }
}

Wothandizira Zochitika on_send() kuyitanidwa pambuyo pa OS kupanga chochitika EPOLLOUT, kutanthauza kuti kugwirizana analembetsa on_recv(), okonzeka kutumiza deta. Ntchitoyi imatumiza yankho la HTTP lomwe lili ndi HTML ndi chithunzi kwa kasitomala ndikusintha chowongolera kuti chibwerere on_recv().

Onetsani pa_send () ntchito

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

Ndipo potsiriza, mu fayilo http_server.c, mu ntchito main() timapanga choyatsira cha I/O pogwiritsa ntchito reactor_new(), pangani soketi ya seva ndikulembetsa, yambitsani makinawo pogwiritsa ntchito reactor_run() kwa mphindi imodzi yokha, ndiyeno timamasula zothandizira ndikutuluka pulogalamuyo.

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

Tiyeni tiwone ngati zonse zikuyenda momwe timayembekezera. Kupanga (chmod a+x compile.sh && ./compile.sh muzu wa polojekiti) ndikuyambitsa seva yodzilemba yokha, tsegulani http://127.0.0.1:18470 mu msakatuli ndikuwona zomwe timayembekezera:

Choyatsira chathunthu chopanda-C I/O

Muyeso wa magwiridwe antchito

Onetsani zagalimoto yanga

$ 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

Tiyeni tiwone momwe seva yamtundu umodzi imagwirira ntchito. Tiyeni titsegule ma terminals awiri: mu imodzi tidzathamanga ./http_server, m'malo osiyanasiyana - mkokomo. Pambuyo pa mphindi imodzi, ziwerengero zotsatirazi ziwonetsedwa mu terminal yachiwiri:

$ 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

Seva yathu yokhala ndi ulusi umodzi idakwanitsa kuchita zopempha zoposa 11 miliyoni pamphindi imodzi kuchokera ku malumikizidwe 100. Osati zotsatira zoipa, koma kodi izo zikhoza kusintha?

Multithreaded seva

Monga tafotokozera pamwambapa, choyatsira cha I / O chikhoza kupangidwa mu ulusi wosiyana, pogwiritsa ntchito ma CPU onse. Tiyeni tigwiritse ntchito njira iyi:

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

Tsopano ulusi uliwonse ali ndi zake choyatsira:

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

Chonde dziwani kuti mkangano wantchito new_server() oyimira true. Izi zikutanthauza kuti timapereka mwayi ku socket ya seva SO_REUSEPORTkuzigwiritsa ntchito m'malo okhala ndi ulusi wambiri. Mutha kuwerenga zambiri apa.

Kuthamanga kwachiwiri

Tsopano tiyeni tiwone momwe seva yamitundu yambiri imagwirira ntchito:

$ 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

Chiwerengero cha zopempha zomwe zasinthidwa mu mphindi imodzi chawonjezeka ndi ~ nthawi 1! Koma tinali ochepa ~ 3.28 miliyoni okha pa nambala yozungulira, ndiye tiyeni tiyesetse kukonza.

Choyamba tiyeni tione ziwerengero zopangidwa wangwiro:

$ 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

Kugwiritsa ntchito CPU Affinity, kupanga ndi -march=native, PGO, kuwonjezeka kwa chiwerengero cha kugunda posungira, wonjezani MAX_EVENTS ndi kugwiritsa EPOLLET sichinapereke kuwonjezeka kwakukulu kwa ntchito. Koma chimachitika ndi chiyani ngati muwonjezera kuchuluka kwa maulumikizidwe munthawi imodzi?

Ziwerengero zamalumikizidwe 352 munthawi imodzi:

$ 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

Chotsatira chomwe chinkafunidwa chinapezedwa, ndipo ndi chithunzi chosangalatsa chomwe chikuwonetsa kudalira kuchuluka kwa zopempha zomwe zasinthidwa mu miniti imodzi pa kuchuluka kwa maulumikizidwe:

Choyatsira chathunthu chopanda-C I/O

Tikuwona kuti pambuyo polumikizana mazana angapo, kuchuluka kwa zopempha zomwe zasinthidwa kwa ma seva onsewa kumatsika kwambiri (mumitundu yamitundu yambiri izi zimawonekera kwambiri). Kodi izi zikugwirizana ndi kukhazikitsa kwa Linux TCP/IP? Khalani omasuka kulemba malingaliro anu pamachitidwe awa a graph ndi kukhathamiritsa kwamitundu yambiri ndi ulusi umodzi mu ndemanga.

Kodi adazindikira m'mawu, kuyesa kwa magwiridwe antchitowa sikuwonetsa machitidwe a I / O reactor pansi pa katundu weniweni, chifukwa pafupifupi nthawi zonse seva imalumikizana ndi database, imatulutsa zipika, imagwiritsa ntchito cryptography ndi TLS etc., chifukwa chake katunduyo amakhala wopanda yunifolomu (zamphamvu). Mayesero pamodzi ndi zigawo za chipani chachitatu adzachitidwa m'nkhani yonena za I/O proactor.

Kuipa kwa I/O riyakitala

Muyenera kumvetsetsa kuti riyakitala ya I / O ilibe zovuta zake, zomwe ndi:

  • Kugwiritsa ntchito chojambulira cha I / O m'malo okhala ndi ulusi wambiri kumakhala kovuta, chifukwa muyenera kuyang'anira pamanja zoyenda.
  • Zoyeserera zikuwonetsa kuti nthawi zambiri katunduyo amakhala wopanda yunifolomu, zomwe zingayambitse kudula ulusi umodzi pomwe wina ali wotanganidwa ndi ntchito.
  • Ngati wosamalira zochitika m'modzi atsekereza ulusi, ndiye kuti wosankhayo amatsekereza, zomwe zingayambitse zovuta kupeza.

Amathetsa mavutowa Wosewera wa I/O, yomwe nthawi zambiri imakhala ndi ndondomeko yomwe imagawa mofanana katunduyo ku dziwe la ulusi, komanso imakhala ndi API yabwino. Tidzakambirana pambuyo pake, m'nkhani yanga ina.

Pomaliza

Apa ndipamene ulendo wathu kuchokera ku chiphunzitso molunjika ku profil exhaust wafika kumapeto.

Simuyenera kukhazikika pa izi, chifukwa palinso njira zina zambiri zosangalatsa zolembera mapulogalamu apaintaneti omwe ali ndi mwayi wosiyanasiyana komanso kuthamanga. Zosangalatsa, mwa lingaliro langa, maulalo amaperekedwa pansipa.

Zikomo kwambiri!

Ntchito zosangalatsa

Ndiwerengenso chiyani?

Source: www.habr.com

Kuwonjezera ndemanga