Netty实战应用学习笔记-中级拓展篇(十一)

📦 项目十一: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字节,实际可设置更大

适用场景

  • 文件传输(大文件分块上传/下载)
  • 视频流传输(视频数据分块发送)
  • 大数据量推送(避免内存溢出)
  • 实时数据流(日志流、监控数据流)

关键要点

  • 分块传输有效避免内存问题
  • 可以监控传输进度
  • 适用于大数据传输场景

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值