Рубрики
Uncategorized

Следуйте исходному коду DBB – Redis 4 – что означает сервер, управляемый событиями?

Автор оригинала: David Wong.

Как мы все знаем, сервер Redis является драйвером событий. Что означает управление событиями для Redis? Как реализовать управление событиями в исходном коде? Сегодня давайте познакомимся с драйверами событий серверов Redis.

Для Redis сервер должен обрабатывать следующие два типа событий:

  • Событие файла Сервер Redis соединяется с клиентом через сокет, а событие файла-это абстракция работы сокета сервером. Связь между сервером и клиентом приведет к возникновению соответствующих событий файлов, и сервер выполнит ряд операций сетевого взаимодействия, отслеживая и обрабатывая эти события.
  • Событие времени Некоторые операции на серверах Redis (например, функции Cron сервера) должны выполняться в определенный момент времени, и события времени являются абстракцией таких операций синхронизации сервером.

Далее давайте рассмотрим следующие события.

1 событие файла

Redis разработала собственный процессор сетевых событий на основе режима реактора, который называется Обработчик событий файлов :

  • Обработчики событий файлов используют мультиплексоры ввода-вывода для одновременного мониторинга нескольких сокетов и связывают различные обработчики событий для сокетов в соответствии с задачами, выполняемыми в настоящее время сокетами.
  • Когда контролируемый сокет будет готов к выполнению таких операций, как принятие, чтение, запись и закрытие, произойдут события файла, соответствующие операции, а затем обработчик событий файла вызовет обработчик событий, связанный перед сокетом, для обработки этих событий.

Несмотря на то, что файловый процессор работает в режиме одного потока, он отслеживает несколько сокетов через мультиплексор ввода-вывода, который не только реализует высокопроизводительную модель сетевой связи, но также хорошо соединяется с другими модулями, работающими в одном потоке на сервере Redis, тем самым сохраняя дизайн одного потока в Redis лаконичным.

Состав обработчика событий файлов 1.1

На рисунке 1 показаны четыре компонента процессора файловых событий:

  • Разъем;
  • Программа мультиплексирования ввода-вывода;
  • Диспетчер событий файлов;
  • Обработчик событий;

События файлов-это абстракции сокетов. Событие файла происходит всякий раз, когда сокет готов к выполнению таких операций, как принятие, запись, чтение, закрытие и т.д. Поскольку сервер обычно подключает несколько сокетов, одновременно может происходить несколько событий с файлами.

Мультиплексоры ввода-вывода отслеживают несколько сокетов и распределяют те сокеты, которые генерируют события, диспетчеру событий файлов.

Хотя одновременно может происходить несколько событий с файлами, мультиплексоры ввода-вывода всегда помещают все сокеты, генерирующие события, в очередь, а затем передают через очередь в Порядок и синхронизацию Таким образом, каждый сокет передается диспетчеру событий файлов. Когда событие, сгенерированное предыдущим сокетом, обработано (то есть сокет выполняется обработчиком событий, связанным с событием), мультиплексор ввода-вывода продолжит передачу следующего сокета диспетчеру событий файлов. Как показано на рисунке 2:

Диспетчер событий файлов получает сокеты от мультиплексора ввода-вывода и вызывает соответствующий обработчик событий в соответствии с типом событий, генерируемых сокетами.

Сервер связывает различные обработчики событий для сокетов, которые выполняют разные задачи. Эти процессоры, по сути, являются функцией . Они определяют действия, которые сервер должен выполнять при возникновении события.

1.2 Реализация программы мультиплексирования ввода-вывода

Все функции мультиплексора ввода-вывода Redis являются общими благодаря упаковке Select, epoll, newport и kqueue Реализованы эти библиотеки мультиплексирования ввода-вывода. Каждая библиотека мультиплексирования ввода-вывода соответствует одному файлу в исходном коде Redis, такому как ae_select.c, ae_poll.c, ae_kqueue.c и т. Д.

Поскольку Redis реализует один и тот же API для каждой библиотеки мультиплексоров ввода-вывода, базовая реализация мультиплексора ввода-вывода является взаимозаменяемой, как показано на рисунке 3:

Исходный код реализации мультиплексора ввода-вывода Redis #включить Макрос определяет соответствующие правила, и программа ** автоматически выбирает библиотеку мультиплексирования ввода-вывода с наивысшей производительностью в системе во время компиляции в качестве базовой реализации программы мультиплексирования ввода-вывода Redis, что гарантирует совместимость и высокую производительность Redis на различных платформах. Соответствующий исходный код выглядит следующим образом:

/* Include the best multiplexing layer supported by this system.
 * The following should be ordered by performances, descending. */
#ifdef HAVE_EVPORT
#include "ae_evport.c"
#else
    #ifdef HAVE_EPOLL
    #include "ae_epoll.c"
    #else
        #ifdef HAVE_KQUEUE
        #include "ae_kqueue.c"
        #else
        #include "ae_select.c"
        #endif
    #endif
#endif

1.3 Типы событий

Мультиплексор ввода-вывода может отслеживать несколько сокетов ae.h/AE_READABLE и ae.h/AE_WRITABLE События, эти два типа событий и операций с сокетами имеют следующую соответствующую взаимосвязь:

  • Когда сокет сервера становится читаемым, сокет генерирует событие AE_READABLE . Читаемость сокета здесь относится к событию AE_READABLE, которое происходит, когда клиент выполняет операции записи или закрытия сокета или когда появляется новый приемлемый сокет (клиент выполняет операции подключения к прослушивающему сокету сервера).
  • Когда сокет сервера становится доступным для записи, сокет генерирует событие AE_WRITABLE.

Мультиплексор ввода-вывода позволяет серверу одновременно прослушивать события AR_READABLE сокета и события AE_WRITABLE. Если сокет генерирует два события одновременно, диспетчер файлов сначала обработает событие AE_READABLE, а затем событие AE_WRITABLE. Проще говоря, если сокет можно прочитать и записать, сервер сначала прочитает сокет, а затем запишет сокет.

1.4 Обработчик Событий Файлов

Redis записывает несколько процессоров для событий файлов, которые используются для реализации различных требований к сетевому взаимодействию. Например:

  • Чтобы ответить каждому клиенту, подключающемуся к серверу, серверу необходимо Подключить процессор ответов для прослушивания сокетов
  • Чтобы получать запросы команд от клиентов, серверу необходимо Запросить процессор для команды, связанной с клиентским сокетом
  • Чтобы вернуть клиенту результат выполнения команды, серверу необходимо Вернуть процессор для команды Ассоциации клиентских сокетов
  • Когда главный и подчиненный серверы реплицируются, Как главному, так и подчиненному серверам требуется соответствующий процессор репликации

Среди этих обработчиков событий наиболее часто используются серверы Процессор ответов на подключение, Процессор запросов команд и Процессор ответов на команды для связи с Клиентом

1) Подключите процессор ответов

сеть.c/принять Tcp-Дескриптор Эта функция представляет собой процессор ответа на подключение Redis, который используется для ответа клиентам, подключающимся к серверам для прослушивания сокетов. Реализация выглядит следующим образом sys/socket.h/accept Упаковка функций.

Когда сервер Redis инициализирован, программа связывает процессор ответа на соединение с событием AE_READABLE сокета прослушивания сервера. Соответствующий исходный код выглядит следующим образом

# server.c/initServer
...
/* Create an event handler for accepting new connections in TCP and Unix
 * domain sockets. */
for (j = 0; j < server.ipfd_count; j++) {
    if (aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE,
        acceptTcpHandler,NULL) == AE_ERR)
        {
            serverPanic(
                "Unrecoverable error creating server.ipfd file event.");
        }
}
...

Когда есть клиент sys/scoket.h/connect , когда сервер функционального подключения прослушивает сокет, сокет генерирует событие AE_READABLE, запускает процессор ответа на соединение для выполнения и выполняет соответствующую операцию ответа на сокет. Как показано на рисунке 4:

2) Процессор командных запросов networking.c/readQueryFromClient Функция является процессором командных запросов Redis, который считывает содержимое командного запроса, отправленного клиентом из сокета, и реализует его следующим образом unistd.h/read Упаковка функций.

Когда клиент успешно подключается к серверу через процессор ответов на подключение, сервер связывает событие AE_READABLE клиентского сокета с процессором запросов команд.( networking.c/acceptCommonHandler Функция.

Когда клиент отправляет запрос команды на сервер, сокет генерирует событие AR_READABLE, запускает процессор запросов команд для выполнения и выполняет соответствующую операцию чтения сокета, как показано на рисунке 5:

В течение всего процесса подключения клиента к серверу сервер всегда запрашивает у процессора команду ассоциации событий AE_READABLE клиентского сокета.

3) Процессор ответов на команды networking.c/sendreplytoclient Функция-это процессор ответов на команды Redis, который отвечает за возврат ответа на команды, полученного сервером после выполнения команды клиенту через сокет.

Когда у сервера есть ответ на команду для отправки клиенту, сервер связывает событие AE_WRITABLE клиентского сокета с обработчиком ответов на команды.( networking.c/handleClientsWithPendingWrites Функция.

Когда клиент будет готов получить ответ на команду от сервера, будет сгенерировано событие AE_WRITABLE, которое запускает процессор ответов на команды для выполнения и выполнения соответствующей операции записи сокета. Как показано на рисунке 6:

Когда ответ на команду будет отправлен, сервер отменит корреляцию события AE_WRITABLE между обработчиком ответа на команду и клиентским сокетом. Соответствующий исходный код выглядит следующим образом:

# networking.c/writeToClient
...
if (!clientHasPendingReplies(c)) {
    c->sentlen = 0;
    # Buffer Buffer Command Reply Sent, Delete Socket and Event Association
    if (handler_installed) aeDeleteFileEvent(server.el,c->fd,AE_WRITABLE);

    /* Close connection after entire reply has been sent. */
    if (c->flags & CLIENT_CLOSE_AFTER_REPLY) {
        freeClient(c);
        return C_ERR;
    }
}
...

1.5 События Подключения Клиент-Сервер

Ранее у нас было общее представление о процессе соединения между клиентом и сервером в форме отладки. Теперь, с точки зрения событий файлов, мы отслеживаем весь процесс подключения и отправки команд между клиентом Redis и сервером, чтобы увидеть, какие события произойдут в процессе и как эти события обрабатываются.

Давайте сначала рассмотрим процесс установления соединения между клиентом и сервером.

  1. Запустите наш сервер Redis (127.0.0.1-8379). После успешного запуска отслеживается событие AE_READABLE сокета сервера (127.0.0.1-8379), которое соответствует подключению процессора ответов. ( server.c/initServer() )。
  2. Подключитесь к серверу с помощью redis -cli. Это означает, что серверный сокет (127.0.0.1-8379) сгенерирует событие AR_READABLE, которое запускает выполнение процессора ответа на соединение.( networking.c/acceptTcpHandler() )。
  3. Ответ на запрос на подключение клиента, создайте клиентский сокет, сохраните информацию о состоянии клиента и обработайте событие с возможностью чтения и запрос команды клиентского сокета( networking.c/acceptCommonHandler () ), чтобы сервер мог получать запросы команд от клиента.

На данный момент клиент успешно установил соединение с сервером. В описанном выше процессе мы все еще можем отлаживать с помощью GDB, чтобы увидеть процесс выполнения функции. Конкретный процесс отладки выглядит следующим образом:

gdb ./src/redis-server
(gdb) B acceptCommonHandler # Set breakpoints for acceptCommonHandler functions
(gdb) R redis-conf -- port 8379#Start the server

Откройте другое окно и используйте redis-cli для подключения к серверу: redis-cli -p 8379

Вернувшись в окно сервера, мы увидим, что вошли в режим отладки GDB, введите: в стек Вы можете увидеть информацию о стеке, показанную на рисунке 6.

Теперь давайте взглянем на выполнение заказа:

  1. Клиент отправляет запрос команды на сервер. Клиентский сокет генерирует событие AE_READABLE, запускает readQueryFromClient для выполнения и считывает содержимое команды клиента.
  2. В соответствии с содержанием команды, отправленной клиентом, соответствующие значения атрибутов значений, такие как argc и argv клиента, форматируются.
  3. Найдите соответствующую функцию в соответствии с именем команды. server.c/команда процесса() в Команда поиска Вызов функции;
  4. Функция, связанная с именем команды, выполняется для получения возвращаемого результата, и генерируется клиентский сокет. server.c/processCommad() в вызов Вызовы функций.
  5. Верните ответ на команду и удалите связь между клиентским сокетом и событием AE_WRITABLE. network.c/writeToClient() Функция.

На рисунке 7 показана информация о стеке процесса выполнения команды. На рисунке 8 показана информация о стеке процесса ответа на команду.

В последнем разделе мы вместе узнали об инциденте с документом. Далее, давайте еще раз познакомимся с событием времени.

2 События Времени

Время повторного просмотра можно разделить на следующие две категории:

  • Время синхронизации Пусть программа выполняется через указанное время. Например, пусть программы выполняются один раз через 60 миллисекунд текущего времени.
  • Периодические события Позволяют программе выполняться каждое указанное время. Например, пусть программа N выполняется каждые 30 миллисекунд.

Для событий времени источник структуры данных (ae.h/aeTimeEvent):

/* Time event structure */
typedef struct aeTimeEvent {
    long long id; /* time event identifier. */
    long when_sec; /* seconds */
    long when_ms; /* milliseconds */
    aeTimeProc *timeProc;
    aeEventFinalizerProc *finalizerProc;
    void *clientData;
    struct aeTimeEvent *next;
} aeTimeEvent;

Описание основных атрибутов:

  • Идентификатор: глобальный уникальный идентификатор, созданный сервером для события времени. Идентификационный номер увеличивается по порядку от малого к большому.
  • When_sec: метка времени UNIX в секундах, которая записывает время прибытия событий времени.
  • When_ms: метка времени UNIX в миллисекундах, которая записывает время прибытия событий времени.
  • Процесс времени: обработчик событий времени, соответствующий функции. Когда происходит событие времени, сервер вызывает соответствующий процессор для обработки события.

Функция, которую выполняет процесс события времени, – это ae.c/события времени процесса()

Кроме того, различение типов событий времени зависит от возвращаемого значения обработчика событий времени:

  • Возвращаемое значение ae.h/AE_НО БОЛЬШЕ Для События синхронизации . Событие будет удалено, как только оно поступит.
  • Возвращаемое значение не является ae.h/AE_НО БОЛЬШЕ Для Периодических событий . При поступлении периодического события времени сервер обновляет атрибуты when_sec и when_ms события времени в соответствии со значением, возвращаемым обработчиком событий, так что событие возвращается снова через определенный период времени и выполняется таким образом. Например, если обработчик события таймера возвращает 30, то сервер должен обновить событие времени и разрешить ему снова выполняться через 30 миллисекунд.

2.1 Функция Серркрона от временных событий

Непрерывные серверы Redis должны регулярно проверять и корректировать свои собственные ресурсы и статус, чтобы обеспечить долгосрочную и стабильную работу сервисов. Эти периодические операции выполняются сервером.Функции c/server Cron() отвечают за выполнение. Основные операции включают в себя:

  • Обновите различные статистические данные сервера. Например, время, использование памяти, использование базы данных и т.д.
  • Очистите пары ключ-значение с истекшим сроком действия в базе данных.
  • Закройте и очистите несостоявшихся клиентов.
  • Попробуйте операции сохранения AOF или RDB.
  • Если сервер является главным сервером, периодически синхронизируйте подчиненный сервер.
  • Если вы находитесь в режиме кластера, в кластере проводятся периодические тесты синхронизации и подключения.

Сервер Redis запускает функцию Cron сервера в виде периодических событий. Во время работы сервера Cron сервера выполняется каждый раз, пока сервер не закроется.

Количество выполнений см. в документе redis.conf Document hz Параметры. По умолчанию он выполняется 10 раз в секунду.

3 Планирование и выполнение мероприятий

Поскольку на сервере есть как события файла, так и события времени, сервер должен запланировать эти два события, чтобы решить, когда обрабатывать события файла, когда обрабатывать события времени и сколько времени на их обработку.

События планируются и выполняются следующим образом ae.c/aeProcessEvents() Функции несут ответственность. Исходный код выглядит следующим образом:

int aeProcessEvents(aeEventLoop *eventLoop, int flags)
{
    int processed = 0, numevents;
    /* Nothing to do? return ASAP */
    if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return 0;

    /* Firstly, it judges whether there are file events that need to be monitored. If there are file events that need to be monitored, they are obtained by IO multiplexer.
     * Ready file events, as to whether or not IO multiplexer waits and how long it waits, are determined by the nearest time event occurring.
     * If eventLoop - > maxfd == - 1 indicates that there are no file events that need to be monitored, but time events must exist (serverCron ()),
     * If the AE_DONT_WAIT flag is not set at this time, IO multiplexing is invoked at this time. The purpose is not to monitor whether the file event is ready.
     * Rather, it lets threads sleep until the most recent time event occurs (similar to the sleep function in unix).
     * The purpose of this dormancy operation is to avoid unnecessary waste of resources caused by unordered lists of time events that threads are constantly traversing.*/
    if (eventLoop->maxfd != -1 ||
        ((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) {
        int j;
        aeTimeEvent *shortest = NULL;
        struct timeval tv, *tvp;

        /* Look for the closest time event to the current time. The difference between the time of the event and the current time is the time that IO multiplexer should wait for.*/
        if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT))
            shortest = aeSearchNearestTimer(eventLoop);
        if (shortest) {
            long now_sec, now_ms;

            //Create timeval structure
            aeGetTime(&now_sec, &now_ms);
            tvp = &tv

            /* How many milliseconds we need to wait for the next
             * time event to fire? */
            long long ms =
                (shortest->when_sec - now_sec)*1000 +
                shortest->when_ms - now_ms;

            /* If the difference of time is greater than 0, it means that the time of the time event has not arrived, then wait for the corresponding time.
             * If the time interval is less than 0, it means that the time event has arrived, and if not
             * When the file event is ready, the IO multiplexer should be returned immediately to avoid it.
             * Delays in processing time events*/
            if (ms > 0) {
                tvp->tv_sec = ms/1000;
                tvp->tv_usec = (ms % 1000)*1000;
            } else {
                tvp->tv_sec = 0;
                tvp->tv_usec = 0;
            }
        } else {
            /* If we have to check for events but need to return
             * ASAP because of AE_DONT_WAIT we need to set the timeout
             * to zero */
            if (flags & AE_DONT_WAIT) {
                tv.tv_sec = tv.tv_usec = 0;
                tvp = &tv
            } else {
                /* Otherwise we can block */
                tvp = NULL; /* wait forever */
            }
        }

        // Blocking and wait for file events to occur. The maximum blocking event is determined by the timeval structure.
        numevents = aeApiPoll(eventLoop, tvp);
        for (j = 0; j < numevents; j++) {
            // Handling all generated file events
            aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];
            int mask = eventLoop->fired[j].mask;
            int fd = eventLoop->fired[j].fd;
            int fired = 0; /* Number of events fired for current fd. */
            int invert = fe->mask & AE_BARRIER;

            if (!invert && fe->mask & mask & AE_READABLE) {
                fe->rfileProc(eventLoop,fd,fe->clientData,mask);
                fired++;
            }

            /* Fire the writable event. */
            if (fe->mask & mask & AE_WRITABLE) {
                if (!fired || fe->wfileProc != fe->rfileProc) {
                    fe->wfileProc(eventLoop,fd,fe->clientData,mask);
                    fired++;
                }
            }

            /* If we have to invert the call, fire the readable event now
             * after the writable one. */
            if (invert && fe->mask & mask & AE_READABLE) {
                if (!fired || fe->wfileProc != fe->rfileProc) {
                    fe->rfileProc(eventLoop,fd,fe->clientData,mask);
                    fired++;
                }
            }

            processed++;
        }
    }
    /* Check time events */
    if (flags & AE_TIME_EVENTS)
        // Handling all arrived time events
        processed += processTimeEvents(eventLoop);

    return processed; /* return the number of processed file/time events */
}

возьмите aeProcessEvents Функции, помещенные в цикл, вместе с функциями инициализации и очистки, составляют основную функцию сервера Redis. server.c/main() 。 Вот псевдокод для основной функции:

def main():
    // Initialization Server
    init_server();
    // Processing events until the server closes
    while server_is_not_shutdown():
        aeProcessEvents();
    // Server shutdown to perform cleanup
    clear_server()

С точки зрения обработки событий рабочий процесс сервера Radius может быть обобщен блок-схемой 1:

Ниже приведены правила планирования и выполнения событий:

  1. Максимальное событие блокировки функции aeApiPoll определяется событием времени, время прибытия которого ближе всего к текущему времени. Этот метод позволяет избежать частого опроса событий времени сервером, а также гарантирует, что функция aeApiPoll не будет блокировать слишком долго.
  2. Поскольку события файла происходят случайным образом, если после ожидания и обработки события файла в любое время не произойдет никакого события, сервер будет ждать и снова обрабатывать событие файла. При непрерывном выполнении событий файла время будет постепенно приближаться ко времени прибытия, заданному событиями времени, и в конечном итоге прибудет, после чего сервер сможет начать обработку событий времени прибытия.
  3. Обрабатываются как файловые, так и временные события Синхронизация, Порядок, Атом Наземное выполнение. Сервер не прерывает обработку событий посередине и не упреждает события. Поэтому, будь то обработчик событий файлов или обработчик событий времени, они борются за минимизацию событий блокировки программы и добровольно отказываются от выполнения при необходимости, тем самым уменьшая вероятность голода событий. Например, когда процессор ответа на команду записывает ответ на команду в клиентский сокет, если количество записанных байтов превышает заданную константу, процессор ответа на команду активно завершит цикл записи и сохранит оставшиеся данные для следующего раза. Кроме того, события времени также помещают очень трудоемкие операции сохранения в подпроцессы или подпроцессы для выполнения.
  4. Поскольку события времени выполняются после события документа и между событиями нет упреждающих событий, фактическое время обработки событий времени обычно немного позже, чем заданное событиями времени.

резюме

  1. Сервер Redis является драйвером событий, и события, обрабатываемые сервером, делятся на Событие времени и Файловые события две категории.
  2. События файла-это абстракции операций сокета. ** Каждый раз, когда сокет становится приемлемым, доступным для записи или чтения, происходит соответствующее событие файла.
  3. События файла классифицируются на события AE_READABLE (события чтения) и события AE_WRITABLE (события записи).
  4. Временные события делятся на временные события и периодические события. События синхронизации выполняются только один раз в указанное время, в то время как периодические события выполняются каждые два указанных времени.
  5. Как правило, серверы выполняют только функцию Cron сервера, которая является периодическим событием времени.
  6. Существует взаимосвязь между событиями времени и событиями файлов. Сервер обрабатывает эти два события по очереди, и в процессе обработки событий нет никаких исключений.