在我的Python应用程序中,我有一个使用来自Amazon SQS FIFO队列的消息的函数。
def consume_msgs():
sqs = boto3.client('sqs',
region_name='us-east-1',
aws_access_key_id=AWS_ACCESS_KEY_ID,
aws_secret_access_key=AWS_SECRET_ACCESS_KEY)
print('STARTING WORKER listening on {}'.format(QUEUE_URL))
while 1:
response = sqs.receive_message(
QueueUrl=QUEUE_URL,
MaxNumberOfMessages=1,
WaitTimeSeconds=10,
)
messages = response.get('Messages', [])
for message in messages:
try:
print('{} > {}'.format(threading.currentThread().getName(), message.get('Body')))
body = json.loads(message.get('Body'))
sqs.delete_message(QueueUrl=QUEUE_URL, ReceiptHandle=message.get('ReceiptHandle'))
except Exception as e:
print('Exception in worker > ', e)
sqs.delete_message(QueueUrl=QUEUE_URL, ReceiptHandle=message.get('ReceiptHandle'))
time.sleep(10)
为了扩大规模,我正在使用多线程处理消息。
if __name__ == '__main__:
for i in range(3):
t = threading.Thread(target=consume_msgs, name='worker-%s' % i)
t.setDaemon(True)
t.start()
while True:
print('Waiting')
time.sleep(5)
该应用程序作为服务运行。如果我需要部署新版本,则必须重新启动。当主进程终止时,有没有办法使线程正常存在?而不是突然终止线程,它们首先以当前消息结束,并停止接收下一条消息。
答案 0 :(得分:2)
由于线程一直在循环,因此不能仅join
进行循环,但是您需要向它们发出信号,也该退出循环了,以便能够做到这一点。这个docs提示可能有用:
守护程序线程在关闭时突然停止。它们的资源(例如打开的文件,数据库事务等)可能无法正确释放。如果您希望线程正常停止,请将它们设置为非守护进程,并使用适当的信令机制,例如Event。
因此,我将以下示例放在一起,希望可以有所帮助:
from threading import Thread, Event
from time import sleep
def fce(ident, wrap_up_event):
cnt = 0
while True:
print(f"{ident}: {cnt}", wrap_up_event.is_set())
sleep(3)
cnt += 1
if wrap_up_event.is_set():
break
print(f"{ident}: Wrapped up")
if __name__ == '__main__':
wanna_exit = Event()
for i in range(3):
t = Thread(target=fce, args=(i, wanna_exit))
t.start()
sleep(5)
wanna_exit.set()
将单个事件实例传递到fce
,它将连续运行,但是在每次迭代完成后,如果事件已设置为True
,则返回最高检查之前。在退出脚本之前,我们从控制线程将此事件设置为True
。由于这些线程不再标记为守护程序线程,因此我们不必显式join
。
根据要关闭脚本的精确程度,您将需要处理SIGTERM
的传入信号(也许是KeyboardInterrupt
)或SIGINT
异常。并在退出之前进行清理,其机制保持不变。除了不让python立即停止执行外,您还需要让线程知道它们不应重新进入循环并等待它们加入。
SIGINT
有点简单,因为它作为python异常公开,您可以例如对“ main”位执行以下操作:
if __name__ == '__main__':
wanna_exit = Event()
for i in range(3):
t = Thread(target=fce, args=(i, wanna_exit))
t.start()
try:
while True:
sleep(5)
print('Waiting')
except KeyboardInterrupt:
pass
wanna_exit.set()
当然,您不仅可以从控制终端发送SIGINT
到kill
的进程中。