1. 如何重现丢包问题
关于openfire丢包的问题还是比较严重的,重现的方式很简单
- 使用两个客户端连接同一台openfire(我用的是两个android手机)
- 使用一个客户端给另一个客户端发送消息
- 在opengfire将消息投递到另一台openfire客户端之前使用断点断住(断点位置:org.jivesoftware.openfire.nio.NIOConnection.deliver(Packet) 260行)
- 另一台测试机断网
- 释放openfire断点
不可思议的事情出现了,mina显示已经将消息成功投递,也没有报任何异常,但是现在客户端已经离线无法收到消息,而且openfire认为消息已经发送成功并不会把投递的消息作为离线消息处理,就这样我们的包丢了。(为什么会丢失还请路过的大神指点)
2. 解决方案
今天我们说的解决方式是采用聊天记录替代openfire离线消息的机制来解决问题。
- openfire在收到message消息后保存聊天记录(这里保存的内容为这个Message对象,不做任何解包操作),并且修改openfire源代码,不在保存离线消息
- 编写openfire插件,用于接收客户端请求离线消息请求,客户端在请求离线消息的同时需要传递一个时间戳(客户端最后接收包的时间)给服务端,服务端根据这个时间觉得推送那些消息下去
- openfire推送离线的消息给客户端
首先创建表,用于保存聊天记录,每一条经过的Message都要保存下来(分表什么的不考虑了)。
表的id推荐使用服务端生成的UUID,因为可能会涉及到分表,为了保证每条消息的唯一性,需要一个id的生成策略
package org.yimaoqiche.offline.message.plugin.model;
import java.util.Date;
public class OfflineModel {
private String id;
private String message;
private Date createTime;
public OfflineModel(){
}
public OfflineModel(String id, String message, Date createTime) {
this.id = id;
this.message = message;
this.createTime = createTime;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
}
下面编写openfire插件,插件实现了Plugin和Component接口。
Plugin接口就不多少了,所有openfire的插件都要实现此接口
Component接口,是openfire的组件接口,我们可以定义Component的名称,然后客户端可以通过名称直接发送指令到我们自定义的组件。
<pre name="code" class="java">package org.yimaoqiche.offline.message.plugin;
import java.io.File;
import org.dom4j.Element;
import org.jivesoftware.openfire.container.Plugin;
import org.jivesoftware.openfire.container.PluginManager;
import org.xmpp.component.Component;
import org.xmpp.component.ComponentException;
import org.xmpp.component.ComponentManager;
import org.xmpp.component.ComponentManagerFactory;
import org.xmpp.packet.IQ;
import org.xmpp.packet.JID;
import org.xmpp.packet.Message;
import org.xmpp.packet.Message.Type;
import org.xmpp.packet.Packet;
import org.yimaoqiche.offline.message.plugin.provider.OfflineMessageProvider;
import org.yimaoqiche.offline.message.plugin.thread.OfflinePushThread;
import org.yimaoqiche.offline.message.plugin.thread.ThreadP