a5e2a633e3
* Refactored RoutingTable, in order to work on string target representation. * RoutingTableAdaptor - provides usable adaptation of RoutingTable to use with Target object. * RoutingTableUpdater - implements asynchronous table updates from the matchmaker. * Implemented dynamic connections for DealerPublisherDirect * Use dynamic connections for direct messages Change-Id: I20a76c3b2e8f9e71ffcc5ac658fbc659ad4c8153
184 lines
6.3 KiB
Python
184 lines
6.3 KiB
Python
# Copyright 2016 Mirantis, Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import abc
|
|
import logging
|
|
import threading
|
|
|
|
import futurist
|
|
import six
|
|
|
|
from oslo_messaging._drivers.zmq_driver.client import zmq_response
|
|
from oslo_messaging._drivers.zmq_driver import zmq_async
|
|
from oslo_messaging._drivers.zmq_driver import zmq_names
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
zmq = zmq_async.import_zmq()
|
|
|
|
|
|
@six.add_metaclass(abc.ABCMeta)
|
|
class ReceiverBase(object):
|
|
"""Base response receiving interface."""
|
|
|
|
def __init__(self, conf):
|
|
self.conf = conf
|
|
self._lock = threading.Lock()
|
|
self._requests = {}
|
|
self._poller = zmq_async.get_poller()
|
|
self._executor = zmq_async.get_executor(method=self._run_loop)
|
|
self._executor.execute()
|
|
|
|
@abc.abstractproperty
|
|
def message_types(self):
|
|
"""A set of supported incoming response types."""
|
|
|
|
def register_socket(self, socket):
|
|
"""Register a socket for receiving data."""
|
|
self._poller.register(socket, recv_method=self.recv_response)
|
|
|
|
def unregister_socket(self, socket):
|
|
"""Unregister a socket from receiving data."""
|
|
self._poller.unregister(socket)
|
|
|
|
@abc.abstractmethod
|
|
def recv_response(self, socket):
|
|
"""Receive a response and return a tuple of the form
|
|
(reply_id, message_type, message_id, response).
|
|
"""
|
|
|
|
def track_request(self, request):
|
|
"""Track a request via already registered sockets and return
|
|
a dict of futures for monitoring all types of responses.
|
|
"""
|
|
futures = {}
|
|
message_id = request.message_id
|
|
for message_type in self.message_types:
|
|
future = self._get_future(message_id, message_type)
|
|
if future is None:
|
|
future = futurist.Future()
|
|
self._set_future(message_id, message_type, future)
|
|
futures[message_type] = future
|
|
return futures
|
|
|
|
def untrack_request(self, request):
|
|
"""Untrack a request and stop monitoring any responses."""
|
|
for message_type in self.message_types:
|
|
self._pop_future(request.message_id, message_type)
|
|
|
|
def stop(self):
|
|
self._poller.close()
|
|
self._executor.stop()
|
|
|
|
def _get_future(self, message_id, message_type):
|
|
with self._lock:
|
|
return self._requests.get((message_id, message_type))
|
|
|
|
def _set_future(self, message_id, message_type, future):
|
|
with self._lock:
|
|
self._requests[(message_id, message_type)] = future
|
|
|
|
def _pop_future(self, message_id, message_type):
|
|
with self._lock:
|
|
return self._requests.pop((message_id, message_type), None)
|
|
|
|
def _run_loop(self):
|
|
data, socket = self._poller.poll(
|
|
timeout=self.conf.oslo_messaging_zmq.rpc_poll_timeout)
|
|
if data is None:
|
|
return
|
|
reply_id, message_type, message_id, response = data
|
|
assert message_type in self.message_types, \
|
|
"%s is not supported!" % zmq_names.message_type_str(message_type)
|
|
future = self._get_future(message_id, message_type)
|
|
if future is not None:
|
|
LOG.debug("Received %(msg_type)s for %(msg_id)s",
|
|
{"msg_type": zmq_names.message_type_str(message_type),
|
|
"msg_id": message_id})
|
|
future.set_result((reply_id, response))
|
|
|
|
|
|
class ReplyReceiver(ReceiverBase):
|
|
|
|
message_types = {zmq_names.REPLY_TYPE}
|
|
|
|
|
|
class ReplyReceiverProxy(ReplyReceiver):
|
|
|
|
def recv_response(self, socket):
|
|
empty = socket.recv()
|
|
assert empty == b'', "Empty expected!"
|
|
reply_id = socket.recv()
|
|
assert reply_id is not None, "Reply ID expected!"
|
|
message_type = int(socket.recv())
|
|
assert message_type == zmq_names.REPLY_TYPE, "Reply expected!"
|
|
message_id = socket.recv_string()
|
|
reply_body, failure = socket.recv_loaded()
|
|
reply = zmq_response.Reply(
|
|
message_id=message_id, reply_id=reply_id,
|
|
reply_body=reply_body, failure=failure
|
|
)
|
|
return reply_id, message_type, message_id, reply
|
|
|
|
|
|
class ReplyReceiverDirect(ReplyReceiver):
|
|
|
|
def recv_response(self, socket):
|
|
empty = socket.recv()
|
|
assert empty == b'', "Empty expected!"
|
|
raw_reply = socket.recv_loaded()
|
|
assert isinstance(raw_reply, dict), "Dict expected!"
|
|
reply = zmq_response.Reply(**raw_reply)
|
|
return reply.reply_id, reply.msg_type, reply.message_id, reply
|
|
|
|
|
|
class AckAndReplyReceiver(ReceiverBase):
|
|
|
|
message_types = {zmq_names.ACK_TYPE, zmq_names.REPLY_TYPE}
|
|
|
|
|
|
class AckAndReplyReceiverProxy(AckAndReplyReceiver):
|
|
|
|
def recv_response(self, socket):
|
|
empty = socket.recv()
|
|
assert empty == b'', "Empty expected!"
|
|
reply_id = socket.recv()
|
|
assert reply_id is not None, "Reply ID expected!"
|
|
message_type = int(socket.recv())
|
|
assert message_type in (zmq_names.ACK_TYPE, zmq_names.REPLY_TYPE), \
|
|
"Ack or reply expected!"
|
|
message_id = socket.recv_string()
|
|
if message_type == zmq_names.REPLY_TYPE:
|
|
reply_body, failure = socket.recv_loaded()
|
|
reply = zmq_response.Reply(
|
|
message_id=message_id, reply_id=reply_id,
|
|
reply_body=reply_body, failure=failure
|
|
)
|
|
response = reply
|
|
else:
|
|
response = None
|
|
return reply_id, message_type, message_id, response
|
|
|
|
|
|
class AckAndReplyReceiverDirect(AckAndReplyReceiver):
|
|
|
|
def recv_response(self, socket):
|
|
# acks are not supported yet
|
|
empty = socket.recv()
|
|
assert empty == b'', "Empty expected!"
|
|
raw_reply = socket.recv_loaded()
|
|
assert isinstance(raw_reply, dict), "Dict expected!"
|
|
reply = zmq_response.Reply(**raw_reply)
|
|
return reply.reply_id, reply.msg_type, reply.message_id, reply
|