在分析云主机热迁移的时候,下面函数中有绿色线程,今天回过头来分析一下这里面绿色线程。
def _live_migration(self, context, instance, dest, post_method,
recover_method, block_migration,
migrate_data):
"""Do live migration.
This fires off a new thread to run the blocking migration
operation, and then this thread monitors the progress of
migration and controls its operation
"""
#上面注释可以理解为:由于迁移操作会阻塞,需要一个新的thread 来执行迁移操作,本thread(现有代码 #所在的thread)监控迁移并处理。
dom = self._host.get_domain(instance)
#创建绿色线程,来执行迁移,注意spawn相当于注册了事件处理函数,此处并没有开始调用。
opthread = utils.spawn(self._live_migration_operation,
context, instance, dest,
block_migration,
migrate_data, dom)
#创建event对象,下文中会和opthread进行关联。
finish_event = eventlet.event.Event()
def thread_finished(thread, event):
#此log只能打印Migration operation thread notification,instance是打印不出来的,应 #该是个bug。
LOG.debug("Migration operation thread notification", instance=instance)
#此处的event=<Event at 0x5ed7d90 result=NOT_USED _exc=None _waiters[0]>
LOG.debug('thread_finished event=%s', event)
#event.send函数不会引起绿色线程的调度,send函数只是创建一个timer。send函数和wait函数配
#对使用,因为send会引起wait注册的waiters的调度。
event.send()
#opthread 执行完,会调用thread_finished函数,和event结合的目的主要是获取绿色线程执行完的结
#果,若不想获取执行完的结果,此处代码就没有必要了。
opthread.link(thread_finished, finish_event)
# Let eventlet schedule the new thread right away 这里是原始注释
#此处直接使用time模块,直接使用感觉不规范,应该使用eventlet.sleep(0)
time.sleep(0)
try:
LOG.debug("Starting monitoring of live migration",
instance=instance)
#这里就是文章一开始提到的,迁移监控处理函数。若迁移绿色线程发生了阻塞,那么就可以执行下面函 #数了,_live_migration_monitor中会每隔0.5执行一次读取迁移状态
self._live_migration_monitor(context, instance, dest,
post_method, recover_method,
block_migration, migrate_data,
dom, finish_event)
except Exception as ex:
LOG.warn(_LW("Error monitoring migration: %(ex)s"),
{"ex": ex}, instance=instance, exc_info=True)
raise
finally:
LOG.debug("Live migration monitoring is all done",
instance=instance)
下面是eventlet模块event.py文件中Event类中的send和wait函数。
eventlet\event.py
def wait(self):
"""Wait until another coroutine calls :meth:`send`.
Returns the value the other coroutine passed to
:meth:`send`.
>>> from eventlet import event
>>> import eventlet
>>> evt = event.Event()
>>> def wait_on():
... retval = evt.wait()
... print("waited for {0}".format(retval))
>>> _ = eventlet.spawn(wait_on)
>>> evt.send('result')
>>> eventlet.sleep(0)
waited for result
Returns immediately if the event has already
occurred.
>>> evt.wait()
'result'
"""
current = greenlet.getcurrent()
if self._result is NOT_USED:
self._waiters.add(current)
try:
return hubs.get_hub().switch()
finally:
self._waiters.discard(current)
if self._exc is not None:
current.throw(*self._exc)
return self._result
def send(self, result=None, exc=None):
"""Makes arrangements for the waiters to be woken with the
result and then returns immediately to the parent.
>>> from eventlet import event
>>> import eventlet
>>> evt = event.Event()
>>> def waiter():
... print('about to wait')
... result = evt.wait()
... print('waited for {0}'.format(result))
>>> _ = eventlet.spawn(waiter)
>>> eventlet.sleep(0)
about to wait
>>> evt.send('a')
>>> eventlet.sleep(0)
waited for a
It is an error to call :meth:`send` multiple times on the same event.
>>> evt.send('whoops')
Traceback (most recent call last):
...
AssertionError: Trying to re-send() an already-triggered event.
Use :meth:`reset` between :meth:`send` s to reuse an event object.
"""
assert self._result is NOT_USED, 'Trying to re-send() an already-triggered event.'
self._result = result
if exc is not None and not isinstance(exc, tuple):
exc = (exc, )
self._exc = exc
hub = hubs.get_hub()
for waiter in self._waiters:
hub.schedule_call_global(
0, self._do_send, self._result, self._exc, waiter)
def schedule_call_global(self, seconds, cb, *args, **kw):
"""Schedule a callable to be called after 'seconds' seconds have
elapsed. The timer will NOT be canceled if the current greenlet has
exited before the timer fires.
seconds: The number of seconds to wait.
cb: The callable to call after the given time.
*args: Arguments to pass to the callable when called.
**kw: Keyword arguments to pass to the callable when called.
"""
t = timer.Timer(seconds, cb, *args, **kw)
self.add_timer(t)
return t