在以前的 SpringBoot 整合长链接心跳机制 一文中认识了 Netty。java
但其实只是能用,为何要用 Netty?它有哪些优点?这些其实都不清楚。git
本文就来从历史源头说道说道。github
在 Netty 以及 NIO 出现以前,咱们写 IO 应用其实用的都是用 java.io.*
下所提供的包。 bootstrap
好比下面的伪代码:多线程
ServeSocket serverSocket = new ServeSocket(8080); Socket socket = serverSocket.accept() ; BufferReader in = .... ; String request ; while((request = in.readLine()) != null){ new Thread(new Task()).start() }
<!--more-->并发
大概是这样,其实主要想表达的是:这样一个线程只能处理一个链接。异步
若是是 100 个客户端链接那就得开 100 个线程,1000 那就得 1000 个线程。socket
要知道线程资源很是宝贵,每次的建立都会带来消耗,并且每一个线程还得为它分配对应的栈内存。高并发
即使是咱们给 JVM 足够的内存,大量线程所带来的上下文切换也是受不了的。oop
而且传统 IO 是阻塞模式,每一次的响应必须的是发起 IO 请求,处理请求完成再同时返回,直接的结果就是性能差,吞吐量低。
所以业界经常使用的高性能 IO 模型是 Reactor
。
它是一种异步、非阻塞的事件驱动模型。
一般也表现为如下三种方式:
从图中能够看出:
它是由一个线程来接收客户端的链接,并将该请求分发到对应的事件处理 handler 中,整个过程彻底是异步非阻塞的;而且彻底不存在共享资源的问题。因此理论上来讲吞吐量也还不错。
但因为是一个线程,对多核 CPU 利用率不高,一旦有大量的客户端链接上来性能必然降低,甚至会有大量请求没法响应。
最坏的状况是一旦这个线程哪里没有处理好进入了死循环那整个服务都将不可用!
所以产生了多线程模型。
其实最大的改进就是将原有的事件处理改成了多线程。
能够基于 Java 自身的线程池实现,这样在大量请求的处理上性能提示是巨大的。
虽然如此,但理论上来讲依然有一个地方是单点的;那就是处理客户端链接的线程。
由于大多数服务端应用或多或少在链接时都会处理一些业务,如鉴权之类的,当链接的客户端愈来愈多时这一个线程依然会存在性能问题。
因而又有了下面的线程模型。
该模型将客户端链接那一块的线程也改成多线程,称为主线程。
同时也是多个子线程来处理事件响应,这样不管是链接仍是事件都是高性能的。
以上谈了这么多其实 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
欢迎关注公众号一块儿交流: