This post is a follow-up to the post I created that gives a basic multi threaded example. There isn’t any context and when I came back to the code I found it difficult to translate into one of my functions.
In this example, I will take a function of my F5 Flask Dashboard that has no multi threading and make it use another function that will handle the multi threading.
The background to this is to run three API requests to each F5. I am looking for details of the device version and failover stats and status. Currently, each F5 must run each of these APIs before moving on to the next API and so on. Some of the F5s take a while to respond and hold the process up. Using a multi threaded approach means that the F5s that take a while to respond can be in their waiting state while the responsive F5s can return the data and another F5 can join the multi threading queue.
I am currently running this as a test against 24 F5s. Each API is still performed in sequence for each F5. A second version of this will be to send a number of APIs, so a new connection isn’t required to further speed things up. However, in the current state, the time is a big difference.
– Before the multi threading was added: 234.580 seconds.
– Single thread (using the multi threaded functions): 246.64 seconds
– 16 threads: 44.57 seconds
Original Version
Flask Function
This is the function that kicks it all off. Here are the three API calls. Each is calling the exact same function, just passing in a different path.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
@info_blueprint.route('/bg_get_info', methods = ['GET', 'POST']) def bg_get_info(): f5_ip_list = ["1.2.3.4",] # Example list get_info = F5BackgroundProcesses() # Run timer import time start = time.time() # Get F5 basic Info path = 'cm/device' f5_info = get_info.get_f5_info(f5_ip_list, path) # print(json.dumps(f5_info, sort_keys=True, indent=4)) # Get F5 HA failover active (days) path = 'sys/failover' f5_failover1 = get_info.get_f5_info(f5_ip_list, path) # print(json.dumps(f5_failover1, sort_keys=True, indent=4)) # Get F5 HA failover mate path = 'cm/failover-status' f5_failover2 = get_info.get_f5_info(f5_ip_list, path) # print(json.dumps(f5_failover2, sort_keys=True, indent=4)) # Print timer end = time.time() print("Time consumed in working: ",end - start) |
Create API Request Function
This is the function that will be modified to move into a multi threaded state. Currently, it just loops through each F5 IP and path and passes that to the actual F5 function that will make all the F5 calls for the project.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
def get_f5_info(self, f5_ip_list, path): return_list = [] for hostip in f5_ip_list: try: # Setup F5 API f5_commands = F5_Commander(self.username, self.password) # Type of F5 command get_info_command = f5_commands.F5_api_request(path, hostip) return_list.append(get_info_command) except: return_list.append({'lastCheck':'Failed'}) return return_list#, domain_names_list |
F5 API Function
This is the last function of this journey, to actually send the API request to each F5. The multi threading will essentially be calling multiple instances of this function.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class F5_Commander: def __init__(self, username, password): self.username = username self.password = password # make an api call for the requested path and respond with a dictionary of the response payload def F5_api_request(self, path, hostip): auth = HTTPBasicAuth ('admin', self.password) url = 'https://{}/mgmt/tm/{}'.format(hostip,path) request = requests.get(url, verify=False, auth=auth) print(url) print(request) return json.loads(request.text) |
Multi Threaded Version
As I said, the only changes made are to the Create API Request Function. The process of this is to follow the same procedure, as the original, by having the flask function send a list of IPs and a path to make the API call. This function is now split into two parts.
– Function get_f5_info to take in the IP list, path and start the worker thread
– Function get_f5_info_MT is the worker thread, this take the IP and path and calls the API send function
What will happen
1. The function named get_f5_info takes in the F5 IP list and path (same as earlier)
2. The function named get_f5_info starts worker threads for each IP
3. The function named get_f5_info_MT will make the API request for each worker thread created until IP list is completed
3. Once completed, a list is returned
4. Process repeats, the next API path is sent with the IP list
The get_f5_info function is creating worker threads that call the multi threading function to make the API requests.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
import queue from threading import Thread import time def get_f5_info(self, f5_ip_list, path): start = time.time() get_f5_info = F5BackgroundProcesses() q = queue.Queue() for i in range(16): worker = Thread(target=get_f5_info.get_f5_info_MT, args=(q, path, return_list, ), daemon=True) worker.start() # Threading starts here for ip in f5_ip_list: q.put(ip) q.join() # Print timer end = time.time() print("Time consumed in working: ",end - start) return return_list#, domain_names_list |
The multi threading function will make the call to the API
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
def get_f5_info_MT(self, q, path, return_list): while True: hostip = q.get() try: # Setup F5 API f5_commands = F5_Commander(self.username, self.password) # Type of F5 command get_info_command = f5_commands.F5_api_request(path, hostip) return_list.append(get_info_command) except: return_list.append({'lastCheck':'Failed'}) q.task_done() |