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

简介: 在之前的 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 整合长连接心跳机制 中的服务端代码:


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 的线程模型之后能否对我们平时做高性能应用带来点启发呢?


我认为是可以的:


  • 接口同步转异步处理。


  • 回调通知结果。


  • 多线程提高并发效率。


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


  • 异步之后事务如何保证?


  • 回调失败的情况?


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


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

上文相关的代码:


github.com/crossoverJi…


相关文章
|
7天前
|
缓存 Dubbo Java
Dubbo线程模型设计解析
该文章主要介绍了Dubbo线程模型的设计解析,包括Dubbo作为一个支持大量并发请求的网络框架的特点,以及其线程模型的工作原理。
|
5天前
|
编解码 网络协议 API
Netty运行原理问题之Netty的主次Reactor多线程模型工作的问题如何解决
Netty运行原理问题之Netty的主次Reactor多线程模型工作的问题如何解决
|
5天前
|
开发者
Netty运行原理问题之Netty高性能实现的问题如何解决
Netty运行原理问题之Netty高性能实现的问题如何解决
|
17天前
|
前端开发 网络协议
Netty实战巅峰:从零构建高性能IM即时通讯系统,解锁并发通信新境界
【8月更文挑战第3天】Netty是一款高性能、异步事件驱动的网络框架,适用于开发高并发网络应用,如即时通讯(IM)系统。本文将指导你利用Netty从零构建高性能IM程序,介绍Netty基础及服务器/客户端设计。服务器端使用`ServerBootstrap`启动,客户端通过`Bootstrap`连接服务器。示例展示了简单的服务器启动过程。通过深入学习,可进一步实现用户认证等功能,打造出更完善的IM系统。
42 1
|
19天前
|
编解码 NoSQL Redis
(十一)Netty实战篇:基于Netty框架打造一款高性能的IM即时通讯程序
关于Netty网络框架的内容,前面已经讲了两个章节,但总归来说难以真正掌握,毕竟只是对其中一个个组件进行讲解,很难让诸位将其串起来形成一条线,所以本章中则会结合实战案例,对Netty进行更深层次的学习与掌握,实战案例也并不难,一个非常朴素的IM聊天程序。
|
27天前
|
缓存 编译器 Go
开发与运维线程问题之Go语言的goroutine基于线程模型实现如何解决
开发与运维线程问题之Go语言的goroutine基于线程模型实现如何解决
37 3
|
27天前
|
算法 调度 人工智能
人工智能线程问题之无锁化编程如何解决
人工智能线程问题之无锁化编程如何解决
29 2
|
5天前
|
存储 Kubernetes NoSQL
Tair的发展问题之Tair在适配不同的存储介质时对于线程模型该如何选择
Tair的发展问题之Tair在适配不同的存储介质时对于线程模型该如何选择
|
30天前
|
Java Linux
Java演进问题之1:1线程模型对于I/O密集型任务如何解决
Java演进问题之1:1线程模型对于I/O密集型任务如何解决
|
1月前
|
前端开发 Java 数据处理
使用Netty构建高性能的网络应用
使用Netty构建高性能的网络应用