📦 项目十一:ChunkedStream数据流切块传输
项目概述
项目名称:itstack-demo-netty-2-11
核心功能:基于ChunkedStream实现数据流切块传输
技术要点:ChunkedWriteHandler、ChunkedStream、流量控制
为什么需要这个项目?
实际问题:
- 大数据量传输容易导致内存溢出
- 写操作非阻塞,数据可能堆积在内存中
- 网络传输饱和时,数据处理拥堵
- GC频繁,影响系统性能
解决方案:
- ✅ ChunkedStream:自动从InputStream分块读取
- ✅ ChunkedWriteHandler:Netty提供的分块写入处理器
- ✅ 流量控制:避免数据堆积,保护内存
- ✅ 进度监控:实时监控传输进度
适用场景:
- 文件传输(大文件分块上传/下载)
- 视频流传输(视频数据分块发送)
- 大数据量推送(避免内存溢出)
- 实时数据流(日志流、监控数据流)
核心问题
为什么需要切块传输?
在Netty异步NIO框架下,高效、频繁、大量写入大块数据时:
- 网络传输饱和 → 数据处理拥堵
- GC频繁 → 内存压力大
- 用户掉线 → 连接不稳定
- 内存耗尽风险 → 写操作非阻塞,即使数据没写完也会返回
核心原因:写操作是非阻塞的,即使没有写出所有数据,写操作也会在完成时返回并通知ChannelFuture。
核心知识点
1. Netty提供的ChunkedInput实现
| 实现类 | 用途 |
|---|---|
| ChunkedFile | 从文件中逐块获取数据 |
| ChunkedNioFile | 使用FileChannel逐块获取 |
| ChunkedStream | 从InputStream中逐块传输 ⭐ |
| ChunkedNioStream | 从ReadableByteChannel中逐块传输 |
2. Pipeline配置
protected void initChannel(SocketChannel channel) {
// 1. 基于换行符的帧解码器
channel.pipeline().addLast(new LineBasedFrameDecoder(1024));
// 2. 流量分块处理器 ⭐关键
channel.pipeline().addLast(new ChunkedWriteHandler());
// 3. 自定义分块处理器
channel.pipeline().addLast(new MyServerChunkHandler());
// 4. 字符串解码/编码
channel.pipeline().addLast(new StringDecoder(Charset.forName("GBK")));
channel.pipeline().addLast(new StringEncoder(Charset.forName("GBK")));
// 5. 业务处理器
channel.pipeline().addLast(new MyServerHandler());
}
3. 核心分块逻辑
MyServerChunkHandler.java
public class MyServerChunkHandler extends ChannelOutboundHandlerAdapter {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
// 1. 获取ByteBuf数据
ByteBuf buf = (ByteBuf) msg;
byte[] data = this.getData(buf);
// 2. 将数据写入输入流
ByteInputStream in = new ByteInputStream();
in.setBuf(data);
// 3. 创建分块流 - 每次传输10个字节 ⭐核心
ChunkedStream stream = new ChunkedStream(in, 10);
// 4. 创建进度监听器
ChannelProgressivePromise progressivePromise =
ctx.channel().newProgressivePromise();
progressivePromise.addListener(new ChannelProgressiveFutureListener() {
@Override
public void operationProgressed(ChannelProgressiveFuture future,
long progress, long total) {
// 监控传输进度
}
@Override
public void operationComplete(ChannelProgressiveFuture future) {
if (future.isSuccess()) {
System.out.println("消息发送成功");
promise.setSuccess();
} else {
System.out.println("消息发送失败");
promise.setFailure(future.cause());
}
}
});
// 5. 释放原始消息
ReferenceCountUtil.release(msg);
// 6. 写入分块流
ctx.write(stream, progressivePromise);
}
}
4. ChunkedStream工作原理
ChunkedStream stream = new ChunkedStream(in, 10);
- 参数1:InputStream - 数据源
- 参数2:10 - 每次读取的字节数(分块大小)
工作流程:
1. 系统以固定速率向令牌桶中添加令牌
例如:10字节/秒 = 每100ms添加1字节令牌
2. 当需要发送数据时:
- 检查令牌桶中是否有足够的令牌
- 有令牌:消耗令牌,发送数据
- 无令牌:将数据放入队列,等待令牌
3. 流量整形效果:
- 即使应用层快速写入大量数据
- 实际发送速率被限制在设定值
- 超出部分被缓存,不会丢失
数据流程
客户端发送30字节数据
↓
ChunkedWriteHandler拦截
↓
创建ChunkedStream(in, 10) // 每次10字节
↓
第1秒:发送10字节,剩余20字节进入队列
↓
第2秒:发送10字节,剩余10字节进入队列
↓
第3秒:发送10字节,全部发送完成
↓
触发operationComplete回调
项目总结
核心技术:
- ✅ ChunkedStream:自动从InputStream分块读取
- ✅ ChunkedWriteHandler:Netty提供的分块写入处理器
- ✅ ChannelProgressivePromise:监控传输进度
- ✅ 分块大小可调整:示例10字节,实际可设置更大
适用场景:
- 文件传输(大文件分块上传/下载)
- 视频流传输(视频数据分块发送)
- 大数据量推送(避免内存溢出)
- 实时数据流(日志流、监控数据流)
关键要点:
- 分块传输有效避免内存问题
- 可以监控传输进度
- 适用于大数据传输场景
&spm=1001.2101.3001.5002&articleId=157907374&d=1&t=3&u=4794213b90b9431fb59642ced4727558)
5191

被折叠的 条评论
为什么被折叠?



