netty java что это
Введение в Netty
Узнайте, как настроить небольшой сервер Netty и клиента на Java.
Введение в Netty
1. Введение
В этой статье мы собираемся взглянуть на Netty – асинхронную систему сетевых приложений, управляемых событиями.
Основной целью Netty является создание высокую производительность протокольных серверов на основе NIO (или, возможно, NIO.2) с разделением и свободным соединением компонентов сетевой и бизнес-логики. Он может реализовать широко известный протокол, такой как HTTP, или ваш собственный конкретный протокол.
2. Основные концепции
Netty — это не блокирующая структура. Это приводит к высокой пропускной способности по сравнению с блокированием IO. Понимание не блокируя IO имеет решающее значение для понимания основных компонентов Netty и их отношений.
2.1. Канал
Канал является основой Java NIO. Он представляет собой открытое соединение, способное выполнять io-операции, такие как чтение и письмо.
2.2. Будущее
Каждая операция IO на канал в Netty не блокируется.
Это означает, что каждая операция возвращается сразу после вызова. Существует Будущие интерфейс в стандартной библиотеке Java, но это не удобно для целей Netty – мы можем спросить только Будущие о завершении операции или о блокировке текущего потока до завершения операции.
Вот почему Netty имеет свои собственные ChannelFuture интерфейс . Мы можем передать обратный звонок ChannelFuture которые будут призваны к завершению операции.
2.3. События и обработчики
Netty использует парадигму приложения, движимую событиями, поэтому конвейер обработки данных — это цепочка событий, проходят через обработчиков. События и обработчики могут быть связаны с потоком входящих и исходящих данных. Входящие события могут быть следующими:
Исходящие события проще и, как правило, связаны с открытием/закрытием данных соединения и написания/промывки.
Кроме того, существует множество реализаций конкретных протоколов, таких, как HTTP, например, HttpRequestDecoder, HttpResponseEncoder, HttpObjectAggregator. Было бы хорошо, чтобы познакомиться с ними в Javadoc Нетти.
2.4. Кодеры и декодеры
Работая с сетевым протоколом, мы должны выполнять сериализацию данных и дезириализацию. Для этого Netty вводит специальные расширения ChannelInboundHandler для декодеры которые способны декодировать входящие данные. Базовый класс большинства декодеров ByteToMessageDecoder.
Для кодирования исходящих данных Netty имеет расширения ChannelOutboundHandler называется Кодеры. СообщениеToByteEncoder является основой для большинства кодера реализации . Мы можем преобразовать сообщение из последовательности byte в java-объект и наоборот с помощью кодеров и декодеров.
3. Пример серверного приложения
Давайте создадим проект, представляющий простой протокольный сервер, который получает запрос, выполняет расчет и отправляет ответ.
3.1. Зависимости
Прежде всего, мы должны обеспечить зависимость Netty в нашей пом.xml :
3.2. Модель данных
Класс данных запросов будет иметь следующую структуру:
Допустим, сервер получает запрос и возвращает intValue умножается на 2. Ответ будет иметь одно int значение:
3.3. Запрос декодера
Теперь нам нужно создать кодеры и декодеры для наших протокольных сообщений.
Мы должны убедиться, что мы получили полное сообщение перед и есть много способов сделать это.
Прежде всего, мы можем создать временную ByteBuf и придаток к нему все входящие байты, пока мы не получим необходимое количество байтов:
Пример, показанный выше, выглядит немного странно, но помогает нам понять, как работает Netty. Каждый метод нашего обработчика вызывается, когда происходит соответствующее событие. Таким образом, мы инициализируем буфер при добавлении обработчика, заполняем его данными о получении новых байтов и начинаем обрабатывать его, когда получаем достаточно данных.
Мы сознательно не использовали stringValue – расшифровка таким образом была бы неоправданно сложной. Именно поэтому Netty предоставляет полезные классы декодеров, которые являются реализациями ChannelInboundHandler : ByteToMessageDecoder и Воспроизведениедекодера.
Как мы уже отмечали выше, мы можем создать канал обработки трубопровода с Netty. Таким образом, мы можем поставить наш декодер в качестве первого обработчика и обработчик логики обработки может прийти после него.
Декодер для RequestData отображается следующим:
Идея этого декодера довольно проста. Он использует реализацию ByteBuf который бросает исключение, когда в буфере недостаточно данных для операции чтения.
Когда исключение поймано, буфер перематывается в начало, и декодер ждет новую порцию данных. Декодирование останавливается, когда из список не пуст после декодировать исполнение.
3.4. Ответ Encoder
Кроме того, расшифровка ЗапросДанные нам нужно закодировать сообщение. Эта операция проще, потому что у нас есть полные данные сообщения, когда происходит операция записи.
Мы можем писать данные Канал в нашем главном обработчике или мы можем отделить логику и создать обработчик, расширяющий СообщениеToByteEncoder который будет ловить писать ОтветДанные операция:
3.5. Обработка запросов
Так как мы провели расшифровку и кодирование в отдельных обработчиках, мы должны изменить наши ОбработкаХэндлер :
3.6. Сервер Bootstrap
Теперь давайте ставим все это вместе и запустить наш сервер:
Подробную информацию о классах, используемых в приведенном выше примере загрузки сервера, можно найти в их Javadoc. Самая интересная часть этой строки:
Здесь мы определяем входящих и исходящих обработчиков, которые будут обрабатывать запросы и выход в правильном порядке.
4. Клиентская заявка
Клиент должен выполнять обратное кодирование и расшифровку, поэтому мы должны иметь ЗапросDataEncoder и ОтветДанныйдекодер :
Кроме того, мы должны определить КлиентХэндлер который отправит запрос и получит ответ с сервера:
Теперь давайте bootstrap клиента:
Как мы видим, Есть много деталей, общих с сервера загрузки.
Теперь мы можем запустить основной метод клиента и взглянуть на выход консоли. Как и ожидалось, мы ОтветДанные с intValue равно 246.
5. Заключение
Русские Блоги
Основы Netty: что такое Netty?
Прежде чем мы начнем понимать, что такое Netty, давайте сначала рассмотрим его.Если нам нужно реализовать программу взаимодействия клиент-сервер, используя традиционное программирование ввода-вывода, как мы должны этого достичь?
IO программирование
Давайте упростим сценарий: клиент отправляет на сервер отметку времени «hello world» каждые две секунды, а сервер распечатывает ее после ее получения.
Для удобства демонстрации в следующем примере есть один класс для сервера и один для клиента. Скопируйте эти два класса в свою среду IDE и запустите их один за другим. IOServer.java с участием IOClient.java Эффект виден.
Ниже представлена реализация сервера в традиционном программировании ввода-вывода.
Сторона сервера сначала создала serverSocket Чтобы контролировать порт 8000, а затем создать поток, поток продолжает вызывать метод блокировки serversocket.accept(); Получите новое соединение, см. (1), когда будет получено новое соединение, создайте новый поток для каждого соединения, этот поток отвечает за чтение данных из соединения, см. (2), а затем читает данные Для режима потока байтов см. (3).
Ниже приведена реализация клиента в традиционном программировании ввода-вывода.
Код клиента относительно прост. После подключения к порту 8000 сервера мы каждые 2 секунды отправляем на сервер сообщение «hello world» с меткой времени.
Модель программирования ввода-вывода хорошо работает с меньшим количеством клиентов, но для предприятий с большим количеством клиентов может потребоваться автономный сервер для поддержки тысяч подключений. Модель ввода-вывода может не подходить, давайте проанализируем Вот в чем причина.
В приведенной выше демонстрации мы можем видеть из кода сервера, что в традиционной модели ввода-вывода каждое соединение должно поддерживаться потоком после его успешного создания. Каждый поток содержит цикл while, поэтому соединения 1w соответствуют 1w Thread, а затем бесконечный цикл while 1w, что приводит к следующим проблемам:
Ресурсы потоков ограничены: потоки являются очень ценными ресурсами в операционной системе. Большое количество потоков блокируется одновременно, что является очень серьезной тратой ресурсов, и операционная система не может позволить себе их использовать.
Эффективность переключения потоков невысока: количество отдельных ядер ЦП фиксировано, и операционная система часто выполняет переключение потоков после взрыва потока, и производительность приложения резко падает.
В дополнение к двум вышеупомянутым проблемам в программировании ввода-вывода мы видим, что чтение и запись данных основаны на потоках байтов, что неэффективно.
Чтобы решить эти три проблемы, JDK предложил NIO после версии 1.4.
NIO программирование
В Интернете также есть много статей о NIO. Я не собираюсь здесь анализировать подробно. Ниже кратко описывается, как NIO решает три вышеуказанные проблемы.
Ресурсы потоков ограничены
В модели программирования NIO новое соединение больше не создает новый поток, но может привязать это соединение непосредственно к фиксированному потоку, и затем этот поток отвечает за все чтение и запись этого соединения, затем Как он это сделал? Давайте использовать картинку для сравнения IO и NIO.
Как показано на рисунке выше, в модели ввода-вывода при установлении соединения будет создан поток, соответствующий циклу while. Целью цикла является постоянный мониторинг наличия данных в этом соединении. Чтение, в большинстве случаев, только небольшое количество соединений имеет данные для чтения в одно и то же время в соединениях 1w.Поэтому многие бесконечные циклы while теряются, потому что нет данных для чтения.
В модели NIO он превращает так много циклов while в бесконечный цикл, и этот бесконечный цикл управляется потоком, поэтому как он достигает потока, цикл while может отслеживать, есть ли данные в соединениях 1w Удобочитаемый? Это роль селектора в модели NIO. После установления соединения вместо создания цикла while для отслеживания того, доступны ли данные для чтения, он напрямую регистрирует соединение с селектором, а затем, проверяя селектор, Вы можете контролировать соединения с читаемыми данными в пакетном режиме, а затем читать данные.Ниже я приведу очень простой пример из жизни, чтобы проиллюстрировать разницу между IO и NIO.
В детском саду детям необходимо сходить в туалет, а дети еще слишком малы, чтобы спросить его, хочет ли он пойти в туалет, прежде чем он скажет вам. В детском саду 100 детей, есть два решения проблемы посещения туалета детьми:
У каждого ребенка есть учитель. Каждый учитель через определенные промежутки времени спрашивает ребенка, хочет ли он пойти в туалет. Если он хочет пойти, его отведут в туалет. 100 детям нужно 100 учителей, чтобы они спросили, и каждому ребенку нужен учитель, который приведет его в туалет. Выше это модель ввода-вывода, одно соединение соответствует одному потоку.
Все дети заслуживают одного учителя. Этот учитель спрашивает всех детей, хочет ли кто-нибудь ходить в туалет через определенные промежутки времени, а затем каждый момент водит всех детей, которые хотят в туалет, группами, в туалет. Это модель NIO. Все дети зарегистрированы у одного и того же учителя, что соответствует всем Соединения регистрируются в потоке, а затем опрашиваются партиями.
Это решение модели NIO для ограниченных ресурсов потоков. В фактическом процессе разработки мы будем открывать несколько потоков. Каждый поток управляет пакетом соединений. По сравнению с моделью ввода-вывода, один поток управляет одним соединением, которое потребляет много ресурсов потоков. сокращать
Неэффективное переключение потоков
Поскольку количество потоков в модели NIO значительно уменьшено, эффективность переключения потоков также значительно улучшена.
IO чтение и запись в байтах
Способ, которым NIO решает эту проблему, заключается в том, что чтение и запись данных больше не в байтах, а в байтовых блоках. В модели ввода-вывода данные каждый раз считываются побайтно с нижнего уровня операционной системы, в то время как NIO поддерживает буфер.Каждый раз, когда часть данных может быть прочитана из этого буфера, это похоже на тарелку вкусной еды. Бобы кладут перед вами, и вы используете палочки для еды, чтобы держать их одну за другой (по одной). Это, конечно, не так эффективно, как есть ложкой (по одной порции за раз).
Кратко поговорив о решении JDK NIO, давайте воспользуемся решением NIO для замены решения ввода-вывода. Давайте сначала взглянем. Если мы будем использовать собственный NIO JDK для реализации сервера, что нам делать?
Впереди повышенное энергопотребление: следующий код может вызвать у вас крайний дискомфорт. Если вы плохо себя чувствуете, пропустите
Я считаю, что большинство студентов, которые не контактировали с NIO, должны пропустить код и перейти к следующей строке: Оказывается, очень сложно реализовать простую программу взаимодействия на стороне сервера с использованием собственного API NIO JDK!
Это настолько сложно, что у меня нет терпения объяснить логику выполнения этого кода (шучу), давайте сначала объясним несколько основных идей против NIO
В модели NIO обычно два потока, каждый поток привязан к селектору опроса, в нашем примере serverSelector Отвечает за опрос новых подключений, clientSelector Отвечает за опрос соединения для чтения данных
clientSelector Обернутый циклом while, если в определенный момент есть несколько соединений с читаемыми данными, то передать clientSelector.select(1) Методы могут опрашиваться и обрабатываться партиями, см. (2)
Чтение и запись данных основаны на блоках памяти, см. (3)
Я не хочу говорить о других деталях, потому что это слишком сложно, и вам не нужно вдаваться в детали кода. Короче говоря, настоятельно не рекомендуется напрямую разрабатывать сеть на основе NIO JDK. Ниже приведены причины, по которым я резюмировал
Программирование JDK с помощью NIO требует понимания множества концепций, программирование является сложным, оно очень недружелюбно по отношению к введению NIO, модель программирования недружелюбна, а API ByteBuffer просто античеловеческий
Для программирования NIO более подходящая модель потоковой передачи может полностью раскрыть ее преимущества, но JDK не реализует ее для вас, вам нужно реализовать ее самостоятельно, даже простая распаковка настраиваемого протокола должна быть реализована самостоятельно
Нижний уровень NIO JDK реализован epoll, и критикуемая ошибка пустого вращения в этой реализации приведет к увеличению производительности ЦП на 100%.
После того, как проект станет огромным, самореализованный NIO подвержен различным ошибкам, а стоимость обслуживания высока.Я не могу гарантировать, что приведенный выше код не содержит ошибок.
Из-за этого мой клиентский код слишком ленив, чтобы писать его вам ==! Вы можете использовать его напрямую IOClient.java против NIOServer.java Общение
NIO от JDK похож на колючую розу. Несмотря на то, что она прекрасна и желанна, неправильное использование заставит вас почесать голову и почувствовать себя несчастным. Из-за этого родилась Нетти!
Netty программирование
Ниже приводится краткое изложение причин, по которым Netty не использует собственный NIO JDK.
Чтобы использовать NIO, который поставляется с JDK, вам нужно понимать слишком много концепций, программирование сложное, а ошибки вылетают случайно
Базовая модель ввода-вывода Netty может быть переключена по желанию, и все это требует лишь незначительных изменений, изменения параметров, а Netty может напрямую преобразовать модель NIO в модель ввода-вывода.
Собственная распаковка, распаковка, обнаружение аномалий и другие механизмы Netty позволяют вам оторваться от тяжелых деталей NIO, поэтому вам нужно заботиться только о бизнес-логике.
Netty решает множество ошибок JDK, включая пустой опрос
На нижнем уровне Netty произведено множество небольших оптимизаций потоков и селекторов, а хорошо спроектированная модель потоковой обработки реактора обеспечивает очень эффективную параллельную обработку.
Поставляется с множеством стеков протоколов, поэтому вы можете работать с любым общим протоколом почти без необходимости делать это самостоятельно
Сообщество Netty активно, пожалуйста, не стесняйтесь обращаться к списку рассылки или спрашивать, когда у вас возникнут проблемы
Netty прошла обширную онлайн-проверку основных фреймворков rpc, промежуточного программного обеспечения сообщений и распределенного промежуточного программного обеспечения связи, и является чрезвычайно надежной.
Неважно, если вы не понимаете, мы можем изучить все это в последующих курсах. Затем давайте воспользуемся версией Netty, чтобы повторно реализовать функции, описанные в начале этой статьи.
Сначала представьте зависимость Maven
Далее следует часть реализации сервера.
Такой небольшой фрагмент кода реализует все функции из нашего предыдущего программирования NIO, включая запуск сервера, прием новых подключений, печать данных от клиента, как насчет того, что лучше? Нативное программирование NIO JDK намного элегантнее?
Когда я впервые изучил Netty, из-за того, что большинству людей не хватало опыта программирования NIO, было бы лучше объединить концепции Netty с моделью ввода-вывода.
Затем я подробно проанализирую остальную логику в следующей серии статей. Вы можете сначала скопировать этот код в свою IDE, а затем запустить основную функцию.
Далее следует часть реализации клиента NIO.
В клиентской программе group Соответствует нам IOClient.java Поток из основной функции в основной функции, оставшуюся логику я подробно проанализирую в следующей статье, теперь вам нужно скопировать этот код в свою среду IDE, затем запустить основную функцию и, наконец, вернуть NettyIOServer.java На консоли вы увидите эффект.
После использования Netty вы чувствуете, что весь мир прекрасен? С одной стороны, Netty так прекрасно инкапсулирует NIO, а написанный код очень элегантен. С другой стороны, после использования Netty вам не нужно беспокоиться о производительности сетевого взаимодействия. Позвольте Netty истощить ваш процессор.
Русские Блоги
Углубленный анализ ввода-вывода Java (5) Введение в Netty
Прежде чем говорить о netty, мы суммируем недостатки JAVA NIO / JAVA AIO.
1. Недостатки JAVA NIO и AIO
Два, введение Нетти
Три, пример кода
Заказчик был дан во второй статье.
Сервер:
Объяснение основного кода
Пул потоков BOSS на самом деле является рабочей ролью селектора в структуре JAVA NIO (это будет подробно обсуждено позже). Для локального IP-порта существует поток, работающий в пуле потоков BOSS, и рабочий контент относительно прост, то есть обнаружение новых соединений; Netty поддерживает мониторинг нескольких портов одновременно, поэтому размер пула потоков BOSS можно установить в соответствии с количеством портов сервера, которые необходимо отслеживать.
В-четвертых, важная концепция Netty
Механизм потока Netty
В предыдущей статье о поддержке JAVA технологии мультиплексированного ввода-вывода мы говорили, что Selector может работать в основном потоке или в независимом потоке. В Netty часть работы здесь передана ветке BOSS. Поток BOSS отвечает за обнаружение нового канала, подключенного к серверу (событие ACCEPT для SocketServerChannel), и регистрацию этого канала в потоке EventLoop пула соединений WORK после проверки.
Когда рабочий поток обнаруживает, что операционная система имеет событие ввода-вывода, которое его интересует (например, событие READ для SocketChannel), он вызывает соответствующее событие ChannelHandler. Когда канал выходит из строя (например, при вызове ctx.close ()), канал удаляется из привязанного EventLoop.
В Netty, если мы используем пакет фреймворка JAVA NIO, этот цикл выполняет класс NioEventLoop (при реализации поддержки мультиплексирования). См. Методы processSelectedKeysPlain и processSelectedKey в этом классе. Кроме того, в этом классе Netty устраняет ошибку «Selector.select (timeout) CPU 100%» и «NullPointerException в Selector.open ()» (http://bugs.java.com/view_bug.do?bug_id=6427854) ОШИБКА
Поток пула рабочих потоков будет определять, какой метод события в ChannelHandler выполнять в соответствии с состоянием события базового селектора JAVA NIO (события в Netty включают channelRegistered, channelUnregistered, channelActive, channelInactive и т. Д.) После завершения выполнения рабочий поток будет продолжать опрос, пока операционная система не ответит, что произошло новое событие ввода-вывода в следующем канале, которым он управляет.
ByteBuf
Вышеприведенная цитата взята из объяснения кеширования ByteBuf в официальном документе JBOSS-Netty. В переводе на китайский язык: Netty переписывает структуру кеша в структуре JAVA NIO и применяет эту структуру к пакету более высокого уровня.
Зачем его переписывать? Объяснение, данное JBOSS-Netty: Написанный мной кеш лучше, чем ByteBuffer в JAVA.
Вот некоторые специальные реализации ByteBuf в Netty:
Channel
Канал, канал. Вы можете использовать Channel в JAVA NIO, чтобы понять его впервые, но на самом деле его значение отличается от значения в JAVA NIO. Мы можем понимать это как «более абстрактное и более богатое». Как показано ниже:
ChannelPipeline и ChannelHandler
Каждый канал в Netty имеет независимый ChannelPipeline, который на китайском языке называется «водопроводный канал». Просто водопровод является двунаправленным, по нему текут данные.Данные могут поступать на сервер через эту «водопроводную трубу» или выходить с сервера через эту «водопроводную трубу».
В ChannelPipeline есть несколько фильтров. Мы называем это «ChannelHandler» (процессор или фильтр). Соответствует понятиям «приток» и «отток»: ChannelHandler, используемый для обработки / фильтрации входящих данных, называется ChannelInboundHandler; ChannelHandler, используемый для обработки / фильтрации исходящих данных, называется «ChannelOutboundHandler».
Цепочка ответственности и применение адаптера
В приведенном выше примере кода сервера написанный служебный процессор TCPServerHandler наследует адаптер ChannelInboundHandlerAdapter. Ниже мы представим несколько часто используемых процессоров ChannelInboundHandler и ChannelOutboundHandler.
Пример класса ChannelInboundHandler
Пример класса ChannelOutboundHandler
Пятый, жизненный цикл канала
Вышеупомянутые важные концепции в Netty. Мы потратили много времени на объяснение Channel, ChannelPipeline, ChannelHandler, их контактов и методов работы.
Говоря о том, почему ChannelInHandler использует режим «адаптера», я конкретно указал на причину: поскольку методы в интерфейсе ChannelInHandler плюс методы в родительском интерфейсе, необходимо реализовать в общей сложности 11 методов событий интерфейса. Фактически, часто нас интересуют только один или два метода интерфейса.
Итак, когда срабатывают эти методы? Речь идет о жизненном цикле канала в Netty (рассматриваемый здесь жизненный цикл относится к инкапсуляции Netty технологической структуры JAVA NIO):
Существует событие канала, которое не показано на рисунке, это событие exceptionCaught (ChannelHandlerContext, Throwable). Пока вызывается исключение при вызове всех методов событий в графе, будет вызываться метод exceptionCaught.
Кроме того, метод события channelInactive не обязательно вызывается после вызова метода channelReadComplete (ChannelHandlerContext). ChannelReadComplete и channelRead могут вызываться повторно, пока у клиента есть данные для отправки.
Шесть, инкапсуляция Netty модели ввода-вывода
Синхронный и асинхронный: операции ввода-вывода выполняются операционной системой (операции ввода-вывода здесь представляют собой широкое понятие: подсчитываются дисковые операции ввода-вывода, сетевой ввод-вывод), разные операционные системы имеют разные режимы для операций ввода-вывода разных устройств. Обе концепции синхронного и асинхронного относятся к уровню операционной системы. Синхронный ввод-вывод означает, что, когда операционная система и устройство взаимодействуют, оно должно дождаться завершения полного запроса-ответа, прежде чем переходить к следующей операции (конечно, операционная система и само устройство также Многие технологии ускоряют этот процесс реакции (например, технология «упреждающего чтения с диска» и технология кэширования данных); асинхронный ввод-вывод означает, что когда операционная система и устройство взаимодействуют, они могут напрямую перейти к следующему запросу операции, не дожидаясь ответа на этот раз. После того, как устройство обработало определенный запрос,Возьмем на себя инициативу уведомить соответствующий ответ операционной системе。
Вышеупомянутые рабочие модели ввода-вывода могут найти соответствующую поддержку в JAVA: традиционный сокет JAVA Socket поддерживает синхронный ввод-вывод в режиме блокировки / неблокирования (некоторые технические документы также называются OIO или BIO); структура JAVA NIO Поддержка различных типов технологий мультиплексированного ввода-вывода в разных операционных системах (выберите модель под Windows, модель опроса / epoll под Linux); инфраструктура JAVA AIO поддерживает асинхронный ввод-вывод (IOCP под Windows и аналог epoll AIO для Linux)
Семь, инкапсуляция Netty формата информации данных
Сама структура «технического уровня» инкапсулирует только техническую реализацию модели ввода-вывода и не заботится о формате данных, передаваемых в модели ввода-вывода; структура «бизнес-уровня» также обрабатывает формат данных, позволяя нам уделять внимание самому бизнесу.