реализация epoll в netty

Netty

предисловие

В java функция мультиплексирования ввода-вывода передается через nio.SelectorПри условии, что jdk будет загружать разные реализации через spi под разными операционными системами, например под macosKQueueSelectorProvider,KQueueSelectorProviderНижний уровень использует KQueue для выполнения мультиплексирования ввода-вывода; версия после Linux 2.6EPollSelectorProvider,EPollSelectorProviderНижний слой использует epoll. Хотя сама jdk обеспечивает реализацию селектора epoll, netty по-прежнему реализует свою собственную версию epoll.Ответ разработчика Netty на StackOverflow, по двум основным причинам:

  1. Поддержка дополнительных параметров сокета, таких как TCP_CORK и SO_REUSEPORT.
  2. Используется режим с запуском по фронту (ET).

Затем взгляните на то, как netty реализовали собственную версию epoll о логике.

Общее введение

Как пользоваться

В netty, если вам нужно использовать собственную реализацию epoll netty, вам нужно добавить в проект зависимость netty-transport-native-epoll, а затем добавитьNioEvnetLoop,NioSocketChannel,NioServerSocketChannelПросто замените его на класс, начинающийся с Epoll. конкретная ссылкаUsing the Linux native transport

Отличие от нативной реализации jdk

В общем, будь то версия jdk или netty, она напрямую вызывает epoll из linux для обеспечения мультиплексирования ввода-вывода.Есть два основных различия между реализацией epoll для netty и jdk:

  1. Используется запуск по фронту (см. мойдругая статья)
  2. Используйте eventfd и timerfd для реализации управления пробуждением и тайм-аутом, в то время как реализация jdk использует механизм тайм-аута, который поставляется с pipe и epoll.

Выполнение

инициализация

EpollEventLoop在初始化时会创建三个fd:epollFd、eventFd、timerFd。 epollFd用于进一步调用epoll_wait,而另外两个fd的作用前面已经提到了。 Кроме,EpollEventLoopТакже поддерживает внутреннююselectStrategyПеременная,selectStrategyОн используется для определения поведения в текущем цикле.Содержание не сложное, и детали не будут расширяться.

EpollEventLoopтакже поддерживатьEpollEventArrayТип событий объекта, events — второй параметр вызова epoll, указывающий набор интересующих дескрипторов, эта переменная будет передана в нативный метод.

такжеEpollEventLoopесть еще одинIntObjectMap<AbstractEpollChannel>Поле typechannels, Channel представляет все объекты, зарегистрированные в данный момент EventLoop, где ключ является каналом, соответствующим FD (файловый дескриптор), epoll принимается в качестве параметров, а результаты возвращаются в виде целочисленного представления файлового дескриптора, значение представляет собой объект Channel , после чтения и записи канала будет выполняться поиск (Примечание: IntObjectMap, используемый здесь, представляет собой собственную реализацию коллекции netty, основная цель которой — повысить производительность, установленную, когда собственный тип используется в качестве ключа или значения, аналогичная реализация также существует hppc, FastUtil и т.д.).

Регистрация заинтересованных контактов

EpollEventLoopизdoRegisterЛогика регистрации соединения реализована в методе, который заключается в вызовеEpollEventLoopизaddметод:

    void add(AbstractEpollChannel ch) throws IOException {
        assert inEventLoop();
        int fd = ch.socket.intValue();
        Native.epollCtlAdd(epollFd.intValue(), fd, ch.flags);
        AbstractEpollChannel old = channels.put(fd, ch);
    }

Звонок можно посмотреть здесьNative.epolCtlAdd, как видно из названия, нижний слой — это вызов метода epoll_ctl, а затем параметр op — EPOLL_ADD.

цикл событий

EpollEventLoopпредметrunметод, вrunОсновной цикл метода пройдет первымselectStrategyРешите, будет ли выполняться операция epollWait или epollBusyWait. Разница между epollWait и epollBusyWait заключается в том, что первый вычисляет подходящее время ожидания, а затем вызывает epoll_wait один раз до тех пор, пока дескриптор не будет готов или не истечет время ожидания, а второй будет циклически вызывать epoll_wait и устанавливать время ожидания равным 0 (то есть немедленно возвращаться) до тех пор, пока подключение готово.

Результаты, полученные с помощью ePollWait или EPOLLBUSYWAIT, будут сохранены в Events, поэтому будет вызван следующийprocessReadyОбрабатывать каждый готовый fd в событиях. Процесс обработки заключается в том, чтобы найти соответствующий канал из каналов в соответствии с fd, а затем выполнить такие операции, как чтение и запись.Подробное чтение и запись не будут представлены.

тайм-аут и пробуждение

Как упоминалось ранее, eventfd и timerfd используются в логике epoll netty для реализации управления пробуждением и тайм-аутом.evnetfd и timerfd были добавлены в ядро, начиная с версии Linux 2.6.22, и их основная функция заключается в обеспечении механизма уведомления о событиях. eventfd может создать файловый дескриптор, в который можно передавать целые числа без знака, которые можно использовать в качестве управляющей информации. timerfd также создает дескриптор файла, в котором могут быть прочитаны события таймера, и timerfd может поддерживать уровни наносекунд. Поскольку и eventfd, и timerfd основаны на дескрипторах, они более совместимы с такими API, как select/poll/epoll.

EpollEventLoopВо время инициализации сначала будут созданы epollfd, eventfd и timerfd, а затем и eventfd, и timerfd будут добавлены в очередь прослушивания epoll. eventfd используется для поддержки пробуждения, когда требуется пробуждениеEpollEventLoop, напишите число в eventfd, тогда eventfd станет доступным для чтения, а epoll вернется вовремя. timerfd используется для управления тайм-аутом epoll.Когда требуется тайм-аут, на timerfd устанавливается временной интервал.По истечении периода тайм-аута timerfd станет доступным для чтения, и epoll вернется вовремя. Есть две причины использовать timerfd в качестве контроля тайм-аута вместо использования тайм-аута, поставляемого с epoll: во-первых, с помощью timerfd можно унифицированно обрабатывать события тайм-аута и события ввода-вывода, а во-вторых, timerfd поддерживает более высокую точность времени тайм-аута. .

Кстати, в нативной реализации jdk пробуждение достигается через пайп,SelectorВнутреннее обслуживание трубы, при инициализации чтения трубы, чтобы присоединиться к очереди прослушивания EPOLL, необходимо проснуться, когда он пишет данные записи конца трубы, поэтому EPOLL будет быстро возвращена. Если он будет найден после возврата Dipoll-читаемой трубы передачи данных, труба завершит чтение.

разное

Как упоминалось в предыдущей статье, если при регистрации fd в epoll используется запуск по фронту, рекомендуемый способ его использования — установить fd в неблокирующий режим и прочитать все готовые данные, когда дескриптор будет готов (встреча с EAGAIN), в противном случае может возникнуть ситуация, когда уведомление о готовности больше не будет поступать.

В реализации netty epoll все сокеты регистрируются в режиме ET, а eventfd и timerfd немного отличаются. В предыдущих версиях netty 4.1.38.Final eventfd использовал LT вместо ET при регистрации в epolfd.Если eventfd был доступен для чтения каждый раз, когда processReady вызывал для него read. timerfd использует ET при регистрации в epolfd, но если timerfd доступен для чтения каждый раз, когда processReady, он также вызовет чтение для него один раз. В версии 4.1.38.Final и eventfd, и timerfd используют ET, но эти два fd не читаются в методе processReady. Для eventfd чтение будет вызываться каждый раз, когда запись возвращает EAGAIN, потому что eventfd может хранить только целое число внутри, поэтому, когда EAGAIN появляется в записи, это означает, что в данный момент есть данные для чтения. Для timerfd чтение будет вызываться только один раз, когда истечет время ожидания epollWait, и чтение не будет вызываться для timerfd в других случаях. Поскольку в реализации netty время ожидания timerfd сбрасывается каждый раз, когда выполняется epoll_wait, и каждый раз, когда время ожидания timerfd обновляется, timerfd снова становится нечитаемым, поэтому нет необходимости вызывать для него read.