大家好,我是马丁,今天给大家分享的是即时通讯系统常见的挑战和解决办法~
一、通讯服务如何应对海量连接
单台服务器能够承载的连接是有限的,要想能够应对海量的连接,让我们的系统支持弹性扩容,在负责和用户终端进行连接的IM服务集群中,我们可以通过增加多台IM服务,通过Nginx对websocket连接请求进行负载均衡。
那用户发送消息时,如何找对接受人终端所连接的IM服务呢?
答:可以将用户ID、终端和IM服务进行绑定并缓存起来,当用户打开应用时与IM服务建立连接、校验用户身份通过后缓存连接的IM服务编号。这样,当用户发送消息时,就可以先通过缓存找到接受人连接的IM服务,然后给对应的服务发送消息了。
对发送消息给IM服务这块,为了提高系统性能,也可以通过MQ实现异步调用,同时,也可以更好地应对更大的流量。
二、多端消息同步
客户端现有 PC 浏览器、Windows 客户端、H5、iOS/Android,系统允许用户多端同时在线,且同一端可以多个状态,这就需要保证多端、多用户、多状态的消息是同步的。
解决办法:可以用到 Redis 的 Hash 存储,将用户信息、终端标识、连接信息等记录下来,这样通过 key(uid) 就能找到一个用户在多个端的连接,通过 key+field 能定位到一条连接。
三、用户在线状态同步:
比如一个用户先后和 4 个客服咨询过,那么这个用户会出现在 4 个客服的咨询列表里。当用户上线时,要保证 4 个客服看到用户都是在线状态。
要做到这一点有两种方案:
- 一种是通过轮询获取用户的状态,但这样当用户在线状态没有变化时,会发起很多无效的请求;
- 另外一种是用户上线时,给客服推送上线通知,这样会造成消息扩散,每一个咨询过的客服都需要扩散通知。如果是大量的通知依然会消耗服务器的计算资源和网络带宽。
针对第二种的办法,可以通过将同时上线的多个用户的通知进行聚合,以减少通知数量。比如可以将一段时间内上线的用户信息汇总成一个通知发送给客服。
四、消息的不丢失,不重复:
为了避免消息丢失,使用 WebSocket 方式的,服务端会在推送给客户端消息后,等待客户端的 ACK,如果客户端没有 ACK,服务端会尝试多次推送。
这时就需要客户端根据消息 ID 做消息重复的处理,避免客户端可能已收到消息,但是由于其它原因导致 ACK 确认失败,触发重试,导致消息重复。如果服务端进行多次重试后依然推送失败,那么可以考虑将连接断开。
在客户端可以通过生成一个随机数,发送消息时带上这个随机数,如果因为网络原因重试了,服务端可以通过随机数进行去重。
五、用户断网后,重新连接带来的问题
本来你在APP上聊着好着呢,结果路由器因为发热宕机了,你用的APP会每隔一段时间尝试重新连接,当网络恢复之后,终于连接上了,但是链接可能是另外一台IM服务了,毕竟有多台IM服务呢,相当于你的登陆信息在两台服务器上保存了。
解决办法:
1、 将用户终端连接的IM服务关系放在Redis中,在IM服务对连接请求进行登陆校验时,重试时连接的新的IM服务ID会替换掉之前的IM服务ID,这样,就不会保存两份了。另外说一句,如果登陆校验失败,服务端会主动关闭连接;
2、用户终端连接一台IM服务后,记录这台服务的ID,然后,后续断网重试时,连接之前的服务ID即可。
六、万人大群该如何设计
有些IM应用支持万人大群,在节假日,特别是元旦、春节或者双 11 这样的重大活动时期,管理层和员工在大群高频互动,流量洪峰瞬间流过 IM 系统,挑战着系统的极限。这个时候,系统如何设计才能支撑这么庞大的流量。
解决办法:
- 在客户端、服务端使用多级缓存。
- 当系统负载超过一定阈值,对沟通频率高的群进行限流。
七、历史消息该如何设计?
在一个IM系统中,用户有时候会查询历史消息,如何设计才能保证性能最优、成本最低呢?
解决办法:
-
因为消息具有典型的冷热属性: 用户访问的绝大部分都是最近的数据。可以使用冷热分离架构,在冷库使用低成本高压缩率的存储引擎,大幅下降存储成本。
-
最近的消息通过同步协议推送到达客户端本地。而历史的消息,服务端不曾推送,客户端本地没有入库。在用户进入会话时,如果客户端发现本地消息不足,自动从服务端拉取不足的历史消息。采用这种推拉结合的协议,保证了消息不管多么久远,都可以毫无遗漏的从服务端同步下来。
好了,今天就写到这里了,后续会接着分享~看到这里了,如果觉得有点用,一键三连+关注支持下吧