线程模型

Eventlet

在 Flamingo 版本发布之前,所有 OpenStack 服务都使用了通过 Python eventletgreenlet 库实现的绿色线程模型。

绿色线程使用一种协作式的线程模型:线程上下文切换只能在发出特定的 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 服务并发 指南。