openstack中的服务主要有两种:一种是rest服务,提供Rest API;一种RPC服务,提供RPC API。本文讨论RPC服务的实现。Rest服务的实现在《openstack学习之各种API》有所涉及。
RPC服务其实就是一个RPC server,client(客户)可以通过RPC API进行调用。以nova为例,nova中的多数服务(service)都是一个RPC server。比如nova-conductor,nova-compute,nova-consoleauth,nova-scheduler...等
实现一个rpc server需要几个部分:
1)启动命令代码
2)Service定义
3)Manager定义
4)功能实现代码
其中1)2)3)属于框架性代码,每个rpc server的实现都差不多;4)和rpc server要实现的功能相关。
以nova为例,来分析这些部分的实现。
nova.cmd package中定义了所有服务的启动代码,包括rpc server和rest api服务。api.py,api_ec2.py,api_metadata.py,api_os_compute.py是rest api服务。这个目录中还包含了一些命令工具(utility command),如manage.py。读者可自行查看。
rpc server的启动代码基本类似:
1)定义main
2) 加载配置文件
3) 配置log
4) 创建server对象
5)启动service.
步骤4的代码compute:
server = service.Service.create(binary='nova-compute',
topic=CONF.compute_topic,
db_allowed=CONF.conductor.use_local)
步骤4的代码schduler:
server = service.Service.create(binary='nova-scheduler',
topic=CONF.scheduler_topic)
步骤4的代码conductor:
server = service.Service.create(binary='nova-conductor',
topic=CONF.conductor.topic,
manager=CONF.conductor.manager)
步骤5的代码,3者一致
service.serve(server)
service.wait()
Service对象
Service位于nova.service.py
该对象继承于nova.openstack.common.service.py中的Service
__init__方法
初始化几个重要的成员
self.host = host
self.binary = binary
self.topic = topic
self.manager_class_name = manager
参数有启动命令提供。host如果没有传入,从配置文件读取。host = CONF.host
start方法:
该方法用来启动rpc server。
该方法的主要功能就是创建RPC队列并绑定到exchang上。
target = messaging.Target(topic=self.topic, server=self.host)
endpoints = [
self.manager,
baserpc.BaseRPCAPI(self.manager.service_name, self.backdoor_port)
]
endpoints.extend(self.manager.additional_endpoints)
target用来指定该rpc server监听在哪些队列上。
target指定了2个参数:topic和server。查看target代码(olso.messaging: oslo.messaging.target.py)
:param server: Clients can request that a message be directed to a specific
server, rather than just one of a pool of servers listening on the topic.
:param topic: A name which identifies the set of interfaces exposed by a
server. Multiple servers may listen on a topic and messages will be
dispatched to one of the servers in a round-robin fashion.
底层如何使用target创建RPC队列并绑定到exchang上,可以查看代码
transport._listen
self._driver.listen(target)
amqpdriver.listen
conn.declare_topic_consumer(target.topic, listener)
conn.declare_topic_consumer('%s.%s' % (target.topic, target.server),
listener)
conn.declare_fanout_consumer(target.topic, listener)
从代码中可以看到创建了3个队列,这个3个系列用来实现《openstack学习之RPC》提到的3种RPC场景
随机调用某server上的一个方法: 通过第一个队列实现
调用某特定server上的一个方法: 通过第二个队列实现,这个队列通过server参数来唯一标识一个队列
调用所有server上的一个方法:通过第三个队列实现,exchange type为fantout类型
关于队列的概念可以参考另一篇博文《openstack学习之RPC》
endpoints用来定义该rpc server支持哪些API。
manager中部分方法其实就是RPC API的方法,比如ComputeManager中有reboot_instance,这个方法就是一个RPC API。
client调用RCP API时会给出topic,方法名,方法参数等信息。oslo.messaging中的RPCDispatch会根据这些信息将该请求直接转化为对manager的相应方法的调用,并将方法参数准备好。
还可以看到openstack会给每个rpc server加一个额外的API(BaseRPCAPI),这个API目前只有一个ping方法。openstack内部会用这个方法检测一个服务是否已经启动完毕,如果启动完毕,就可以相应ping调用。
Service对象还提供了另外一个功能,定时任务。
self.tg.add_dynamic_timer(self.periodic_tasks,
initial_delay=initial_delay,
periodic_interval_max=
self.periodic_interval_max)
self.periodic_tasks会调用manager.periodic_tasks。
综上,Service对象的核心功能就是创建监听队列,并将收到的消息转化为对manager对象的方法调用。每个openstack项目实现都有定义一个自己的Service对象(如nova.service.py,cinder.service.py,neutron.service.py),这些对象的代码基本类似,相信后边社区会对这些代码进行重构。
Manager对象
如前所述,Manager对象其实就是RPC API的入口。每个RPC API最终会转化为对Manger相应方法的调用,这个方法就是该RPC API的最终实现。
每个服务都会有一个相应的Manager对象,因为一般每个服务所提供的API都是不同的。每个服务的实现都会有一个相应的python package(文件系统中的目录),如conductor,compute,consoleauth,scheduler等。每个package中又会有一个manager.py。这个manager.py就定义了这个服务的Manager对象。如compute.manager.py定义了ComputeManager。nova boot命令最终会调用这个对象中的run_instance方法。
要想进一步看懂Manager中的代码,就需要对相应问题领域(problem domain,也就是我们常说的业务流程)比较熟悉了。ComputeManager是处理虚拟机生命周期的,我们要对虚拟机有了解,才能读懂其中的代码。
掌握了Service + Manager,基本上就了解了RPC server是如何工作的,学习openstack代码就比较直观了。