在Python中仅执行语句一次

5uzkadbs  于 2023-01-10  发布在  Python
关注(0)|答案(2)|浏览(124)

我正在Celery上运行一个周期性任务,每3分钟执行一次相同的代码。如果条件为True,则执行一个操作(发送一条消息),但我只需要发送一次消息。
理想的情况是,如果发送了消息,在接下来的24小时内不能发送(即使函数会每3分钟执行一次),在这24小时之后,条件会被再次检查,如果仍然为True,消息会被再次发送。我如何用Python来实现这一点?我在这里放了一些代码:

if object.shussui:
    client.conversation_start({
        'channelId': 'x',
        'to': 'user',
        'type': 'text',
        'content': {
            'text': 'body message')
        }
    })

这里正在检查条件,如果shussuiTrue,则client.start_conversation发送消息。

o8x7eapl

o8x7eapl1#

您可以保留最后一次发送邮件的时间,并检查是否已经过去24小时:

from time import time
lastSent = None
if object.shussui and (not lastSent or (time() - lastSent) >= 24 * 60 * 60):
    client.conversation_start({
        'channelId': 'x',
        'to': 'user',
        'type': 'text',
        'content': {
            'text': 'body message')
        }
    })
    lastSent = time()
ttygqcqt

ttygqcqt2#

除了像Mureinik's answer中那样添加对上次执行时间的检查之外,如果您有多个工作线程,您应该获取上次执行时间并将其存储在一个中央共享位置,例如,无论您使用的是哪一个消息代理,或者存储到一个数据库。
last_sent的密钥还应包括确定性签名,以匹配唯一消息和/或用户ID;或者任何你需要的条件。值是一个datetime对象或者一个int/float类型的秒数(对于time.time()
如果不将其存储在一个中央/共享位置,那么当另一个worker/示例获得同一消息的下一个任务时,它将立即被重新发送,因为之前的last_sent将只存储在之前worker的内存中,而当前worker将没有更新的值。
如何存储last_sent信息的示例:

  • 在Redis中,keys 的格式可以是last_sent:user:1234:msg:9876,其值可以是datetime.datetimetime.time。这里9876是消息的哈希值,类似于hash('hello')。不要存储消息本身。[适用于多个工作示例]
import redis

client = redis.Redis()
key = 'last_sent:user:1234:msg:9876'
last_sent = client.get(key) or '0'  # missing/None will be converted to 0
if object.shussui and time.time() - int(last_sent) > 24 * 60 * 60:
    last_sent = client.set(key, time.time())
    client.conversation_start({...})

这可以通过一个原子操作来实现,这样可以避免竞态条件。使用set,它的有效期为24小时,因为消息需要在24小时后发送,或者如果它不在Redis中,这可以用来存储任意字符串,并将它在Redis中的存在作为一个标志:

ONE_DAY = 24 * 60 * 60
key = 'last_sent:user:1234:msg:9876'

# set a new flag only if it doesn't exist, with a 24-hour expiration
status = client.set(key, 'A', nx=True, ex=ONE_DAY)

# None implies it was present in redis so 24 hours have not yet passed
if object.shussui and status is not None:
    client.conversation_start({...})

if object.shussui and status is not None可以简化为if object.shussui and status

  • 在数据库中,它可以是包含user_idmsg_hashsent_at的表last_sent[适用于多个工作示例]
  • 在包含user_id和msg hash. * 的字典中[仅适用于单个worker示例,因为它存储在内存中]*:
last_sent = {
    (1234, msg1): ..., # <datetime or time>
    (1234, msg2): ..., # another message
    (5678, msg1): ..., # another user, same message
    ... # etc.
}

此外,根据您的需要和情况,您需要决定如何处理争用条件。例如,同一消息在一行中触发两次,并且在第一次更新“last_sent”之前触发。

相关问题