Netty (2) Почему Netty обладает высокой производительностью с точки зрения многопоточности?

задняя часть JVM GitHub Netty
Netty (2) Почему Netty обладает высокой производительностью с точки зрения многопоточности?

предисловие

в предыдущемSpringBoot интегрирует механизм сердцебиения длинных соединенийВ этой статье я познакомился с Нетти.

Но на самом деле его можно только использовать, зачем использовать Netty? Каковы его преимущества? Это на самом деле не ясно.

Эта статья взята из исторического источника, чтобы говорить о.

традиционный ввод-вывод

До появления Netty и NIO то, что мы использовали для написания приложений ввода-вывода, заключалось в использованииjava.io.*Пакеты представлены ниже.

Например, следующий псевдокод:

ServeSocket serverSocket = new ServeSocket(8080);
Socket socket = serverSocket.accept() ;
BufferReader in = .... ;

String request ;
 
while((request = in.readLine()) != null){
	new Thread(new Task()).start()
}

Наверное, так оно и есть, на самом деле главное высказать:Такой поток может обрабатывать только одно соединение.

Если это 100 клиентских подключений, ему придется открыть 100 потоков, а если 1000, ему придется открыть 1000 потоков.

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

Даже если мы дадим JVM достаточно памяти, переключение контекста, вызванное большим количеством потоков, невыносимо.

А традиционный ввод-вывод — это блокирующий режим.Каждый ответ должен инициировать запрос ввода-вывода, обрабатывать запрос и затем возвращаться одновременно.Прямым результатом является низкая производительность и низкая пропускная способность.

Модель реактора

Таким образом, широко используемая в отрасли модель высокопроизводительного ввода-выводаReactor.

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

Обычно также проявляется следующими тремя способами:

один поток

Как видно из рисунка:

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

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

Многопоточность

Отсюда и многопоточная модель.

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

Его можно реализовать на основе собственного пула потоков Java, поэтому подсказки по производительности при обработке большого количества запросов огромны.

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

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

Итак, существует следующая модель потока.

многопоточность ведущий-ведомый

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

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

Нетти реализация

На самом деле, с аналогичной потоковой моделью Netty можно говорить гораздо больше.

прежде чем мы вернемсяSpringBoot интегрирует механизм сердцебиения длинных соединенийСерверный код в:

    private EventLoopGroup boss = new NioEventLoopGroup();
    private EventLoopGroup work = new NioEventLoopGroup();


    /**
     * 启动 Netty
     *
     * @return
     * @throws InterruptedException
     */
    @PostConstruct
    public void start() throws InterruptedException {

        ServerBootstrap bootstrap = new ServerBootstrap()
                .group(boss, work)
                .channel(NioServerSocketChannel.class)
                .localAddress(new InetSocketAddress(nettyPort))
                //保持长连接
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                .childHandler(new HeartbeatInitializer());

        ChannelFuture future = bootstrap.bind().sync();
        if (future.isSuccess()) {
            LOGGER.info("启动 Netty 成功");
        }
    }

По сути, босс здесь эквивалентен пулу потоков, который обрабатывает клиентские соединения в модели Reactor.

Work — это, естественно, пул потоков для обработки событий.

那么如何来实现上文的三种模式呢? Это на самом деле довольно просто:

Однопоточная модель:

private EventLoopGroup group = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap()
                .group(group)
                .childHandler(new HeartbeatInitializer());

Многопоточная модель:

private EventLoopGroup boss = new NioEventLoopGroup(1);
private EventLoopGroup work = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap()
                .group(boss,work)
                .childHandler(new HeartbeatInitializer());

Многопоточность ведущий-ведомый:

private EventLoopGroup boss = new NioEventLoopGroup();
private EventLoopGroup work = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap()
                .group(boss,work)
                .childHandler(new HeartbeatInitializer());

Я верю, что каждый может это понять.

Суммировать

На самом деле, после прочтения модели многопоточности Netty, можем ли мы вдохновиться нашими обычными высокопроизводительными приложениями?

Я думаю, это нормально:

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

Ничего больше, чем это, но после этого появятся другие проблемы:

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

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

Соответствующий код выше:

GitHub.com/crossover J я…

Добро пожаловать, чтобы обратить внимание на публичный аккаунт, чтобы общаться вместе: