发布时间:2026-07-21阅读(1)
分布式系统的根基在于网络编程,而 Netty 是 Java 领域网络编程的王者。
第一部分 NIO 编程,三大组件
第二部分 netty 入门学习,EventLoop、Channel、Future、Pipeline、Handler、byteBuf
第三部分 Netty 进阶学习,粘包半包的解决方法、协议的设计、序列化知识
第四部分 Netty 常见参数的学习及优化
第五部分 源码
non Blocking IO 非阻塞 IO
channel 有一点类似于 stream,它就是读写数据的双向通道,可以从 channel 将数据读入 buffer,也可以将 buffer 的数据写入 channel,而之前的 stream 要么是写入,要么是输出。
常见的 Channel 有:
FileChannel
DatagramChannel
socketChannel
ServerSocketChannel
buffer 则用来缓冲读写数据,常见的 buffer 有:
ByteBuffer
MappedByteBuffer
DirectByteBuffer
HeapByteBuffer
short/Int/Long/Float/Double/Char Buffer
使用多线程技术
为每个连接分别开辟一个线程,分别去处理对应的 socket 连接

:exclamation: 多线程缺点
内存占用高
线程上下文切换成本高
只适合连接数较少的场景
使用线程池技术
使用线程池,让线程池中的线程去处理连接

这种方式存在以下几个问题:
阻塞模式下,线程仅能处理一个连接
仅适合短连接场景
使用 Selector
selector 的作用就是配合一个线程来管理多个 channel(fileChannel 因为是阻塞式的,所以无法使用 selector),获取这些 channel 上发生的事件,这些 channel 工作在非阻塞模式下,当一个 channel 中没有执行任务时,可以去执行其他 channel 中的任务。适合连接数多,但流量较少的场景。

若事件未就绪,调用 selector 的 select 方法会阻塞线程,直到 channel 发生了就绪事件。这些事件就绪后,select 方法就会返回这些事件交给 thread 来处理。
2.ByteBuffer
使用案例
有一普通文本文件 data.txt 内容为
1234567890abc
使用 FileChannel 来读取文件内容
@Slf4jpublicclassTestByteBuffer{publicstaticvoidmain(String[] args) {// FileChannel// 1.输入输出流 2.RandomAccessFiletry {FileChannel fileChannel = new FileInputStream("data.txt").getChannel;// 准备缓冲区ByteBuffer buf = ByteBuffer.allocate(10);// 从 Channel 中读取数据,向 Buffer 写入int len;while ((len = fileChannel.read(buf)) != -1) {log.info("读取到的字节:{}", len);buf.flip; // 切换至读模式log.debug("输出内容为:{}", new String(buf.array, 0, len));// while (buf.hasRemaining) { // 是否还剩余数据// byte b = buf.get;// log.debug("输出内容为:{}", (char) b);// }// 切换为写模式buf.clear;}} catch (IOException e) {e.printStackTrace;}}}
向 buffer 写入数据,e.g. 调用 channel.read(buf)
调用 flip切换至读模式
向 buffer 读取数据,e.g. 调用 buf.get
调用 clear或compact切换至写模式
重复 1~4 步骤
2.2 ByteBuffer 结构
核心属性
字节缓冲区的父类 Buffer 中有几个核心属性,如下:
// Invariants: mark <= position <= limit <= capacityprivate int mark = -1;private int position = 0;private int limit;private int capacity;
capacity:缓冲区的容量。通过构造函数赋予,一旦设置,无法更改。
limit:缓冲区的界限。位于 limit 后的数据不可读写。缓冲的限制不能为负,并且不能大于其容量。
position:下一个读写位置的索引(类似 PC)。缓冲区的位置不能为负,并且不能大于 limit。
mark:记录当前 position 的值。position 被改变后,可以通过调用reset方法恢复到 mark 的位置。
核心方法:
put方法
put 方法可以将一个数据放入缓冲区
进行该操作后,position 的值会 1,指向下一个可以放入的位置。capacity = limit。
flip方法
flip 方法会切换对缓冲区的操作模式,由写 -> 读 / 读 -> 写
进行该操作后
如果是 写模式 -> 读模式,position = 0,limit 指向最后一个元素的下一个位置,capacity 不变
如果是读 -> 写,则恢复为 put 方法中的值
get方法
get方法会读取缓冲区中的一个值
进行该操作后,position 会 1,如果超过了 limit 则会抛出异常
注意:get(i)方法不会改变 position 的值
rewind方法
该方法只能在读写模式下使用
rewind方法后,会恢复 position、limit 和 capacity 的值,变为进行 get 前的值
clean方法
clean 方法会将缓冲区中的各个属性恢复为最初的状态,position = 0,capacity = limit
此时,缓冲区的数据依然存在,处于“被遗忘”状态,下次进行写操作时会覆盖这些数据
mark和reset方法
mark 方法会将 position 的值保存到 mark 属性中
reset 方法会将 position 的值改为 mark 中保存的值
compact方法
此方法为 ByteBuffer 的方法,而不是 Buffer 的方法
compact 会把未读完的数据向前压缩,然后切换到写模式
数据前移后,原位置的值并未清零,写时会覆盖之前的值
ByteBuffer 有以下重要属性
capacity
position
limit
刚开始

写模式下,position 是写入位置,limit 等于容量,下图表示写入了 4 个字节后的状态。

flip 动作发生后,position 切换为读取位置,limit 切换为读取限制。

读取 4 个 byte 后,状态:

clear 动作发生后,状态变为一开始。
compact 方法,是把未读完的部分向前压缩,然后切换至写模式。

Copyright © 2024 有趣生活 All Rights Reserve吉ICP备19000289号-5 TXT地图HTML地图XML地图