我知道终止通知是通过元数据网址提供的,我可以做类似的事情
if requests.get("http://169.254.169.254/latest/meta-data/spot/termination-time").status_code == 200
以确定通知是否已过帐。我在竞价型实例上运行Python服务:
这项工作是幂等的,因此如果相同的有效负载多次运行,我会节省处理时间/成本,但不会对应用程序工作流产生负面影响。
我正在寻找一种优雅的方式,现在也在后台每隔五秒轮询终止通知。一旦终止通知出现,我就立即将消息释放回SQS队列,以便另一个实例尽快将其提取。
作为奖励,我想关闭工作,杀掉线程池,让服务进入停滞状态。如果我终止服务,supervisord将只是重新启动它。
更大的奖金!是不是有一个python模块可以简化这个并且只是有效?
答案 0 :(得分:0)
我编写此代码来演示如何使用线程轮询Spot实例终止。它首先启动一个轮询线程,负责检查http端点。
然后我们创建假工作者(模仿要完成的实际工作)并开始运行池。最终,轮询线程将启动(执行时执行大约10秒)并终止整个事情。
为了防止脚本在Supervisor重新启动后继续工作,我们只需检查__main__
的开头,如果终止通知在那里,我们会睡2.5分钟,这比那个更长通知在实例关闭之前持续。
#!/usr/bin/env python
import threading
import Queue
import random
import time
import sys
import os
class Instance_Termination_Poll(threading.Thread):
"""
Sleep for 5 seconds and eventually pretend that we then recieve the
termination event
if requests.get("http://169.254.169.254/latest/meta-data/spot/termination-time").status_code == 200
"""
def run(self):
print("Polling for termination")
while True:
for i in range(30):
time.sleep(5)
if i==2:
print("Recieve Termination Poll!")
print("Pretend we returned the message to the queue.")
print("Now Kill the entire program.")
os._exit(1)
print("Well now, this is embarassing!")
class ThreadPool:
"""
Pool of threads consuming tasks from a queue
"""
def __init__(self, num_threads):
self.num_threads = num_threads
self.errors = Queue.Queue()
self.tasks = Queue.Queue(self.num_threads)
for _ in range(num_threads):
Worker(self.tasks, self.errors)
def add_task(self, func, *args, **kargs):
"""
Add a task to the queue
"""
self.tasks.put((func, args, kargs))
def wait_completion(self):
"""
Wait for completion of all the tasks in the queue
"""
try:
while True:
if self.tasks.empty() == False:
time.sleep(10)
else:
break
except KeyboardInterrupt:
print "Ctrl-c received! Kill it all with Prejudice..."
os._exit(1)
self.tasks.join()
class Worker(threading.Thread):
"""
Thread executing tasks from a given tasks queue
"""
def __init__(self, tasks, error_queue):
threading.Thread.__init__(self)
self.tasks = tasks
self.daemon = True
self.errors = error_queue
self.start()
def run(self):
while True:
func, args, kargs = self.tasks.get()
try:
func(*args, **kargs)
except Exception, e:
print("Exception " + str(e))
error = {'exception': e}
self.errors.put(error)
self.tasks.task_done()
def do_work(n):
"""
Sleeps a random ammount of time, then creates a little CPU usage to
mimic some work taking place.
"""
for z in range(100):
time.sleep(random.randint(3,10))
print "Thread ID: {} working.".format(threading.current_thread())
for x in range(30000):
x*n
print "Thread ID: {} done, sleeping.".format(threading.current_thread())
if __name__ == '__main__':
num_threads = 30
# Start up the termination polling thread
term_poll = Instance_Termination_Poll()
term_poll.start()
# Create our threadpool
pool = ThreadPool(num_threads)
for y in range(num_threads*2):
pool.add_task(do_work, n=y)
# Wait for the threadpool to complete
pool.wait_completion()