Neutron 的服务

本文会讲述 Neutron 是如何启动一个 Web Server 的。Neutron Server 对外提供的 Service API (RESTful)并不是一个实实在在的模块。对于用户来说,它们只是一批 RESTful 接口,对于 Neutron 来说,它们只是资源(network、subnet、port 等)的 CRUD 的一种外在体现。

Neutron Plugin 与 Neutron Agent 之间的通信机制是 RPC,Neutron Plugin 既是 RPC Producer,也是 RPC Consumer。从进程的角度来讲,Neutron Plugin 是 Neutron Server 的一部分,因此本文也会讲述 Neutron Server 是如何创建一个 RPC Consumer的。

Neutron 启动一个 Web Server

想要启动一个Web Server,Neutron Server 首先作为一个普通的进程启动起来,然后它有两种选择:

  • 在当前进程以协程的方式,开启 Web Server
  • 另外启动进程,在新的进程中开启 Web Server

Web Server 的启动过程

在 neutron/setup.cfg 文件中有这么一句话,定义了 Neutron Server启动函数名称:

neutron-server = neutron.cmd.eventlet.server:main

Neutron Server 的启动函数的实现如下:

#neutron/neutron/cmd/eventlet/server/__init__.py
# 使用的是 Paste + PasteDeploy + Routes + WebOb 框架

def main():
    server.boot_server(wsgi_eventlet.eventlet_wsgi_server)

函数 wsgi_eventlet.eventlet_wsgi_server() 的代码如下:

def eventlet_wsgi_server():
    # 启动了一个 Web Server
    neutron_api = service.serve_wsgi(service.NeutronApiService)
    start_api_and_rpc_workers(neutron_api)

Web Server 启动过程中的关键参数

# neutron/neutron/wsgi.py

# 创建一个协程池
self.pool = eventlet.GreenPool(1)

# 启动一个协程,启动函数是 _run,它的两个参数是:application、socket
pool.spawn(_run,application,socket)

# 启动函数 _run 的定义
def _run(self, application, socket):
    """Start a WSGI server in a new green thread."""
    # 启动一个符合 WSGI 规范的 Web Server
    eventlet.wsgi.server(socket, application,
        max_size=self.num_threads,
        log=LOG,
        keepalive=CONF.wsgi_keep_alive,
        log_format=CONF.wsgi_log_format,
        socket_timeout=self.client_socket_timeout)

代码的含义是:

  1. 创建一个协程池
  2. 协程的启动函数就是 _run
  3. _run 函数的本质是创建一个符合 WSGI 规范的 Web Server
  4. 这个 Web Server 的 WSGI Applicaiton 就是传入的参数 application
  5. 这个 Web Server 绑定的 Socket 就是传入的 socket

Socket 的 IP 和端口号,在配置文件中配置

neutron/tests/etc/neutron.conf

# 绑定到 Web Server 的 IP 地址
bind_host = 0.0.0.0

# 绑定到 Web Server 的端口号
bind_port = 9696

除了 Server IP 和 Server 端口号,最重要的参数就是 WSGI Application 了,因为 WSGI Application 才是真正处理 HTTP 请求的实体。_run 函数的参数 application,在 Neutron Server 启动的过程中,是靠如下函数加载的,涉及 RESTful API 的发布与处理,以及 Neutron Plugins 的加载。

# neutron/neutron/service.py
def _run_wsgi(app_name):
    app = config.load_paste_app(app_name)

Neutron 将它的 Service 分为两大类:Core Service 和 Extension Service。Core Service 包括 networks、subnets、ports、subnetpools 等资源的 RESTful API,Extension Service 包括 routers、segments、trunks、policies 等其他各种资源的 RESTful API。

Core Service API(RESTful)的处理流程

Core Service 的 WSGI Application 到底是什么。在 api-paste-ini 中,有如下定义:

# neutron/etc/api-paste.ini
[app:neutronapiapp_v2_0]
paste.app_factory = neutron.api.v2.router:APIRouter.factory

也就是说,Core Sevice 的 WSGI Application 将由 neutron.api.v2.router:APIRouter.factory 这个函数创建。Core Service 的 WSGI Application 是一个 class APIRouter 的实例。

Core Service 处理 HTTP Request 的基本流程

  1. Web Client 发送 HTTP Request 请求到达 Web Server(即客户端调用 Neutron Server 的 Core Server API(RESTful))
  2. Web Server 调用它的 WSGI Application(class APIRouter  的实例)的 __call__ 函数,即调用 class APIRouter 父类 class Router 的 __call__ 函数
  3. class Router 的 __call__ 函数,返回了 class RoutesMiddleware 的一个实例
  4. Web Server 继续调用 class RoutesMiddleware 实例的 __call__ 函数
  5. class RoutesMiddleware 的 __call__ 函数,反过来调用 class Router 的静态函数 _dispatch

class Router 的 _dispatch 函数代码:

    match = req.environ['wsgiorg.routing_args'][1]
    app = match['controller']

对于 Core Service 的 WSGI Application 而言,一个 HTTP Request 过来以后,基本的处理流程是在父类的 class Router 中完成了。class Router 处理到最后,是从它的成员变量 map 中查找对应的处理函数并执行它

class Router 的成员变量 map 实在子类 class APIRouter 中完成构建的。class APIRouter 中 __init__ 函数完成了三件事情:

  • 初始化 map 成员变量
  • 加载了 ML2 Plugin
  • 实例化了 Core Service 的 Extension Manger

针对一个资源,Neutron 提供了 6 个 Service API。分为两大类:

  • 针对复数资源(比如 networks)的操作,成为 collections action,包括:index、create、bulk create
  • 针对单个资源(资源后面带上 ID 参数,比如 networks/{network_id})的操作成为 member action,包括 show、update、delete

 class Controller 的成员函数,最终会调用 ML2Plugin 相应的成员函数。

Plugin 的加载

当一个 HTTP Request 过来以后,最终处理这个 Request 的函数都是相应的 Plugin (插件)的函数。Neutron 的 Plugin 最终会在 NeutronMananer 的 __init__ 函数中加载:

class NeutronManager(object):
    def __init__(self, options=None, config_file=None):
        # 加载 Core Service Plugin
        plugin_provider = cfg.CONF.core_plugin
        plugin = self._get_plugin_instance(CORE_PLUGINS_NAMESPACE,
                                           plugin_provider)
        # load services from the core plugin first
        self._load_services_from_core_plugin(plugin)
        self._load_service_plugins()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值