线程模型¶
Eventlet¶
在 Flamingo 版本发布之前,所有 OpenStack 服务都使用了通过 Python eventlet 和 greenlet 库实现的绿色线程模型。
绿色线程使用一种协作式的线程模型:线程上下文切换只能在发出特定的 eventlet 或 greenlet 库调用时发生(例如,sleep、某些 I/O 调用)。从操作系统的角度来看,每个 OpenStack 服务都在单个线程中运行。
使用绿色线程降低了发生竞争条件的可能性,但并不能完全消除它们。在某些情况下,您可能需要使用 @lockutils.synchronized(...) 装饰器来避免竞争。
此外,由于只有一个操作系统线程,阻塞该主线程的调用将阻塞整个进程。
在长时间运行的任务中让出线程¶
如果代码路径执行时间过长,并且不包含任何触发 eventlet 上下文切换的方法,那么长时间运行的线程将阻塞任何待处理的线程。
可以通过在长时间运行的代码路径中添加对 eventlet sleep 方法的调用来避免这种情况。如果存在待处理的线程,sleep 调用将触发上下文切换,并且使用 0 作为参数将避免在只有一个绿色线程的情况下引入延迟。
from eventlet import greenthread
...
greenthread.sleep(0)
在当前代码中,time.sleep(0) 如果 time 模块通过 eventlet.monkey_patch() 进行修补,则与 greenthread.sleep(0) 具有相同的效果。为了明确起见,我们建议贡献者使用 greenthread.sleep() 而不是 time.sleep()。
MySQL 访问和 eventlet¶
对于 oslo.db,有一些 MySQL DB API 驱动程序,例如 PyMySQL、MySQL-python 等。PyMySQL 是 oslo.db 的默认 MySQL DB API 驱动程序,并且与 eventlet 配合良好。MySQL-python 使用外部 C 库来访问 MySQL 数据库。由于 eventlet 无法使用 monkey-patching 来拦截 C 库中的阻塞调用,因此对 MySQL 数据库的查询将阻塞服务的为主线程。
Diablo 版本包含一个不阻塞的线程池实现,但该实现导致了一个 bug 并被移除。
原生线程¶
自 Flamingo 版本发布以来,OpenStack 开始从 eventlet 迁移。在此迁移期间,Nova 仍然支持使用 eventlet 运行服务,同时致力于添加对使用 原生 线程 运行服务的支持。
为了使用相同的代码库支持这两种模式,Nova 开始使用 futurist 库。在原生线程模式下,futurist.ThreadPoolsExecutors 用于运行并发任务,并且 oslo.service 和 oslo.messaging 库都配置为使用原生线程来执行诸如周期性任务和 RPC 消息处理程序之类的任务。
要了解如何配置和调整原生线程模式,请阅读 Nova 服务并发 指南。