项目背景
作为团队的内部技术需求,需要开发一个长链接服务,主要用作为服务端向客户端主动推送消息的通道,计划用做一些配置信息的下发。对于Netty、webSocket等属于0经验。接到这个需求,开始进行技术调研,搭建demo服务,与客户端进行方案讨论,一步一步完成服务的正式上线。
经过持续的优化,技术具备了实时推送能力,后续逐步接入了更多的业务侧实时触达需求。
技术选型
Java + springBoot + Netty + MQ + Kafka. 采用WebSocket协议。
公司日活百万用户,同时APP在线设备数据15w左右,上线后,整体服务运行稳定。
总体的架构设计

协议层
与客户端同学一起讨论方案后,最后采用webSocekt协议。
服务端可以同时支持Socket IO + ProtoBuf方式。
长链接服务
采用核心服务与接入服务解耦分开,这样设计初衷保证核心服务的高稳定性,与业务进行隔离,核心服务将设备的绑定/解绑信息通过kafka 推送给接入服务进行设备实时在线信息的维护,同时接入端负责对接业务层的需求,通过MQ将推送的消息推给接入服务,接入端通过RPC方式推给核心服务,找到在线设备,进行消息推送。同时对于离线消息,通过一定规则,当设备再次上线后,可进行投递。
业务流程

补充介绍
socket方式
socket方式采用ProtoBuf 协议,webSocket 目前仅采用字符串方式(TextWebSocketFrame)
Socket 方式网络协议头
/** * +---------------------------------------------------------------+ * | 魔数 2byte | 协议版本号 1byte | 序列化算法 1byte | 报文类型 1byte | * +---------------------------------------------------------------+ * | 状态 1byte | 消息 ID 8byte | 数据长度 4byte | * +---------------------------------------------------------------+ * | 数据内容 (长度不定) | * +---------------------------------------------------------------+ */
内容 | 属性名 | 类型 | 描述 | 默认值 |
魔数 | magic | short | 魔数,是协定的一个共识字段,判断请求协议的魔数是否合法。 | 2 |
协议版本号 | version | byte | 为了应对业务需求的变化,可能需要对自定义协议的结构或字段进行改动。不同版本的协议对应的解析方法也是不同的 | 1 |
序列化算法 | serialization | byte | 序列化算法字段表示发送方将对象转换成二进制流,以及接收方将接收的二进制流转换成对象的方法,具体类型值稍后补充 | 1 |
报文类型 | messageType | byte | 报文类型用于描述业务场景中存在的不同报文类型。如 RPC 框架中有请求、响应、心跳类型。IM 通讯场景中有登陆、创建群聊、发送消息、接收消息、退出群聊等类型 | 根据业务场景讨论确认
1:绑定
2:重连
3:关闭
4:响应(response)
5:心跳(heartBeat)ping
6:心跳(heartBeat)pong
7:业务1
8:业务2
9:业务3 |
状态 | status | byte | 状态字段用于标识请求是否正常,一般由被调用方设置,这里暂时可以设置默认值 | 1 |
请求ID | requestId | long | 作为串联整个请求的ID,请求方自定义就好 | 请求方生成 |
数据长度 | messageLength | int | 消息体内容长度(二进制字节流) | ㅤ |
消息内容 | ㅤ | byte | 消息内容二进制字节 | ㅤ |
核心流程设计
1、服务部署
长链接服务的核心服务,4节点,单节点4C12G。突发应急情况下K8S可支持动态扩缩容。
长链接服务的接入服务,4节点,单节点4C4G。同样支持动态扩展。
2、通道维护
每个设备的连接,通过Map<deviceId,Channel>维护在服务内存里,链接的创建及断开进行增删。
3、消息路由
通过MQ的广播消息实现。(也可以通过Redis的发布订阅模式、Zookeeper的watch机制)
当长链接接入端收到消息触达请求,组装好请求后,通过rpc推送给长链接的核心服务,由于是多节点部署,核心服务由一个节点收到请求后,判断此设备是否在当前节点,如果在,则找到对应的Channel,判断channel存活状态后,将消息推送出去,获得response success后,将推送成功的消息由kafka推送给接入服务。如果此设备通道不在当前节点,则发送MQ广播消息,去其它几个节点寻找,找到后,则进行推送。
4、离线消息
当推送消息时,设备正好离线没有成功推送,则通过一定规则比如:24小时内,推送不超过3次,设备再次上线则进行消息触达。
5、埋点监控
对于设备的在线情况,可以通过metrics埋点,看到设备的实时在线情况。
监控数据看板

项目代码示例
- 作者:DaLong
- 链接:https://www.dalongxyz.cc/article/990e192d-8142-478d-9ad4-b33c1d5e2a67
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

