Netty ультра-подробная запись, см. этого достаточно!

Java
Netty ультра-подробная запись, см. этого достаточно!

карта разума

предисловие

В этой статье в основном описываются некоторые функции и важные компоненты фреймворка Netty. Я надеюсь, что после ее прочтения у меня появится более интуитивное представление о фреймворке Netty. Я надеюсь, что это поможет читателям быстро начать работу с Netty и сократить количество обходных путей.

1. Обзор Нетти

Официальное введение:

Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients.

NettyЯвляетсяАсинхронное событиекаркас веб-приложений дляБыстрая разработка поддерживаемых высокопроизводительных протокольных серверов и клиентов..

2. Зачем использовать Нетти

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

2.1 Недостатки NIO

Я уже писал статью на эту тему«Введение в НИО»Есть более подробное введение в NIO Основные проблемы NIO:

  • Библиотеки классов и API-интерфейсы NIO сложны, а стоимость обучения высока.Вы должны хорошо разбираться в Selector, ServerSocketChannel, SocketChannel, ByteBuffer и т. д.
  • Требуется знание многопоточного программирования на Java. Это связано с тем, что программирование NIO включает шаблон Reactor, и вы должны хорошо разбираться в многопоточности и сетевом программировании, чтобы писать высококачественные программы NIO.
  • Печально известный баг epoll. Это приводит к тому, что опрос Selector пуст, и в конечном итоге ЦП достигает 100%. До версии JDK1.7 он не был принципиально решен.

2.2 Преимущества Нетти

Напротив, Netty имеет много преимуществ:

  • API прост в использовании, недорогое обучение.
  • Мощный, со встроенными несколькими декодерами и кодировщиками, поддерживает несколько протоколов.
  • Высокая производительность, по сравнению с другими основными платформами NIO, Netty имеет лучшую производительность.
  • Сообщество активное, найденные ошибки вовремя исправляются, цикл итеративной версии короткий, постоянно добавляются новые функции.
  • Dubbo и Elasticsearch приняли Netty, и качество было проверено.

3. Схема архитектуры

На картинке выше представлена ​​схема архитектуры на главной странице официального сайта, давайте проанализируем ее сверху вниз.

зеленая частьCoreБазовые модули, включая нулевое копирование, библиотеку API и расширяемую модель событий.

оранжевая частьProtocol SupportПоддержка протоколов, включая протокол Http, webSocket, SSL (протокол защищенных сокетов), протокол Google Protobuf, сжатие и распаковку zlib/gzip, передачу больших файлов и т. д.

красная частьTransport ServicesУслуги передачи, включая Socket, Datagram, Http Tunnel и т. д.

Из вышеизложенного видно, что функции, протоколы и методы передачи Netty относительно полны и эффективны.

В-четвертых, навсегда Hello Word

Сначала постройте проект Hovelord, сначала ознакомьтесь с API и проложите путь для более позднего обучения. На основе картины ниже:

4.1 Знакомство с зависимостями Maven

Используется версия 4.1.20, относительно стабильная версия.

<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.20.Final</version>
</dependency>

4.2 Создайте класс запуска сервера

public class MyServer {
    public static void main(String[] args) throws Exception {
        //创建两个线程组 boosGroup、workerGroup
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            //创建服务端的启动对象,设置参数
            ServerBootstrap bootstrap = new ServerBootstrap();
            //设置两个线程组boosGroup和workerGroup
            bootstrap.group(bossGroup, workerGroup)
                //设置服务端通道实现类型    
                .channel(NioServerSocketChannel.class)
                //设置线程队列得到连接个数    
                .option(ChannelOption.SO_BACKLOG, 128)
                //设置保持活动连接状态    
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                //使用匿名内部类的形式初始化通道对象    
                .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            //给pipeline管道设置处理器
                            socketChannel.pipeline().addLast(new MyServerHandler());
                        }
                    });//给workerGroup的EventLoop对应的管道设置处理器
            System.out.println("java技术爱好者的服务端已经准备就绪...");
            //绑定端口号,启动服务端
            ChannelFuture channelFuture = bootstrap.bind(6666).sync();
            //对关闭通道进行监听
            channelFuture.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

4.3 Создание серверного процессора

/**
 * 自定义的Handler需要继承Netty规定好的HandlerAdapter
 * 才能被Netty框架所关联,有点类似SpringMVC的适配器模式
 **/
public class MyServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //获取客户端发送过来的消息
        ByteBuf byteBuf = (ByteBuf) msg;
        System.out.println("收到客户端" + ctx.channel().remoteAddress() + "发送的消息:" + byteBuf.toString(CharsetUtil.UTF_8));
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        //发送消息给客户端
        ctx.writeAndFlush(Unpooled.copiedBuffer("服务端已收到消息,并给你发送一个问号?", CharsetUtil.UTF_8));
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        //发生异常,关闭通道
        ctx.close();
    }
}

4.4 Создание класса запуска клиента

public class MyClient {

    public static void main(String[] args) throws Exception {
        NioEventLoopGroup eventExecutors = new NioEventLoopGroup();
        try {
            //创建bootstrap对象,配置参数
            Bootstrap bootstrap = new Bootstrap();
            //设置线程组
            bootstrap.group(eventExecutors)
                //设置客户端的通道实现类型    
                .channel(NioSocketChannel.class)
                //使用匿名内部类初始化通道
                .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            //添加客户端通道的处理器
                            ch.pipeline().addLast(new MyClientHandler());
                        }
                    });
            System.out.println("客户端准备就绪,随时可以起飞~");
            //连接服务端
            ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6666).sync();
            //对通道关闭进行监听
            channelFuture.channel().closeFuture().sync();
        } finally {
            //关闭线程组
            eventExecutors.shutdownGracefully();
        }
    }
}

4.5 Создание клиентского обработчика

public class MyClientHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        //发送消息到服务端
        ctx.writeAndFlush(Unpooled.copiedBuffer("歪比巴卜~茉莉~Are you good~马来西亚~", CharsetUtil.UTF_8));
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //接收服务端发送过来的消息
        ByteBuf byteBuf = (ByteBuf) msg;
        System.out.println("收到服务端" + ctx.channel().remoteAddress() + "的消息:" + byteBuf.toString(CharsetUtil.UTF_8));
    }
}

4.6 Тестирование

Сначала запустите сервер, затем запустите клиент, вы можете увидеть результат:

MyServer выводит результат:

MyClient выводит результат:

5. Возможности и важные компоненты Netty

5.1 очередь задач taskQueue

Если процессор Handler должен заниматься каким-то делом долгое время, его можно передатьTaskQueue обрабатывается асинхронно. Как его использовать, смотрите демонстрацию кода:

public class MyServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //获取到线程池eventLoop,添加线程,执行
        ctx.channel().eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                try {
                    //长时间操作,不至于长时间的业务操作导致Handler阻塞
                    Thread.sleep(1000);
                    System.out.println("长时间的业务处理");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

Когда мы выполняем отладку отладки, мы видим, что добавленная задача TaskQueue имеет задачу.

5.2 ScheduleTaskQueue задерживает очередь задач

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

ctx.channel().eventLoop().schedule(new Runnable() {
    @Override
    public void run() {
        try {
            //长时间操作,不至于长时间的业务操作导致Handler阻塞
            Thread.sleep(1000);
            System.out.println("长时间的业务处理");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
},5, TimeUnit.SECONDS);//5秒后执行

Все еще открытый отладчик для отладки и просмотра, у нас может быть задача scheduleTaskQueue для выполнения

5.3 Будущий асинхронный механизм

При построении инжиниринга Helloword мы видим вот такую ​​строчку:

ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6666);

Многие операции возвращают этот объект ChannelFuture. Для чего используется этот объект ChannelFuture?

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

//添加监听器
channelFuture.addListener(new ChannelFutureListener() {
    //使用匿名内部类,ChannelFutureListener接口
    //重写operationComplete方法
    @Override
    public void operationComplete(ChannelFuture future) throws Exception {
        //判断是否操作成功	
        if (future.isSuccess()) {
            System.out.println("连接成功");
        } else {
            System.out.println("连接失败");
        }
    }
});

5.4 Bootstrap и ServerBootStrap

Bootstrap и ServerBootStrap — это фабричные классы, предоставляемые Netty для создания стартеров клиентов и серверов. Использование этого фабричного класса очень удобно для создания стартовых классов. Согласно приведенным выше примерам видно, что он может значительно снизить сложность разработки. Сначала посмотрите на диаграмму классов:

Видно, что все они унаследованы от абстрактного класса AbstractBootStrap, поэтому общие методы настройки одинаковы.

В целом шаги по созданию стартера с помощью BootStrap можно разделить на следующие этапы:

5.4.1 group()

в предыдущей статье«Реакторный режим»В разделе мы упоминали, что сервер использует две группы потоков:

  • BossGroup используется для мониторинга клиентских соединений и отвечает за создание соединений с клиентами и регистрацию соединений в селекторе рабочей группы.
  • Рабочая группа обрабатывает события чтения и записи для каждого соединения.

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

EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();

Немного любопытно, поскольку это группа потоков, каково количество потоков по умолчанию? Погрузитесь в исходный код:

    //使用一个常量保存
    private static final int DEFAULT_EVENT_LOOP_THREADS;

    static {
        //NettyRuntime.availableProcessors() * 2,cpu核数的两倍赋值给常量
        DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
                "io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));

        if (logger.isDebugEnabled()) {
            logger.debug("-Dio.netty.eventLoopThreads: {}", DEFAULT_EVENT_LOOP_THREADS);
        }
    }
	
    protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
        //如果不传入,则使用常量的值,也就是cpu核数的两倍
        super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
    }

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

//设置bossGroup线程数为1
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
//设置workerGroup线程数为16
EventLoopGroup workerGroup = new NioEventLoopGroup(16);

5.4.2 channel()

Этот метод используется для установки типа канала.Когда соединение будет установлено, соответствующий экземпляр канала будет создан в соответствии с этой настройкой.

Используйте режим отладки, чтобы увидеть

Типы каналов следующие:

NioSocketChannel: Асинхронное неблокирующее клиентское соединение TCP Socket.

NioServerSocketChannel: Асинхронное неблокирующее соединение TCP Socket на стороне сервера.

Эти два типа каналов обычно используются, потому что они асинхронны и не блокируют. Так что это первый выбор.

OioSocketChannel: синхронная блокировка клиентского TCP-сокета.

OioServerSocketChannel: синхронно заблокированное соединение TCP Socket на стороне сервера.

Немного отлажен локально, он немного отличается от Nio, он заблокирован, поэтому вызовы API тоже другие. Поскольку он блокирует ввод-вывод, почти никто не хочет использовать Oio, поэтому трудно найти примеры. Я немного подумал об этом, и после нескольких ошибок я, наконец, справился. код показывает, как показано ниже:

//server端代码,跟上面几乎一样,只需改三个地方
//这个地方使用的是OioEventLoopGroup
EventLoopGroup bossGroup = new OioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup)//只需要设置一个线程组boosGroup
        .channel(OioServerSocketChannel.class)//设置服务端通道实现类型

//client端代码,只需改两个地方
//使用的是OioEventLoopGroup
EventLoopGroup eventExecutors = new OioEventLoopGroup();
//通道类型设置为OioSocketChannel
bootstrap.group(eventExecutors)//设置线程组
        .channel(OioSocketChannel.class)//设置客户端的通道实现类型

NioSctpChannel: подключение асинхронного клиента через протокол Sctp (протокол передачи управления потоком).

NioSctpServerChannel: асинхронное соединение Sctp на стороне сервера.

Локальная не запустилась успешно.Я читал комментарии некоторых пользователей сети в Интернете, что она может быть запущена только в среде Linux. Судя по сообщению об ошибке: SCTP не поддерживается на этой платформе, эта платформа не поддерживается. Поскольку на моем компьютере установлена ​​система Windows, то, что сказали пользователи сети, имеет смысл.

5.4.3 Опция () и дочерняя опция ()

Сначала говорите о разнице между ними.

Параметр option() используется сервером для получения входящих соединений, то есть потока boosGroup.

childOption() предоставляется соединению, полученному родительским конвейером, который является потоком workerGroup.

Разобравшись с этим, давайте взглянем на некоторые часто используемые настройки:

Параметры SocketChannel, которые обычно используются в качестве параметров childOption():

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

Параметры ServerSocketChannel, которые являются общими параметрами option():

SO_BACKLOGПараметр сокета, длина очереди для приема соединений сервером.Если очередь заполнена, клиентское соединение будет отклонено. По умолчанию 200 для Windows и 128 для других.

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

5.4.4 Настройка конвейера (выделено)

ChannelPipeline — это цепочка ответственности Netty за обработку запросов, а ChannelHandler — процессор, который специально обрабатывает запросы. Фактически у каждого канала есть конвейер процессоров.

В Bootstrap метод childHandler() должен инициализировать канал и создать экземпляр ChannelInitializer. В настоящее время необходимо переписать метод инициализации канала initChannel(). В этом месте выполняется сборочная линия. Демонстрация кода выглядит следующим образом:

//使用匿名内部类的形式初始化通道对象
bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
	@Override
	protected void initChannel(SocketChannel socketChannel) throws Exception {
		//给pipeline管道设置自定义的处理器
		socketChannel.pipeline().addLast(new MyServerHandler());
	}
});

Обработчик процессора в основном делится на два типа:

ChannelInboundHandlerAdapter (входящий процессор), ChannelOutboundHandler (исходящий процессор)

Входящий относится к каналу данных от базового канала java NIO до Netty.

Исходящий относится к управлению основным каналом Java NIO через канал Netty.

Обычно используемые события для процессоров ChannelInboundHandlerAdapter::

  1. Зарегистрируйтесь для участия в мероприятии fireChannelRegistered.

  2. Событие установления соединения fireChannelActive.

  3. События чтения и события завершения чтения fireChannelRead, fireChannelReadComplete.

  4. Событие уведомления об исключении fireExceptionCaught.

  5. Определяемое пользователем событие fireUserEventTriggered.

  6. Событие изменения состояния канала для записи fireChannelWritabilityChanged.

  7. Событие закрытия соединения fireChannelInactive.

События, обычно используемые процессором ChannelOutboundHandler::

  1. привязка портов.

  2. Подключиться к серверу подключиться.

  3. Напишите событие, напишите.

  4. Смыв времени смыва.

  5. Прочитано событие прочитано.

  6. Отсоединить активное отключение.

  7. Закройте событие закрытия канала.

Существует также аналогичный обработчик(), который в основном используется для сборки родительского канала, который является потоком bossGroup. Как правило, этот метод не используется.

5.4.5 bind()

Предоставляет адрес сервера и номер порта для привязки сервера или клиента.По умолчанию используется асинхронный запуск. Если вы добавите метод sync(), он станет синхронным.

Существует пять перегруженных методов с одинаковыми именами, и все они используются для привязки адреса и номера порта. Не вводится по одному.

5.4.6 Мягкое закрытие EventLoopGroup

//释放掉所有的资源,包括创建的线程
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();

Все дочерние каналы будут закрыты. После закрытия базовые ресурсы освобождаются.

5.5 Channel

Что такое канал? Взгляните на официальную документацию:

A nexus to a network socket or a component which is capable of I/O operations such as read, write, connect, and bind

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

Если приведенное выше описание является более абстрактным, ниже приведено другое описание:

A channel provides a user:

the current state of the channel (e.g. is it open? is it connected?), the configuration parameters of the channel (e.g. receive buffer size), the I/O operations that the channel supports (e.g. read, write, connect, and bind), and the ChannelPipeline which handles all I/O events and requests associated with the channel.

Перевод в действие:

канал предоставляет пользователям:

  1. Текущее состояние канала (например, он открыт? или подключен?)

  2. Параметры конфигурации канала (например, размер буфера)

  3. Операции ввода-вывода, поддерживаемые каналом (такие как чтение, запись, подключение и связывание), и ChannelPipeline, который обрабатывает все события ввода-вывода и запросы, связанные с каналом.

5.5.1 Получить статус канала

boolean isOpen(); //如果通道打开,则返回true
boolean isRegistered();//如果通道注册到EventLoop,则返回true
boolean isActive();//如果通道处于活动状态并且已连接,则返回true
boolean isWritable();//当且仅当I/O线程将立即执行请求的写入操作时,返回true。

Выше приведены методы получения четырех состояний канала.

5.5.2 Получить параметры конфигурации канала

Чтобы получить единую информацию о конфигурации, используйте getOption(), демонстрация кода:

ChannelConfig config = channel.config();//获取配置参数
//获取ChannelOption.SO_BACKLOG参数,
Integer soBackLogConfig = config.getOption(ChannelOption.SO_BACKLOG);
//因为我启动器配置的是128,所以我这里获取的soBackLogConfig=128

Получите несколько сведений о конфигурации, используйте getOptions(), демонстрация кода:

ChannelConfig config = channel.config();
Map<ChannelOption<?>, Object> options = config.getOptions();
for (Map.Entry<ChannelOption<?>, Object> entry : options.entrySet()) {
	System.out.println(entry.getKey() + " : " + entry.getValue());
}
/**
SO_REUSEADDR : false
WRITE_BUFFER_LOW_WATER_MARK : 32768
WRITE_BUFFER_WATER_MARK : WriteBufferWaterMark(low: 32768, high: 65536)
SO_BACKLOG : 128
以下省略...
*/

5.5.3 Поддержка канала IO Операции

операция записи, вот демонстрация записи сообщения с сервера клиенту:

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
	ctx.channel().writeAndFlush(Unpooled.copiedBuffer("这波啊,这波是肉蛋葱鸡~", CharsetUtil.UTF_8));
}

Консоль клиента:

//收到服务端/127.0.0.1:6666的消息:这波啊,这波是肉蛋葱鸡~

соединятьЭксплуатация, демонстрация кода:

ChannelFuture connect = channelFuture.channel().connect(new InetSocketAddress("127.0.0.1", 6666));//一般使用启动器,这种方式不常用

Получить ChannelPipeline через канали выполните соответствующую обработку:

//获取ChannelPipeline对象
ChannelPipeline pipeline = ctx.channel().pipeline();
//往pipeline中添加ChannelHandler处理器,装配流水线
pipeline.addLast(new MyServerHandler());

5.6 Selector

В NioEventLoop есть селектор переменных-членов, который является селектором пакета nio перед«Введение в НИО», я уже говорил о Selector.

Селектор в Netty аналогичен селектору NIO, который используется для мониторинга событий, управления каналами, зарегистрированными в селекторе, и реализации мультиплексоров.

5.7 PiPeline и ChannelPipeline

Когда мы представили Channels ранее, мы знали, что процессоры конвейера ChannelHandler могут быть собраны в каналы. Канал не может иметь только один процессор channelHandler. Их должно быть много. Поскольку в конвейере работает много обработчиков каналов, должен быть порядок.

Затем появился конвейер, процессор конвейера соответствует контейнеру. Инициализация канала, ChannelHandler последовательно устанавливается в конвейер, можно добиться упорядоченного выполнения channelHandler.

В канале есть только один ChannelPipeline. Конвейер создается при создании канала. ChannelPipeline содержит список, сформированный ChannelHander, и все ChannelHandlers будут зарегистрированы в ChannelPipeline.

5.8 ChannelHandlerContext

В Netty мы определяем обработчики обработчиков, которые реализуются путем интеграции входящих или исходящих процессоров, как указано выше. В настоящее время, если мы хотим получить объект конвейера или объект канала в обработчике, как его получить.

Итак, Нетти разработала этот объект контекста ChannelHandlerContext, вы можете получить канал, конвейер и другие объекты, вы можете читать и записывать операции.

На диаграмме классов ChannelHandlerContext представляет собой интерфейс с тремя классами реализации, указанными ниже.

На самом деле ChannelHandlerContext представляет собой связанный список в конвейере. Посмотрите на исходный код, чтобы понять:

//ChannelPipeline实现类DefaultChannelPipeline的构造器方法
protected DefaultChannelPipeline(Channel channel) {
	this.channel = ObjectUtil.checkNotNull(channel, "channel");
	succeededFuture = new SucceededChannelFuture(channel, null);
	voidPromise =  new VoidChannelPromise(channel, true);
	//设置头结点head,尾结点tail
	tail = new TailContext(this);
	head = new HeadContext(this);
	
	head.next = tail;
	tail.prev = head;
}

Ниже я использую картинку для представления, так будет понятнее:

5.9 EventLoopGroup

Давайте сначала посмотрим на диаграмму классов EventLoopGroup:

Он включает широко используемый класс реализации NioEventLoopGroup. В предыдущем примере также использовалась группа OioEventLoopGroup.

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

Каждая EventLoopGroup включает в себя один или несколько EventLoop, и каждый EventLoop поддерживает экземпляр Selector.

5.9.1 Принцип реализации механизма опроса

Давайте посмотрим на исходный код DefaultEventExecutorChooserFactory:

private final AtomicInteger idx = new AtomicInteger();
private final EventExecutor[] executors;

@Override
public EventExecutor next() {
    //idx.getAndIncrement()相当于idx++,然后对任务长度取模
    return executors[idx.getAndIncrement() & executors.length - 1];
}

Этот код может определить, что способ выполнения — это механизм опроса, а затем отладить его:

Здесь тоже суждение: если количество потоков не равно 2, реализуется алгоритм формирования паттерна.

@Override
public EventExecutor next() {
    return executors[Math.abs(idx.getAndIncrement() % executors.length)];
}

напиши в конце

Обратитесь к официальной документации веб-сайта Netty:Документация API

Творить не просто, если вы найдете это полезнымпоставить лайкБар.

Я не хочу этого в следующий раз, надеюсьНа этот раз качество должно быть три последовательных,благодарный!

Если вы хотите впервые увидеть мои обновленные статьи, вы можете выполнить поиск в общедоступной учетной записи на WeChat "java技术爱好者",Не хочу быть соленой рыбой, я программист, стремящийся запомниться всем. Увидимся в следующий раз! ! ! 在这里插入图片描述

Возможности ограничены, если есть какие-то ошибки или неуместности, просьба критиковать и исправлять их, учиться и обмениваться вместе!