119 lines
4.3 KiB
Python
119 lines
4.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.
|
|
|
|
from concurrent import futures
|
|
import logging
|
|
|
|
from oslo_messaging._drivers.zmq_driver.client import zmq_publisher_manager
|
|
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
|
|
from oslo_messaging._i18n import _LE, _LW
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
zmq = zmq_async.import_zmq()
|
|
|
|
|
|
class AckManager(zmq_publisher_manager.PublisherManagerBase):
|
|
|
|
def __init__(self, publisher):
|
|
super(AckManager, self).__init__(publisher, with_pool=True)
|
|
|
|
@staticmethod
|
|
def _check_ack(ack, request):
|
|
if ack is not None:
|
|
assert isinstance(ack, zmq_response.Ack), "Ack expected!"
|
|
assert ack.reply_id == request.routing_key, \
|
|
"Ack from recipient expected!"
|
|
|
|
def _wait_for_ack(self, request, ack_future=None):
|
|
if ack_future is None:
|
|
ack_future = self._schedule_request_for_ack(request)
|
|
|
|
retries = \
|
|
request.retry or self.conf.oslo_messaging_zmq.rpc_retry_attempts
|
|
if retries is None:
|
|
retries = -1
|
|
timeout = self.conf.oslo_messaging_zmq.rpc_ack_timeout_base
|
|
|
|
done = ack_future is None
|
|
while not done:
|
|
try:
|
|
ack = ack_future.result(timeout=timeout)
|
|
done = True
|
|
self._check_ack(ack, request)
|
|
except AssertionError:
|
|
LOG.error(_LE("Message format error in ack for %s"),
|
|
request.message_id)
|
|
except futures.TimeoutError:
|
|
LOG.warning(_LW("No ack received within %(tout)s seconds "
|
|
"for %(msg_id)s"),
|
|
{"tout": timeout,
|
|
"msg_id": request.message_id})
|
|
if retries != 0:
|
|
if retries > 0:
|
|
retries -= 1
|
|
self.sender.send(ack_future.socket, request)
|
|
timeout *= \
|
|
self.conf.oslo_messaging_zmq.rpc_ack_timeout_multiplier
|
|
else:
|
|
LOG.warning(_LW("Exhausted number of retries for %s"),
|
|
request.message_id)
|
|
done = True
|
|
|
|
if request.msg_type != zmq_names.CALL_TYPE:
|
|
self.receiver.untrack_request(request)
|
|
|
|
@zmq_publisher_manager.target_not_found_warn
|
|
def _send_request(self, request):
|
|
socket = self.publisher.acquire_connection(request)
|
|
self.publisher.send_request(socket, request)
|
|
return socket
|
|
|
|
def _schedule_request_for_ack(self, request):
|
|
socket = self._send_request(request)
|
|
if socket is None:
|
|
return None
|
|
self.receiver.register_socket(socket)
|
|
ack_future, _ = self.receiver.track_request(request)
|
|
ack_future.socket = socket
|
|
return ack_future
|
|
|
|
def send_call(self, request):
|
|
ack_future = self._schedule_request_for_ack(request)
|
|
if ack_future is None:
|
|
self.publisher._raise_timeout(request)
|
|
self.pool.submit(self._wait_for_ack, request, ack_future)
|
|
try:
|
|
return self.publisher.receive_reply(ack_future.socket, request)
|
|
finally:
|
|
if not ack_future.done():
|
|
ack_future.set_result(None)
|
|
|
|
def send_cast(self, request):
|
|
self.pool.submit(self._wait_for_ack, request)
|
|
|
|
send_fanout = _send_request
|
|
send_notify = _send_request
|
|
|
|
|
|
class AckManagerAsyncMultisend(AckManager):
|
|
|
|
def _send_request_async(self, request):
|
|
self.pool.submit(self._send_request, request)
|
|
|
|
send_fanout = _send_request_async
|
|
send_notify = _send_request_async
|