Это пятый день моего участия в августовском испытании обновлений, подробности о мероприятии:Испытание августовского обновления
предисловие
оNetty
В последнее время я посмотрел много связанных видео и книг, и я многому научился.Я надеюсь поделиться с вами тем, что я знаю, усердно работать и расти вместе. Раньше мы былиJava IO
,BIO
,NIO
,AIO
Был проведен анализ, и ссылки на соответствующие статьи выглядят следующим образом:
Углубленный анализ Java IO (1) Обзор
Углубленный анализ Java IO (2) BIO
Углубленный анализ Java IO (3) NIO
Углубленный анализ Java IO (4) AIO
В этой статье мы начнемNetty
Для более глубокого анализа давайте сначала разберемсяJAVA NIO
,AIO
неадекватности.
Боль Java Native API
несмотря на то чтоJAVA NIO
а такжеJAVA AIO
Платформа обеспечивает поддержку мультиплексирования ввода-вывода/асинхронного ввода-вывода, но не обеспечивает хорошей инкапсуляции «информационного формата» верхнего уровня. Внедрить настоящее веб-приложение с помощью этих API непросто.
JAVA NIO
а такжеJAVA AIO
Он не обеспечивает обработку отключения и повторного подключения, сетевой флэш-памяти, чтения и записи половинных пакетов, кэширования сбоев, перегрузки сети и ненормального потока кода и т. д., которые требуют от разработчиков выполнения соответствующей работы.
AIO
На практике нетNIO
лучше.AIO
Существуют разные реализации на разных платформах, и в системе Windows используется технология асинхронного ввода-вывода:IOCP
;Поскольку в Linux нет такой технологии асинхронного ввода-вывода, она используетepoll
Моделируйте асинхронный ввод-вывод. Так что производительность AIO под Linux не идеальна. AIO также не поддерживает UDP.
Подводя итог, можно сказать, что в реальных крупномасштабных интернет-проектах собственный API Java широко не используется и заменяется сторонним фреймворком Java, которыйNetty
.
Преимущества Nettyty
Предоставлено НеттиАсинхронная среда веб-приложений, управляемая событиями, и инструменты для быстрой разработки высокопроизводительных и надежных веб-серверов и клиентских программ.
Неблокирующий ввод-вывод
Нетти основана наJava NIO
Платформа веб-приложений, реализованная на основе API, которую можно использовать для быстрой и простой разработки веб-приложений, таких как серверные и клиентские программы. Netty значительно упрощает процесс разработки сетевых программ, таких как разработка сервисов Socket для TCP и UDP.
Благодаря API на основе NIO Netty может предоставлять неблокирующиеI/O
операции, что значительно повышает производительность. В то же время Netty внутренне инкапсулирует сложность Java NIO API и обеспечивает обработку пула потоков, что делает разработку приложений NIO чрезвычайно простой.
богатый протокол
Netty предоставляет простой и удобный в использовании API, но это не означает, что приложение будет сложно поддерживать и иметь низкую производительность. Netty — это хорошо спроектированная платформа, которая во многом опирается на реализацию многих протоколов, таких как FTP, SMTP, HTTP и многих традиционных двоичных и текстовых протоколов.
Netty поддерживает многофункциональные сетевые протоколы, такие какTCP
,UDP
,HTTP
,HTTP/2
,WebSocket
,SSL/TLS
и т. д. Эти протоколы реализованы «из коробки», поэтому разработчики Netty могут добиться простоты разработки, высокой производительности и стабильности без потери гибкости.
Асинхронный и управляемый событиями
Netty — это асинхронная управляемая событиями платформа, в которой реализованы всеI/O
операции асинхронны, всеI/O
Вызов вернется немедленно, нет гарантии, что вызов будет успешным или нет, но вызов вернетсяChannelFuture
. Netty будет проходитьChannelFuture
Уведомляет об успешности вызова, сбое или его отмене.
В то же время Netty управляется событиями, и вызывающая сторона не может получить результат немедленно, но через механизм мониторинга событий пользователь может легко получить его активно или получить через механизм уведомления.I/O
результат операции.
когдаFuture
Когда объект только что создан, он находится в незавершенном состоянии, и вызывающая сторона может передать возвращенноеChannelFuture
Чтобы получить статус выполнения операции, а затем зарегистрировать функцию прослушивателя для выполнения завершенной операции, общие операции следующие:
- пройти через
isDone
способ определить, завершена ли текущая операция. - пройти через
isSuccess
метод, чтобы определить, была ли текущая завершенная операция успешной. - пройти через
getCause
метод, чтобы получить причину, по которой завершенная текущая операция не удалась. - пройти через
isCancelled
метод, чтобы определить, была ли отменена текущая завершенная операция. - пройти через
addListener
метод для регистрации прослушивателя после завершения операции (isDone
метод возвращает завершенный), указанный слушатель будет уведомлен; еслиfuture
Когда объект завершен, он уведомляет указанного слушателя.
Например, в следующем коде привязка порта является асинхронной операцией.При обработке операции привязки будет вызвана соответствующая логика обработки прослушивателя.
serverBootstrap.bind(port).addListener(future -> {
if(future.isSuccess()){
System.out.println("端口绑定成功!");
}else {
System.out.println("端口绑定失败!");
}
});
по сравнению с традиционной блокировкойI/O
, преимущество асинхронной обработки Netty заключается в том, что она не вызывает блокировку потока, и потокI/O
Во время работы могут выполняться другие программы, которые будут более стабильными и будут иметь более высокую пропускную способность в ситуациях с высокой степенью параллелизма.
Хорошо продуманный API
Netty с самого начала предоставила пользователям лучший API и дизайн реализации.
Например, при небольшом количестве пользователей может быть выбран традиционный API блокировки, ведь использовать API блокировки будет проще, чем Java NIO. Однако проблемы возникают, когда объем трафика растет в геометрической прогрессии и серверу необходимо обрабатывать тысячи одновременных клиентских подключений. В этом случае можно попробовать Java NIO, но сложный программный интерфейс NIO Selector отнимает много времени и в конечном итоге препятствует быстрой разработке.
Нетти предоставляетchannel
Унифицированный асинхронныйI/O
Интерфейс программирования, который абстрагирует все операции двухточечной связи. То есть, если приложение основано на определенной транспортной реализации Netty, то это же приложение может работать и на другой транспортной реализации Netty.Channel
Общие субинтерфейсы:
Богатый буфер для достижения
Netty использует собственный API кеша вместо Java NIO.ByteBuffer
для представления непрерывной последовательности байтов. а такжеByteBuffer
По сравнению с этим методом он имеет очевидные преимущества.
Netty использует новый тип буфераByteBuf
, и предназначен для решения с нуляByteBuffer
Проблема, но и для удовлетворения ежедневных потребностей разработки веб-приложений типа буфера.
Netty обладает следующими важными особенностями:
- Позволяет использовать пользовательские типы буферов.
- Прозрачная реализация нулевого копирования встроена в составной тип буфера.
- Стандартные типы динамических буферов с такими функциями, как
StringBuffer
Та же емкость динамической буферизации. - больше не нужно звонить
flip()
метод. - обычно имеет соотношение
ByteBuffer
Более быстрое время отклика.
Эффективная передача по сети
Собственная сериализация Java в основном имеет следующие недостатки:
-
Не может скрещивать языки.
-
Поток кода слишком велик после сериализации.
-
Производительность после сериализации слишком низкая.
В отрасли существует множество фреймворков для решения вышеуказанных проблем, таких какGoogle Protobuf
,JBoss Marshalling
,Facebook Thrift
Ждать. Для этих фреймворков Netty предоставляет соответствующие пакеты для интеграции этих фреймворков в приложения. В то же время сама Netty также предоставляет множество инструментов кодеков для разработчиков. Разработчики могут разрабатывать эффективные приложения для передачи по сети на основе Netty, такие как высокопроизводительное промежуточное ПО для сообщений.Apache RocketMQ
, высокопроизводительная среда RPCApache Dubbo
Ждать.
Концепции ядра Netty
Как видно из вышеуказанной архитектуры диаграммы, Netty в основном состоит из трех основных компонентов:
- основные компоненты
- Служба передачи
- протокол
основные компоненты
Основные компоненты включают в себя: модель событий, байтовые буферы и коммуникационные API.
модель события
Netty основана на асинхронном управлении событиями, структура отражает всеI/O
Операции асинхронные, вызывающий абонент не получает результаты немедленно, но по механизму слушателя событий пользователи могут легко получить или получить активный механизм уведомленияI/O
результат операции.
Netty классифицирует все события в соответствии с их отношением к входящему или исходящему потоку данных.
События, которые могут быть инициированы входящими данными или связанными изменениями состояния, включают следующее:
- Соединение было активировано или соединение деактивировано.
- данные прочитаны.
- Пользовательские события.
- Событие ошибки.
Исходящее событие — это результат действия, которое будет инициировано в будущем, включая следующие действия:
- Откройте или закройте соединение с удаленным узлом.
- Записать или сбросить данные в сокет.
Каждое событие может быть отправлено наChannelHandler
Метод, реализованный пользователем в классе.
байтовый буфер
Нетти использует различие междуJava ByteBuffer
новый тип буфераByteBuf
,ByteBuf
Предоставляет богатые возможности.
Коммуникационный API
Коммуникационные API Netty абстрагируются отChannel
, с унифицированным асинхроннымI/O
Интерфейс программирования для выполнения всех операций связи «точка-точка».
Служба передачи
В Netty уже есть несколько встроенных транспортных сервисов. Поскольку не все их транспорты поддерживают все протоколы, необходимо выбрать транспорт, совместимый с протоколом, используемым приложением. Ниже приведены все транспорты, предоставляемые Netty.
NIO
io.netty.channel.socket.nio
Пакеты используются для поддержки NIO. Реализация пакета ниже должна использоватьjava.nio.channels
Пакеты служат базой (подход на основе селекторов).
epoll
io.netty.channel.epoll
Пакет для поддержки epoll и неблокирующего ввода-вывода, управляемого JNI.
Следует отметить, что этоepoll
Транспорт поддерживается только в Linux.epoll
В то же время он предоставляет множество функций, таких как: SO_REUSEPORT и т. д., что быстрее, чем передача NIO, и полностью не блокируется.
OIO
io.netty.channel.socket.oio
Пакеты используются для поддержки использованияjava.net
пакетная блокировкаI/O
.
местный
io.netty.channel.local
Пакеты используются для поддержки локальных транспортов, которые обмениваются данными через каналы внутри виртуальной машины.
в линию
io.netty.channel.embedded
Пакеты передаются как встроенные, что позволяет использоватьChannelHandler
без необходимости в истинном сетевом транспорте.
Поддержка протокола
Netty поддерживает многофункциональные сетевые протоколы, такие какTCP
,UDP
,HTTP
,HTTP/2
,WebSocket
,SSL/TLS
и т. д. Эти протоколы реализованы «из коробки», поэтому разработчики Netty могут добиться простоты разработки, высокой производительности и стабильности без потери гибкости.
Нетти простое приложение
Введение зависимостей Maven
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.49.Final</version>
</dependency>
Обработчик конвейера на стороне сервера
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
//读取数据实际(这里我们可以读取客户端发送的消息)
/*
1. ChannelHandlerContext ctx:上下文对象, 含有 管道pipeline , 通道channel, 地址
2. Object msg: 就是客户端发送的数据 默认Object
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("server ctx =" + ctx);
Channel channel = ctx.channel();
//将 msg 转成一个 ByteBuf
//ByteBuf 是 Netty 提供的,不是 NIO 的 ByteBuffer.
ByteBuf buf = (ByteBuf) msg;
System.out.println("客户端发送消息是:" + buf.toString(CharsetUtil.UTF_8));
System.out.println("客户端地址:" + channel.remoteAddress());
}
//数据读取完毕
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
//writeAndFlush 是 write + flush
//将数据写入到缓存,并刷新
//一般讲,我们对这个发送的数据进行编码
ctx.writeAndFlush(Unpooled.copiedBuffer("公司最近账户没啥钱,再等几天吧!", CharsetUtil.UTF_8));
}
//处理异常, 一般是需要关闭通道
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
NettyServerHandler
унаследовано отChannelInboundHandlerAdapter
, этот класс реализуетChannelInboundHandler
интерфейс.ChannelInboundHandler
Предоставляет множество методов интерфейса для обработки событий.
покрыто здесьchannelRead()
Метод обработчика событий. Этот метод вызывается при получении сообщения всякий раз, когда от клиента поступают новые данные.
channelReadComplete()
Метод обработки события вызывается при чтении данных путем вызоваChannelHandlerContext
изwriteAndFlush()
метод, который записывает сообщение в канал и, наконец, отправляет его клиенту.
exceptionCaught()
Метод обработки событий заключается в том, что когдаThrowable
объект будет называться.
Основная программа сервера
public class NettyServer {
public static void main(String[] args) throws Exception {
//创建BossGroup 和 WorkerGroup
//说明
//1. 创建两个线程组 bossGroup 和 workerGroup
//2. bossGroup 只是处理连接请求 , 真正的和客户端业务处理,会交给 workerGroup完成
//3. 两个都是无限循环
//4. bossGroup 和 workerGroup 含有的子线程(NioEventLoop)的个数
// 默认实际 cpu核数 * 2
//
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup(); //8
try {
//创建服务器端的启动对象,配置参数
ServerBootstrap bootstrap = new ServerBootstrap();
//使用链式编程来进行设置
bootstrap.group(bossGroup, workerGroup) //设置两个线程组
.channel(NioServerSocketChannel.class) //bossGroup使用NioSocketChannel 作为服务器的通道实现
.option(ChannelOption.SO_BACKLOG, 128) // 设置线程队列得到连接个数 option主要是针对boss线程组,
.childOption(ChannelOption.SO_KEEPALIVE, true) //设置保持活动连接状态 child主要是针对worker线程组
.childHandler(new ChannelInitializer<SocketChannel>() {//workerGroup使用 SocketChannel创建一个通道初始化对象 (匿名对象)
//给pipeline 设置处理器
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//可以使用一个集合管理 SocketChannel, 再推送消息时,可以将业务加入到各个channel 对应的 NIOEventLoop 的 taskQueue 或者 scheduleTaskQueue
ch.pipeline().addLast(new NettyServerHandler());
}
}); // 给我们的workerGroup 的 EventLoop 对应的管道设置处理器
System.out.println(".....服务器 is ready...");
//绑定一个端口并且同步, 生成了一个 ChannelFuture 对象
//启动服务器(并绑定端口)
ChannelFuture cf = bootstrap.bind(7788).sync();
//给cf 注册监听器,监控我们关心的事件
cf.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (cf.isSuccess()) {
System.out.println("服务已启动,端口号为7788...");
} else {
System.out.println("服务启动失败...");
}
}
});
//对关闭通道进行监听
cf.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
NioEventLoopGroup
используется для обработкиI/O
Многопоточный цикл событий для операций. Netty предоставляет множество различныхEventLoopGroup
реализация для обработки различных видов транспорта.
В приведенном выше серверном приложении есть дваNioEventLoopGroup
использовал. Первый называетсяbossGroup
, используемый для приема входящих подключений. второй называетсяworkerGroup
, используемый для обработки соединений, которые уже были получены, один разbossGroup
После получения соединения информация о соединении будет зарегистрирована вworkerGroup
начальство.
ServerBootstrap
Является загрузочным классом для сервисов NIO. можно использовать непосредственно в этом сервисеChannel
.
-
group
Метод настройкиEventLoopGroup
. - пройти через
Channel
метод, вы можете указать вновь подключенный входящийChannel
ТипNioServerSocketChannel
Добрый. -
childHandler
используется для указанияChannelHandler
, то есть реализованный ранееNettyServerHandler
. - в состоянии пройти
option
установить указанныйChannel
реализоватьNioServerSocketChannel
параметры конфигурации. -
childOption
Основные настройкиSocketChannel
сынChannel
Опции. -
bind
Используется для привязки порта для запуска службы.
Обработчик клиентских каналов
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
//当通道就绪就会触发该方法
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("client ctx =" + ctx);
ctx.writeAndFlush(Unpooled.copiedBuffer("老板,工资什么时候发给我啊?", CharsetUtil.UTF_8));
}
//当通道有读取事件时,会触发
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
System.out.println("服务器回复的消息:" + buf.toString(CharsetUtil.UTF_8));
System.out.println("服务器的地址: "+ ctx.channel().remoteAddress());
}
//处理异常, 一般是需要关闭通道
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
channelRead
Метод преобразует полученное сообщение в строку для удобной печати на консоли.
channelRead
Тип полученного сообщенияByteBuf
,ByteBuf
Предоставляются удобные методы для преобразования в строки.
Основная программа клиента
public class NettyClient {
public static void main(String[] args) throws Exception {
//客户端需要一个事件循环组
EventLoopGroup group = new NioEventLoopGroup();
try {
//创建客户端启动对象
//注意客户端使用的不是 ServerBootstrap 而是 Bootstrap
Bootstrap bootstrap = new Bootstrap();
//设置相关参数
bootstrap.group(group) //设置线程组
.channel(NioSocketChannel.class) // 设置客户端通道的实现类(反射)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new NettyClientHandler()); //加入自己的处理器
}
});
System.out.println("客户端 ok..");
//启动客户端去连接服务器端
//关于 ChannelFuture 要分析,涉及到netty的异步模型
ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 7788).sync();
//给关闭通道进行监听
channelFuture.channel().closeFuture().sync();
} finally {
group.shutdownGracefully();
}
}
}
Клиенту нужен только одинNioEventLoopGroup
Вот и все.
тестовый забег
Запускать серверы отдельноNettyServer
и клиентNettyClient
программа
Вывод консоли сервера:
.....服务器 is ready...
服务已启动,端口号为7788...
server ctx =ChannelHandlerContext(NettyServerHandler#0, [id: 0xa1b2233c, L:/127.0.0.1:7788 - R:/127.0.0.1:63239])
客户端发送消息是:老板,工资什么时候发给我啊?
客户端地址:/127.0.0.1:63239
Вывод клиентской консоли:
客户端 ok..
client ctx =ChannelHandlerContext(NettyClientHandler#0, [id: 0x21d6f98e, L:/127.0.0.1:63239 - R:/127.0.0.1:7788])
服务器回复的消息:公司最近账户没啥钱,再等几天吧!
服务器的地址: /127.0.0.1:7788
На данный момент завершены простой сервер и клиент на базе Netty.
Компоненты Netty
Здесь мы сначала кратко разберемся в функциях следующих компонентов, оставим изображение, а позже подробно расскажем о каждом компоненте.
Channel
учитесь с намиJava NIO Channel
Точно так же Netty Channel сделал на этой основе очень абстрактную инкапсуляцию, в основном используемую для базовых операций данных сетевого ввода-вывода, таких какbind ()
,connect ()
,read ()
,write ()
Ждать.
EventLoop
В течение всего жизненного цикла сетевого соединения обработка всех происходящих событий в основномEventLoop
иметь дело с
ChannelFuture
В Netty операции ввода-вывода в основном выполняются асинхронно.Когда операция происходит, нам нужен способ узнать результат выполнения операции в будущем моменте времени.ChannelFutrue
серединаaddListener ()
способ регистрации слушателейChannelFutureListener
, прослушиватель может активно уведомлять нас о завершении операции.
ChannelHandler
channelHandler
В основном используется для обработки бизнес-логики в приложении.Через него обрабатываются входящие и исходящие данные в сети.При возникновении событияchannelHandler
будет запущен для выполнения.
ChannelPipeline
ChannelPipeline
Предоставляет контейнер для определения потоков обработки при входе и выходе данных. могуPipeline
Рассматриваемый как сборочная линия, исходный исходный материал (поток байтов) поступает, проходит обработку и, наконец, выводится.
Bootstrap
В основном используется для настройки информации о запуске программы Netty сервера или клиента.
ByteBuf
байтовый контейнер данных, который обеспечивает отношенияJava NIO ByteBuffer
лучший API.
Суммировать
В этой статье в основном объясняются предыстория, характеристики, основные компоненты Netty и способы быстрого запуска первого приложения Netty.
Позже мы проанализируемNetty架构设计
,Channel
,ChannelHandler
, байтовый буферByteBuf
,线程模型
,编解码
,引导程序
знания и тд.
конец
Я кодер, которого бьют, и я пытаюсь двигаться вперед. Если статья была вам полезна, не забудьте поставить лайк и подписаться, спасибо!