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在执行某些操作时具有密钥空间事件通知功能,包括由EXPIRE,EXPIREAT和其他变体设置的密钥到期命令。此信息可通过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数据库设计,用于存储将来需要发送的消息的主要内容,如果未能解决你的问题,请参考以下文章