后端系统¶
介绍¶
在 oslo.service 中,后端系统提供了一种模块化架构,允许您在不修改服务逻辑的情况下,选择不同的并发模型。
目前,已经实现了并支持两种后端
Eventlet 后端:基于绿线程和协作式多任务处理(greenlet 库)的传统并发后端。
线程后端:使用原生 Python 线程、Cotyledon 和 Futurist 的新后端,旨在与现代 Python 版本兼容,简化调试并提高可维护性。
为什么需要后端系统?¶
引入此系统是为了能够逐步过渡离开 Eventlet 后端,该后端在扩展性、协作式多任务处理的复杂性和某些 Python 环境下的调试方面存在已知限制。通过使用后端系统,您可以测试、采用或迁移到线程后端,同时保持相同的公共 API 和服务结构。
未来,一旦线程后端涵盖所有用例,Eventlet 后端可能会被弃用。与 Eventlet 相关的某些功能,例如嵌入式 WSGI 服务器和后门 shell,也计划删除。
有关更多信息,请参阅
工作原理¶
后端系统通过动态选择机制暴露相同的公共组件(ServiceLauncher、ProcessLauncher、ThreadGroup、LoopingCall 等)。
在运行时,您通过调用 init_backend() 函数并提供所需的 BackendType 来选择要使用的后端。
from oslo_service.backend import init_backend, BackendType
# Use the Threading backend
init_backend(BackendType.THREADING)
如果您没有显式初始化后端,系统将默认使用 Eventlet 后端。
后端初始化后,无法更改。 尝试重新初始化将引发 BackendAlreadySelected 异常。
如果您看到此异常,请确保您的后端只在应用程序启动的早期初始化一次(例如在您的 main() 入口点)。 避免在共享库或多次调用 init_backend() 中调用。 如果您需要在单元测试中重置后端,请使用 _reset_backend() 辅助函数清除后端缓存。
from oslo_service.backend import _reset_backend
_reset_backend()
如何选择后端¶
有两种支持的方式来选择要使用的后端
显式初始化
在您的应用程序入口点中早期调用
init_backend()from oslo_service.backend import init_backend, BackendType init_backend(BackendType.THREADING)
这将加载线程后端并缓存其组件。
有关实际用法,请参阅 Nova 如何操作:https://review.opendev.org/c/openstack/nova/+/948311
动态钩子
如果您没有调用
init_backend(),您可以注册一个 Python 钩子以动态决定后端。from oslo_service.backend \ import register_backend_default_hook, BackendType def my_backend_decider(): # Add custom logic here if needed return BackendType.THREADING register_backend_default_hook(my_backend_decider)
如果没有设置显式后端,系统将调用您的钩子以决定加载哪个后端。
注意
没有用于 service_backend 的 oslo.config 选项。 如果您希望通过应用程序的配置文件使后端可配置,则需要自己定义和解析该选项,然后使用所选值调用 init_backend()。
访问后端组件¶
后端初始化后,您可以通过 get_component() 辅助函数访问其组件。
示例:获取 ProcessLauncher 实例
from oslo_service.backend import get_component
ProcessLauncher = get_component("ProcessLauncher")
launcher = ProcessLauncher(conf)
launcher.launch_service(my_service)
launcher.wait()
可用的组件包括
ServiceLauncherProcessLauncherThreadGroupLoopingCall变体SignalHandler以及其他相关的服务实用程序
如果您尝试访问当前后端中不存在的组件,将引发 KeyError 异常。
示例用法¶
这是一个最小的示例,展示了如何初始化线程后端并启动服务
from oslo_service.backend import (
init_backend,
get_component,
BackendType
)
def main():
# Select the Threading backend
init_backend(BackendType.THREADING)
# Get the ProcessLauncher component
ProcessLauncher = get_component("ProcessLauncher")
# Initialize your service and launch it
launcher = ProcessLauncher(conf)
launcher.launch_service(my_service)
launcher.wait()
if __name__ == "__main__":
main()
了解从 Eventlet 迁移到线程的影响¶
下表不是关于选择后端,而是关于了解从 Eventlet 迁移到线程后端的影响
比较方面 |
Eventlet 后端 |
线程后端 |
|---|---|---|
并发模型 |
使用绿线程(greenlet)的协作式多任务处理 |
原生 Python 线程(抢占式) |
进程管理 |
使用内置 eventlet 进程 |
使用 Cotyledon 进行多进程支持 |
循环调用 |
使用 eventlet 绿色线程池 |
使用 Futurist 线程池 |
嵌入式 WSGI 服务器 |
已提供(与 eventlet 一起) |
计划删除;改用外部 WSGI 服务器 |
后门 shell |
已提供(仅 eventlet) |
计划删除 |
API 兼容性 |
相同的公共 API |
相同的公共 API |
调试 |
由于协作式多任务处理和绿线程,更复杂。 请参阅 eventlet_backdoor.py |
可能会产生更清晰的原生线程堆栈跟踪,并受益于最近的 CPython 改进(例如 PEP 768)。 |
Python 兼容性 |
与 CPython 原生线程和 RLocks 存在已知问题。 可能会在新 Python 版本中中断。 |
与现代 CPython 线程完全兼容。 |
Devstack 和 CI 门中的测试设置¶
如果您想在 Devstack 或您的 CI 门中测试线程后端,请参阅
参考¶
如有任何问题,请参阅 OpenStack 邮件列表或 oslo.service 维护者。