【最佳实践】 - Netty

概述

本文介绍如何玩转 Netty.

一、服务端启动流程

  1. 通过 bind 方法添加监听器, 用以自动绑定递增端口.
  2. attr 方法, 为每条连接增加属性, 能够实现全单例模式. 类似于 request.attr 属性
  3. childOption 方法, 关于 TCP 连接的优化:
    • SO_KEEPALIVE 底层心跳
    • TCP_NODELAY 延迟发送
    • SO_BACKLOG 等待队列

二、客户端启动流程

  1. 还是通过监听器实现重试,但是是 connect 返回的 future,且重试间隔时间左移 1 位增加(性能优化,不使用乘二 ,牛的一逼)。
  2. 重试不在主线程,而是使用 bootstrap.config().group().schedule 搞定时任务
  3. 客户端需要 CONNECT_TIMEOUT_MILLIS 属性

三、客户端与服务端双向通信

  • 客户端在 channelActive 立刻搞事情,嗯,rpc 通信通常也会做一些处理,例如: 打印客户端ip 之类的。

四、客户端与服务端通信协议编解码(扩展较多)

emm,这个其实就是自定义应用层协议。

  1. 4 字节魔数校验,例如 dubbo 就使用0xdabb进行校验,Java 字节码也使用 0xcafebabe 校验字节码。
  2. 版本号肯定需要的
  3. 序列化算法,肯定也需要的
  4. 指令,肯定也是需要的,不过,也可以使用别的方式。
  5. 后面的 数据长度,也是需要的,方便拆包。

其实这里可以参照 RPC 协议 来看,这里更像一个简化的 RPC 协议。
一般 RPC 框架首先获取协议类型,根据这个协议类型得到协议处理器,然后再处理(一个端口处理多个协议的场景)。
生产级别的 RPC 通常较为复杂,以 SOFABolt 为例,需要以下字段:

  1. 协议版本
  2. 请求类型,即指令(Request,Response, oneway)
  3. 指令版本
  4. RequestID 负责数据对应
  5. 序列号器
  6. 协议开关(例如 CRC 校验,安全校验)
  7. 响应码,约定异常,简化异常
  8. 类名长度,Java rpc 框架必备
  9. 请求头长度(参照 http header)
  10. 请求体长度(参照http body)
  11. 类名
  12. 业务请求头内容(一般是 Map,SOFABolt 支持自定义,SOFARPC 里面藏着是否泛化调用等信息)
  13. 业务请求体内容(一般就是个Request对象或 Response对象,里面包含约定的属性,例如参数,返回值,超级多,SOFARPC 有个属性类 RemotingConstants,这里都有)
  14. CRC 校验码(金融场景必备)

五、实现客户端与服务端收发消息

  • 使用 hannel.attr(Attributes.LOGIN).set(true) 绑定登录标识方便哟

六、构建客户端与服务端 pipeline

  1. 常用的 ChannelInboundHandlerAdapterChannelOutboundHandlerAdapter 但是需要强转哦,麻烦, 建议使用 SimpleChannelInboundHandler (还帮你释放内存哟)。
  2. 不使用 MessageToByteEncoder ,可以自己编解码哦,虽然麻烦点

七、拆包粘包理论与解决方案

  1. 常用拆包:固定长度分隔符基于长度行拆包(不常用)
  2. 最通用的就是 基于长度,只要你的自定义协议中包含长度域字段,就可以使用
  3. LengthFieldBasedFrameDecoder 代替自己继承 ByteToMessageDecoder 哟。
  4. LengthFieldBasedFrameDecoder 扩展一下,校验魔数关闭错误连接。

八、channelHandler 的生命周期

  1. channelReadComplete 方法里执行 flush,批量刷新,性能提升。
  2. channelActivechannelInActive 增减连接,RPC 都这么干

九、使用 channelHandler 的热插拔实现客户端身份校验

  1. ctx.pipeline().remove(this) 删除没有必要的 handler
  2. handlerRemoved 回调

十、客户端互聊原理与实现

  1. Session 通过 channel.attr(Attributes.SESSION).set(session) 绑定连接。
  2. channel.attr(Attributes.SESSION).set(null) 删除 session
  3. channel.attr(Attributes.SESSION).get()

十一、群聊的发起与通知

  1. ChannelGroup channelGroup = new DefaultChannelGroup(ctx.executor()) 批量处理连接
  2. channelGroup.writeAndFlush 批量写连接

最佳实践

牛逼的性能优化
  1. 共享 handler
  2. 压缩 handler - 合并编解码器 —— MessageToMessageCodec
  3. 虽然 有状态 的 handler 不能搞单例,但是你可以绑定到 channel 属性 上, 强行单例
  4. 缩短事件传播路径 —— 放 Map 里,在第一个 handler 里根据指令来找具体 handler。
  5. 更改事件传播源 —— 用 ctx.writeAndFlush() 不要用 ctx.channel().writeAndFlush()
  6. 减少阻塞主线程的操作 —— 使用业务线程池,RPC 优化重点
  7. 计算耗时,使用回调 Future
心跳和空闲检测
  1. 空闲检测 IdleStateHandler 用起来很爽, channelIdleuserEventTriggered 都可以处理
  2. 定时心跳 ctx.executor().scheduleAtFixedRate 很优秀
  3. 通常 空闲检测时间 要比 发送心跳的时间两倍要长一些(3倍),这也是为了排除偶发的公网抖动,防止误判。

666 彩蛋

刚开始写博客, 希望大家支持, 如果有没疑问或不清楚的地方可以留言噢!

下周再见~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值