Openstack 通信

1.概述

OpenStack的通信方式有两种,项目之间通过基于HTTP协议的RESTFul API通信,项目内部不同服务之间则通过消息总线通信
两种通信方式的应用场景有所不同,在OpenStack中,前者主要用于各组件之间的通信(如nova与glance的通信),而后者则用于同一组件中各个不同模块之间的通信(如nova组件中nova-compute与nova-scheduler的通信)。

2.WSGI

OpenStack中基于RESTFul API的通信方式主要是应用了WSGI
全称是Web Server Gateway Interface,WSGI只是一种规范,描述web server与web application通信的规范

WSGI协议主要包括server和application两部分:
• Web server/gateway: 即 HTTP Server,处理 HTTP 协议,接受用户 HTTP 请求和提供并发,调用 web application 处理业务逻辑。WSGI server负责从客户端接收请求,将request转发给application,将application返回的response返回给客户端;
• Python Web application/framework: WSGI application接收由server转发的request,处理请求,并将处理结果返回给server。application中可以包括多个栈式的中间件(middlewares),这些中间件需要同时实现server与application,因此可以在WSGI服务器与WSGI应用之间起调节作用:对服务器来说,中间件扮演应用程序,对应用程序来说,中间件扮演服务器。
WSGI协议其实是定义了一种server与application解耦的规范,即可以有多个实现WSGI server的服务器,也可以有多个实现WSGI application的框架,那么就可以选择任意的server和application组合实现自己的web应用。

Application/Framework
Application/framework 端必须定义一个 callable object,callable object 可以是以下三者之一:

function
method
class/instance with a _call_ method

Callable object 必须满足以下两个条件:
• 接受两个参数:字典(environ),回调函数(start_response,返回 HTTP status,headers 给 web server)
• 返回一个可迭代的值
基于 callable function 的 application/framework 样例如下:

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/plain')])
    return ['This is a python application!']

基于 callable class 的 application/framework 样例如下:

class ApplicationClass(object):
    def __init__(self, environ, start_response):
        self.environ = environ
        self.start_response = start_response
    def __iter__(self):
        self.start_response('200 OK', [('Content-type', 'text/plain')])
        yield "Hello world!n"

Server/Gateway
Server/gateway 端主要专注 HTTP 层面的业务,重点是接收 HTTP 请求和提供并发。每当收到 HTTP 请求,server/gateway 必须调用 callable object:
接收 HTTP 请求,但是不关心 HTTP url, HTTP method 等
为 environ 提供必要的参数,实现一个回调函数 start_response,并传给 callable object

# application/framework side
def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/plain')])
    return ['This is a python application!']
# server/gateway side
if __name__ == '__main__':
    from wsgiref.simple_server import make_server
    server = make_server('0.0.0.0', 8080, application)
    server.serve_forever()

Middleware: Components that Play Both Sides
Middleware 处于 server/gateway 和 application/framework 之间,对 server/gateway 来说,它相当于 application/framework;对 application/framework 来说,它相当于 server/gateway。每个 middleware 实现不同的功能,我们通常根据需求选择相应的 middleware 并组合起来,实现所需的功能。比如,可在 middleware 中实现以下功能:
• 根据 url 把用户请求调度到不同的 application 中。
• 负载均衡,转发用户请求
• 预处理 XSL 等相关数据
• 限制请求速率,设置白名单

3.消息总线

(1)远程过程调用(Remote Procedure Call,RPC)。
通过远程过程调用,一个服务进程可以调用其他远程服务进程的方法,并且有两种调用方式call 和cast。通过call的方式调用,远程方法会被同步执行,调用者会被阻塞直到结果返回;通过cast的方式调用,远程方法会被异步执行,结果并不会立即返回,调用者也不会被阻塞,但是调用者要利用其他方式查询这次远程调用的结果。
(2)事件通知(Event Notification)。
某个服务进程可以把事件通知发送到消息总线上,然后该消息总线上所有对此类事件感兴趣的服务进程,都可以获得此事件通知并进行进一步的处理,但是处理的结果并不会返回给事件发送者。这种通信方式不但可以实现在同一个项目内部的各个服务进程之间发送通知,也可以实现跨项目之间的通知发送。Ceilometer 就通过这种方式大量获取其他OpenStack 项目的事件通知,从而进行计量和监控。

通过不同的配置选项,远程过程调用和事件通知可以使用不同的消息总线后端(Backend),比如,RPC使用RabbitMQ,事件通知使用Kafka,从而满足不同环境下的特定应用需求,极大地增加了灵活性。

AMQP

RPC采用AMQP协议实现进程间通信,而RabbitMQ正是AMQP的实现方式,所以可以说OpenStack中的RPC调用都是基于RabbitMq完成的
RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议”。这个概念听起来还是比较抽象,下面我会结合OpenStack中RPC调用的具体应用来具体分析一下这个协议。
其次,为什么要采用RPC呢?单纯的依靠RESTFul API不可以吗?

  1. 由于RESTFul API是基于HTTP协议的,因此客户端与服务端之间所能传输的消息仅限于文本
  2. RESTFul API客户端与服务端之间采用的是同步机制,当发送HTTP请求时,客户端需要等待服务端的响应。当然对于这一点是可以通过一些技术来实现异步的机制的
  3. 采用RESTFul API,客户端与服务端之间虽然可以独立开发,但还是存在耦合。比如,客户端在发送请求的时,必须知道服务器的地址,且必须保证服务器正常工作

基于上面这几个原因,所以OpenStack才采用了另一种远程通信机制,这就是我们今天要讨论的鼎鼎大名的RPC。
要了解OpenStack中的RPC,有一个组件是必不可少的,那就是RabbitMQ(消息队列)。OpenStack中,RPC采用AMQP协议实现进程间通信,而RabbitMQ正是AMQP的实现方式,所以可以说OpenStack中的RPC调用都是基于RabbitMq完成的。
在Nova中,定义了两种远程调用方式——rpc.call和rpc.cast

rpc.call方式是指request/response(请求/响应)模式,即客户端在发送请求后,继续等待服务器端的响应结果,待响应结果到达后,才结束整个过程。
rpc.cast方式是指客户端发送RPC调用请求后,不等待服务器端的响应结果

为了处理rpc.call,Nova采用了Topic Exchange(主题交换器)和Direct Exchange(直接交换器)两种消息交换器。其中Topic Exchange用于客户端向服务器端rpc.call的发起,Direct Exchange用于服务器端向客户端返回rpc.call。对应于这两种交换机,Nova中定义了Topic/Direct消息消费者、Topic/Direct消息发布者、Topic/Direct交换器。

Exchange类型

类型说明模式
Directbinding key 和 routing key 必须完全一致,不支持通配符P2P
Topic同Diroci类型,但支持通配符。“·”匹配一个单字。“g”匹配零个或多个单字,单字之间是由“.”来分割的发布-订阅
Fanout忽略binding key 和l routing key,消息会被传递到所有绑定的队列上多播模式

一个主题交换器可以关联多个队列,而一个直接交换器只能关联一个队列。

AMQP是用于异步消息通讯的消息中间件协议;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。

OpenStack层封装call和cast接口用于远程调用RPC的server上的方法,这些方法都是构造RPC的server的endpoints内的方法。远程调用时,需要提供一个字典对象来指明调用的上下文,调用方法的名字和传递给调用方法的参数(用字典表示)。如:

cctxt =self.client.prepare(vesion=’2.0’)
cctxt.cast(context,‘build_instances’, **kw)

基于AMQP messaging 实现RPC
在这里插入图片描述

• Broker: 接收和分发消息的应用,RabbitMQ Server就是Message Broker。
• Virtual host:出于多租户和安全因素设计的,把AMQP的基本组件划分到一个虚拟的分组中,类似于网络中的namespace概念。当多个不同的用户使用同一个RabbitMQserver提供的服务时,可以划分出多个vhost,每个用户在自己的vhost创建exchange/queue等
• Connection: publisher/consumer和broker之间的TCP连接。断开连接的操作只会在client端进行,Broker不会断开连接,除非出现网络故障或broker服务出现问题。
• Channel: 如果每一次访问RabbitMQ都建立一个Connection,在消息量大的时候建立TCP Connection的开销将是巨大的,效率也较低。Channel是在connection内部建立的逻辑连接,如果应用程序支持多线程,通常每个thread创建单独的channel进行通讯,AMQP method包含了channel id帮助客户端和message broker识别channel,所以channel之间是完全隔离的。Channel作为轻量级的Connection极大减少了操作系统建立TCP connection的开销。
• Exchange: message到达broker的第一站,根据分发规则,匹配查询表中的routing key,分发消息到queue中去。常用的类型有:direct (point-to-point), topic (publish-subscribe) and fanout (multicast)。
• Queue: 消息最终被送到这里等待consumer取走。一个message可以被同时拷贝到多个queue中。
• Binding: exchange和queue之间的虚拟连接,binding中可以包含routing key。Binding信息被保存到exchange中的查询表中,用于message的分发依据。

在这里插入图片描述

1.客户端给 Exchange 发送一条请求消息,并指定routing key 为op_quewe,同时指明一个消息队列名用来获取响应,图中为res_qucue。
2.Exchange 把此消息转发到消息队列op_queue。
3.消息队列op_qucue把消息推送给服务端,服务端执行此RPC调用对应的任务。在执行结束 后,服务端把响应结果发送给消息队列,并指明routing key为res_qucue.
4.Exchange 把此消息转发到消息队列res_queue。
5.客户端从消息队列res_queue中获取响应。

“生产/消费”消息模型
生产者发送消息到broker server(RabbitMQ)。在Broker内部,用户创建Exchange/Queue,通过Binding规则将两者联系在一起。Exchange分发消息,根据类型/binding的不同分发策略有区别。消息最后来到Queue中,等待消费者取走。
在线程世界里,生产者就是生产数据(或者说发布任务)的线程,消费者就是消费数据(或者说处理任务)的线程。在任务执行过程中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者提供更多的任务,本质上,这是一种供需不平衡的表现。为了解决这个问题,我们创造了生产者和消费者模式。

生产者消费者模式的工作机制:
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而是通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不直接找生产者要数据,而是从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力,解耦了生产者和消费者。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值