简体   繁体   English

在 Python 只执行一次语句

[英]Execute statement only once in Python

I'm running a periodic task on Celery that executes the same code once every 3 minutes.我在 Celery 上运行一个周期性任务,每 3 分钟执行一次相同的代码。 If a condition is True, an action is performed (a message is sent), but I need that message to be sent only once.如果条件为真,则执行操作(发送消息),但我只需要发送一次该消息。

The ideal would be that that message, if sent, it could not be sent in the next 24 hours (even though the function will keep being executed every 3 minutes), and after those 24 hours, the condition will be checked again and message sent again if still True.理想情况是该消息如果发送,则在接下来的 24 小时内无法发送(即使 function 将继续每 3 分钟执行一次),并且在这 24 小时之后,将再次检查条件并发送消息如果仍然正确,请再次。 How can I accomplish that with Python?我怎样才能用 Python 做到这一点? I put here some code:我在这里放了一些代码:

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

Here the condition is being checked, and if shussui is True the client.start_conversation sends a message.这里正在检查条件,如果shussuiTrue ,则client.start_conversation发送消息。

You could keep the last time the message was sent and check that 24 hours have passed:您可以保留上次发送消息的时间并检查是否已过去 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()

Apart from adding a check for time of last execution, as done in Mureinik's answer , if you have multiple workers, you should get and store the last execution time in a central shared location .除了添加对上次执行时间的检查(如Mureinik 的回答中所做的那样),如果您有多个工作人员,您应该获取上次执行时间并将其存储在中央共享位置 For example, whichever message broker you're using with celery, or to a database.例如,无论您使用哪个消息代理与 celery 或数据库。

The key for last_sent should also include a deterministic signature to match unique messages and/or user ids; last_sent的密钥还应包括确定性签名以匹配唯一消息和/或用户 ID; or whatever criteria you need.或者你需要的任何标准。 With the value being a datetime object or an int/float for seconds (for time.time() )值为日期时间 object 或秒数的 int/float(对于time.time()

If you don't store this in a central/shared location, then when a different worker/instance gets the next task for the same message, it would end up getting resent immediately.如果您不将其存储在中央/共享位置,那么当不同的工作人员/实例为同一消息获取下一个任务时,它最终会立即被重新发送。 Because the previous last_sent would be stored only on the previous worker's memory and the current worker would have not have that updated value.因为之前的last_sent只会存储在之前的 worker 的 memory 上,而当前的 worker 不会有更新后的值。

Examples of how you could store the last_sent info:如何存储last_sent信息的示例:

  • In Redis, the keys could be in the form last_sent:user:1234:msg:9876 with the value as a datetime.datetime or time.time .在 Redis 中,可以采用last_sent:user:1234:msg:9876的形式,值为datetime.datetimetime.time Here 9876 is the hash for the message, like hash('hello') .这里9876是消息的 hash,例如hash('hello') Don't store the message itself.不要存储消息本身。 [works for multiple worker instances] [适用于多个工作实例]

     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({...})

    It's possible to do this as an atomic operation which avoids race-conditions.可以将其作为避免竞争条件的原子操作来执行。 Use set with a 24 hour expiration since messages would need to be sent after that period or if it's not present Redis. This can be used to just store some arbitrary string and use its existence in redis as a flag:使用具有 24 小时到期时间的set ,因为消息需要在该时间段之后发送,或者如果它不存在 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({...})

    The if object.shussui and status is not None can be simplified to if object.shussui and status . if object.shussui and status is not None可以简化为if object.shussui and status

  • In a DB, it could be a table last_sent with user_id , msg_hash , sent_at .在数据库中,它可能是一个带有user_idmsg_hashlast_sentsent_at [works for multiple worker instances] [适用于多个工作实例]

  • In a dictionary with the user_id and the msg hash .在带有 user_id 和 msg hash的字典中。 [only works for single worker instances since this is stored in memory] : [仅适用于单个工作实例,因为它存储在内存中]

     last_sent = { (1234, msg1): ..., # <datetime or time> (1234, msg2): ..., # another message (5678, msg1): ..., # another user, same message... # etc. }

Also, based on your needs and situation, you need to decide how you want to handle race conditions.此外,根据您的需要和情况,您需要决定如何处理竞争条件。 Like same message triggers twice in a row and before the first one has updated 'last_sent'.就像同一条消息连续触发两次,并且在第一个消息更新“last_sent”之前。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM