关键概念

本节介绍 Nameko 的核心概念。

服务结构

Nameko 服务只是一个 Python 类。类的方法封装了应用程序逻辑,并将任何 依赖项 声明为属性。

通过 入口点 装饰器,方法向外界公开。

from nameko.rpc import rpc, RpcProxy

class Service:
    name = "service"

    # we depend on the RPC interface of "another_service"
    other_rpc = RpcProxy("another_service")

    @rpc  # `method` is exposed over RPC
    def method(self):
        # application logic goes here
        pass

入口点

入口点是服务方法的访问通道。它们通常监控外部实体,例如消息队列。当相关事件发生时,入口点可能会“触发”,然后由服务 worker 执行被装饰的方法。

依赖项

大多数服务依赖于自身以外的事物。Nameko 鼓励将这些事物实现为依赖项。

依赖项是隐藏不属于核心服务逻辑的代码的好机会。依赖项与服务的接口应尽可能简单。

在服务中声明依赖项是一个好主意,原因有很多 好处,你应该将其视为服务代码与其他所有内容之间的桥梁。这包括其他服务、外部 API,甚至是数据库。

Worker

当入口点触发时,会创建一个 worker。worker 只是服务类的一个实例,但依赖声明会被替换为这些依赖的实例(参见 依赖注入 )。

需要注意的是,worker 只在执行一个方法时存在——服务在每次调用之间是无状态的,这鼓励使用依赖项。

一个服务可以同时运行多个 worker,数量上限由用户定义。详细信息请参见 并发 。

依赖注入

向服务类添加依赖项是声明式的。也就是说,类上的属性是一个声明,而不是 workers 实际可以使用的接口。

该类属性是一个 DependencyProvider 。它负责提供一个被注入到服务 worker 中的对象。

依赖提供者实现了一个 get_dependency() 方法,其结果被注入到新创建的 worker 中。

worker 的生命周期如下:

  1. 入口点触发

  2. 从服务类实例化 worker

  3. 依赖项注入到 worker 中

  4. 方法执行

  5. worker 被销毁

在伪代码中,这看起来像这样:

worker = Service()
worker.other_rpc = worker.other_rpc.get_dependency()
worker.method()
del worker

依赖提供者在服务的整个生命周期内存在,而注入的依赖项可以是每个 worker 唯一的。

并发

Nameko 建立在 eventlet 库之上,该库通过“绿色线程”提供并发。并发模型是带有隐式让渡的协程。

隐式让渡依赖于对标准库进行 猴子补丁,以在线程等待 I/O 时触发让渡。如果您通过命令行使用 nameko run 托管服务,Nameko 将为您应用猴子补丁。

每个 worker 在自己的绿色线程中执行。可以根据每个 worker 等待 I/O 的时间调整最大并发 worker 数量。

worker 是无状态的,因此本质上是线程安全的,但依赖项应确保它们在每个 worker 中是唯一的,或者可以安全地被多个 worker 并发访问。

请注意,许多使用套接字的 C 扩展,通常被认为是线程安全的,可能与绿色线程不兼容。其中包括 librabbitmq 、 MySQLdb 等。

扩展

所有入口点和依赖提供者都被实现为“扩展”。我们这样称呼它们,因为它们在服务代码之外,但并不是所有服务都需要(例如,一个完全暴露于 AMQP 的服务不会使用 HTTP 入口点)。

Nameko 具有多种 内置扩展 ,一些是 由社区提供 ,您也可以 自己编写 。

运行服务(Running Services)

运行服务所需的仅仅是服务类和任何相关的配置。运行一个或多个服务的最简单方法是使用 Nameko CLI:

$ nameko run module:[ServiceClass]

此命令将发现给定 modules 中的 Nameko 服务并开始运行它们。您可以选择将其限制为特定的 ServiceClasss 。

服务容器(Service Containers)

每个服务类都委托给 ServiceContainer。容器封装了运行服务所需的所有功能,并且还封装了服务类上的任何 扩展。

使用 ServiceContainer 运行单个服务:

from nameko.containers import ServiceContainer

class Service:
    name = "service"

# create a container
container = ServiceContainer(Service, config={})

# ``container.extensions`` exposes all extensions used by the service
service_extensions = list(container.extensions)

# start service
container.start()

# stop service
container.stop()

服务运行器(Service Runner)

ServiceRunner 是对多个容器的一个薄包装,暴露出同时启动和停止所有包装容器的方法。这就是 nameko run 在内部使用的方式,它也可以通过编程构造:

from nameko.runners import ServiceRunner
from nameko.testing.utils import get_container

class ServiceA:
    name = "service_a"

class ServiceB:
    name = "service_b"

# create a runner for ServiceA and ServiceB
runner = ServiceRunner(config={})
runner.add_service(ServiceA)
runner.add_service(ServiceB)

# ``get_container`` will return the container for a particular service
container_a = get_container(runner, ServiceA)

# start both services
runner.start()

# stop both services
runner.stop()

如果您创建自己的运行器而不是使用 nameko run ,您还必须应用 eventlet 的 monkey patch 。

有关示例,请参见 nameko.cli.run 模块。