im
Introduction: (1)基于 netty 集群开发 Im,通过 zookeeper 作为注册中心。(2)服务集群连接支持多种负载均衡策略(3)集群间消息转发提供多种解决方案。
Tags:
一、概述
使用 netty 开发分布式 Im,提供分布 netty 集群解决方案。服务端通过负载均衡策略与服务集群建立连接,消息发送通过服务间集群的通信进行消息转发。
二、自定义协议
1.自定义协议要素
- 魔数,用来在第一时间判定是否是无效数据包
- 版本号,可以支持协议的升级
- 序列化算法,消息正文到底采用哪种序列化反序列化方式,可以由此扩展,例如:json、protobuf、hessian、jdk
- 指令类型,是登录、注册、单聊、群聊… 跟业务相关
- 请求序号,为了双工通信,提供异步能力
- 正文长度
- 消息正文
三、集群架构

1.客户端
用户聊天客户端,客户端连接 IM 服务需要进行用户认证。用户认证成功之后,开始连接上线。2.服务路由
服务路由负责将客户端的连接请求按照不同的负载均衡策略路由到不同的 IM 服务,建立长链接。负载均衡策略分为以下四种: - 一致性 HASH 负载均衡策略
- 最少活跃数负载均衡策略
- 随机调用负载均衡策略
- 轮询调用负载均衡策略
3.IM 服务集群
为了避免单节点故障,IM 服务采用集群模式。集群内各个 IM 服务又互为对方的客户端,用于转发远程消息(消息接收客户端连接其他 IM 服务节点)。4.ZK 集群
ZK 集群作为 IM 服务的注册中心,用户 IM 服务的注册与发现以及服务上线、下线的事件监听通知。通过 node 事件,控制 IM 服务之间连接的建立与断开。5.消息队列
消息队列用户发送离线消息、聊天消息。6.MongoDB 集群
存储离线消息及聊天消息。7.Redis 集群
存储客户端的连接 session 信息(客户端与服务端连接的信息)四、netty 集群方案
首先需要明确一个问题,netty 的 channel 是无法存储到 redis 里面的。netty 的 channel 是一个连接,是和机器的硬件绑定的,无法序列化,计算存到 redis 里面,取出来也无法使用。1.ZK 作为注册中心实现
(1)channel 无法存储的问题
channel 是无法存储到 redis 里面的,但是客户端和服务端的连接信息(例如:127.0.0.1:8080 的服务端是 127.0.0.1:9090)是可以存储到 redis 里面的,因此可以通过 redis 存储连接信息。key 为客户端标识,value 为服务端地址信息,获取客户端的连接时,直接通过客户端信息即可获取其服务信息。

(2)服务端连接的问题
客户端连接服务端时,客户端如何知道当前服务端有哪些,需要要连接哪个?这个问题可以通过 ZK 解决。使用 ZK 作为注册中心,服务端上线后在 ZK 中创建 node,连接服务端时,从 ZK 获取在线节点信息,根据负载均衡策略选择服务端连接。

(3)消息转发的问题
连接相同服务的客户端,可以直接通过获连接当前服取客户端信息进行消息的转发,那连接不同服务端消息如何转发?我们可以通过监听 ZK 中 node 的事件(node 创建代表新的服务上线,node 销毁代表服务下线),通过不同的事件方法,实现服务端之间的互相连接。

2.redis 订阅与广播实现(可替换为消息队列进行处理)
redis 支持消息订阅与发布机制机制(消息队列),可以使用该机制实现不同服务间的消息转发。在广播消息时,需要携带能唯一标识接收者身份的字段(例如 clientId)。消息广播结束后,所有服务端会 收到该消息,服务端仅仅需要判断该消息接收者的是否是连接的自己作为服务端。若发现该接收者正是连接的自己,则直接将消息转发到该客户端即可。

