neutron源码分析

  1. 1. 消息总线
    1. 1.1. RPC
    2. 1.2. 事件通知(Event Notification)
    3. 1.3. AMQP
  2. 2. WSGI
    1. 2.1. Paste.Deploy
    2. 2.2. WebOb
  3. 3. Eventlet
    1. 3.1. 协程
    2. 3.2. GreenThread
    3. 3.3. Monkey Path
  4. 4. Oslo
    1. 4.1. Cliff
    2. 4.2. oslo.config
    3. 4.3. oslo.db
    4. 4.4. oslo.i18n
    5. 4.5. oslo.messaging
      1. 4.5.1. Transport
      2. 4.5.2. Target
      3. 4.5.3. Server
      4. 4.5.4. RPC Client
      5. 4.5.5. Notifier
      6. 4.5.6. Notifier Listener

消息总线

Openstack各项目之间通过RESTful API进行通信;而项目内部不同服务进程则需要通过消息总线通信。关于消息总线的实现,包含在Openstack.oslo.messaging库中。

RPC

远程过程调用,一个服务进程可以调用其他远程服务进程的方法,有两种方式:

  • call 远程方法会被同步执行,调用者会阻塞直到取得返回结果。
  • cast 远程方法会被异步执行,调用者需要通过其他方式查询这次远程调用的结果。

事件通知(Event Notification)

服务可以把事件通知发到消息总线上,该消息总线上所有对此类事件感兴趣的服务进程,都可以获得次事件通知并进行处理。处理结果不会返回事件发送者。

AMQP

AMQP是一个异步消息传递所使用的开放的应用层协议规范。包括导向、队列、路由、可靠性和安全性。不同的AMQP实现可以进行相互操作。
所有消息都有一个routing key,所有Queue都有一个binding key。生产者将消息发送给Exchange,然后Exchange根据这两个key把消息送到相匹配的Queue中。不同类型的Exchange有不同的匹配算法。

类型 说明
Direct binding key和routing key必须完全一样,不支持通配符
Topic 同Direct类型,但是支持通配符,”*”匹配单字,“#”匹配零个或多个单字,单字之间由“.”来分割
Fanout 忽略binding key和routing key,广播式路由

WSGI

WSGI 是一个连接服务端和应用端的接口。WSGI把Web组件分为三部分:

  • WSGI Server
  • WSGI Middleware
  • WSGI Application

An Application must return an iterable object.

1
2
3
def application(environ,start_response):
start_response('200 Ok',[('Content-Type','text/plain')])
yield 'Hello World\n'

  • environ:一个dict,包括CGI中定义的环境变量以及7个WSGI所定义的环境变量:wsgi.version,wsgi_input…
  • start_response: 回调函数,要返回一个write(data)对象,用作兼容现有的特殊框架,一般返回None

Paste.Deploy

Paste Deploy通过api-paste.ini配置
Paste配置文件分为多个section,每个section以type:name的格式命名。
(书上P99)

WebOb

对WSGI的封装,包含:

  • webob.Request 对WSGI的environ的封装
  • webob.Response 对WSGI响应的封装
  • webob.exc 对HTTP错误代码的封装

Webob提供了webob.dec.wsgify的decorator,可以快速开发application

1
2
3
4
5
6
7
8
9
10
11
12
13
# 继承自webob.Request
class MyRequest(webob.Request):
@property
def is_local(self):
return self.remote_addr == '127.0.0.1'
@wsgify(RequestClass=MyRequest)
def myfunc(req):
if req.is_local:
return Response('hi!')
else:
raise webob.exc.HTTPForbidden

Eventlet

Openstack的协程模块

协程

与线程类似,拥有独立的栈和局部变量,但是无法同时执行,(Compare To: Javascript callback)

GreenThread

1
2
3
4
5
6
import eventlet
def my_func(param):
# do something in coroutine
return param
gt = eventlet.spawn(my_func,param)
result = gt.wait()

eventlet.spawn只是创建一个协程并不立即执行,直到主线程运行到gt.wait()时才开始进入调度序列。

Monkey Path

实现协程需要使用Patch的方式对Python的网络相关的标准库进行改写,这个patch就叫monkey_patch
Monkey Patch是大部分使用Eventlet函数库之前需要进行的初始化工作

1
2
3
4
# ceilometer/cmd/__init__.py
import eventlet
# patch socket,select,thread三个模块
eventlet.monkey_patch(socket=True,select=True,thread=True)

Oslo

Openstack 通用库

Cliff

构建命令行程序
DEMO: https://github.com/openstack/cliff/tree/master/demoapp

oslo.config

解析命令行和配置文件中的配置选项(书上P111)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from oslo.config import cfg
conf(sys.argv[1:],project=`xyz`) # 初始化,使得oslo.config能够正常解析配置文件和命令行选项
rabbit_group = cfg.OptGroup(name='rabbit',
title='RabbitMQ options')
rabbit_host_opt = cfg.StrOpt('host',
default='localhost',
help='IP/hostname to listen on.'),
rabbit_port_opt = cfg.PortOpt('port',
default=5672,
help='Port number to listen on.')
def register_rabbit_opts(conf):
conf.register_group(rabbit_group)
# options can be registered under a group in either of these ways:
conf.register_opt(rabbit_host_opt, group=rabbit_group)
conf.register_opt(rabbit_port_opt, group='rabbit')

如果没有指定group,则选项默认放在[DEFAULT]组下

1
2
3
4
5
6
7
8
9
10
11
12
# glance-api.conf:
[DEFAULT]
bind_port = 9292
# ...
[rabbit]
host = localhost
port = 5672
use_ssl = False
userid = guest
password = guest
virtual_host = /

从命令行中设置conf,需要使用使用‘-’连接groupname和optionname

1
--rabbit-host localhost --rabbit-port 9999

oslo.db

SQLAlchemy数据库模型的抽象

oslo.i18n

是对Python gettext的封装,主要用于字符串翻译和国际化

oslo.messaging

Openstack各项目使用RPC和事件通知的统一的接口。其中包括下面4个对象。

Transport

传输层,主要实现RPC底层的通信,支持rabbit、qpid、zmq三种协议。可以通过URL来指定不同的transport的实现。

1
transport://user:pass@host1:port[,hostN:portN]/virtual_host

Target

封装了指定某一消息最终目的地的所有信息

Server

即服务端,创建一个Server对象需要指定Transport、Target、endpoints(包含多组可被远程调用的方法,比如某一资源包含CUID四个操作)

RPC Client

即客户端,一个RPC Client可以调用RPC Server上的方法。

Notifier

Notifier是一个用来发送通知消息的对象。格式如下:

1
2
3
4
5
6
7
8
{
message_id:six_text_type(uuid.uuid4()), #消息id号
publisher_id:'computer.host1',#发送者id
timestamp:timeutils.utcnow(),#时间戳
priority:'WARN', # 通知优先级
event_type:'compute.create_instance',#通知类型
payload:{'instance_id':12,...} #通知内容
}

Notifier Listener

消息监听者,跟Server类似,可以暴露多个endpoint,每个endpoint中也可以包含多种方法,但是它们只是对应者不同的消息优先级。(P118)