【java入门到放弃】Netty初阶
write和writeAndFlush
write是写入消息,writeAndFlush是写入消息并发送
ctx.writeAndFlush(msg); 等价于 ctx.write(msg); ctx.flush();
Channel和 ChannelHandlerContext调用 write()/ writeAndFlush()
Channel:从整个 Pipeline 的“尾部”发数据,也就是会经过所有Handler
ChannelHandlerContext:从“当前 Handler 的位置”发数据,只经过当前Handler及当前Handler前面的Handler
不要在 EventLoop 线程里 sync / await
EventLoop 线程是“干活的线程”,sync() / await() 是“等结果的操作”;
在 EventLoop 里等待 EventLoop 自己完成任务,必然产生死锁。
Promise
概念
Promise = 可写的 ChannelFuture
ChannelFuture:只能查看异步结果,不可主动完成
Promise:可以主动设置结果,同时可以注册 Listener 等待或回调
创建
在 Netty 中,绝大多数情况下都是使用 DefaultPromise 作为 Promise 的实现类。
DefaultPromise promise = new DefaultPromise<>(eventExecutors);
这里的 Integer 表示 Promise 中保存的结果类型 。
使用都是我们自己给promise赋值,而不是使用promise来接收值
方法
promise.setSuccess(result); // 成功完成
promise.setFailure(cause); // 失败完成
promise.isSuccess(); // 是否成功
promise.isDone(); // 是否已完成(成功 or 失败)
promise.cause(); // 失败原因(成功时为 null)
promise.getNow(); // 非阻塞,没完成返回 null,失败不抛异常
promise.get(); // 阻塞等待完成,失败会抛异常
//监听完成(核心)
promise.addListener(future -> {
if (future.isSuccess()) {
Object result = future.getNow();
}
});
promise.sync(); // 等待完成,失败会抛异常
promise.await(); // 等待完成,不抛异常
promise.get();//阻塞期间,如果线程被中断 → 抛 InterruptedException
promise.sync();//阻塞期间,如果线程被中断 → 抛 InterruptedException
promise.await();//阻塞期间,如果线程被中断 → 抛 InterruptedException
promise.awaitUninterruptibly();//阻塞但忽略中断,不抛异常;失败也不抛异常
ByteBuf
注意
ByteBuf 既是 Netty Handler 之间的底层数据载体,也是客户端与服务器之间网络通信的最终数据格式。任何发送到网络的数据,最终都会被编码成 ByteBuf。
接收消息时的 ByteBuf,是 Netty 在 EventLoop 的 IO 读阶段、在数据进入 pipeline 之前创建的;Handler 只是接收并处理它,而不是创建它。
释放
ChannelInboundHandlerAdapter:默认不会释放ByteBuf,但是pipeline末尾有 TailContext,会自动释放 ByteBuf。
默认传给下一个 handler 不需要 retain
SimpleChannelInboundHandle:channelRead0() 处理完立即 release ByteBuf。
如果要继续传递:ctx.fireChannelRead(msg.retain()); // 保留引用计数
TailContext 是 pipeline 的最后一个节点
入站消息如果 没人消费,TailContext 会自动调用 ReferenceCountUtil.release(msg)
这保证了即使你忘记释放,Netty 也不会泄漏内存
解决半包粘包
FixedLengthFrameDecoder固定长度
//把收到的 ByteBuf 按固定长度拆包,每 8 个字节作为一条消息传递给下一个 Handler
//拆包器要放在 StringDecoder / 自定义 Handler 之前
ch.pipeline().addLast(new FixedLengthFrameDecoder(8));
LineBasedFrameDecoder固定分隔符
//服务端加入,默认以 \n 或 \r\n 作为分隔符,如果超出指定长度仍未出现分隔符,则抛出异常
ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
//LineBasedFrameDecoder(int maxLength)
//LineBasedFrameDecoder(int maxLength, boolean stripDelimiter, boolean failFast)
//maxLength 一行最大长度,如果超过会抛 TooLongFrameException
//stripDelimiter 是否去掉行尾的换行符(默认 true)
//failFast 超过最大长度是否立即抛异常(true = 立即抛,false = 等到行尾)
//缺点,处理字符数据比较合适,但如果内容本身包含了分隔符(字节数据常常会有此情况),那么就会解析错误
LengthFieldBasedFrameDecoder预设长度
//构造方法
LengthFieldBasedFrameDecoder(
int maxFrameLength,
int lengthFieldOffset,
int lengthFieldLength,
int lengthAdjustment,
int initialBytesToStrip
)
//maxFrameLength 消息最大长度,如果超过会抛 TooLongFrameException
//lengthFieldOffset 长度字段在消息中的偏移量(从 0 开始)(1 → 长度字段从消息的第 2 个字节开始)
//lengthFieldLength 长度字段占用字节数(1,2,3,4,8 都可以)
//lengthAdjustment 长度字段指的长度是否包括长度字段本身或消息头,需要调整
//0表示消息长度字段只算消息体
//-4表示消息长度字段减去4,才是消息体长度
//initialBytesToStrip 传给下一个 Handler 时,是否去掉长度字段前多少字节(通常去掉长度字段本身)
DelimiterBasedFrameDecoder指定分隔符
//构造方法
DelimiterBasedFrameDecoder(
int maxFrameLength,
boolean stripDelimiter,
ByteBuf... delimiters
)
//maxFrameLength 消息最大长度,超过抛 TooLongFrameException
//stripDelimiter 是否去掉分隔符(true = 去掉)
//delimiters 一个或多个 ByteBuf 分隔符(支持多个分隔符)
//Netty 提供 Unpooled.copiedBuffer() 来生成分隔符:
ByteBuf delimiter = Unpooled.copiedBuffer("\n", Charset.forName("UTF-8"));
//例
ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, true, Unpooled.copiedBuffer("\n", Charset.forName("UTF-8")));
更多推荐



所有评论(0)