《Netty实战》-学习笔记1

《Netty实战》-学习笔记1

Chapter 3 Netty的组件及设计

  • Channel:Socket,对应一个链接。java

    • 提供基本的 I/O 操做:如 bind()connect()read()write() 等,就是对 Java 中实现的 Socket 进行必定层次的封装。
  • EventLoop:控制流、多线程处理、并发。数组

    • 在一个 EventLoopGroup 中包含一个或多个 EventLoop
    • 一个 EventLoop 在它的生命周期内只和一个 Thread 绑定。
    • 全部 EventLoop 处理的 I/O 事件都在它专有的 Thread 上被处理。
    • 一个 Channel 在它的生命周期内只注册于一个 EventLoop
    • 一个 EventLoop 可能会被分配给一个或多个 Channel
  • ChannelFuture:异步通知。缓存

    • 在 Netty 中全部操做的是异步的,调用时不会当即返回结果,可是返回一个 Future 。可以在以后的某个时间点肯定结果,对 Future 添加 Listener 注册回调,再操做完成后通知执行相应的回调。
  • ChannelHandler:在 Channel 上发生事件的处理器。服务器

    • 由网络事件触发的处理器,实际上能够响应任何类型的动做。
    • ChannelInboundHandler:接收入站事件和数据。
    • ChannelOutboundHandler:数据出站处理器。
  • ChannelPipelineChannelHandler 链的容器。网络

    • ChannelPipeline 就是一些 ChannelHandler 的编排顺序,每一个 ChannelHandler 接收事件,执行所实现的处理逻辑,完毕后交给下个一个 ChannelHandler
    • 从客户端角度来讲,发送数据到服务器就是出站的。则经过 ChannelPipeline 的编排,在必定次序下调用 ChannelOutboundHandler 。响应的接收服务器发来数据就是入站的,在一个 ChannelPipeline 按顺序调用 ChannelInboundHandler
    • 以下图:客户端操做系统从网络上接收服务器发来数据放入缓存中,接下来,在 Netty 中从 ChannelPipeline 中从头部开始按顺序调用 ChannnelInboundHandler。从客户端发送数据到服务器则是从 ChannelPipeline 的尾部开始执行,直到发送到网络上。
  • ChannelHandlerContext:表明了 ChannelHandlerChannelPipeline 之间的绑定。多线程

    • 能够用于获取底层Channel
  • 两种发送消息的方式:并发

    • 直接写入 Channel 中:消息从 ChannelPipeline 的尾部开始流动。
    • 写入到 ChannelHandler 相关联的 ChannelHandlerContext 对象中: 消息从 ChannelPipeline 的下一个ChannelHandler开始流动。

Chapter 4 传输API

Channel

每一个 Channel 都会被分配一个 ChannelPipelineChannelConfig

  • ChannelConfig:对应 Channel 的配置设置,支持热更新。
  • ChannelPipeline: 持有全部将应用于入站和出站数据以及事件的 ChannelHandler 实例,这些 ChannelHandler 实现了应用程序用于处理状态变化以及数据处理的逻辑。

内置传输

名称 描述
NIO io.netty.channel.socket.nio 使用 java.nio.channels 包做为基础——基于选择器(select)
Epoll io.netty.channel.epoll 由 JNI 驱动的 epoll() 和非阻塞 IO。这个传输支持只有在Linux上可用的多种特性,如 SO_REUSEPORT,比NIO 传输更快,并且是彻底非阻塞的
OIO io.netty.channel.socket.oio 使用 java.net 包做为基础——使用阻塞流
Local io.netty.channel.local 能够在 VM 内部经过管道进行通讯的本地传输
Embedded io.netty.channel.embedded Embedded 传输,容许使用 ChannelHandler 而又不须要一个真正的基于网络的传输。这在测试 ChannelHandler 实现时很是有用
  • NIO 基于选择器,每一个 Channel 在选择器(Selector)上注册,当 Selector 调用 select() 的时候是阻塞的,直到已经注册的 Channel 中有相应的事件发生。
事件 描述
OP_ACCEPT 请求在接受新链接并建立 Channel 时得到通知
OP_CONNECT 请求在创建一个链接时得到通知
OP_READ 请求当数据已经就绪,能够从 Channel 中读取时得到通知
OP_WRITE 请求当能够向 Channel 中写更多的数据时得到通知。这处理了套接字缓冲区被彻底填满时的状况,这种状况一般发生在数据的发送速度比远程节点可处理的速度更快的时候

处理模型: 异步

  • Epoll NIO 是基于 Select 的,那么 Epoll 如其名,则是基于 Linux 的 epoll 。好处不言而喻,提供比旧的 select 和 poll 更好的性能。 对应的则是 EpollEventLoopGroupEpollServerSocketChannelsocket

  • OIO 创建在 java.net 包下,不是异步的。可以经过设置 SO_TIMEOUT 来在单线程中处理多个链接。函数

  • Embedded Embedded 提供了能够将一组 ChannelHandler 做为帮助器嵌入到其余的 ChannelHandler 内部。这样能够扩展一个 ChannelHandler 的功能,但又不须要修改其内部代码。

Chapter 5 ByteBuf

ByteBuf工做原理

维护了两个不一样的索引:readerIndexwriterIndex ,一个用于读取,一个用于写入。当从 ByteBuf 读取时,readerIndex 递增。一样当从 ByteBuf 写入时, writerIndex 递增。 若是 readerIndex 递增到和 writerIndex 相同的值,那么到达“可读取数据的末尾”,这时再进行读取则会像读取超出数组末尾的数据同样,抛出 IndexOutOfBoundsException。 另外,名称以 read 或者 write 开头的 ByteBuf 方法会推动相应的索引值,而以 set 或者 get 开头的方法则不会(这样只是在相应的索引值上进行操做)。

使用模式

在弄清楚 ByteBuf 的使用模式以前,应该理解 Java 中的直接缓冲区和非直接缓冲区:

  • 直接缓冲区:缓冲区创建在物理内存中,能够提升效率。

  • 非直接缓冲区:缓冲区创建在 JVM 的内存中。

  • 堆缓冲区(backing 模式) ByteBuf 数据存储在 JVM 的堆空间(非直接缓冲区)中。这种模式也被称为“支撑数组(backing array)”,可以在没有使用池化的的状况下提供快速的分配和释放。

  • 直接缓冲区(direct 模式) 经过本地调用分配内存,直接分配的是物理内存。

  • 复合缓冲区 为多个 ByteBuf 提供一个聚合视图。

    • CompositeByteBuf:可以提供将多个缓冲区(每一个 ByteBuf 能够是 backing 模式或者 direct 模式)表示为单个合并缓冲区的虚拟表示。

字节级操做

  1. 随机访问索引 ByteBuf 的索引是从 0 开始到 capacity() - 1。 获取每一个字节的方法则是:byte getByte(int i); 这样倒不会改变 readerIndexwriterIndex 的值。 固然能够经过:ByteBuf readerIndex(int index)ByteBuf writerIndex(int index)来手动移动读取/写入索引值。
  2. 顺序访问索引 有三个区域:
  3. 可丢弃字节 经过调用discardReadBytes()方法,能够丢弃已经被读过的字节以回收空间。是的,调用discardReadBytes()能够确保可写分段的最大化,可是有可能致使内存复制(可读字节会移动到缓冲区的开始位置)。一样的可丢弃字节空间也只是相应的更改了 writerIndex 的值,并不会保证空间中值的擦写。
  4. 可读字节 ByteBuf 的可读字节分段(CONTENT)存储了实际数据。任何名称以 read 或者 skip 开头的操做都将检索或者跳过位于当前 readerIndex 的数据,而且将它增长已读字节数。
  5. 可写字节 可写字节分段是指一个拥有未定义内容的、写入就绪的内存区域。
  6. 索引管理 在 JDK 中 InputStream定义了mark(int readlimit)reset()方法,来标记流中指定位置,以及重置。在 ByteBuf 中也有相应的方法来标记和重置 ByteBuf 中的 readerIndexwriterIndex
    • markReaderIndex()
    • markWriterIndex()
    • resetReaderIndex()
    • resetWriterIndex() 还有clear()方法只是重置索引,且不会复制内存。
  7. 查找操做 最简单的,可使用indexOf()方法。 或者使用ByteProcessor类。
  8. 派生缓冲区 提供了专门的方法来呈现器内容的视图:
    • duplicate()
    • slice()
    • slice(int , int)
    • Unpooled.unmodifiableBuffer(...)
    • order(ByteOrder)
    • readSlice(int) 以上每一个方法都会返回一个新的ByteBuf实例,具备本身的读索引、写索引和标记索引。可是内部存储是共享的。因此若是修改了它的内容,也同时修改了其对应的源实例。 若是要深拷贝得到真实副本,使用copy()或者copy(int ,int)函数。
  9. 读/写操做 两种类别的读/写操做:
    • get()set()操做,从给定的索引值开始,且保持索引值不变。
    • read()write()操做,从给定的索引开始,且会根据以及访问过的字节数堆索引进行调整。 get()方法:
      set()方法:
      read()方法:
      write()方法:
  10. 更多的操做

ByteBuf分配

  1. 按需分配:ByteBufAllocator接口 ByteBufAllocator的方法:
    能够经过Channel或者ChannelHandlerContext获取到一个ByteBufAllocator的引用。 Netty提供了两种ByteBufAllocator的实现:
  • PooledByteBufAllocator:池化了ByteBuf的实例以提升性能而且最大限度的减小内存碎片。
  • UnpooledByteBufAllocator:不池化ByteBuf实例,可是每次被调用时会返回一个新的实例。
  1. Unpooled缓冲区 没法获取到ByteBufAllocator的引用时,有一个简单的Unpooled工具类,提供静态的辅助方法来建立未池化的ByteBuf实例。
  2. ByteBufUtil类 这个类提供了用于操做ByteBuf的静态辅助方法。
相关文章
相关标签/搜索