通信传输模式、ByteBuf

1、选择合适的内置通信传输模式

  • NIO io.netty.channel.socket.nio 使用java.nio.channels 包作为基础——基于选择器的方式。这个是JDK考虑到所有操作系统所做出的妥协。

  • Epoll(仅Linux):io.netty.channel.epoll 由 JNI 驱动的 epoll()和非阻塞 IO。这个传输支持只有在Linux 上可用的多种特性,如SO_REUSEPORT,比NIO 传输更快,而且是完全非阻塞的。将NioEventLoopGroup替换为EpollEventLoopGroup , 并且将NioServerSocketChannel.class 替换为EpollServerSocketChannel.class 即可。

  • OIOio.netty.channel.socket.oio 使用java.net 包作为基础——使用阻塞流

  • Local:io.netty.channel.local 可以在VM 内部通过管道进行通信的本地传输

  • Embeddedio.netty.channel.embedded Embedded 传输,允许使用ChannelHandler 而又不需要一个真正的基于网络的传输。在测试ChannelHandler 实现时非常有用。

2、Bootstrap

        “服务器”和“客户端”实际上表示了不同的网络行为;换句话说,是监听传入的连接还是建立到一个或者多个进程的连接。因此,有两种类型的引导:一种用于客户端(简单地称为Bootstrap),而另一种(ServerBootstrap)用于服务器。无论你的应用程序使用哪种协议或者处理哪种类型的数据,唯一决定它使用哪种引导类的是它是作为一个客户端还是作为一个服务器。

区别:

  1.  ServerBootstrap 将绑定到一个端口,因为服务器必须要监听连接,而Bootstrap 则是由想要连接到远程节点的客户端应用程序所使用的。

  2. 第二个区别可能更加明显。引导一个客户端只需要一个EventLoopGroup,但是一个ServerBootstrap 则需要两个(也可以是同一个实例)。

      与ServerChannel 相关联的EventLoopGroup 将分配一个负责为传入连接请求创建Channel 的EventLoop。一旦连接被接受,第二个EventLoopGroup 就会给它的Channel分配一个EventLoop。因为服务器需要两组不同的Channel。第一组将只包含一个ServerChannel,代表服务器自身的已绑定到某个本地端口的正在监听的套接字。而第二组将包含所有已创建的用来处理传入客户端连接(对于每个服务器已经接受的连接都有一个)的Channel。

3、ByteBuf

        Java NIO提供了ByteBuffer作为字节容器,但是这个类使用起来过于复杂,而且也有些麻烦。所以在Netty中用ByteBuf代替ByteBuffer。

3.1 读写操作

        ByteBuf 维护了两个不同的索引:一个用于读取,一个用于写入。当你从ByteBuf 读取时,它的readerIndex 将会被递增已经被读取的字节数。同样地,当你写入ByteBuf 时,它的writerIndex 也会被递增。图5-1展示了一个空ByteBuf 的布局结构和状态。

名称以read 或者write 开头的ByteBuf 方法,将会推进其对应的索引readerIndex和writerInde,而名称以set 或者get 开头的操作则不会。

 ByteBuf被它的两个索引划分为了三个区域,如图所示:

可丢弃字节:

在上图中标记为可丢弃字节的分段包含了已经被读过的字节。通过调用discardReadBytes()方法,可以丢弃它们并回收空间。这个分段的初始大小为0,存储在readerIndex 中,会随着read 操作的执行而增加(get*操作不会移动readerIndex)。

 索引管理

  • markReaderIndex()+resetReaderIndex() ----- markReaderIndex()是先备份当前的readerIndex,resetReaderIndex()则是将刚刚备份的readerIndex恢复回来。常用于dump ByteBuf的内容,又不想影响原来ByteBuf的readerIndex的值
  • readerIndex(int) ----- 设置readerIndex为固定的值
  • writerIndex(int) ----- 设置writerIndex为固定的值
  • clear() ----- 效果是: readerIndex=0, writerIndex(0)。不会清除内存
  • 调用clear()比调用discardReadBytes()轻量的多。仅仅重置readerIndex和writerIndex的值,不会拷贝任何内存,开销较小。

3.2 ByteBuf的使用模式

netty 中的 ByteBuf 可以从两个大的角度分为一下四类:

  • DirectByteBuf:直接内存
  • HeapByteBuf:堆内存
  • PooledByteBuf:池化的 buffer(默认)
  • UnpooledByteBuf:非池化的 buffer

        直接内存与堆内存的区别在于,直接内存不属于 JVM 堆内存,其优点是由于采用直接内存省去了程序运行过程中数据反复在直接内存于堆内存之间不断复制,所以大大提高了I/O的效率,也减少了 JVM GC 的时间,但缺点是其创建和销毁过程开销会比堆内存大。

        所以为了提高直接内存的效率,netty 采用了池化内存的方案,池化直接内存首先会在直接内存中申请一块内存,创建相应的 ByteBuf 时会从池中获取内存空间,用完之后将空间回收。

         ByteBuf本质是: 一个由不同的索引分别控制读访问和写访问的字节数组。请记住这句话。ByteBuf共有三种模式: 堆缓冲区模式(Heap Buffer)、直接缓冲区模式(Direct Buffer)和复合缓冲区模式(Composite Buffer).

1. 堆缓冲区模式(Heap Buffer)

堆缓冲区模式又称为:支撑数组(backing array)。将数据存放在JVM的堆空间,通过将数据存储在数组中实现

  • 堆缓冲的优点: 由于数据存储在Jvm堆中可以快速创建和快速释放,并且提供了数组直接快速访问的方法
  • 堆缓冲的缺点: 每次数据与I/O进行传输时,都需要将数据拷贝到直接缓冲区
public static void heapBuffer() {
    // 创建Java堆缓冲区
    ByteBuf heapBuf = Unpooled.buffer(); 
    if (heapBuf.hasArray()) { // 是数组支撑
        byte[] array = heapBuf.array();
        int offset = heapBuf.arrayOffset() + heapBuf.readerIndex();
        int length = heapBuf.readableBytes();
        handleArray(array, offset, length);
    }
}

2. 直接缓冲区模式(Direct Buffer)

Direct Buffer属于堆外分配的直接内存,不会占用堆的容量。适用于套接字传输过程,避免了数据从内部缓冲区拷贝到直接缓冲区的过程,性能较好

  • Direct Buffer的优点: 使用Socket传递数据时性能很好,避免了数据从Jvm堆内存拷贝到直接缓冲区的过程。提高了性能
  • Direct Buffer的缺点: 相对于堆缓冲区而言,Direct Buffer分配内存空间和释放更为昂贵
  • 对于涉及大量I/O的数据读写,建议使用Direct Buffer。而对于用于后端的业务消息编解码模块建议使用Heap Buffer。
public static void directBuffer() {
    ByteBuf directBuf = Unpooled.directBuffer();
    if (!directBuf.hasArray()) {
        int length = directBuf.readableBytes();
        byte[] array = new byte[length];
        directBuf.getBytes(directBuf.readerIndex(), array);
        handleArray(array, 0, length);
    }
}

 3. 复合缓冲区模式(Composite Buffer)

Composite Buffer是Netty特有的缓冲区。本质上类似于提供一个或多个ByteBuf的组合视图,可以根据需要添加和删除不同类型的ByteBuf。

  • 想要理解Composite Buffer,请记住:它是一个组合视图。它提供一种访问方式让使用者自由的组合多个ByteBuf,避免了拷贝和分配新的缓冲区。
  • Composite Buffer不支持访问其支撑数组。因此如果要访问,需要先将内容拷贝到堆内存中,再进行访问
  • 下图是将两个ByteBuf:头部+Body组合在一起,没有进行任何复制过程。仅仅创建了一个视图
public static void byteBufComposite() {
    // 复合缓冲区,只是提供一个视图
    CompositeByteBuf messageBuf = Unpooled.compositeBuffer();
    ByteBuf headerBuf = Unpooled.buffer(); // can be backing or direct
    ByteBuf bodyBuf = Unpooled.directBuffer();   // can be backing or direct
    messageBuf.addComponents(headerBuf, bodyBuf);
    messageBuf.removeComponent(0); // remove the header
    for (ByteBuf buf : messageBuf) {
        System.out.println(buf.toString());
    }
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值