OpenStack-RPC-server的构建(一)

        我们主要分析Transport为rabbit方式的RPC-server构建,当我们执行如下命令时:

[[email protected](keystone_admin)]# rabbitmqctl list_consumers

Listingconsumers ...

alarm_notifier  <[email protected]>    1      true    0       []

alarm_notifier.jun      <[email protected]>    2      true    0       []

alarm_notifier_fanout_bcc5514c42a24c78928bf08dc4bc844e  <[email protected]>    3      true    0       []

cert    <[email protected]>    1      true    0       []

cert.jun        <[email protected]>    2      true    0       []

cert_fanout_8c23d4a227af4488a5ee22249422a5e8    <[email protected]>    3      true    0       []

……….

reply_32583cb2867d476db776e2d4d2093606  <[email protected]>   1      true    0       []

reply_9b28643406614d8280db511872854dbb  <[email protected]>   1      true    0       []

reply_eb5af11fd3aa42dda88eea22446d3fc1  <[email protected]>   1       true   0       []

scheduler       <[email protected]>    1      true    0       []

scheduler.jun   <[email protected]>    2      true    0       []

scheduler_fanout_eb88ae2d696c40538615fb8e7af84f2f       <[email protected]>    3      true    0       []

        我们可以利用linux的systemd技术,在linux系统启动时,加载OpenStack组件服务,从而形成RPC的consumer。所以我们可以从systemd的相关文件找到OpenStack组件服务的启动入口,在这里我们主要关注OpenStack组件服务中RPC-server的构建过程,且以Nova-scheduler组件为例进行分析。

[root@jun~(keystone_admin)]# cat/etc/systemd/system/multi-user.target.wants/openstack-nova-scheduler.service

[Unit]

Description=OpenStackNova Scheduler Server

After=syslog.targetnetwork.target

 

[Service]

Type=notify

NotifyAccess=all

TimeoutStartSec=0

Restart=always

User=nova

ExecStart=/usr/bin/nova-scheduler

 

[Install]

WantedBy=multi-user.target

        Systemd通过执行/usr/bin/nova-scheduler命令来启动Nova-scheduler组件,那么/usr/bin/nova-scheduler做了什么呢?我们继续往下分析。

[root@jun~(keystone_admin)]# cat /usr/bin/nova-scheduler

#!/usr/bin/python

#PBR Generated from u'console_scripts'

 

importsys

fromnova.cmd.scheduler import main

 

if__name__ == "__main__":

    sys.exit(main())

        原来这个文件执行/usr/lib/python2.7/site-packages/nova/cmd/scheduler.py中的main函数。如下所示:

"""Starterscript for Nova Scheduler."""

…….

CONF= cfg.CONF

CONF.import_opt('scheduler_topic','nova.scheduler.rpcapi')

 

defmain():

    config.parse_args(sys.argv)

    logging.setup(CONF, "nova")

    utils.monkey_patch()

    objects.register_all()

 

    gmr.TextGuruMeditation.setup_autorun(version)

 

    server =service.Service.create(binary='nova-scheduler',

                                   topic=CONF.scheduler_topic)

    service.serve(server)

    service.wait()

        其中,红色标记中的内容与RPC-server的创建有关,所以我们重点关注红色部分的函数调用内容。

1. config.parse_args(sys.argv)

defparse_args(argv, default_config_files=None):

   log.set_defaults(_DEFAULT_LOGGING_CONTEXT_FORMAT, _DEFAULT_LOG_LEVELS)

    log.register_options(CONF)

    options.set_defaults(CONF,connection=_DEFAULT_SQL_CONNECTION,

                        sqlite_db='nova.sqlite')

    rpc.set_defaults(control_exchange='nova')

    debugger.register_cli_opts()

    CONF(argv[1:],

         project='nova',

         version=version.version_string(),

         default_config_files=default_config_files)

    rpc.init(CONF)

        由于control_exchange的默认值为:’openstack’,所以通过set_defaults函数修改为:’nova’。因此随后的get_transport函数中获得的control_exchange的值为:’nova’。

rpc.init(CONF)代码如下:

definit(conf):

    global TRANSPORT, NOTIFIER

    exmods = get_allowed_exmods()

    TRANSPORT = messaging.get_transport(conf,

                                       allowed_remote_exmods=exmods,

                                       aliases=TRANSPORT_ALIASES)

    serializer =RequestContextSerializer(JsonPayloadSerializer())

    NOTIFIER = messaging.Notifier(TRANSPORT,serializer=serializer)

A. get_transport方法通过从用户配置文件中搜集到与transport相关的配置文件来构造且返回一个Transport对象。如下所示:

defget_transport(conf, url=None, allowed_remote_exmods=None, aliases=None):

    """A factory method forTransport objects.

 

    This method will construct a Transportobject from transport configuration

    gleaned from the user's configuration and,optionally, a transport URL.

 

    If a transport URL is supplied as aparameter, any transport configuration

    contained in it takes precedence. If notransport URL is supplied, but

    there is a transport URL supplied in theuser's configuration then that

    URL will take the place of the URLparameter. In both cases, any

    configuration not supplied in the transportURL may be taken from

    individual configuration parameters in theuser's configuration.

 

    An example transport URL might be::

 

        rabbit://me:passwd@host:5672/virtual_host

 

    and can either be passed as a string or aTransportURL object.

 

    :param conf: the user configuration

    :type conf: cfg.ConfigOpts

    :param url: a transport URL

    :type url: str or TransportURL

    :param allowed_remote_exmods: a list ofmodules which a client using this

                                  transportwill deserialize remote exceptions

                                  from

    :type allowed_remote_exmods: list

    :param aliases: A map of transport alias totransport name

    :type aliases: dict

    """

    allowed_remote_exmods =allowed_remote_exmods or []

    conf.register_opts(_transport_opts)

 

    if not isinstance(url, TransportURL):

        url = url or conf.transport_url

        parsed = TransportURL.parse(conf, url,aliases)

        if not parsed.transport:

            raise InvalidTransportURL(url, 'Noscheme specified in "%s"' % url)

        url = parsed

 

    kwargs = dict(default_exchange=conf.control_exchange,

                  allowed_remote_exmods=allowed_remote_exmods)

 

    try:

        mgr =driver.DriverManager('oslo.messaging.drivers',

                                  url.transport.split('+')[0],

                                  invoke_on_load=True,

                                  invoke_args=[conf, url],

                                  invoke_kwds=kwargs)

    except RuntimeError as ex:

        raise DriverLoadFailure(url.transport,ex)

 

    return Transport(mgr.driver)

        这里get_transport方法将_transport_opts对象register到conf对象中,而_transport_opts对象中的control_exchange属性在上面的set_defaults方法中设置为:’nova’,不是默认值:’openstack’。

        driver.DriverManager是stevedore组件中的方法,stevedore组件可以在运行时发现和载入所谓的”插件”,它在Setuptools的entrypoints基础上,构造一层抽象层,使开发者可以更加容易地在运行时发现和载入插件。

        在这里我们利用oslo_messaging中的entry points来发现和载入插件。在我的OpenStack环境中(kilo版本),entry points文件为:/usr/lib/python2.7/site-packages/oslo.messaging-1.8.2-py2.7.egg-info/entry_points.txt。

[oslo.messaging.zmq.matchmaker]

local= oslo_messaging._drivers.matchmaker:MatchMakerLocalhost

ring= oslo_messaging._drivers.matchmaker_ring:MatchMakerRing

redis= oslo_messaging._drivers.matchmaker_redis:MatchMakerRedis

 

[oslo.config.opts]

oslo.messaging= oslo_messaging.opts:list_opts

 

[oslo.messaging.notify.drivers]

log= oslo_messaging.notify._impl_log:LogDriver

messagingv2= oslo_messaging.notify._impl_messaging:MessagingV2Driver

noop= oslo_messaging.notify._impl_noop:NoOpDriver

routing= oslo_messaging.notify._impl_routing:RoutingDriver

test= oslo_messaging.notify._impl_test:TestDriver

messaging= oslo_messaging.notify._impl_messaging:MessagingDriver

 

[console_scripts]

oslo-messaging-zmq-receiver= oslo_messaging._cmd.zmq_receiver:main

 

[oslo.messaging.drivers]

qpid= oslo_messaging._drivers.impl_qpid:QpidDriver

amqp= oslo_messaging._drivers.protocols.amqp.driver:ProtonDriver

kombu= oslo_messaging._drivers.impl_rabbit:RabbitDriver

rabbit =oslo_messaging._drivers.impl_rabbit:RabbitDriver

fake= oslo_messaging._drivers.impl_fake:FakeDriver

zmq= oslo_messaging._drivers.impl_zmq:ZmqDriver

 

[oslo.messaging.executors]

aioeventlet= oslo_messaging._executors.impl_aioeventlet:AsyncioEventletExecutor

threading= oslo_messaging._executors.impl_thread:ThreadExecutor

blocking= oslo_messaging._executors.impl_blocking:BlockingExecutor

eventlet= oslo_messaging._executors.impl_eventlet:EventletExecutor

        其中[*]中的内容为entry points的namespace名称,它下面的内容为插件相关内容,如oslo.messaging.drivers即为namespace名称,其中它下面包括6个插件。其中每个插件都符合”名字 = 模块:可导入对象”。上述driver.DriverManager方法的第一个参数:” oslo.messaging.drivers”即为namespace名称。第二个参数为插件名,其中该场景下使用的是rabbit插件,即:rabbit =oslo_messaging._drivers.impl_rabbit:RabbitDriver。然后利用mgr.driver(即oslo_messaging._drivers.impl_rabbit.RabbitDriver)去构造Transport对象。

B.NOTIFIER = messaging.Notifier(TRANSPORT, serializer=serializer)创建一个Notifier对象通过TRANSPORT发送通知消息。通知消息遵循如下的格式:

{'message_id':six.text_type(uuid.uuid4()),  #消息id号

 'publisher_id': 'compute.host1',                       #发送者id

 'timestamp': timeutils.utcnow(),                      #时间戳

 'priority': 'WARN',                                              #通知优先级

 'event_type': 'compute.create_instance',        #通知类型

 'payload': {'instance_id': 12, ... }                        #通知内容

}

        可以在不同的优先级别上发送通知,这些优先级包括sample,critical,error,warn,info,debug和audit等。

        总结:本篇文章从linux的systemd入手,找到Nova-scheduler的启动流程,然后主要分析/usr/lib/python2.7/site-packages/nova/cmd/scheduler.py中的main函数的config.parse_args(sys.argv)初始化过程。即创建一个Notify对象,该对象通过某种transport(这里是rabbit)发送通知消息。其中

server =service.Service.create(binary='nova-scheduler',

                                   topic=CONF.scheduler_topic)

    service.serve(server)

    service.wait()

        对于这三行代码,我们将在后面进行详细分析。

你可能感兴趣的:(openstack,oslo_messaging)