开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 4 天,点击查看活动详情
Netty集群实现方案
单节点的IM服务是不可靠的,流量多一点就会扛不住,所以需要做集群提高稳定性和并发性。 由于Netty本身没有提供开箱即用的集群方案,所以需要借助第三方中间件来实现。下面看看Netty集群的实现方案。
基于Zookeeper实现集群
现在很多都拥抱Nacos了,原理是一样的。甚至如果不想引入新的中间件,使用Redis也是可以的。
- 选用Zookeeper的临时节点存储服务的信息(主要关注ip和port),当连接断开或Session超时会删除数据。
- Netty启动时把自身的ip+port写入到Zookeeper的临时节点目录。
- 服务调用者读取ZooKeeper的数据获取服务列表并设置回调监听(Watcher机制),当数据发生变化时会接收到通知做更新。
- 3中服务调用者拿到Netty服务的地址列表,就可以根据负载均衡算法进行服务调用了。
上面调用Netty集群的服务调用者有两种类型,业务层和终端设备。终端设备容易理解,通俗讲就是用户端和Netty集群建立长连接。那业务层为什么需要调用Netty集群?下面说说业务层。
先看看IM消息在系统是怎么发送到长连接服务层的:
有疑问的可以参考:juejin.cn/post/719771…
上图(1)用户A发送IM消息经过一个logic服务处理消息(敏感词检验、用户是否封禁等)然后通过(2)MQ转发到push推送服务,(3)push使用gRpc将im消息发送到长连接层,(4)长连接层的Netty服务调用writeAndFlush()发送消息给B。
如果长连接层的Netty服务只有一个节点,那么上图中的(3)gRpc通讯就可以直接写死Netty服务的地址,但为了可靠性,Netty通常都会部署多台。而多台Netty服务做集群时,业务层(即3中的gRpc)就会从ZooKeeper中获取Netty服务的地址列表,这样就可以做单播、广播调用了。
调用Netty集群的负载均衡实现
上面讲到有两种类型的客户端调用Netty集群,分别为终端设备和业务层调用。
终端设备调用Netty集群的负载均衡实现
终端设备一般指用户端(下文都称用户端)与Netty集群建立长连接,然后使用负载权衡算法选择集群中的某一台服务建立连接。下面看几种实现方式。
1. 基于Zookeeper实现
1.1 随机/轮询/一致性Hash 算法
从上面得知ZooKeeper中存着所有Netty的ip+port信息,用户端建立连接前先发送HTTP请求拉取到Netty服务地址的列表,然后根据这个列表可以实现随机/轮询/一致性Hash负载均衡算法挑选出一台Netty服务建立连接。
2.2 最少活跃算法
实现最少活跃(即Netty集群中连接最少的服务)算法需要在Netty服务做一个统计上报数据功能。当有新连接或断开连接,可以利用Redis的incr()自增decr()自减对该服务做统计,用户端请求获取Netty服务列表地址时把这个统计数据一并返回去。客户端选用最少连接的Netty服务即可。
Redis做统计只是其中一种,也可以直接写入到ZooKeeper中统计,实现方式多样性。
以上2种方式都是基于Zookeeper,这里会存在以下问题:
- 客户端是直接使用机器的ip+port发起连接,这增加运维难度,企业应用中都是使用域名统一发起请求的。
- 实时性较差:当Netty集群数有变更时(上下线服务),客户端有可能会选中一台已下线的服务,或者新服务调用不到。
所以是不推荐这种方式。
2. 基于Nginx实现
Nginx可以支持7层协议,也支持4层协议。那TCP长连接是4层协议,那就可以在Nginx配置,然后使用Nginx提供的负载均衡算法就可以了。
3. 云服务
不差钱的中小型项目可以考虑下直接购买云产品,比如阿里云的SLB。这种方式是最省事的,花点小钱就可以完成。
4. 其他:LVS、F5
业务层调用Netty集群的负载均衡实现
业务层调用Netty集群就简单多了,从ZooKeeper中获取Netty服务列表,然后根据用户的缓存获取用户在哪台Netty上,发起调用就可以了。这里可以参考juejin.cn/post/719779…