Netty入门

Netty 是什么?

  • Netty是 一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。
  • Netty是基于nio的,它封装了jdk的nio,让我们使用起来更加方法灵活。

Netty 的特点是什么?

  • 高并发:Netty 是一款基于 NIO(Nonblocking IO,非阻塞IO)开发的网络通信框架,对比于 BIO(Blocking I/O,阻塞IO),他的并发性能得到了很大提高。
  • 传输快:Netty 的传输依赖于零拷贝特性,尽量减少不必要的内存拷贝,实现了更高效率的传输。
  • 封装好:Netty 封装了 NIO 操作的很多细节,提供了易于使用调用接口。

Netty 的优势有哪些?

  • 使用简单:封装了 NIO 的很多细节,使用更简单。
  • 功能强大:预置了多种编解码功能,支持多种主流协议。
  • 定制能力强:可以通过 ChannelHandler 对通信框架进行灵活地扩展。
  • 性能高:通过与其他业界主流的 NIO 框架对比,Netty 的综合性能最优。
  • 稳定:Netty 修复了已经发现的所有 NIO 的 bug,让开发人员可以专注于业务本身。
  • 社区活跃:Netty 是活跃的开源项目,版本迭代周期短,bug 修复速度快。

Netty 的应用场景有哪些?

  • 阿里分布式服务框架 Dubbo,默认使用 Netty 作为基础通信组件
  • RocketMQ 也是使用 Netty 作为通讯的基础。
  • Lettuce redis客户端也是基于netty的连接实例(StatefullRedisConnection),可以在多线程下并发访问,线程安全;

Netty 高性能表现在哪些方面?

  • IO 线程模型:同步非阻塞,用最少的资源做更多的事。
  • 内存零拷贝:尽量减少不必要的内存拷贝,实现了更高效率的传输。
  • 内存池设计:申请的内存可以重用,主要指直接内存。内部实现是用一颗二叉查找树管理内存分配情况。
  • 串形化处理读写:避免使用锁带来的性能开销。
  • 高性能序列化协议:支持 protobuf 等高性能序列化协议。

IO 线程模型

BIO:同步阻塞,一个连接一个线程
NIO:同步非阻塞,多个连接一个线程(selector用于接收连接的线程)
I/O复用模型:当有多个客户端连接请求时会先请求到selector多路复用器上,selector作用第一用于接收连接,第二轮询判断哪些连接有io操作,有io操作的会真正调用io操作函数;这样做的好处是:
同时接收多个并发连接,空闲时可以处理部分io请求
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-427jG7L5-1619252593217)(en-resource://database/2090:1)]
下面举一个例子,模拟一个tcp服务器处理30个客户socket。
假设你是一个老师,让30个学生解答一道题目,然后检查学生做的是否正确,你有下面几个选择:

  1. 第一种选择:按顺序逐个检查,先检查A,然后是B,之后是C、D。。。这中间如果有一个学生卡主,全班都会被耽误。
    这种模式就好比,你用循环挨个处理socket,根本不具有并发能力。
  2. 第二种选择:你创建30个分身,每个分身检查一个学生的答案是否正确。 这种类似于为每一个用户创建一个进程或者线程处理连接。
  3. 第三种选择,你站在讲台上等,谁解答完谁举手。这时C、D举手,表示他们解答问题完毕,你下去依次检查C、D的答案,然后继续回到讲台上等。此时E、A又举手,然后去处理E和A。。。
    这种就是IO复用模型,Linux下的select、poll和epoll就是干这个的。将用户socket对应的fd注册进epoll,然后epoll帮你监听哪些socket上有消息到达,这样就避免了大量的无用操作。此时的socket应该采用非阻塞模式。
    这样,整个过程只在调用select、poll、epoll这些调用的时候才会阻塞,收发客户消息是不会阻塞的,整个进程或者线程就被充分利用起来,这就是事件驱动,所谓的reactor模式。

线程模型

一般设计事件处理模型有两种:

  • 轮询:启动一个线程轮询事件发生源有没有事件发生,有的话就调用事件处理器执行;
  • 事件驱动(事件通知):当发生事件时,主线程将事件加入事件队列中,然后子线程会轮询消费事件队列中的事件,然后调用事件处理器去处理,类似观察者模式思路
Reactor线程模型

是事件驱动模型netty实现的一种方式

  1. Reactor是反应堆的意思,Reactor模型,是指通过一个或多个输入同时传递给服务处理器的服务请求的事件驱动处理模式。 服务端程序处理传入多路请求,并将它们同步分派给请求对应的处理线程,Reactor模式也叫Dispatcher模式,即I/O多了复用统一监听事件,收到事件后分发(Dispatch给某进程)。
  2. 具体思路如下图:其中
    一个eventdispatch:主要负责监听事件和分发事件
    多个handler:主要处理实际的事件请求
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GgqPPLR6-1619252593219)(en-resource://database/2092:1)]

3.三种线程模型

  • 单Reactor单线程执行handler
  • 单Reactor多线程执行handler
  • 多(主从)Reactor多线程执行handler(Netty采用)
    注意:
    netty是基于主从Reactor多线程,借用了MainReactor和SubReactor的结构,但是实际实现上,SubReactor和Worker线程在同一个线程池中。
    EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); ServerBootstrap server = new ServerBootstrap(); server.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class)
    上面代码中的bossGroup 和workerGroup是Bootstrap构造方法中传入的两个对象,这两个group均是线程池

bossGroup线程池则只是在bind某个端口后,获得其中一个线程作为MainReactor,专门处理端口的accept事件,每个端口对应一个boss线程
workerGroup线程池会被各个SubReactor和worker线程充分利用

总结:
Netty通过Reactor模型基于多路复用器接收并处理用户请求,内部实现了两个线程池,boss线程池和work线程池,其中boss线程池的线程负责处理请求的accept事件,当接收到accept事件的请求时,把对应的socket封装到一个NioSocketChannel中,并交给work线程池,其中work线程池负责请求的read和write事件,由对应的Handler处理。
主从线程模型见下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9wFz4yDF-1619252593220)(en-resource://database/2094:1)]

零拷贝

Netty 的零拷贝主要包含三个方面:

  • Netty 的接收和发送 ByteBuffer 采用 DIRECT BUFFERS,使用堆外直接内存进行 Socket 读写,不需要进行字节缓冲区的二次拷贝。如果使用传统的堆内存(HEAP BUFFERS)进行 Socket 读写,JVM 会将堆内存 Buffer 拷贝一份到直接内存中,然后才写入 Socket 中。相比于堆外直接内存,消息在发送过程中多了一次缓冲区的内存拷贝。
  • Netty 提供了组合 Buffer 对象,可以聚合多个 ByteBuffer 对象,用户可以像操作一个 Buffer 那样方便的对组合 Buffer 进行操作,避免了传统通过内存拷贝的方式将几个小 Buffer 合并成一个大的 Buffer。
  • Netty 的文件传输采用了 transferTo 方法,它可以直接将文件缓冲区的数据发送到目标 Channel,避免了传统通过循环 write 方式导致的内存拷贝问题。

你可能感兴趣的:(网络技术)