oslo Transport Driver实现指南

原文地址 :https://docs.openstack.org/developer/oslo.messaging/driver-dev-guide.html#module-oslo_messaging._drivers.base

目录:

  1. 简介
  2. 驱动接口
  3. IncomingMessage
  4. RpcIncomingMessage
  5. Listener
  6. PollStyleListener
  7. BaseDriver

一、简介

本文用来指导开发者在 Oslo.Messaging中新建新的驱动,同时也可以看成是对驱动行为的一个指导。
另外,这里所描述的API是在oslo.messging库中的内部API,因此是private的。

二、驱动接口

驱动接口由一系列的抽象基本组成。需要创建一个新的驱动的开发人员需要实现这些基类的具体子类。这些子类具体完成了特定的所支持的功能。

这些基类定义在__drivers子目录下的base.py文件中。
大概的样子如下:(代码来源:https://code.csdn.net/openstack/oslo.messaging/tree/feature/pika/oslo_messaging/_drivers

import abc

from oslo_config import cfg
from oslo_utils import timeutils
import six
from six.moves import range as compat_range


from oslo_messaging import exceptions

base_opts = [
    cfg.IntOpt('rpc_conn_pool_size',
               default=30,
               deprecated_group='DEFAULT',
               help='Size of RPC connection pool.'),
]


def batch_poll_helper(func):
    """Decorator to poll messages in batch

    This decorator helps driver that polls message one by one,
    to returns a list of message.
    """
    def wrapper(in_self, timeout=None, prefetch_size=1):
        incomings = []
        watch = timeutils.StopWatch(duration=timeout)
        with watch:
            for __ in compat_range(prefetch_size):
                msg = func(in_self, timeout=watch.leftover(return_none=True))
                if msg is not None:
                    incomings.append(msg)
                else:
                    # timeout reached or listener stopped
                    break
        return incomings
    return wrapper


class TransportDriverError(exceptions.MessagingException):
    """Base class for transport driver specific exceptions."""


@six.add_metaclass(abc.ABCMeta)
class IncomingMessage(object):

    def __init__(self, listener, ctxt, message):
        self.conf = listener.conf
        self.listener = listener
        self.ctxt = ctxt
        self.message = message

    @abc.abstractmethod
    def reply(self, reply=None, failure=None, log_failure=True):
        "Send a reply or failure back to the client."

    def acknowledge(self):
        "Acknowledge the message."

    @abc.abstractmethod
    def requeue(self):
        "Requeue the message."


@six.add_metaclass(abc.ABCMeta)
class Listener(object):

    def __init__(self, driver):
        self.conf = driver.conf
        self.driver = driver

    @abc.abstractmethod
    def poll(self, timeout=None, prefetch_size=1):
        """Blocking until 'prefetch_size' message is pending and return
        [IncomingMessage].
        Return None after timeout seconds if timeout is set and no message is
        ending or if the listener have been stopped.
        """

    def stop(self):
        """Stop listener.
        Stop the listener message polling
        """
        pass

    def cleanup(self):
        """Cleanup listener.
        Close connection (socket) used by listener if any.
        As this is listener specific method, overwrite it in to derived class
        if cleanup of listener required.
        """
        pass


@six.add_metaclass(abc.ABCMeta)
class BaseDriver(object):

    def __init__(self, conf, url,
                 default_exchange=None, allowed_remote_exmods=None):
        self.conf = conf
        self._url = url
        self._default_exchange = default_exchange
        self._allowed_remote_exmods = allowed_remote_exmods or []

    def require_features(self, requeue=False):
        if requeue:
            raise NotImplementedError('Message requeueing not supported by '
                                      'this transport driver')

    @abc.abstractmethod
    def send(self, target, ctxt, message,
             wait_for_reply=None, timeout=None, envelope=False):
        """Send a message to the given target."""

    @abc.abstractmethod
    def send_notification(self, target, ctxt, message, version):
        """Send a notification message to the given target."""

    @abc.abstractmethod
    def listen(self, target):
        """Construct a Listener for the given target."""

    @abc.abstractmethod
    def listen_for_notifications(self, targets_and_priorities, pool):
        """Construct a notification Listener for the given list of
        tuple of (target, priority).
        """

    @abc.abstractmethod
    def cleanup(self):
        """Release all resources."""

三、IncomingMessage

1.class oslo_messaging._drivers.base.IncomingMessage(ctxt, message)

代表从messaging 后端接收到的一条具体的消息。该类的实例向上传递给server的messaging 处理逻辑。后端driver必须提供该类的一个具体子类,实现该类的公共方法逻辑。

Parameters:
ctxt (dict) – Context metadata provided by sending application.
message (dict) – The message as provided by the sending application.

主要方法:
(1)acknowledge():由服务器调用,用来通知该消息被收到了。当该方法被调用后,driver必须通知driver后端这个消息确认。该方法调用可能会阻塞,知道driver已经传递了该动作确认。但是可能在后端确认状态响应前返回。

如果确认动作失败,则该方法应该产生日志,记录失败的原因。
异常:不抛出异常。

(2)requeue() :由server调用,用来将某一个消息会送到后端,重新入消息队列(因此,该消息可被其他server 消费)。该方法调用会阻塞,直到driver已经处理了该requeue请求,可在后端真正requeue前返回。

如果确认动作失败,则该方法应该产生日志,记录失败的原因。

driver后端可不实现该方法(即不支持requeue), BaseDriver.require_features()方法应该表明是否支持该功能。

异常:不抛出异常。

四、RpcIncomingMessage

1.class oslo_messaging._drivers.base.RpcIncomingMessage(ctxt, message)

该类代表从后端接收到的一个RPC请求消息。该类用于RPC call(需要返回的情况)
主要方法:
(1)reply(reply=None, failure=None) :

Parameters:
reply (dict) – reply message body
failure (Exception) – an exception thrown by the RPC call

server调用,用于返回一个RPC replly或是异常给client. 如果由于出现错误返回异常,那么driver必须将该异常转化成message的形式(该形式在client端能正常转化成一个异常)。driver必须能够确定reply的目的地址。例如,driver使用收到的消息的replly-to来确定地址。一般来说,driver应该能够对reply设置正确的标识符来帮助路由到正确的RPC 客户端中。

driver应该为reply提供at-most-once保证。该函数调用应该阻塞,至少阻塞到replly 消息被移交到后端。但是无需确认reply是否一定被发送出去了。

如果reply动作失败,则该方法应该产生日志,记录失败的原因。
异常:不抛出异常。

五、Listener

1.class oslo_messaging._drivers.base.Listener(batch_size, batch_timeout, prefetch_size=-1)

将收到的消息从driver发送到server中。同时driver使用回调来完成该动作。

Parameters:
batch_size (int) – desired number of messages passed to single on_incoming_callback notification
batch_timeout (float) – defines how long should we wait in seconds for batch_size messages if we already have some messages waiting for processing
prefetch_size (int) – defines how many messages we want to prefetch from the messaging backend in a single request. May not be honored by all backend implementations.

主要方法:
(1)cleanup() :清理listener的资源,该方法阻塞,直到清理完成。
(2)start(on_incoming_callback):开始接受消息。该方法让driver开始从后端接受消息。当消息到达后,driver调用“on_incoming_callback ”,将收到的消息作为IncomingMessages列表传递出去。
(3)stop() :停止接受消息。

六、PollStyleListener

1.class oslo_messaging._drivers.base.PollStyleListener(prefetch_size=-1)

将收到的消息发送到server中处理。polling 模式可用来检索消息。PollStyleListener 使用单独的线程来执行polling 循环。根据普通的Listener创建PollStyleListener,可使用PollStyleListenerAdapter 。

Parameters: prefetch_size (int) – The number of messages that should be pulled from the backend per receive transaction. May not be honored by all backend implementations.

主要方法:
(1)cleanup() :同Listener 。
(2)poll(timeout=None, batch_size=1, batch_timeout=None) :被server调用来获取接收到的消息。该返回会阻塞,直到batch__size条消息可用、或是超时、或是被stop方法中断。如果batch__size >1,则要么收到这么多条消息,或是至少收到一条消息并且超时。

关于batch_timeout (float) 参数:Time to wait in seconds for a full batch to arrive. A timer is started when the first message in a batch is received. If a full batch’s worth of messages is not received when the timer expires then poll() returns all messages received thus far.

异常:不抛出异常。

(3)stop() :该方法可是阻塞的poll方法返回。该方法必须是线程安全的。

七、BaseDriver

1.class oslo_messaging._drivers.base.BaseDriver(conf, url, default_exchange=None, allowed_remote_exmods=None)

定义后端driver的接口。某个后端driver的实现必须提供该类的子类,实现了公共方法的逻辑。

Parameters:
conf (ConfigOpts) – The configuration settings provided by the user.
url (TransportURL) – The network address of the messaging backend(s).
default_exchange (str) – The exchange to use if no exchange is specified in a Target.
allowed_remote_exmods (list) – whitelist of those exception modules which are permitted to be re-raised if an exception is returned in response to an RPC call.

主要方法:
(1)cleanup() :清理driver相关的所有资源。

(2)listen(target, batch_size, batch_timeout):创建给定target的listener。该listener可以是Listener或是PollStyleListener(具体类型根据driver的偏好决定)。该方法被RPC server调用。

driver必须创建订阅信息,记录target提供的地址信息,且该信息要和该方法返回的Listener或是PollStyleListner关联起来。

driver必须支持从如下地址接收消息:1,所有发送到target 指定的(exchange,topic)中的消息,包括使用fanout 模式的消息;2,server的target属性设置了,则driver必须能够获取到该target指定的(exchange,topic,server)的消息。

例如,对于exchange=‘my-exchange’, topic=‘my-topic’, server =‘my-server’的target,driver需要创建如下订阅信息:1,所有送到my-exchange 的 my-topic类型的消息(包括fanout消息);2,所有送到my-exchange 的 my-topic、my-server上的消息。

driver必须将这些消息传送到对应的listner中。对于PollStyleListener 类型的监听器,driver应该能够使得PollStyleListener.poll()从阻塞状态中返回消息列表。对于Listener类型的监听器,应该调用相应的回调函数,处理消息。

该方法的主要开销在于:建立订阅消息,构造监听器。如果该方法失败,则引用恢复订阅信息。另外,如果方法成功,则订阅信息一直有效,直到listener 调用了stop方法。

(3)listen_for_notifications(targets_and_priorities, pool, batch_size, batch_timeout) : 根据(target,prority)信息,构造notification listener。driver必须为每条(target,priority)信息构建订阅信息。当构建订阅信息时,只需要考虑target和priority信息,不需要考虑server和fanout信息。

如果声明了pool参数,则driver应该创建一条订阅信息,该订阅记录由具有相同的pool identifier的listener共享。每个pool都会获取到一份消息的拷贝。例如,存在两个pool ,foo和bar,则foo中的一个订阅用户和bar中的一个订阅用户会收到消息的一个拷贝。driver如果实现了pool功能的话,则应用在pool中分配消息时,做到负载均衡。如果driver没有实现pool功能,则该方法应该在传入了pool identifier参数时,抛出NotImplementedError 异常。

异常: MessagingException, NotImplementedError

(4)require_features(requeue=False):当不支持的特征标识符设置为ture时,则应该抛出NotImplementedError异常。

(5)send(target, ctxt, message, wait_for_reply=None, timeout=None, envelope=False, retry=None) :发送消息到特定的target。该方法在RPC client中调用,用来给服务器发送RPC请求。 driver应该使用target中topic,exchange,server(如果有的话)来构造后端特定的(backend-native)消息地址。该地址应该和BaseDriver.listen() 建立的订阅信息匹配。

如果设置了target的fanout属性值,则该消息会被发送到所有的具有相同exchange,topic的订阅者。没有设置的话,会从这些server中挑出一个server,处理该消息,这时,driver从这些server中填出某一个server时,应该注意负载均衡。

该方法的调用应该造成阻塞,指定发生如下情况:1,send操作完成;2,超时;3,retry次数达到。

wait__for__reply 参数指定了caller是否期待RPC请求的响应数据。如果wait__for__reply =true,则该方法会一直阻塞,直到收到RPC响应,然后方法返回。driver必须实现一种机制,支持根据响应路由到响应的RPC send请求。具体怎么实现,因具体driver后端不同而不同,但是,通常来说,一般是通过设置message的reply_to 消息头,并在内部创建reply的订阅信息。同时,driver也应用能够处理RpcIncomingMessage.reply()的绑定。

如果wait_for_reply=false,则该方法一直阻塞,直到该消息发送到driver后端后。没有必要确认消息是否真正发送出去了。

当消息发送失败时,driver可能会尝试再次发送。retry参数指定了最大重试次数,如果达到最大值后,还没有发送成功,则抛出MessageDeliveryFailure异常。retry=-1或是为空时,表示重试次数无限制,retry=0表示不重试。注意:driver必须保证在重试时,消息不会重复。

(6)send_notification(target, ctxt, message, version, retry) :发送Notification到特定的target。该方法在Notifier中调用,用来给响应的listener发送Notification。

notification使用存储-转发模式。driver必须允许该种情况:当发送notification时,相应的接受者还没有启动。这就需要后端具有存储功能,将消息先存储起来,等到消费者出现再发送出去。因此,该方法会阻塞,直到后端收到消息后。该方法不保证消息一定会被消费者处理。

driver应该使用target中topic,exchange,server(如果有的话)来构造后端特定的(backend-native)消息地址。该地址应该和 BaseDriver.listen_for_notifications()建立的订阅信息匹配。如果多个订阅者具有相同的topic,exchange,则只有一个订阅者能收到该消息。这时,driver从这些server中填出某一个server时,应该注意负载均衡。

另外,关于pool,参见BaseDriver.listen_for_notifications() 中的说明https://docs.openstack.org/developer/oslo.messaging/driver-dev-guide.html#oslo_messaging._drivers.base.BaseDriver.listen_for_notifications

version参数指示是否将消息封装起来。version<2.0,则不用封装。

猜你喜欢

转载自blog.csdn.net/youyou1543724847/article/details/71173439
今日推荐