Openfire 是怎么存离线消息

原文:http://myopenfire.com/article/getarticle/26

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_srcsrcjavaorgjivesoftwareopenfire下,有一个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离线消息进行扩展的同学,不妨多看看这个类的实现。

原文地址:https://www.cnblogs.com/shihaiming/p/6213878.html