Merge "[simulator] Automatic stopping of rpc-servers"
This commit is contained in:
commit
874a69b0cd
@ -24,6 +24,7 @@ import os
|
|||||||
import random
|
import random
|
||||||
import signal
|
import signal
|
||||||
import six
|
import six
|
||||||
|
import socket
|
||||||
import string
|
import string
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
@ -39,6 +40,7 @@ from oslo_utils import timeutils
|
|||||||
LOG = logging.getLogger()
|
LOG = logging.getLogger()
|
||||||
RANDOM_GENERATOR = None
|
RANDOM_GENERATOR = None
|
||||||
CURRENT_PID = None
|
CURRENT_PID = None
|
||||||
|
CURRENT_HOST = None
|
||||||
CLIENTS = []
|
CLIENTS = []
|
||||||
MESSAGES = []
|
MESSAGES = []
|
||||||
IS_RUNNING = True
|
IS_RUNNING = True
|
||||||
@ -320,6 +322,37 @@ class RpcEndpoint(object):
|
|||||||
return reply
|
return reply
|
||||||
|
|
||||||
|
|
||||||
|
class ServerControlEndpoint(object):
|
||||||
|
def __init__(self, controlled_server):
|
||||||
|
self.connected_clients = set()
|
||||||
|
self.controlled_server = controlled_server
|
||||||
|
|
||||||
|
def sync_start(self, ctx, message):
|
||||||
|
"""Handle start reports from clients"""
|
||||||
|
|
||||||
|
client_id = message['id']
|
||||||
|
LOG.info('The client %s started to send messages' % client_id)
|
||||||
|
self.connected_clients.add(client_id)
|
||||||
|
|
||||||
|
def sync_done(self, ctx, message):
|
||||||
|
"""Handle done reports from clients"""
|
||||||
|
|
||||||
|
client_id = message['id']
|
||||||
|
LOG.info('The client %s finished msg sending.' % client_id)
|
||||||
|
|
||||||
|
if client_id in self.connected_clients:
|
||||||
|
self.connected_clients.remove(client_id)
|
||||||
|
|
||||||
|
if not self.connected_clients:
|
||||||
|
LOG.info(
|
||||||
|
'The clients sent all messages. Shutting down the server..')
|
||||||
|
threading.Timer(1, self._stop_server_with_delay).start()
|
||||||
|
|
||||||
|
def _stop_server_with_delay(self):
|
||||||
|
self.controlled_server.stop()
|
||||||
|
self.controlled_server.wait()
|
||||||
|
|
||||||
|
|
||||||
class Client(object):
|
class Client(object):
|
||||||
def __init__(self, client_id, client, method, has_result,
|
def __init__(self, client_id, client, method, has_result,
|
||||||
wait_after_msg):
|
wait_after_msg):
|
||||||
@ -341,6 +374,12 @@ class Client(object):
|
|||||||
self.round_trip_messages = MessageStatsCollector(
|
self.round_trip_messages = MessageStatsCollector(
|
||||||
'round-trip-%s' % client_id)
|
'round-trip-%s' % client_id)
|
||||||
|
|
||||||
|
def host_based_id(self):
|
||||||
|
_id = "%(client_id)s %(salt)s@%(hostname)s"
|
||||||
|
return _id % {'hostname': CURRENT_HOST,
|
||||||
|
'salt': hex(id(self))[2:],
|
||||||
|
'client_id': self.client_id}
|
||||||
|
|
||||||
def send_msg(self):
|
def send_msg(self):
|
||||||
msg = make_message(self.seq, MESSAGES[self.position], time.time())
|
msg = make_message(self.seq, MESSAGES[self.position], time.time())
|
||||||
self.sent_messages.push(msg)
|
self.sent_messages.push(msg)
|
||||||
@ -366,12 +405,55 @@ class Client(object):
|
|||||||
|
|
||||||
class RPCClient(Client):
|
class RPCClient(Client):
|
||||||
def __init__(self, client_id, transport, target, timeout, is_cast,
|
def __init__(self, client_id, transport, target, timeout, is_cast,
|
||||||
wait_after_msg):
|
wait_after_msg, sync_mode=False):
|
||||||
client = rpc.RPCClient(transport, target).prepare(timeout=timeout)
|
|
||||||
|
client = rpc.RPCClient(transport, target)
|
||||||
method = _rpc_cast if is_cast else _rpc_call
|
method = _rpc_cast if is_cast else _rpc_call
|
||||||
|
|
||||||
super(RPCClient, self).__init__(client_id, client, method,
|
super(RPCClient, self).__init__(client_id,
|
||||||
|
client.prepare(timeout=timeout),
|
||||||
|
method,
|
||||||
not is_cast, wait_after_msg)
|
not is_cast, wait_after_msg)
|
||||||
|
self.sync_mode = sync_mode
|
||||||
|
self.is_sync = False
|
||||||
|
|
||||||
|
# prepare the sync client
|
||||||
|
if sync_mode:
|
||||||
|
if sync_mode == 'call':
|
||||||
|
self.sync_client = self.client
|
||||||
|
else:
|
||||||
|
self.sync_client = client.prepare(fanout=True, timeout=timeout)
|
||||||
|
|
||||||
|
def send_msg(self):
|
||||||
|
if self.sync_mode and not self.is_sync:
|
||||||
|
self.is_sync = self.sync_start()
|
||||||
|
super(RPCClient, self).send_msg()
|
||||||
|
|
||||||
|
def sync_start(self):
|
||||||
|
try:
|
||||||
|
msg = {'id': self.host_based_id()}
|
||||||
|
method = _rpc_call if self.sync_mode == 'call' else _rpc_cast
|
||||||
|
method(self.sync_client, msg, 'sync_start')
|
||||||
|
except Exception:
|
||||||
|
LOG.error('The client: %s failed to sync with %s.' %
|
||||||
|
(self.client_id, self.client.target))
|
||||||
|
return False
|
||||||
|
LOG.info('The client: %s successfully sync with %s' % (
|
||||||
|
self.client_id, self.client.target))
|
||||||
|
return True
|
||||||
|
|
||||||
|
def sync_done(self):
|
||||||
|
try:
|
||||||
|
msg = {'id': self.host_based_id()}
|
||||||
|
method = _rpc_call if self.sync_mode == 'call' else _rpc_cast
|
||||||
|
method(self.sync_client, msg, 'sync_done')
|
||||||
|
except Exception:
|
||||||
|
LOG.error('The client: %s failed finish the sync with %s.'
|
||||||
|
% (self.client_id, self.client.target))
|
||||||
|
return False
|
||||||
|
LOG.info('The client: %s successfully finished sync with %s'
|
||||||
|
% (self.client_id, self.client.target))
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class NotifyClient(Client):
|
class NotifyClient(Client):
|
||||||
@ -432,9 +514,13 @@ def run_server(server, duration=None):
|
|||||||
|
|
||||||
|
|
||||||
def rpc_server(transport, target, wait_before_answer, executor, duration):
|
def rpc_server(transport, target, wait_before_answer, executor, duration):
|
||||||
|
|
||||||
endpoints = [RpcEndpoint(wait_before_answer)]
|
endpoints = [RpcEndpoint(wait_before_answer)]
|
||||||
server = rpc.get_rpc_server(transport, target, endpoints,
|
server = rpc.get_rpc_server(transport, target, endpoints, executor)
|
||||||
executor=executor)
|
|
||||||
|
# make the rpc server controllable by rpc clients
|
||||||
|
endpoints.append(ServerControlEndpoint(server))
|
||||||
|
|
||||||
LOG.debug("starting RPC server for target %s", target)
|
LOG.debug("starting RPC server for target %s", target)
|
||||||
|
|
||||||
run_server(server, duration=duration)
|
run_server(server, duration=duration)
|
||||||
@ -444,15 +530,18 @@ def rpc_server(transport, target, wait_before_answer, executor, duration):
|
|||||||
|
|
||||||
@wrap_sigexit
|
@wrap_sigexit
|
||||||
def spawn_rpc_clients(threads, transport, targets, wait_after_msg, timeout,
|
def spawn_rpc_clients(threads, transport, targets, wait_after_msg, timeout,
|
||||||
is_cast, messages_count, duration):
|
is_cast, messages_count, duration, sync_mode):
|
||||||
p = eventlet.GreenPool(size=threads)
|
p = eventlet.GreenPool(size=threads)
|
||||||
targets = itertools.cycle(targets)
|
targets = itertools.cycle(targets)
|
||||||
|
|
||||||
for i in six.moves.range(threads):
|
for i in six.moves.range(threads):
|
||||||
target = next(targets)
|
target = next(targets)
|
||||||
LOG.debug("starting RPC client for target %s", target)
|
LOG.debug("starting RPC client for target %s", target)
|
||||||
client_builder = functools.partial(RPCClient, i, transport, target,
|
client_builder = functools.partial(RPCClient, i, transport, target,
|
||||||
timeout, is_cast, wait_after_msg)
|
timeout, is_cast, wait_after_msg,
|
||||||
p.spawn_n(send_messages, i, client_builder, messages_count, duration)
|
sync_mode)
|
||||||
|
p.spawn_n(send_messages, i, client_builder,
|
||||||
|
messages_count, duration)
|
||||||
p.waitall()
|
p.waitall()
|
||||||
|
|
||||||
|
|
||||||
@ -493,12 +582,17 @@ def send_messages(client_id, client_builder, messages_count, duration):
|
|||||||
break
|
break
|
||||||
LOG.debug("Client %d has sent %d messages", client_id, messages_count)
|
LOG.debug("Client %d has sent %d messages", client_id, messages_count)
|
||||||
|
|
||||||
time.sleep(1) # wait for replies to be collected
|
# wait for replies to be collected
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
# send stop request to the rpc server
|
||||||
|
if isinstance(client, RPCClient) and client.is_sync:
|
||||||
|
client.sync_done()
|
||||||
|
|
||||||
|
|
||||||
def _rpc_call(client, msg):
|
def _rpc_call(client, msg, remote_method='info'):
|
||||||
try:
|
try:
|
||||||
res = client.call({}, 'info', message=msg)
|
res = client.call({}, remote_method, message=msg)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.exception('Error %s on CALL for message %s', str(e), msg)
|
LOG.exception('Error %s on CALL for message %s', str(e), msg)
|
||||||
raise
|
raise
|
||||||
@ -507,9 +601,9 @@ def _rpc_call(client, msg):
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
def _rpc_cast(client, msg):
|
def _rpc_cast(client, msg, remote_method='info'):
|
||||||
try:
|
try:
|
||||||
client.cast({}, 'info', message=msg)
|
client.cast({}, remote_method, message=msg)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.exception('Error %s on CAST for message %s', str(e), msg)
|
LOG.exception('Error %s on CAST for message %s', str(e), msg)
|
||||||
raise
|
raise
|
||||||
@ -663,6 +757,9 @@ def main():
|
|||||||
client.add_argument('--is-fanout', dest='is_fanout', action='store_true',
|
client.add_argument('--is-fanout', dest='is_fanout', action='store_true',
|
||||||
help='fanout=True for CAST messages')
|
help='fanout=True for CAST messages')
|
||||||
|
|
||||||
|
client.add_argument('--sync', dest='sync', choices=('call', 'fanout'),
|
||||||
|
help="stop server when all msg was sent by clients")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
_setup_logging(is_debug=args.debug)
|
_setup_logging(is_debug=args.debug)
|
||||||
@ -717,14 +814,16 @@ def main():
|
|||||||
show_client_stats(CLIENTS, args.json_filename)
|
show_client_stats(CLIENTS, args.json_filename)
|
||||||
|
|
||||||
elif args.mode == 'rpc-client':
|
elif args.mode == 'rpc-client':
|
||||||
targets = [target.partition('.')[::2] for target in args.targets]
|
|
||||||
targets = [messaging.Target(
|
targets = []
|
||||||
topic=topic, server=server_name, fanout=args.is_fanout) for
|
for target in args.targets:
|
||||||
topic, server_name in targets]
|
tp, srv = target.partition('.')[::2]
|
||||||
|
t = messaging.Target(topic=tp, server=srv, fanout=args.is_fanout)
|
||||||
|
targets.append(t)
|
||||||
|
|
||||||
spawn_rpc_clients(args.threads, TRANSPORT, targets,
|
spawn_rpc_clients(args.threads, TRANSPORT, targets,
|
||||||
args.wait_after_msg, args.timeout, args.is_cast,
|
args.wait_after_msg, args.timeout, args.is_cast,
|
||||||
args.messages, args.duration)
|
args.messages, args.duration, args.sync)
|
||||||
|
|
||||||
show_client_stats(CLIENTS, args.json_filename, not args.is_cast)
|
show_client_stats(CLIENTS, args.json_filename, not args.is_cast)
|
||||||
|
|
||||||
if args.exit_wait:
|
if args.exit_wait:
|
||||||
@ -735,4 +834,5 @@ def main():
|
|||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
RANDOM_GENERATOR = init_random_generator()
|
RANDOM_GENERATOR = init_random_generator()
|
||||||
CURRENT_PID = os.getpid()
|
CURRENT_PID = os.getpid()
|
||||||
|
CURRENT_HOST = socket.gethostname()
|
||||||
main()
|
main()
|
||||||
|
Loading…
Reference in New Issue
Block a user