IM学习总结

前言

个人出于兴趣关于IM的一些学习总结,欢迎各位大佬指点,我会从客户端发送端,服务器,客户端接收端三个层次,然后描述各个层次作用以及注意点。此篇文章要求有一定的netty和IM基础,不懂得地方可以评论我会尽力解答。

文章中一些概念的解释

消息风暴扩散系数:是指一个消息发出时,变成N个消息的扩散系数,这个系数与业务及数据相关,一定程度上它的大小决定了技术采用推送还是拉取。如果对业务实时性要求较高,可以采用推送的方式同步;如果实时性要求不高,可以采用轮询拉取的方式同步。
举例

  • 好友状态,如果对实时性要求较高,可以采用推送的方式同步;如果实时性要求不高,可以采用轮询拉取的方式同步;
  • 群友的状态,由于消息风暴扩散系数过大,可以采用按需拉取,延时拉取的方式同步;
  • 系统消息/开屏广告等对实时性要求不高的业务,可以采用拉取的方式获取消息;

IM通信数据格式参考:
跟着源码学IM(二):自已开发IM很难?手把手教你撸一个Andriod版IM_7-2.jpg
在此基础上还可以定义一种回执数据格式ACK,MsgType有三种,分别是sent(已发送),delivered(已送达), read(已读),以及加密相关的。

举例:
消息回执表:用来记录消息的已读回执
msg_acks(sender_uid, msgid, recv_uid, gid,if_ack);
各字段的含义为:发送方UID,消息ID,回执方UID,群ID,回执标记。

消息通信注意TCP出现拆包/粘包的原因,那么,如何解决呢?

通常来说,有以下四种解决方式:

  • 1)消息定长;
  • 2)用回车换行符作为消息结束标志;
  • 3)用特殊分隔符作为消息结束标志,如\t、\n等,回车换行符其实就是特殊分隔符的一种;
  • 4)将消息分为消息头和消息体,在消息头中用字段标识消息总长度。
  • netty针对以上四种场景,给我们封装了以下四种对应的解码器:
  • 1)FixedLengthFrameDecoder,定长消息解码器;
  • 2)LineBasedFrameDecoder,回车换行符消息解码器;
  • 3)DelimiterBasedFrameDecoder,特殊分隔符消息解码器;
  • 4)LengthFieldBasedFrameDecoder,自定义长度消息解码器。

一、客户端发送端

功能点:

1.启动netty的IdleStateHandler进行心跳检测,检测不到了就关闭channel

2.单独开辟线程在channel记录时间实现重连逻辑,进行最大重试次数进行重试连接。

3.发送方每次发送一个消息,就必须要等待对方的ack回应,并且在ack确认消息中应该带有收到的id以便发送方识别。
4.发送方需要维护一个等待ack的队列。 每次发送一个消息之后,就将消息和一个计时器入队,另外存在一个线程一直轮询队列,如果有超时未收到ack的,就取出消息重发。
5.客户端登录时,先去数据库拉取自己的好友列表,再去缓存获取所有好友的在线状态,如果接收方下线要对自己的好友进行推送自己的下线状态。

注意点:

1.已读回执过多怎么办?
答:轮询拉取,比如群里发送方每发一条消息,会收到40个已读回执,采用轮询拉取(例如1分钟一次,一个小时也就60个请求),可以大大降低请求量。
2.发送方不在线怎么办?
答:在线实时推送,不在则考虑下次在线再拉取。
3.接收方下线要对自己的好友进行推送自己的下线状态,如果是群那怎么办,所有好友都互相推送系统可以承受住吗?
答:客户端发送方主动拉取,考虑到加的群一般很多所以只有进入的那个群才会开始拉取。
4.为什么需要单独开辟线程在channel记录时间进行重连逻辑?
在调用 ctx.writeAndFlush() 发送消息获取回调时。
其中是 isSuccess 并不能作为消息发送成功与否的标准:这是因为这里的 success 只是告知我们消息写入了 TCP 缓冲区成功了而已。
所以我们不能依据此来关闭客户端的连接,而是要判断 Channel 上绑定的时间与当前时间只差是否超过了阈值。超过了这个时间没收到ack则认为断线需要重连,指定时间收到ack则认为连接正常。

一个单独的线程来判断是否需要重连,不依赖于 IdleStateHandler。不然会因为IdleStateHandler 作为一个 ChannelInbound 也重写了 channelInactive() 方法,导致你自己写的重连代码逻辑被忽略

5.消息已读同步如何保证?
1)同步状态维护,为用户的每一个Session,维护一个时间戳,保存最后的读消息时间;
2)同一个人发出的消息,排序按消息附带的本地时间来排。不同人的消息,按照服务器时间排序。

6.为什么不能用keepalive做心跳?
考虑一种情况,某台服务器因为某些原因导致负载超高或者说掉线了无法及时响应,TCP探测周期太长。对客户端而言,这时的最好选择就是断线后重新连接其他服务器,而不是一直认为当前服务器是可用状态,一直向当前服务器发送些必然会失败的请求。
KeepAlive 并不适用于检测双方存活的场景,这种场景还得依赖于应用层的心跳。应用层心跳有着更大的灵活性,可以控制检测时机,间隔和处理流程,甚至可以在心跳包上附带额外信息。从这个角度而言,应用层的心跳的确是最佳实践。netty种pipeline 中加入了一个 10秒没有收到写消息的 IdleStateHandler,到时他会回调 ChannelInboundHandler 中的 userEventTriggered 方法。
7.超时未收到ack的消息有两种处理方式:

  • 1)和tcp一样不断发送直到收到ack为止。
  • 2)设定一个最大重试次数,超过这个次数还没收到ack,就使用失败机制处理,节约资源。可以主动断开和客户端接收方的连接,剩下未发送的消息就作为离线消息入库,客户端断连后尝试重连服务器即可。

二、服务器

功能点:

1.离线消息:
如果用户当前不在线,就必须把消息持久化下来,等待用户下次上线再推送,可以考虑使用mysql存储离线消息。为了方便地水平扩展,我们使用消息队列进行解耦。

  • 1)服务器接收到消息后如果发现接受方不在线,就发送给消息队列入库;
  • 2)接收方登录时,服务器从库里拉取离线消息进行推送。

注意点:

如何保证离线消息可靠传输?

1.服务器返回客户端“发送成功”ACK确认包(对于消息发送方而言,消息一旦落地存储至DB就认为是发送成功了)。离线消息删除也是同样的,要确保收到接收方ACK以后才进行消息删除。

2.防止离线消息重复推送:
我们思考一下多端登录的情况,Alice有两台设备同时登陆,在这种并发的情况下,我们就需要某种机制来保证离线消息只被读取一次。

这里利用CAS机制来实现:

  • 1)首先取出所有has_read=false的字段;
  • 2)检查每条消息的has_read值是否为false,如果是,则改为true。这是原子操作,修改成功则推送,失败则不推送。

3.服务器推送离线消息过多要考虑分页拉取,这样可以保证显示的实时性

4.服务端自动剔除离线客户端

5.IM的群聊消息,究竟存1份(即扩散读方式)还是存多份(即扩散写方式)?

任何架构方案都不是灵光一现,而是逐步迭代优化产生的:

  • 方案1:群聊消息存多份,只存在线,消息容易丢;
  • 方案2:群聊消息存多份,所有群友都存储,消息冗余多;
  • 方案3:群聊消息存多份,只存ID,未利用偏序;
  • 终极方案:群聊消息存一份,只存last_ack_msgid。

客户端接收方

注意点:

不重复

有的时候因为网络原因可能导致ack收到较慢,发送方就会重复发送,那么接收方必须有一个去重机制。

去重的方式是给每个消息增加一个唯一id。这个唯一id并不一定是全局的,只需要在一个会话中唯一即可。例如某两个人的会话,或者某一个群。如果网络断连了,重新连接后,就是新的会话了,id会重新从0开始。

不乱序

接收方需要在当前会话中维护收到的最后一个消息的id,叫做lastId。

每次收到一个新消息, 就将id与lastId作比较看是否连续,如果不连续,就放入一个暂存队列 queue中稍后处理。如果是重复的就直接发送ACK

1)当前会话的lastId=1,接着服务器收到了消息msg(id=2),可以判断收到的消息是连续的,就处理消息,将lastId修改为2;
2)但是如果服务器收到消息msg(id=3),就说明消息乱序到达了,那么就将这个消息入队,等待lastId变为2后,(即服务器收到消息msg(id=2)并处理完了),再取出这个消息处理。

降低离线拉取ACK带来的额外与服务器的交互次数

不用每一页消息都ACK,在拉取第二页消息时相当于第一页消息的ACK,此时服务器再删除第一页的离线消息即可,最后一页消息再ACK一次(实际上:最后一页拉取的肯定是空返回,这样可以极大地简化这个分页过程

否则客户端得知道当前离线消息的总页数,而由于消息读取延迟的存在,这个总页数理论上并非绝对不变,从而加大了数据读取不一致的可能性)
这样的效果是,不管拉取多少页离线消息,只会多一个ACK请求,与服务器多一次交互。

接收方累计收到N条群消息再批量ack,避免一次性ack带来的性能影响

IM登录相关:

可以看看这个
http://www.52im.net/thread-2863-1-1.html

常见登录方式:

在这里插入图片描述

第三方登录:

在这里插入图片描述

群消息处理策略

群消息怎么存?
“不管是否在线,都冗余一份群消息”带来的问题是:同一条消息存储了很多次,对磁盘和带宽造成了很大的浪费。

很容易想到的优化是:群消息实体存储一份,用户只冗余消息ID。

  • 方案1:群聊消息存多份,只存在线,消息容易丢;
  • 方案2:群聊消息存多份,所有群友都存储,消息冗余多;
  • 方案3:群聊消息存多份,只存ID,未利用偏序;
  • 终极方案:群聊消息存一份,只存last_ack_msgid。
    为了减少消息风暴,可以批量ACK;
    如果收到重复消息,需要msg_id去重,让用户无感知;

对于群消息已读回执,怎么处理?

  • 如果发送方在线,会实时被推送已读回执;
  • 如果发送方不在线,会在下次在线时拉取已读回执。

如果要对进行优化,可以:

  • 接收方累计收到N条群消息再批量ack;
  • 会带来什么副作用?
  • 批量消息再ack可能导致异常退出时候有些消息没有发送ack,然后客户端收到重复消息,这里要注意客户端去重
  • 发送方轮询拉取已读回执。
  • 还可能存在的问题:群离线消息过多:拉取过慢。
    解决方案:分页拉取(按需拉取)

群消息一定要主动推送吗,能否改为接收方轮询拉取?
答:不能,消息接收,实时性是核心指标。服务器必须马上推送到另一个客户端,但是不一定要马上收获ack

群聊消息发送原理

群聊消息的分发通常有两种技术实现方式,我们一一来看看。

方式一:假设一个群有100人,如果Client1给一个群的所有人发消息,其实相当于Client1分别给其余99人分别发一条消息。我们可以直接在Client端,通过循环,分别给群里的99人发消息即可,相当于Client发送给NettyServer发送了99次相同的消息(除了to_uid不同)。

上述方案有很严重的性能问题:Client1通过循环99次,分别把消息发给NettyServer,NettyServer收到这99条消息后,分别将消息发给群内其余的用户。先抛开移动端的特殊性(比如循环还没完成手机就有可能退到后台被系统挂起),显然Client1到NettyServer的99次循环存在明显不合理地方。

方式二:上节的消息体中to_uid_list字段就是为了解决这个方式一的性能问题的。Client1把群内其余99个Client的uid保存在to_uid_list中,然后NettyServer只发一条消息,NettyServer收到这一条消息后,通过to_uid_list字段解析群内其余99的Client的uid,再通过循环把消息分别发送给群内其余的Client。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
四 级 复 习 资 料 序(方法) 英语是你生活的必需,而不是为了考试。那么然后要建立一个小小的英语环境。 首先下载“龙卷风网络收音机”免费软件,安装后就可以收听VOA,BBC等几百个英语电台的英语节目了。如果你的英语发音特别不准的话,你可以先从VOA的慢速英语来练习口语的正确发音。(听) 有了这个环境,我们开始解决第一个问题,记忆单词——这是一切的基础。学英语首先要学会正确的发音。新东方的4+1课堂的语音语调,美国英语班,李阳的《疯狂英语》教材,郭锐峰的ASAP英语教材,邱政政的《TOEFL新听力》一书都可以很好的解决这个问题。(发音) 词根词缀记忆法,刘仁的《十天突破雅思真题词汇》是方法学讲的最好的,刘毅的《英文字根字典》是拆分最好的,蒋争的《英语词汇的奥秘》是分类最好的,朗文词典第三版。(记忆单词) 关于通过看电影学英语的方法,新东方的俞敏洪,杜伟老师建议,根据你的实际能力,先看2~3遍没有字幕的,然后再看一遍有字幕的,如果还搞不定,下载该电影的剧本,把剧本过一遍,先把剧本搞定(这已经是阅问题了),然后再看一遍有英文字幕的,保证看着字幕理解是没有问题的。如果字幕的版本不理想,可以到http://www.shooter.com.cn/射手网搜索下载,是全免费的(看电影学英语) 口语往往是衡量一个人英语水平的标准,可以根据走遍美国的磁带或者VOA的Special English进行语音语调的模仿。要求你大声的朗,坚持背诵(reciting)。语言看懂了不是你的,背下来才是你的,背下来再说出来才真正是你的。在口语的发音方面,李阳疯狂英语的方法很值得推荐。针对中国人英语发音的缺点,李阳总结了五个发音秘诀: 双元音和长元音发音要饱满 I made a terrible mistake 短元音收小腹,短促有力 let‘s get together again soon great minds think alike I am so prode of you Go Home Sweat Dream 连 I am working on it I will think it over 省略 I don”t know what to do 咬舌头 3333 It’s the same thing 精确地发音有助于你正确的表达你的思想不被误解,例如下面的单词如果你发音不准的话,很有可能造成误会: bad [i:] bed beach bitch * sheet shit fool full 这是英语播音员常使用的方法。首先要保证的是,你的发音是要正确的。然后先做热身,尽最大可能噘嘴,发“屋”的音,然后尽最大可能咧嘴,发“一”的音,然后再噘嘴发“屋”的音,再转成咧嘴的“一”音。然后快速的转换,知道两腮酸痛为止。然后双手轻拍双颊,做一下简单的放松。接着找一份阅材料来阅。但是要注意,的时候要咬住牙齿不要分开,然后尽量正确的发音,把这篇短文度2遍。你会觉得这么做很难受,本来发音就不准,现在更没谱了。没关系,当你觉得两腮酸痛的时候,张开嘴,以正常的方式再朗刚才的短文,你就会发现,你的发音已经有质的突破了,元音自然就饱满了,嘴自然就张开了,自己会明显的感觉到发音到位了。(发音) 想学基础英语就选《新概念》,想学生活英语就选《走遍美国》 总结:学好实用英语(不是应试)的一些方法,总结如下:   1.不要浪费时间在选择题和枯燥的背单词上  2.多多阅英文报刊,网站,或是看英文肥皂剧,电影  3.听VOA,BBC  4.查单词用英英字典  5.看DVD电影,要看不带字幕的,然后再看带字幕,实在还是没看懂,就下剧本研究,研究完毕再次看一边电影(一定要不带字幕,。练习听力~~) 6.看原版英文小说 四级 四级考试中常见的考察词汇的题型: I 押韵题型(押头韵、押尾韵); 答案总在相似中,如果有三个一样,基本上就在其中了。 54. The rain was heavy and _A_ the land was flooded. A consequently B continuously C constantly D consistently continue v. 继续,连续; continually adv. 时断时续地; continuously adv. 连续不断地。 说不停的咳嗽时,continually是间歇的时断时续的咳嗽,continuously是一直不停的咳嗽。 consequently adv. 因此,所以;(heavy rain大雨, light rain小雨) constantly adv. 始终如一地,连续发生地;constant temperature 恒温 consistently adv. 一贯地,一致地; consistent adj. consistent policy 一贯的政策。 36. I hate people who _C_ the end of a film that you haven't seen before. A revise B rewrite C reveal D reverse rewrite v. 重,改; revise vt. 修改,修正; reveal vt. 揭示,揭露; reverse vt. 颠倒,使反转,使反向。(vers是词根,表示转动;re是前缀,表示向相反方向) 42. There were no tickets _D_ for Friday’s performance. A preferable B considerable C possible D available performance n. 表演,演出,演奏; perform vt. possible adj. 可能的 动词后加able构成形容词通常表示“可…的” read -> readable accept -> acceptable consider vt. 考虑; considerable adj. (数量或尺寸)相当大(或多)的。 preferable adj. 更好的,更可取的; available* adj. 可获得的,可利用的,可支配的。(重点词) 33. In general, the amount that a student spends for housing should be held to one-fifth of the total _D_ for living expenses. A acceptable B applicable C advisable D available living expenses 生活费; acceptable adj. 可接受的; apply vt. 申请,应用; applicable adj. 可应用的,适当的,合适的; advise vt. 建议; advice n. 建议; advisable adj. 明智的,可取的。 54. It is our _A_ policy that we will achieve unity through peaceful means. A consistent B continuous C considerate D continual achieve unity through peaceful means 通过和平手段取得统一; consistent policy 一贯政策 II 构词法题型(词的转化,合成,派生); 构成符合形容词的名词和数量词一律用单数。(见下面2个例题) 31. Despite the wonderful acting and well-developed plot the _B_ movie could not hold our attention. A three-hours B three-hour C three-hours’ D three-hour’s 267. Professor White wrote a _C_ report yesterday. A two-thousand-words B two-thousands-word C two-thousand-word D two-thousands-words 以ly结尾的不全是副词; friendly, lonely, lovely, likely, lively adj. 考试中常见的否定前缀: un-、dis-、in-、im- 56. _B_ his sister, Jack is quiet and does not easily make friends with others. A Dislike B Unlike C Alike D Liking like vt. 喜欢; dislike vt. 不喜欢,厌恶; unlike prep. 不象…; alike adj. & adv. 同样的(地),相象的(地); liking n. 爱好,嗜好; take a liking for喜欢…,对…产生好感。 III 近义词含义比较; 44. There were some _A_ flowers on the table. A artificial B unnatural C false D unreal unreal adj. 不真实的(不是真实世界所拥有的,虚幻的); Ends justify means 不择手段; false adj. 具有欺骗性的,假的,伪造的; false coin/passport/hair,a false tooth/false teeth unnatural adj. 不自然的,经常用来修饰人的行为举止,表示做作的,矫揉造作的。 artificial adj. 人造人为的 artificial leg 假肢artificial leather 人造皮 genuine leather 真皮 54. When people become unemployed, it is _C_ which is often worse than lack of wages. A laziness B poverty C idleness D inability laziness n. 懒惰; poverty n. 贫穷; poor adj. 贫穷的; idleness n. 无事可做(中性,有时也有贬义含义); inability n. 没有能力,没有办法。 69. A lot of ants are always invading my kitchen. They are a thorough _A_. A nuisance B trouble C worry D anxiety invade 进攻,侵略; nuisance n. (具体的)令人讨厌的东西; trouble n. 烦恼,麻烦,问题; worry n. 担心,发愁; anxiety n. 焦虑。 What a nuisance. 真是烦。 IV 搭配关系问题; extent n. 程度; to... extent 到达…程度,在…程度之上; extent 只能和to搭配。 object vi. 反对; object + to + 动名词(动词的ing形式)。 objection n. 反对; objection + to + 动名词(动词的ing形式)。 V 形相近,意相远; 65. In Britain, the best season of the year is probably _A_ spring. A late B last C latter D later late adj. 晚的,
IM springboot是基于springboot框架的一种即时聊天服务器。通过引入依赖,可以方便地创建一个自己的IM聊天服务器。其中,可以使用封装了Netty的springboot启动器来简化IM服务器的搭建过程。另外,还可以引入相关的依赖来支持IM服务器的功能,如使用netty-all来实现网络通信功能。 除了基于Netty的IM服务器,还有一种名为J-IMIM系统,它是用JAVA语言基于t-io开发的。J-IM具有轻量、高性能的特点,并且支持单机上的几十万至百万在线用户。它的主要目标是降低即时通讯门槛,快速打造低成本的在线IM系统。通过简洁的消息格式,可以实现多端不同协议之间的消息发送。所以IM springboot是一种基于springboot框架的IM服务器,可以使用它来搭建高性能的即时聊天系统。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [封装netty 一个webSocket即时聊天的Im服务器springboot 启动器](https://blog.csdn.net/qq_41082092/article/details/112179296)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [WebSocket聊天室实现J-IM+SpringBoot+Zookeeper+Redis](https://download.csdn.net/download/ddmfony/12065327)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值