大家好,我是小趴菜,本次我分享一下关于我们项目中引入IM服务的一些技术挑战与解决方案
对于网络通信,我们可以使用NIO,Mina,Netty来实现,但是NIO开发复杂,API较多,这就导致我们的研发成本就会提高,所以我们使用了当前最流行的Netty来实现我们的IM服务
我们项目中主要是涉及网页版的单人聊天,接下来我们分析一下项目中遇到的挑战以及解决思路
对于一个IM服务来说,我们要把保证数据的安全性,可靠性,唯一性
消息可靠性
对于一条IM消息来说,可靠性是要保证发送方能将一条消息准确的完整的发送给接收方,一条消息的发送流程也就包括以下几个步骤
1:发送方发送一条消息给IM服务器
2:IM服务器将这条消息保存到MySql中,保存成功之后,发送一个ACK给发送方,来告诉发送方,你的消息已经成功发送到了IM服务器
3:IM服务器将消息推送给接收方
4:接收方收到消息之后,发送一个ACk给IM服务器,告诉IM服务器这条消息已经接收到了
那么消息在哪些步骤中可能会发生丢失呢??
-
1:消息发送给IM服务器,这时候由于网络或者其它原因,都有可能导致IM服务器接收不到这条消息
-
2:IM服务器接收到这条消息了,也将消息成功保存到了MySql,但是这时候发送ACk的时候,发送失败了,这就可能导致发送方接收不到这条消息的ACK
-
3:IM服务器将消息推送给接收方,但是由于网络或者其它原因,导致接收方根本就没有收到这条消息
-
4:接收方收到这条消息了,但是在发送ACk的时候失败了,导致IM服务器没有接收到这条消息的ACk确认消息
所以以上几个步骤都有可能出现消息丢失的情况,所以接下来我们来一个一个的解决
解决方案
发送方要确定这条消息是否已经成功发送到IM服务器,就是通过IM服务器发送的确认ACk消息来确认的,这时候如果在一定的时间内,发送方没有接收到ACK确认消息,这时候我们可以通过重试机制来重新发送这条消息,当然也不能一直重试,我们要限制重试次数,直到发送方接收到了这条消息的ACK,这样就可以确定这条消息已经成功保存到了IM服务器了
当然在接收方一端处理方式也一样,在一定的时间内,我们IM服务器没有接收到这条消息的ACK消息,那么服务端就再次推送这条消息给接收方,直到收到这条消息的ACK确认消息
消息的唯一性
虽然我们可以通过重试机制来保证消息能成功发送到IM服务器,也可以通过重试机制来将消息推送给接收方,我们还是来看下整个流程
- 1:第三步的时候,我们发送ACK消息失败了,这时候发送方没接收到ACK那么就会重试,这时候服务端就会出现消息重复的情况,也就是一条消息会被存几份
- 2:第六步的时候,IM服务器没有接收到接收方发送的ACK,这时候IM服务器就会重新将这条消息发送给接收方,那么接收方就会出现重复的数据
解决方案
所以我们要做好消息的唯一性,做好去重的处理,比如说我们给每一条消息设置一个唯一值,比如UUID,雪花算法,redis的自增
消息有序性
对于一个IM系统,消息是一定要保证有序性,如果消息无序,那么同样一句话表达的意思就有可能是完全不一样,所以我们必须要保证消息的有序性
对于消息的有序性,我们可以根据消息的唯一ID来实现,所以这时候UUID就不适合了,但是雪花算法也有可能是会重复的,所以项目中我们是使用redis的自增来实现
但是这时候有个问题,redis的自增是有一个上限的,但是项目中本身就是网页版的,不像是微信这种交友聊天,所以使用redis的自增,完全可以满足我们的业务需求,如果大家想知道微信这种有序实现方案,可以参考 code84.com/126194.html
所以最终在项目中我们决定使用redis的自增方式来实现消息的唯一值,后续就可以根据这个唯一的值来实现消息的有序性,当然就增加了redis的维护成本,以及如果后续redis崩了,那么就无法为消息生成唯一值了,所以相对应的也增加了redis的维护
后续扩展
后续就有可能还会实现消息的已读,未读功能,当然大家还可以进行扩展,比如客户端重练如何加载之前的聊天记录,当然在当前项目中是不需要实现的,客户端重连你不需要加载之前的聊天记录信息。