openfire消息回执插件开发业务处理流程

消息回执插件

参考:

OpenFire源码学习之二十四:消息回执与离线消息(上) - Spark_莫然 - 博客园

OpenFire源码学习之二十五:消息回执与离线消息(下) - Spark_莫然 - 博客园

XMPP协议之消息回执解决方案_weixin_34393428的博客-CSDN博客

openfire用户发送的消息,没有回执。就是说A发送消息给B。而B又一致不回复。所以对于客户端A来讲压根不知道消息是否发送成功。然而S(服务端)接收到A的消息转发给B的时候,B也不做回执。所以S也不知道消息到底是否成功到达。

工作流程

假设是A给B发送一条消息。以下”of服务端“指”openfire服务端“,当然,换成如tigase之类的xmpp服务端,思路也是一致的。

  1. 发送者A发送消息给of服务端

  2. of服务端接收到消息后发送回执给发送者A(不等待B的回执,避免B不在线不能发送回执到of服务端)

  3. 发送者A确认收到则结束,如果未收到就重发

  4. 服务端将消息记录一下,并推送给接收者,等待接收者的回执

  5. 接收者接收消息并发回执给服务端

  6. 服务端接收回执删除掉消息回执对应的记录(通过消息ID),表示已经发送完毕

  7. 如果B不在线,of服务端会把消息放到离线表(此消息在缓存不要删除,放离线表后,用户会收到离线消息,此时用户发送回执才最终删除)

  8. 接收者上线会收到离线消息,接收者发送回执

  9. 服务端收到回执,整个回执流程结束

  10. 缓存消息添加过期策略,在消息过期时,查询B是否离线,如果离线,则可以确认消息被放到离线表(如果需要更严谨,查询数据库中离线表中是否有此消息,但会影响性能)。此时这条缓存消息可以不要了,但要处理服务端发出的那条离线消息,放到缓存。

这样A只要和of服务端通讯没问题,则消息肯定能发成功,但此时并不确定B是否已经收到,of服务端将收到的消息暂存起来,如果能在特定时间内能收到B的回执,此时of服务端把当前消息ID的缓存去掉,整个流程结束。如果在特定时间内没收到B的回执,那么说明B可能没收到或者离线了,of服务端此时查询B的在线状态,如果在线(很有可能是一个假的在线状态),但没回复,把这条消息放到离线表里面(是否需要踢掉有待考虑),同时缓存里的消息保留,当B上线时of服务端将自动推送这条消息给B,B收到后回执给服务端,服务端将缓存消息删除即可。

消息回执分为几种:

  • 普通消息

  • 延迟消息

  • 离线消息

普通消息

回执处理流程

下面模拟A给B发一条消息的整个回执工作流程。

普通消息是客户端正常的点对点发送聊天消息,假设ningkangmingadmin发送一句问候语。

A to server(A将消息发出到server)


  
    hello,admin 你好吗
  

服务端收到后,在未给admin发送消息前,给发送方ningkangming发送回执(仅表示服务端收到了)并缓存这条消息。客户端收到后认为消息已经发出,如果在一定时间内没收到则选择重发(注意,重发时ID依然要保持和上一次的一致,因为有可能服务端确实收到,但在发送回执时A没收到,A重新发时如果ID变了服务端则认为是新消息,这样B端可能会收到两条ID不同,但消息体一致的消息,此情况尽量避免)。客户端收到服务端的回执如下(通过received节点的id来确认对应的消息)

server to A(server给A 回执)

A端此时收到了服务端的回执,认为消息已到达B。实际上只到达了服务端,到达B则由服务端保证,A不用关心。

服务端将消息发送到B,B客户端接收到系统发送的消息后,应该回执如下内容(注意received节点的消息ID和收到的消息ID保持一致,to节点是服务端而不是对方,回执本身的ID不重要,只要确保received节点的id和原消息ID保持一致即可)

B to server(B给回执 server)

服务端收到后,把这条消息对应的缓存消息消费掉,表示确认B端也收到了消息。此时,整个回执过程全部完成,整个流程简单描述如下:

A发消息给of服务端,of服务端收到后回执消息给A,A收到后认为消息已发送成功。of将消息发给B并等待B的回执,等到将缓存消息消费,等待超时将消息发到离线表,等待B上线,of服务端再次发送离线消息给B,并再次放到消息缓存,等待回执,直到回执成功为止。

待确认消息的过期策略

这整个处理流程中,有一环是服务端将消息缓存起来,等待B的回执以消费缓存消息。但存在给B发消息时,B没收到的情况,这样服务端将等不到这条回执。所以这里服务端也要有消息超时处理,确保B收到那条消息。默认情况下,如果B没收到消息,of服务端会把消息放到离线表(这里要注意,B如果异常下线,of服务端要花好几分钟才能确认B下线,从而将消息放到离线表),B上线时即可消费此消息,从而发来回执。

回执如何处理海量待确认消息的堆积

服务端需要将message报文缓存起来,考虑到这种离线消息可能很多,放到内存中肯定不合适,一旦离线消息过多,很容易把服务器撑爆,而且不适合于分布式部署,所以这里的消息缓存考虑用redis或消息队列。redis可以实现,例如把过期时间设置成15秒,用线程去不断轮询,把那些消息存放时间大于10秒的(15秒内即可,超过15秒可拿不到消息了),但又还没收到回执的消息进行处理。另一方面,收到回执后去redis把消息干掉,但这个做法性能估计不怎么好。考虑利用消息队列的TTL+死信队列来处理这个事情可能可以获得较好性能。原理是,把所有消息放到消息队列,收到回执后手动消费队列中对应的消息。如果一段时间内(即设置的TTL)没收到回执,这条消息就会进入死信队列。我们监听死信队列,监听到的消息都是该放到离线表里面去的。直到B上线消费了那条离线消息(这里需要注意,当服务器发这条离线消息给B时,我们要抓到这条消息,再一次把它放到消息队列,目的是再一次监听回执)。

延迟消息

延时消息是指,系统推送聊天消息后。接收方没有回执,系统则会把消息返回发送方。这种消息不需要客户端回执。


  hello,admin 你好吗
  

离线消息

接收方不在线的时候,需要接收到的消息被系统存储。当客户端登陆的时候,推送给接收者。该消息推送后,需要接收方回执。

离线消息内容


  {"time":"2013-12-25T14:25:43 963Z","sd":"sd"}
  

客户端回执规范



openfire自带的消息回执

openfire3.9以上版本已有自带的消息回执,xmpp扩展规范XEP-0184。它是一种端到端的消息回执,只有接收端收到消息后才会返回回执,这样对于发送者来说很麻烦,如果接收者不在线无法得知消息是否发出了,因为服务端不会告知发送者已经拿到消息了。只有等到接收者上线获取了消息后,由接收者发送一条确认的回执给接收者。

你可能感兴趣的:(Openfire,java)