后端系统

介绍

oslo.service 中,后端系统提供了一种模块化架构,允许您在不修改服务逻辑的情况下,选择不同的并发模型。

目前,已经实现了并支持两种后端

  • Eventlet 后端:基于绿线程和协作式多任务处理(greenlet 库)的传统并发后端。

  • 线程后端:使用原生 Python 线程、Cotyledon 和 Futurist 的新后端,旨在与现代 Python 版本兼容,简化调试并提高可维护性。

为什么需要后端系统?

引入此系统是为了能够逐步过渡离开 Eventlet 后端,该后端在扩展性、协作式多任务处理的复杂性和某些 Python 环境下的调试方面存在已知限制。通过使用后端系统,您可以测试、采用或迁移到线程后端,同时保持相同的公共 API 和服务结构。

未来,一旦线程后端涵盖所有用例,Eventlet 后端可能会被弃用。与 Eventlet 相关的某些功能,例如嵌入式 WSGI 服务器和后门 shell,也计划删除。

有关更多信息,请参阅

工作原理

后端系统通过动态选择机制暴露相同的公共组件ServiceLauncherProcessLauncherThreadGroupLoopingCall 等)。

在运行时,您通过调用 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()

如何选择后端

有两种支持的方式来选择要使用的后端

  1. 显式初始化

    在您的应用程序入口点中早期调用 init_backend()

    from oslo_service.backend import init_backend, BackendType
    
    init_backend(BackendType.THREADING)
    

    这将加载线程后端并缓存其组件。

    有关实际用法,请参阅 Nova 如何操作:https://review.opendev.org/c/openstack/nova/+/948311

  2. 动态钩子

    如果您没有调用 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()

可用的组件包括

  • ServiceLauncher

  • ProcessLauncher

  • ThreadGroup

  • LoopingCall 变体

  • 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 维护者。