1. 消息总线
Openstack遵循这样的设计原则:项目之间通过RESTful API进行通信;项目内部,不同的服务进程之间的通信,则必须通过消息总线。
1.1. AMQP(高级优先消息队列协议)
Openstack 组件内部的 RPC(Remote Producer Call)机制的实现是基于 AMQP(Advanced Message Queuing Protocol)作为通讯模型,从而满足组件内部的松耦合性。AMQP 是用于异步消息通讯的消息中间件协议,
Openstack目前已经支持了一些常见的消息总线
1)RabbitMQ
2)ZeroMQ
3)Qpid
RPC 远程过程调用
通过远程过程调用,一个服务进程可以调用其他远程服务进程的方法,并且有两种调用方式
(1)cast 。远程方法会被异步调用
(2)call 。远程方法会被同步调用
AMQP 模型有四个重要的角色:
- Exchange:根据 Routing key 转发消息到对应的 Message Queue 中
- Routing key:用于 Exchange 判断哪些消息需要发送对应的 Message Queue
- Publisher:消息发送者,将消息发送的 Exchange 并指明 Routing Key,以便 Message Queue 可以正确的收到消息
- Consumer:消息接受者,从 Message Queue 获取消息
消息发布者 Publisher 将 Message 发送给 Exchange 并且说明 Routing Key。Exchange 负责根据 Message 的 Routing Key 进行路由,将 Message 正确地转发给相应的 Message Queue。监听在 Message Queue 上的 Consumer 将会从 Queue 中读取消息。
Routing Key 是 Exchange 转发信息的依据,因此每个消息都有一个 Routing Key 表明可以接受消息的目的地址,而每个 Message Queue 都可以通过将自己想要接收的 Routing Key 告诉 Exchange 进行 binding,这样 Exchange 就可以将消息正确地转发给相应的 Message Queue。
AMQP 消息模型:
AMQP 定义了三种类型的 Exchange,不同类型 Exchange 实现不同的 routing 算法:
- Direct Exchange:Point-to-Point 消息模式,消息点对点的通信模式,Direct Exchange 根据 Routing Key 进行精确匹配,只有对应的 Message Queue 会接受到消息
- Topic Exchange:Publish-Subscribe(Pub-sub)消息模式,Topic Exchange 根据 Routing Key 进行模式匹配,只要符合模式匹配的 Message Queue 都会收到消息
- Fanout Exchange:广播消息模式,Fanout Exchange 将消息转发到所有绑定的 Message Queue
OpenStack 目前支持的基于 AMQP 模型的 RPC backend 有 RabbitMQ、QPid、ZeroMQ,对应的具体实现模块在 cinder 项目下 Openstack/common/RPC/目录下,impl_*.py 分别为对应的不同 backend 的实现。
接下来,我们看一个openstack中使用rpc的例子,例子是neutron中的代码,其他openstack项目nova cinder等也是用类似的流程
neutron代码:
(1)neutron server初始化时会调用_setup_rpc和start_rpc_listeners初始化rpc相关资源
代码路径neutron\plugins\ml2\plugin.py
def _setup_rpc(self):
#创建一个rpc client,用于发送rpc消息,后面我会把AgentNotifierApi的代码附上
self.notifier = rpc.AgentNotifierApi(topics.AGENT)
self.agent_notifiers[const.AGENT_TYPE_DHCP] = (
dhcp_rpc_agent_api.DhcpAgentNotifyAPI()
)
def start_rpc_listeners(self):
self.endpoints = [rpc.RpcCallbacks(self.notifier, self.type_manager),
securitygroups_rpc.SecurityGroupServerRpcCallback(),
dvr_rpc.DVRServerRpcCallback(),
dhcp_rpc.DhcpRpcCallback(),
agents_db.AgentExtRpcCallback(),
metadata_rpc.MetadataRpcCallback()]
self.topic = topics.PLUGIN
self.conn = n_rpc.create_connection(new=True)
#创建rpc消费者
self.conn.create_consumer(self.topic, self.endpoints,
fanout=False)
#开始监听消息
return self.conn.consume_in_threads()
(2)AgentNotifierApi类的实现:
代码路径neutron\plugins\ml2\rpc.py
当neutron server需要通知agent进行networkd_delete时就会调用network_delete函数,network_delete中使用rpc的异步消息通知agent
class AgentNotifierApi(dvr_rpc.DVRAgentRpcApiMixin,
sg_rpc.SecurityGroupAgentRpcApiMixin,
type_tunnel.TunnelAgentRpcApiMixin):
"""Agent side of the openvswitch rpc API.
API version history:
1.0 - Initial version.
1.1 - Added get_active_networks_info, create_dhcp_port,
update_dhcp_port, and removed get_dhcp_port methods.
"""
def __init__(self, topic):
self.topic = topic
self.topic_network_delete = topics.get_topic_name(topic,
topics.NETWORK,
topics.DELETE)
self.topic_port_update = topics.get_topic_name(topic,
topics.PORT,
topics.UPDATE)
target = messaging.Target(topic=topic, version='1.0')
self.client = n_rpc.get_client(target)
def network_delete(self, context, network_id):
cctxt = self.client.prepare(topic=self.topic_network_delete,
fanout=True)
#发送异步消息
cctxt.cast(context, 'network_delete', network_id=network_id)
def port_update(self, context, port, network_type, segmentation_id,
physical_network):
cctxt = self.client.prepare(topic=self.topic_port_update,
fanout=True)
cctxt.cast(context, 'port_update', port=port,
network_type=network_type, segmentation_id=segmentation_id,
physical_network=physical_network)
2. SQLAlchemy
SQLAlchemy是Python编程语言下的一款开源软件。提供了SQL工具包及对象关系映射(ORM)工具,使用MIT许可证发行。
SQLAlchemy“采用简单的Python语言,为高效和高性能的数据库访问设计,实现了完整的企业级持久模型”
SQLAlchemy主要分成两部分:
SQLAlchemy Core(SQLAlchemy核心):包含SQL语言表达式、数据引擎、连接池等
SQLAlchemy ORM(SQLAlchemy 对象关系映射器):提供数据映射模式(也即是把程序语言对象数据映射成数据库中的关系数据)
架构图:
3. RESTful API和WSGI
3.1.RESTful
RESTful是一种互联网软件架构。REST(Representational State Transfer,表述性状态转移)是一种软件架构风格。如果一个架构符合REST原则,就称它为RESTful架构。
要理解RESTful架构,最好的方法就是去理解Representational State Transfer这个词组到底是什么意思,它的每一个词代表了什么涵义。如果你把这个名称搞懂了,也就不难体会REST是一种什么样的设计。
3.1.1.资源(Resources)
REST的名称"表述性状态转化"中,省略了主语。"表述性"其实指的是"资源"(Resources)的"表述性"。
所谓"资源",就是网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的实在。你可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的URI。要获取这个资源,访问它的URI就可以,因此URI就成了每一个资源的地址或独一无二的识别符。
所谓"上网",就是与互联网上一系列的"资源"互动,调用它的URI。
3.1.2.表述性(Representation)
"资源"是一种信息实体,它可以有多种外在表现形式。我们把"资源"具体呈现出来的形式,叫做它的"表述性"(Representation)。
比如,文本可以用txt格式表现,也可以用HTML格式、XML格式、JSON格式表现,甚至可以采用二进制格式;图片可以用JPG格式表现,也可以用PNG格式表现。
URI只代表资源的实体,不代表它的形式。严格地说,有些网址最后的".html"后缀名是不必要的,因为这个后缀名表示格式,属于"表现层"范 畴,而URI应该只代表"资源"的位置。它的具体表现形式,应该在HTTP请求的头信息中用Accept和Content-Type字段指定,这两个字段 才是对"表述性"的描述。
3.1.3.状态转化(State Transfer)
访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,势必涉及到数据和状态的变化。
互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State Transfer)。而这种转化是建立在表述性之上的,所以就是"表述性状态转化"。
客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。
3.1.4.综述
综合上面的解释,我们总结一下什么是RESTful架构:
(1)每一个URI代表一种资源;
(2)客户端和服务器之间,传递这种资源的某种表现形式(表述性);
(3)客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表述性状态转化"。
4. Eventlet
eventlet是一个用来处理和网络相关的python库函数,而且可以通过协程来实现并发,在eventlet里,把“协程”叫做 greenthread(绿色线程)。
一个进程中,利用python库Eventlet可以产生出许多个协程,这些协程之间只有在调用到了某些特殊的Eventlet库函数的时候(比如sleep,I/O调用等)才会发生切换
协程(coroutine)有下面几个特点。
1.每个协程都有自己的私有stack及局部变量,同时又于其他协程共享全局变量;
2.同一时间内只有一个协程在运行,故无须对某些共享变量加锁;
3.协程之间的执行顺序,完成由程序来控制;
协程的实现主要是在协程休息时把当前的寄存器保存起来,然后重新工作时再将其恢复。
一篇讲Eventlet的博客:
http://blog.csdn.net/gaoxingnengjisuan/article/details/12913275
5. OpenStack通用库Oslo
Oslo功能:独立出系统中可重用的基础功能
5.1.Cliff
可以用来帮助构建命令行程序
5.2.oslo.config
oslo.config库用于解析命令行和配置文件的配置选项
5.3.oslo.db
oslo.db是针对SQLAlchemy访问的抽象
5.4.oslo.i18n
oslo.i18n是对Python getext模块的封装,主要用于字符串的翻译和国际化。
5.5.oslo.messaging
oslo.messaging库为OpenStack各个项目使用RPC和时间通知提供了一套统一的接口。
5.6.stevedore
stevedore基于setuptools entry point(http://packages.python.org/distribute/pkg_resources.html#convenience-api),提供python应用程序管理插件的功能。
stevedore就是在Setuptools的entry points基础上,构造了一层抽象成,使开发者可以更容易地在运行时发现和载入插件
使用stevdore来帮助程序动态载入插件的过程:
(1)插件的实现
(2)插件的注册
(3)插件的载入
5.7.TaskFlow
通过TaskFlow库,可以更容易地控制任务(Task)的执行。
5.8.cookiecutter
5.9.oslo.policy
5.10.oslo.rootwrap
5.11.oslo.test
******************************************未完待续