[英]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.这里正在检查条件,如果
shussui
为True
,则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.datetime
或time.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_id
、 msg_hash
、 last_sent
的sent_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.