2020-10-21 02:20:24 +02:00
|
|
|
import os
|
|
|
|
import signal
|
2020-10-20 00:45:08 +02:00
|
|
|
import time
|
|
|
|
import sys
|
|
|
|
import json
|
|
|
|
import requests
|
2020-10-21 02:20:24 +02:00
|
|
|
import textwrap
|
2020-10-20 00:45:08 +02:00
|
|
|
from datetime import datetime
|
|
|
|
from dataclasses import dataclass
|
|
|
|
import argparse
|
|
|
|
from multiprocessing import Process, Queue
|
|
|
|
from queue import Empty
|
|
|
|
import paho.mqtt.client
|
2020-10-21 02:20:24 +02:00
|
|
|
from threading import Thread, current_thread, Timer
|
2020-10-20 00:45:08 +02:00
|
|
|
|
|
|
|
|
|
|
|
@dataclass(frozen=False, init=False)
|
|
|
|
class UploadRequest:
|
|
|
|
id: int
|
2020-10-21 01:18:35 +02:00
|
|
|
upload_started: float = None
|
|
|
|
upload_finished: float = None
|
|
|
|
upload_status_code: bool = False
|
|
|
|
alert_arrived: float = None
|
2020-10-20 00:45:08 +02:00
|
|
|
|
2020-10-21 01:18:35 +02:00
|
|
|
def __init__(self, _id: int):
|
|
|
|
self.id = _id
|
2020-10-20 00:45:08 +02:00
|
|
|
|
|
|
|
|
|
|
|
@dataclass(frozen=False, init=True)
|
|
|
|
class ReturnedRequest:
|
|
|
|
id: int
|
|
|
|
arrived: float
|
|
|
|
|
|
|
|
|
2020-10-21 01:18:35 +02:00
|
|
|
def print_progress_meme(char: str):
|
|
|
|
print(char, end='')
|
|
|
|
sys.stdout.flush()
|
|
|
|
|
|
|
|
|
|
|
|
def sender_worker(input_queue: Queue, sent_queue: Queue, target_url: str, file_contents: bytes, request_timeout: float):
|
2020-10-20 00:45:08 +02:00
|
|
|
content_len = len(file_contents)
|
|
|
|
session = requests.Session()
|
|
|
|
try:
|
|
|
|
while True:
|
|
|
|
try:
|
|
|
|
job = input_queue.get_nowait()
|
|
|
|
except Empty:
|
|
|
|
return
|
|
|
|
|
|
|
|
files = {
|
|
|
|
"file": (
|
|
|
|
f"birbbenchmark2_testfile_{job.id}.wav",
|
|
|
|
file_contents,
|
|
|
|
'audio/wave',
|
|
|
|
{'Content-length': content_len}
|
|
|
|
),
|
|
|
|
"description": (
|
|
|
|
None,
|
|
|
|
json.dumps({'date': datetime.now().isoformat(), 'device_id': str(job.id)}),
|
|
|
|
"application/json"
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
# SUPER HIGH PRECISION MEASUREMENT COMPARTMENT DEPARTMENT ADMIRAL GENERAL ALADEEN
|
|
|
|
job.upload_started = time.time()
|
2020-10-21 01:18:35 +02:00
|
|
|
try:
|
|
|
|
r = session.post(target_url, files=files, timeout=request_timeout)
|
|
|
|
job.upload_finished = time.time()
|
|
|
|
if r.status_code == 200:
|
|
|
|
print_progress_meme('.')
|
|
|
|
else:
|
|
|
|
print_progress_meme('!')
|
|
|
|
|
|
|
|
job.upload_status_code = r.status_code
|
|
|
|
except requests.Timeout:
|
|
|
|
print_progress_meme('?')
|
2020-10-20 00:45:08 +02:00
|
|
|
# /'\./'\./'\./'\./'\./'\./'\./'\./'\./'\./'\./'\./'\./'\./'\./'\./'\./'\./'\./'\
|
|
|
|
|
2020-10-21 01:18:35 +02:00
|
|
|
sent_queue.put(job) # Will have none as status_code and upload_finished if the request timed out
|
2020-10-20 00:45:08 +02:00
|
|
|
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
def mqtt_on_connect(client, userdata, flags, rc):
|
|
|
|
client.subscribe(f"command/#")
|
|
|
|
|
|
|
|
|
|
|
|
def mqtt_on_command(client, userdata, message):
|
2020-10-21 01:18:35 +02:00
|
|
|
# msg = json.loads(message.payload.decode())
|
|
|
|
# if msg['command'] == 'doAlert':
|
|
|
|
_id = int(message.topic[8:]) # len('command/') = 8
|
|
|
|
userdata.put(ReturnedRequest(id=_id, arrived=time.time()))
|
|
|
|
print_progress_meme(',')
|
2020-10-20 00:45:08 +02:00
|
|
|
|
|
|
|
|
2020-10-21 01:18:35 +02:00
|
|
|
def infinite_job_generator(input_queue: Queue, target_ready_jobs: int):
|
|
|
|
i = 0
|
|
|
|
target_ready_jobs_local = target_ready_jobs
|
|
|
|
first = True
|
|
|
|
while True:
|
|
|
|
jobs_ready = input_queue.qsize()
|
|
|
|
if jobs_ready < target_ready_jobs_local:
|
2020-10-20 00:45:08 +02:00
|
|
|
|
2020-10-21 01:18:35 +02:00
|
|
|
if not first: # In the first run there are exactly zero jobs ready, so
|
|
|
|
if jobs_ready < (target_ready_jobs_local / 2):
|
|
|
|
target_ready_jobs_local *= 2
|
|
|
|
else:
|
|
|
|
first = False
|
|
|
|
|
|
|
|
jobs_to_generate = target_ready_jobs_local - jobs_ready
|
|
|
|
for _ in range(jobs_to_generate):
|
|
|
|
i += 1
|
|
|
|
input_queue.put(UploadRequest(i))
|
|
|
|
time.sleep(0.02)
|
|
|
|
|
|
|
|
if not current_thread().active:
|
|
|
|
return
|
|
|
|
|
|
|
|
|
2020-10-21 02:20:24 +02:00
|
|
|
def greacefully_stop_all_workers(list_of_worker_pids: list):
|
|
|
|
for pid in list_of_worker_pids:
|
|
|
|
os.kill(pid, signal.SIGINT)
|
|
|
|
print_progress_meme('#')
|
|
|
|
|
|
|
|
|
2020-10-21 01:18:35 +02:00
|
|
|
def run_benchmark(
|
|
|
|
num_requests: int,
|
|
|
|
num_workers: int,
|
|
|
|
sound_file_path: str,
|
|
|
|
target_url: str,
|
|
|
|
timeout: float,
|
|
|
|
inflight_timeout: float,
|
|
|
|
request_timeout: float,
|
|
|
|
mqtt_client: paho.mqtt.client.Client):
|
2020-10-20 00:45:08 +02:00
|
|
|
input_queue = Queue()
|
|
|
|
sent_queue = Queue()
|
|
|
|
returned_queue = Queue()
|
|
|
|
sender_workers = []
|
|
|
|
|
2020-10-21 01:18:35 +02:00
|
|
|
input_generator_thread = None
|
2020-10-20 00:45:08 +02:00
|
|
|
|
2020-10-21 01:18:35 +02:00
|
|
|
if num_requests == 0:
|
|
|
|
print(f"Unlimited amount of requests will be generated...")
|
|
|
|
input_generator_thread = Thread(target=infinite_job_generator, args=(input_queue, num_workers * 100))
|
|
|
|
input_generator_thread.active = True
|
|
|
|
input_generator_thread.start()
|
|
|
|
else:
|
|
|
|
print(f"Preparing {num_requests} requests...")
|
|
|
|
for i in range(num_requests):
|
|
|
|
input_queue.put(UploadRequest(i + 1))
|
2020-10-20 00:45:08 +02:00
|
|
|
|
2020-10-21 01:18:35 +02:00
|
|
|
mqtt_client.user_data_set(returned_queue)
|
|
|
|
mqtt_client.loop_start() # Puts mqtt stuff to a background thread
|
2020-10-20 00:45:08 +02:00
|
|
|
|
2020-10-21 01:18:35 +02:00
|
|
|
print(f"Preparing {num_workers} workers...")
|
2020-10-20 00:45:08 +02:00
|
|
|
|
2020-10-21 01:18:35 +02:00
|
|
|
with open(sound_file_path, "rb") as f:
|
2020-10-20 00:45:08 +02:00
|
|
|
file_contents = f.read()
|
|
|
|
|
2020-10-21 01:18:35 +02:00
|
|
|
for i in range(num_workers):
|
2020-10-20 00:45:08 +02:00
|
|
|
sender_workers.append(
|
2020-10-21 01:18:35 +02:00
|
|
|
Process(target=sender_worker,
|
|
|
|
args=(
|
|
|
|
input_queue,
|
|
|
|
sent_queue,
|
|
|
|
target_url,
|
|
|
|
file_contents,
|
|
|
|
request_timeout
|
|
|
|
))
|
2020-10-20 00:45:08 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
print("Baszatás!")
|
2020-10-21 01:18:35 +02:00
|
|
|
# Starting all workers
|
2020-10-20 00:45:08 +02:00
|
|
|
for worker in sender_workers:
|
|
|
|
worker.start()
|
|
|
|
|
2020-10-21 02:20:24 +02:00
|
|
|
worker_stopper_timer = None
|
|
|
|
if timeout != 0:
|
|
|
|
sender_worker_pids = [w.pid for w in sender_workers]
|
|
|
|
worker_stopper_timer = Timer(timeout, greacefully_stop_all_workers, args=(sender_worker_pids,))
|
|
|
|
worker_stopper_timer.start()
|
|
|
|
|
2020-10-21 01:18:35 +02:00
|
|
|
# Waiting for workers to finish
|
2020-10-20 00:45:08 +02:00
|
|
|
try:
|
|
|
|
for worker in sender_workers:
|
|
|
|
worker.join()
|
|
|
|
except KeyboardInterrupt:
|
2020-10-21 02:20:24 +02:00
|
|
|
if worker_stopper_timer:
|
|
|
|
worker_stopper_timer.cancel() # We don't want the auto stopper to fire again
|
2020-10-21 01:18:35 +02:00
|
|
|
# Azt jelenti: jovan, megállítottam a küldést, most már csak az elveszett válaszokat várom
|
|
|
|
print_progress_meme('|')
|
2020-10-20 00:45:08 +02:00
|
|
|
# Interrupt forwarded for workers, so we just wait for them to quit
|
|
|
|
for worker in sender_workers:
|
2020-10-21 02:20:24 +02:00
|
|
|
if worker.is_alive():
|
|
|
|
exitcode = worker.join(timeout=2)
|
|
|
|
if not exitcode: # = it did not exit
|
|
|
|
worker.terminate() # Not the nicest solution
|
2020-10-20 00:45:08 +02:00
|
|
|
|
2020-10-21 01:18:35 +02:00
|
|
|
# Stop the input generator
|
|
|
|
input_generator_thread.active = False
|
|
|
|
|
|
|
|
# read out all send requests to an array for handling later:
|
|
|
|
results = {}
|
|
|
|
total_successful_uploads = 0
|
2020-10-20 00:45:08 +02:00
|
|
|
while True:
|
|
|
|
try:
|
|
|
|
job = sent_queue.get_nowait()
|
|
|
|
except Empty:
|
|
|
|
break
|
|
|
|
|
2020-10-21 01:18:35 +02:00
|
|
|
if job.upload_status_code == 200:
|
|
|
|
total_successful_uploads += 1
|
|
|
|
|
|
|
|
results[job.id] = job
|
|
|
|
|
|
|
|
# total_successful_uploads = len([req for req in results.values() if req.upload_status_code == 200])
|
2020-10-20 00:45:08 +02:00
|
|
|
|
|
|
|
# Wait for inflight messages for a little
|
2020-10-21 01:18:35 +02:00
|
|
|
for _ in range(int(inflight_timeout)):
|
2020-10-20 00:45:08 +02:00
|
|
|
time.sleep(1)
|
|
|
|
if returned_queue.qsize() >= total_successful_uploads:
|
|
|
|
break
|
|
|
|
|
2020-10-21 01:18:35 +02:00
|
|
|
mqtt_client.loop_stop() # This stops further recieving MQTT messages
|
|
|
|
print() # Everything that might print from other threads are now stopped, so we put a newline
|
2020-10-20 00:45:08 +02:00
|
|
|
|
2020-10-21 02:20:24 +02:00
|
|
|
if int(inflight_timeout) != 0: # print only if there was a timeout
|
|
|
|
if returned_queue.qsize() < total_successful_uploads:
|
|
|
|
print("Waiting for inflight MQTT messages timed out!")
|
2020-10-21 01:18:35 +02:00
|
|
|
|
|
|
|
# Fill out the missing fields in the reuslts
|
|
|
|
total_answered = 0
|
2020-10-20 00:45:08 +02:00
|
|
|
while True:
|
|
|
|
try:
|
2020-10-21 01:18:35 +02:00
|
|
|
returned_job = returned_queue.get_nowait()
|
2020-10-20 00:45:08 +02:00
|
|
|
except Empty:
|
|
|
|
break
|
|
|
|
|
2020-10-21 01:18:35 +02:00
|
|
|
try:
|
|
|
|
results[returned_job.id].alert_arrived = returned_job.arrived
|
|
|
|
except KeyError:
|
|
|
|
print("Bruh moment: Alert arrived with an id that's not sent", returned_job.id)
|
|
|
|
|
|
|
|
total_answered += 1
|
|
|
|
|
|
|
|
# mini statistics
|
2020-10-21 02:20:24 +02:00
|
|
|
print(
|
|
|
|
f"{len(results)} requests completed: {total_successful_uploads} successfully uploaded and {total_answered} answered")
|
2020-10-21 01:18:35 +02:00
|
|
|
|
|
|
|
return results
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
2020-10-21 02:20:24 +02:00
|
|
|
help_epilog = textwrap.dedent('''\
|
|
|
|
Progress chars meaning:
|
|
|
|
HTTP related
|
|
|
|
. -> Upload succesfully completed (return code was 200)
|
|
|
|
! -> Upload failed (return code was not 200)
|
|
|
|
? -> Upload timed out (see --request-timeout)
|
|
|
|
|
|
|
|
MQTT related
|
|
|
|
, -> Alert arrived (Trough MQTT)
|
|
|
|
|
|
|
|
Process related
|
|
|
|
| -> User interrupted (CTRL+C pressed)
|
|
|
|
# -> Timeout expired (see --timeout)
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
One worker send exactly 100 requests (wait 10 secs for all alerts to arrive)
|
|
|
|
benchmark2.py --workers 1 --requests 100 --file /home/testerboi/birbstuff/testdata/oIbBz.wav --inflight-timeout 10
|
|
|
|
|
|
|
|
Ten worker run for exactly one minute (wait 10 secs for all alerts to arrive)
|
|
|
|
benchmark2.py --workers 10 --requests 0 --timeout 60 --file /home/testerboi/birbstuff/testdata/oIbBz.wav --inflight-timeout 10
|
|
|
|
|
|
|
|
Send either 100 requests or run for one minute (whichever comes first)
|
|
|
|
benchmark2.py --requests 100 --timeout 60 --file /home/testerboi/birbstuff/testdata/oIbBz.wav
|
|
|
|
''')
|
|
|
|
|
|
|
|
parser = argparse.ArgumentParser(description='Birbnetes Benchmarker 2', epilog=help_epilog,
|
|
|
|
formatter_class=argparse.RawTextHelpFormatter)
|
2020-10-21 01:18:35 +02:00
|
|
|
parser.add_argument('--workers', type=int, required=False, default=1, help='Number of workers')
|
2020-10-21 02:20:24 +02:00
|
|
|
parser.add_argument('--requests', type=int, required=True, help='Number of requests (0 for infinity)')
|
2020-10-21 01:18:35 +02:00
|
|
|
parser.add_argument('--timeout', type=int, required=False, default=0,
|
2020-10-21 02:20:24 +02:00
|
|
|
help='Maximum time for request sending (0 for unlimited)')
|
2020-10-21 01:18:35 +02:00
|
|
|
|
|
|
|
parser.add_argument('--inflight-timeout', type=int, required=False, default=30,
|
2020-10-21 02:20:24 +02:00
|
|
|
help='Number of seconds to wait for MQTT messages to arrive after uploading finished (not very precise) (0 for not waiting)')
|
2020-10-21 01:18:35 +02:00
|
|
|
|
|
|
|
parser.add_argument('--request-timeout', type=float, required=False, default=5,
|
|
|
|
help='Timeout for HTTP requests')
|
|
|
|
|
2020-10-21 02:20:24 +02:00
|
|
|
parser.add_argument('--file', type=str, required=True, help='Name of the sound file to upload')
|
2020-10-21 01:18:35 +02:00
|
|
|
parser.add_argument('--target-url', type=str, required=False, default="https://birb.k8s.kmlabz.com/sample",
|
|
|
|
help='The target endpoint')
|
|
|
|
|
|
|
|
parser.add_argument('--mqtt-username', type=str, required=False, default="birbnetes",
|
|
|
|
help="Username for the MQTT server")
|
|
|
|
parser.add_argument('--mqtt-port', type=int, required=False, default=1883, help="Port for the MQTT server")
|
|
|
|
parser.add_argument('--mqtt-host', type=str, required=False, default="mqtt.kmlabz.com",
|
|
|
|
help="Hostname for the MQTT server")
|
|
|
|
parser.add_argument('--mqtt-password', type=str, required=False, default=None, help="Username for the MQTT server")
|
|
|
|
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
|
|
print(f"Preparing MQTT (host: {args.mqtt_host}:{args.mqtt_port}, login: {bool(args.mqtt_password)})")
|
|
|
|
client = paho.mqtt.client.Client(client_id="10")
|
|
|
|
client.on_connect = mqtt_on_connect
|
|
|
|
client.on_message = mqtt_on_command
|
|
|
|
if args.mqtt_password:
|
|
|
|
client.username_pw_set(args.mqtt_username, args.mqtt_password)
|
|
|
|
client.connect(args.mqtt_host, args.mqtt_port, 60)
|
|
|
|
|
|
|
|
results = run_benchmark(args.requests, args.workers, args.file, args.target_url, args.timeout,
|
|
|
|
args.inflight_timeout, args.request_timeout, client)
|
|
|
|
|
2020-10-20 00:45:08 +02:00
|
|
|
print("Eposzi kiértékelés idő...")
|
|
|
|
print("id Upload Dur Return Dur Total Dur")
|
2020-10-21 01:18:35 +02:00
|
|
|
# read out all recieved requests to an array for easier handling:
|
|
|
|
for id_, req in results.items():
|
2020-10-20 00:45:08 +02:00
|
|
|
|
2020-10-21 01:18:35 +02:00
|
|
|
if not req.alert_arrived:
|
|
|
|
if req.upload_status_code == 200:
|
2020-10-20 00:45:08 +02:00
|
|
|
print(id_, req.upload_finished - req.upload_started, '???', '???')
|
|
|
|
else:
|
|
|
|
print(id_, req.upload_finished - req.upload_started, '!', '???', '???')
|
|
|
|
else:
|
2020-10-21 01:18:35 +02:00
|
|
|
print(id_, req.upload_finished - req.upload_started, req.alert_arrived - req.upload_finished,
|
|
|
|
req.alert_arrived - req.upload_started)
|
2020-10-20 00:45:08 +02:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|