资讯专栏INFORMATION COLUMN

Netty(二) 从线程模型的角度看 Netty 为什么是高性能的?

G9YH / 2800人阅读

摘要:主从多线程该模型将客户端连接那一块的线程也改为多线程,称为主线程。同时也是多个子线程来处理事件响应,这样无论是连接还是事件都是高性能的。多线程提高并发效率。

前言

在之前的 SpringBoot 整合长连接心跳机制 一文中认识了 Netty。

但其实只是能用,为什么要用 Netty?它有哪些优势?这些其实都不清楚。

本文就来从历史源头说道说道。

传统 IO

在 Netty 以及 NIO 出现之前,我们写 IO 应用其实用的都是用 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 足够的内存,大量线程所带来的上下文切换也是受不了的。

并且传统 IO 是阻塞模式,每一次的响应必须的是发起 IO 请求,处理请求完成再同时返回,直接的结果就是性能差,吞吐量低。
Reactor 模型

因此业界常用的高性能 IO 模型是 Reactor

它是一种异步、非阻塞的事件驱动模型。

通常也表现为以下三种方式:

单线程

从图中可以看出:

它是由一个线程来接收客户端的连接,并将该请求分发到对应的事件处理 handler 中,整个过程完全是异步非阻塞的;并且完全不存在共享资源的问题。所以理论上来说吞吐量也还不错。

但由于是一个线程,对多核 CPU 利用率不高,一旦有大量的客户端连接上来性能必然下降,甚至会有大量请求无法响应。
最坏的情况是一旦这个线程哪里没有处理好进入了死循环那整个服务都将不可用!
多线程

因此产生了多线程模型。

其实最大的改进就是将原有的事件处理改为了多线程。

可以基于 Java 自身的线程池实现,这样在大量请求的处理上性能提示是巨大的。

虽然如此,但理论上来说依然有一个地方是单点的;那就是处理客户端连接的线程。

因为大多数服务端应用或多或少在连接时都会处理一些业务,如鉴权之类的,当连接的客户端越来越多时这一个线程依然会存在性能问题。

于是又有了下面的线程模型。

主从多线程

该模型将客户端连接那一块的线程也改为多线程,称为主线程。

同时也是多个子线程来处理事件响应,这样无论是连接还是事件都是高性能的。

Netty 实现

以上谈了这么多其实 Netty 的线程模型与之的类似。

我们回到之前 SpringBoot 整合长连接心跳机制TCP-Heartbeat/) 中的服务端代码:

    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 成功");
        }
    }

其实这里的 boss 就相当于 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 的线程模型之后能否对我们平时做高性能应用带来点启发呢?

我认为是可以的:

接口同步转异步处理。

回调通知结果。

多线程提高并发效率。

无非也就是这些,只是做了这些之后就会带来其他问题:

异步之后事务如何保证?

回调失败的情况?

多线程所带来的上下文切换、共享资源的问题。

这就是一个博弈的过程,想要做到一个尽量高效的应用是需要不断磨合试错的。

上文相关的代码:

https://github.com/crossoverJie/netty-action

欢迎关注公众号一起交流:

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/71483.html

相关文章

  • 少啰嗦!一分钟带你读懂JavaNIO和经典IO区别

    摘要:的选择器允许单个线程监视多个输入通道。一旦执行的线程已经超过读取代码中的某个数据片段,该线程就不会在数据中向后移动通常不会。 1、引言 很多初涉网络编程的程序员,在研究Java NIO(即异步IO)和经典IO(也就是常说的阻塞式IO)的API时,很快就会发现一个问题:我什么时候应该使用经典IO,什么时候应该使用NIO? 在本文中,将尝试用简明扼要的文字,阐明Java NIO和经典IO之...

    Meils 评论0 收藏0
  • Netty4.x 源码实战系列(五):深入浅出学NioEventLoopGroup

    摘要:接下来的两篇文章,我将从源码角度为大家深入浅出的剖析的线程模型工作机制。我们看一下的源码通过的代码发现,实现了接口,其内部会通过指定的默认线程工厂来创建线程,并执行相应的任务。至此,初始化完成了。下一篇我们将详细介绍,敬请期待。 我们都知道Netty的线程模型是基于React的线程模型,并且我们都知道Netty是一个高性能的NIO框架,那么其线程模型必定是它的重要贡献之一。 在使用ne...

    MSchumi 评论0 收藏0
  • 彻底理解Netty,这一篇文章就够了

    摘要:如果什么事都没得做,它也不会死循环,它会将线程休眠起来,直到下一个事件来了再继续干活,这样的一个线程称之为线程。而请求处理逻辑既可以使用单独的线程池进行处理,也可以跟放在读写线程一块处理。 Netty到底是什么 从HTTP说起 有了Netty,你可以实现自己的HTTP服务器,FTP服务器,UDP服务器,RPC服务器,WebSocket服务器,Redis的Proxy服务器,MySQL的P...

    yy13818512006 评论0 收藏0
  • 【源起Netty 正传】Netty Channel

    摘要:搞懂了这部分后,我们将明白在世界中扮演的角色进击的此图展示的已经算是优化后的了用到了线程池。多线程将这种处理操作分隔出来,非型操作业务操作配备以线程池,进化成多线程模型这样的架构,系统瓶颈转移至部分。 Channel定位 注意:如无特别说明,文中的Channel都指的是Netty Channel(io.netty.channel) 一周时间的Channel家族学习,一度让我怀疑人生——...

    jindong 评论0 收藏0
  • netty实战》阅读笔记(1)——Netty 概念及体系结构

    摘要:它使用了事件通知以确定在一组非阻塞套接字中有哪些已经就绪能够进行相关的操作。目前,可以把看作是传入入站或者传出出站数据的载体。出站事件是未来将会触发的某个动作的操作结果,这些动作包括打开或者关闭到远程节点的连接将数据写到或者冲刷到套接字。 netty的概念 定义 Netty 是一款异步的事件驱动的网络应用程序框架,支持快速地开发可维护的高性能的面向协议的服务器和客户端。我们可以很简单的...

    solocoder 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<