第九课:Openfire 是怎么存离线消息

阅读:13830

1、openfire离线消息类OfflineMessageStore

2、学习openfire离线消息表的数据结构

1、openfire默认怎么存离线消息

在默认情况下,不添加任何插件的情况下,当用户不在线,对于发送给该用户的消息,会被openfire存放到数据库中的ofoffline表中。

为了更深入的了解离线消息的原理,我们来看一下ofoffline这个表,这个表的结构如下所示:

下面对这几个字段进行详细说明:

(1)Username:是用户名,如myopenfire1、myopenfire2。

(2)MessageID:是openfire产生的一个数字,openfire每处理一条消息到离线,这个值就会增1。

(3)CreateDate:表示这条消息被插入数据库的时间,注意插入数据库的时间不一定是这条消息到达服务器的时间。因为他们之间始终是有一点误差的。

(4)MessageSize:表示存储在stanza字段中的消息的字节数。

(5)Stanza:翻译为节的意思,这里指的就是数据包,存放的是message消息。

2、离线表中实际的数据是什么

Ok,我们来看看实际的存储,你就会更容易理解了,如下图是离线表中的数据:

注意stanza是存放的是xmpp协议表示的消息。

3、存放离线消息的类

在目录openfire_src\src\java\org\jivesoftware\openfire下,有一个OfflineMessageStore.java类,这个类就是用来存放离线消息的。

首先这个类是一个单例类,它的实例保存在XMPPServer中,代码如下:

  public static OfflineMessageStore getInstance() {
        return XMPPServer.getInstance().getOfflineMessageStore();
    }

我们可以通过getInstance来获得OfflineMessageStore的实例。

如果要向离线表中插入一条数据,那么可以调用addMessage方法,addMessage的源码分析如下:

public void addMessage(Message message) {
	// 如果消息为null,直接返回
	if (message == null) {
		return;
	}
	// shouldStoreMessage判断哪些消息应该被存储,例如只有类型为chat的消息,才允许被存储。
	if(!shouldStoreMessage(message)) {
		return;
	}
	
	JID recipient = message.getTo();
	String username = recipient.getNode();
	// 如果没有接受者,那么就不必要存离线了。
	if (username == null || !UserManager.getInstance().isRegisteredUser(recipient)) {
		return;
	}
	else
	if (!XMPPServer.getInstance().getServerInfo().getXMPPDomain().equals(recipient.getDomain())) {
		// 如果这条消息的域名不对,是发送到其他服务器的,那么也不需要存了
		return;
	}
	// 产生下一条消息的一个序号
	long messageID = SequenceManager.nextID(JiveConstants.OFFLINE);

	// 获得xml格式的消息
	String msgXML = message.getElement().asXML();
	// 打开数据库连接,并且存储消息到数据库
	Connection con = null;
	PreparedStatement pstmt = null;
	try {
		con = DbConnectionManager.getConnection();
		pstmt = con.prepareStatement(INSERT_OFFLINE);
		pstmt.setString(1, username);
		pstmt.setLong(2, messageID);
		pstmt.setString(3, StringUtils.dateToMillis(new java.util.Date()));
		pstmt.setInt(4, msgXML.length());
		pstmt.setString(5, msgXML);
		pstmt.executeUpdate();
	}

	catch (Exception e) {
		Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
	}
	finally {
		DbConnectionManager.closeConnection(pstmt, con);
	}

	// 为存储离线的用户的缓存空间加上相应的字节数,如果设置了一个用户只能存储几M的数据,那么这个就是统计,这个用户已经存储了多少字节的离线消息
	if (sizeCache.containsKey(username)) {
		int size = sizeCache.get(username);
		size += msgXML.length();
		sizeCache.put(username, size);
	}
}

如果要删除某条消息,可以使用deleteMessages这个函数,传入用户名作为参数,代码如下:

public void deleteMessages(String username) {
	Connection con = null;
	PreparedStatement pstmt = null;
	try {
        // 打开数据库连接
		con = DbConnectionManager.getConnection();
        // DELETE_OFFLINE为 DELETE FROM ofOffline WHERE username=?
		pstmt = con.prepareStatement(DELETE_OFFLINE);
		pstmt.setString(1, username);
		pstmt.executeUpdate();
		
		// 将这个用户已经占用多少字节,重新置为0
		removeUsernameFromSizeCache(username);
	}
	catch (Exception e) {
		Log.error("Error deleting offline messages of username: " + username, e);
	}
	finally {
        // 关闭数据库连接
		DbConnectionManager.closeConnection(pstmt, con);
	}
}

4、小结

本节讲解了离线消息的存储类OfflineMessageStore,这个类中有很多关于离线消息的处理函数,需要对openfire离线消息进行扩展的同学,不妨多看看这个类的实现。

[1楼] wu19** 2018-07-12 16:01

关于老师说的拓展,比如说是可以修改数据库连接技术吗,用其他的数据库连接池什么的吗

[2楼] eei2** 2019-01-02 23:36

[3楼] yang** 2019-03-19 14:13

提问或评论

登陆后才可留言或提问哦:) 登陆 | 注册 登陆后请返回本课提问
用户名
密   码
验证码