Nova-compute数据库读写测试

Posted wangwei1

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Nova-compute数据库读写测试相关的知识,希望对你有一定的参考价值。

目前,nova compute读写数据库都是通过nova-conductor进行转发。数据库的读写操作都是根据objects下面模块中对应的方法来实现的,而这些方法都是使用了装饰器。

目前nova读写数据库所用的装饰器包括:

技术图片
 1 base.remotable_classmethod
 2 base.remotable
 3 staticmethod
 4 db_api.api_context_manager.reader
 5 db_api.api_context_manager.writer
 6 property
 7 db_api.pick_context_manager_reader
 8 db_api.pick_context_manager_writer
 9 base.NovaObjectRegistry.register
10 require_context
View Code

 

参考:https://blog.csdn.net/tiantao2012/article/details/78530213

测试:

1.使用remotable_classmethod

我们看一下remotable_classmethod这个装饰器的定义:

技术图片
 1 from oslo_versionedobjects import base as ovoo_base
 2 remotable_classmethod = ovoo_base.remotable_classmethod
 3 
 4 def remotable_classmethod(fn):
 5     """Decorator for remotable classmethods."""
 6     @six.wraps(fn)
 7     def wrapper(cls, context, *args, **kwargs):
 8         if cls.indirection_api:
 9             version_manifest = obj_tree_get_versions(cls.obj_name())
10             try:
11                 result = cls.indirection_api.object_class_action_versions(
12                     context, cls.obj_name(), fn.__name__, version_manifest,
13                     args, kwargs)
14             except NotImplementedError:
15                 # FIXME(danms): Maybe start to warn here about deprecation?
16                 result = cls.indirection_api.object_class_action(
17                     context, cls.obj_name(), fn.__name__, cls.VERSION,
18                     args, kwargs)
19         else:
20             result = fn(cls, context, *args, **kwargs)
21             if isinstance(result, VersionedObject):
22                 result._context = context
23         return result
24 
25     # NOTE(danms): Make this discoverable
26     wrapper.remotable = True
27     wrapper.original_fn = fn
28     return classmethod(wrapper)
View Code

这里,如果cls.indirection_api为空,则直接调用fn方法,fn这里一般指数据库的操作方法。

如果cls.indirection_api不为空,则调用cls.indirection_api中的object_class_action_versions方法。

我们搜索一下nova中关于indirection_api的初始化相关的代码:

技术图片
[root@controller nova]# grep -rn indirection_api                                
cmd/api_metadata.py:45:    objects_base.NovaObject.indirection_api = conductor_rpcapi.ConductorAPI()    
cmd/dhcpbridge.py:120:    objects_base.NovaObject.indirection_api = conductor_rpcapi.ConductorAPI()    
cmd/compute.py:54:    objects_base.NovaObject.indirection_api = conductor_rpcapi.ConductorAPI()        
cmd/network.py:54:    objects_base.NovaObject.indirection_api = conductor_rpcapi.ConductorAPI()        
View Code

而这里的conductor_rpcapi指的就是nova.conductor中的rpcapi。

我们知道nova-conductor的rpcapi用的是rabbitmq做连接的,分析如下:

技术图片
1 @profiler.trace_cls("rpc")
2 class ConductorAPI(object):
3         def __init__(self):
4             super(ConductorAPI, self).__init__()
5             ......
6             self.client = rpc.get_client(target,
7                                      version_cap=version_cap,
8                                      serializer=serializer)
View Code

 在get_client 中,初始化了一个RPCClient对象:

技术图片
 1 def get_client(target, version_cap=None, serializer=None):
 2     assert TRANSPORT is not None
 3 
 4     if profiler:
 5         serializer = ProfilerRequestContextSerializer(serializer)
 6     else:
 7         serializer = RequestContextSerializer(serializer)
 8 
 9     return messaging.RPCClient(TRANSPORT,
10                                target,
11                                version_cap=version_cap,
12                                serializer=serializer)
View Code

这里用assert声明了TRANSPORT这个变量不为空,那么这个变量是在哪里初始化的呢?

在nova.conductor.rpc.py中的init方法里面,对其进行了初始化:

技术图片
1 def init(conf):
2     global TRANSPORT, NOTIFICATION_TRANSPORT, LEGACY_NOTIFIER, NOTIFIER
3     exmods = get_allowed_exmods()
4     TRANSPORT = create_transport(get_transport_url())
View Code

而这个方法是在nova.config.py中初始化的:

技术图片
1 def parse_args(argv, default_config_files=None, configure_db=True,
2                init_rpc=True):
3     ......
4     if init_rpc:
5         rpc.init(CONF)
View Code

parse_args这个方法又在nova的每个服务初始化的时候就定义了:

技术图片

 

 

从上面我们可以得知,一般是nova-compute和nova-network服务,其对应的cls.indirection_api不为空,所以读写数据库都是通过nova-conductor,也就是rabbitmq来完成的。

从remotable_classmethod的定义来看,对数据库的操作都是调用 cls.indirection_api中的object_class_action_versions方法来实现的。这个方法在nova-conductor的rpcapi里面实现的。

技术图片
1     def object_class_action_versions(self, context, objname, objmethod,            
2                                      object_versions, args, kwargs):                
3         cctxt = self.client.prepare()                            
4         return cctxt.call(context, object_class_action_versions,                
5                           objname=objname, objmethod=objmethod,                
6                           object_versions=object_versions,                    
7                           args=args, kwargs=kwargs)                        
View Code

根据RPC的调用原理,这里调用的是nova.conductor.manager 中的 object_class_action_versions 方法。

这个方法定义如:

技术图片
1     def object_class_action_versions(self, context, objname, objmethod,
2                                      object_versions, args, kwargs):
3         objclass = nova_object.NovaObject.obj_class_from_name(
4             objname, object_versions[objname])
5         args = tuple([context] + list(args))
6         result = self._object_dispatch(objclass, objmethod, args, kwargs)
View Code

因此,可以得知,最终的结果是通过self._object_dispatch来实现的。

self._object_dispatch的定义如:

技术图片
1     def _object_dispatch(self, target, method, args, kwargs):
2         try:
3             return getattr(target, method)(*args, **kwargs)
4         except Exception:
5             raise messaging.ExpectedException()
View Code

这里的target就是对应的object对象,method就是对应的object方法。

又由于nova-conductor对应的self.indirection_api为空,因此,这里直接调用了数据库的操作方法来返回结果。

 

测试在nova boot实例的过程中,nova-compute在操作数据库过程所消耗的时间:

测试  instance.save()

使用remotable:

技术图片
1 cmd/compute.py:
2     cmd_common.block_db_access(nova-compute)
3     objects_base.NovaObject.indirection_api = conductor_rpcapi.ConductorAPI()
View Code

数据库访问次数->消耗时间(s):

技术图片
1-> 0.119282
10->0.704272
100-> 8.679072
1000-> 59.863044
10000-> 312.080721
100000->3140.704155
View Code

不使用remotable:

技术图片
1     cmd/compute.py:
2     #cmd_common.block_db_access(‘nova-compute‘)
3     #objects_base.NovaObject.indirection_api = conductor_rpcapi.ConductorAPI()
View Code

数据库访问次数->消耗时间(s):

技术图片
1->0.076283
10->0.153130
100->1.267794
1000->10.103049
10000->105.528138
100000->1027.132933
View Code

 

因此,通过conductor操作数据库的时间比直接操作数据库的时间要更长,在数据库调用次数越大的情况下越明显。

 

以上是关于Nova-compute数据库读写测试的主要内容,如果未能解决你的问题,请参考以下文章

OpenStack入门篇之nova服务(控制节点)的部署与测试

部署OpenStack问题汇总--openstack中nova-compute状态status显示为'XXX'的问题

Openstack liberty中nova-compute服务的启动过程

nova-compute状态一直在up和down切换

nova-compute 部署 instance 详解 - 每天5分钟玩转 OpenStack(28)

我写的由 GCD 代码支持的读写器锁导致并行测试中的死锁