Mnesia数据库设计,用于存储将来需要发送的消息

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mnesia数据库设计,用于存储将来需要发送的消息相关的知识,希望对你有一定的参考价值。

我正在编写一个ejabberd模块,用户控制何时将消息传递给收件人而不是立即传送(如提前发送的生日祝福)。这是通过向消息节添加自定义xml元素来完成的,如下所示

<message xmlns="jabber:client" from="test2@ubuntu" to="test1@ubuntu/32375806281445450055240436" type="chat">
  <schedule xmlns="ank" year="2015" month="10" day="19" hour="22" minute="36" second="13"/>
  <body>hi</body>
</message>

现在,这些预定的消息必须存储在mnesia数据库中,并在时间到达时发送给收件人。

方法1:一种方法是为每个用户创建一个表,当收到消息时,将消息存储到users表并设置一个计时器来处理消息,并在完成时删除,如下面的示例代码

timer:apply_after(SecondsDelay, ?MODULE, post_message_delete, [TableName, RecordUniqueKeyHash, From, To, Packet]). 

post_message_delete方法将在计时器到期后使用路由方法调用时发送消息,如下所示,并从mnesia数据库中删除记录。

    ejabberd_router:route(From, To, Packet)

由于mnesia中表的数量有限,因此无法为每个用户创建表。

方法2:另一种方法是将所有用户消息存储在一个单独的表中,并在消息到达时为每个消息设置定时器(与上面相同),并且一旦消息被处理,就将其删除。

使用mnesia数据库的整个想法是在ejabberd服务器崩溃的情况下可靠地处理消息。

为此,我们在每条消息的记录中使用pid字段。每个消息记录都有一个pid字段,其中包含正在处理此消息的进程的pid。最初它是未定义的(当消息到达filter_packet挂钩时)但是在生成消息处理方法之后它会更新mnesia数据库中记录中的pid。

因此,如果服务器在模块启动方法中重新启动时崩溃,则迭代所有消息并检查pid是否处于活动状态(is_process_alive),如果不是活动的话,则在消息上生成处理方法,该消息将使用新进程pid更新,处理消息和delte一旦完成。

缺点这种方法的缺点是,即使消息必须在将来(下个月或明年)远期传递,仍然会为此消息运行一个进程,并且运行的进程数量与消息数量一样多。

方法3:

为了克服方法2的缺点,每小时扫描一次数据库,并累积仅在下一个小时内交付的消息并进行处理。

这种方法的缺点是每小时扫描一次可能影响性能的数据库。

方法4:

为了完成方法3的性能,我们可以为每年__thth创建表,并仅在当前月表上生成消息处理函数。

还有哪种方法最适合使用mnesia数据库的这个用例?

答案

即使这是一个老问题,但它可能有一天会成为其他人的问题。

我认为mnesia是这种数据存储用例的错误选择。版本2.8.0中的Redis在执行某些操作时具有密钥空间事件通知功能,包括由EXPIREEXPIREAT和其他变体设置的密钥到期命令。此信息可通过PUBSUB功能访问您的代码。请参阅Redis Keyspace Notifications 了解如何开始。

为每个生日消息生成一个唯一的密钥(K),可能是UUID。存储消息,整个XML,在给定的生成的K下发送。

使用SET命令将此消息密钥存储为名为K:timer的键下的值,TTL设置为now和生日时间戳之间的时差(以秒为单位),或者使用EXPIREAT将消息到期时间设置为Unix时间戳。生日本身。当TTL过期时,pubsub客户端会收到该事件的通知,其中包含要使K:timer过期的密钥的信息。提取K并使用它获取消息。发送您的消息并在之后删除它。

需要考虑的问题:

1:多个pubsub客户端可能会收到相同的到期事件的通知。这可能导致同一消息被多次发送。实现某种锁定以防止这种情况。

2:Redis PUBSUB是一个fire and forget消息传递构造。因此,如果客户端出现故障并再次启动,则可能在此时间窗口内错过了事件通知。确保可靠性的一种方法是将密钥K存储在K的不同键变量下:定时器,K:定时器:1,K:定时器:2,K:定时器:3,......增加TTL偏移量(1, 2,3(中间的分钟)以瞄准最不可用的客户可能变得可用的最差时间窗口。

3:Redis在内存中。存储大量的大消息会花费你的RAM。解决此问题的一种方法是仅在redis中存储消息密钥K,并将消息(XML)与相同的密钥K一起存储在任何磁盘基础键值存储中,如Riak,Cassandra等。

以上是关于Mnesia数据库设计,用于存储将来需要发送的消息的主要内容,如果未能解决你的问题,请参考以下文章

用于存储消息的 ejabberd mod_archive_odbc 表

使用 gen_server 封装一个 mnesia 表?

Mnesia 数据库的存储容量是多少?

Erlang mnesia 数据库访问

mnesia:密集使用表

一个erlang数据库模式生成器