def _process_response_time_api(self, response_time, api): """ Analyses the response time of an API. This function is always called on the response time of an API in order to update the internal state with the average response time. Parameters ---------- response_time : float Elapsed time in milliseconds. api : object The API which had the response time passed above. """ logger.info("%s: response time %sms" % (api.name, response_time)) with self._locks["_api_response_times"]: if (len(self._api_response_times[api.name]) > self.MAX_HISTORY_RTIME): # Remove from the history once it reaches max limit self._api_response_times[api.name].pop(0) self._api_response_times[api.name] += [response_time] # Sorted returns a new cloned list np_array = numpy.array(sorted(self._api_response_times[api.name])) # Compute the response time of self.PERCENTILE percentage of requests p = numpy.percentile(np_array, self.PERCENTILE) with self._locks["_percentile_map"]: self._percentile_map[api.name] = p logger.debug("%s - %s percentile result: %s" % (api.name, self.PERCENTILE, p))
def remove_api(self, api): """ Removes the API from the internal list. Parameters ---------- api : object The object implementing BaseThirdPartyAPIService class. """ logger.info("Removing API: %s" % api.name) self._locks["_api_list"].acquire_write() removed_api = self._api_list.pop(api.name, None) self._locks["_api_list"].release_write() if removed_api is not None: logger.debug("Removed API") else: logger.debug("Tried to remove API which is " "not present in the list") return with self._locks["_api_response_times"]: self._api_response_times.pop(api.name, None) with self._locks["_percentile_map"]: self._percentile_map.pop(api.name, None) logger.info("New list: %s" % self._api_list.keys())
def __init__(self, api_list=[], config_filepath='config.ini'): """ Parameters ---------- api_list : list List of objects that are implementing the default class BaseThirdPartyAPIService. """ apimux_cfg = config.parse_config(config_filepath) self.PERCENTILE = apimux_cfg.getint("PERCENTILE") self.MAX_HISTORY_RTIME = apimux_cfg.getint("MAX_HISTORY_RTIME") self.MAX_WAIT_TIME = apimux_cfg.getint("MAX_WAIT_TIME") self._PERIODIC_CHECK_INTERVAL = apimux_cfg.getint("PERIODIC_CHECK") logger.debug("Initializing the APIMultiplexer class") # Locks used to prevent multi-threading issues self._locks = {} # ReadWriteLock allows multiple readers and only one writer self._locks["_api_list"] = ReadWriteLock() self._api_list = {} self._locks["_api_response_times"] = Lock() self._api_response_times = {} self._locks["_percentile_map"] = Lock() self._percentile_map = {} self._futures_not_finished = {} self._locks["_futures_not_finished"] = Lock() # Registering all APIs passed as parameters if len(api_list) > 0: for x in api_list: self.register_new_api(x) self._ignore_slow_apis = apimux_cfg.getboolean("ignore_slow_apis") self._slow_multiplied = apimux_cfg.getfloat("slow_multiplied") self._exploration_coefficient = apimux_cfg.getint( "exploration_coefficient") # Whether it should enable round robing or not self._round_robin = apimux_cfg.getboolean("round_robin") if self._round_robin: logger.info("Round robin enabled!") # Disable exploration if round robin is enabled self._exploration_coefficient = 0 elif self._exploration_coefficient > 0: logger.info("Exploration with percentage %s enabled!" % self._exploration_coefficient) self._current_order = [] self._locks["_current_order"] = Lock() if apimux_cfg.getboolean("enable_periodic_check"): # Starting a background thread which will run periodically # the 'check' method if implemented by the user for an API self._periodic_check_thread = Thread(target=self._periodic_check, args=()) self._periodic_check_thread.setDaemon(True) self._periodic_check_thread.start()
def register_new_api(self, api): """ Registers new API and adds it to the internal list. Parameters ---------- api : object The object implementing BaseThirdPartyAPIService class. """ logger.info("New API to register: %s" % api.name) self._locks["_api_list"].acquire_write() if self._api_list.get(api.name, None) is not None: raise Exception("API already exists in the list") self._api_list[api.name] = api self._locks["_api_list"].release_write() # All APIs start from 0 initially, this will be automatically # reconfigured based on the performance of the APIs. with self._locks["_api_response_times"]: self._api_response_times[api.name] = [] with self._locks["_percentile_map"]: self._percentile_map[api.name] = 0 logger.info("New list: %s" % self._api_list.keys())
api_list=[ GoogleTranslateWithContext(), GoogleTranslateWithoutContext(), MicrosoftTranslateWithContext(), MicrosoftTranslateWithoutContext(), ], config_filepath=os.environ.get("API_MUX_CONFIG__TRANSLATORS", ""), ) wordnik_api_keys = [] for env_var_name in os.environ: if env_var_name.startswith("WORDNIK_API_KEY"): wordnik_api_keys += [env_var_name] wordnik_translators = [WordnikTranslate(apikey) for apikey in wordnik_api_keys] a_b_testing_wordnik = len(wordnik_translators) > 1 logger.info("Number of wordnik api keys: %s" % len(wordnik_translators)) api_mux_worddefs = APIMultiplexer( api_list=wordnik_translators, config_filepath=os.environ.get("API_MUX_CONFIG__EN2EN", ""), ) def get_all_translations(data): if data["from_lang_code"] == data["to_lang_code"] == "en": # Wordnik case, get only the top result response = get_next_results(data, number_of_results=1) else: response = get_next_results(data, number_of_results=-1) logger.debug(f"Zeeguu-API - Request data: {data}") return response
return None return response api_mux_translators = APIMultiplexer(api_list=[ GoogleTranslateWithContext(), GoogleTranslateWithoutContext(), MicrosoftTranslateWithContext(), MicrosoftTranslateWithoutContext()], config_filepath=os.environ.get("API_MUX_CONFIG__TRANSLATORS", '')) wordnik_api_keys = [] for env_var_name in os.environ: if env_var_name.startswith('WORDNIK_API_KEY'): wordnik_api_keys += [env_var_name] wordnik_translators = [WordnikTranslate(apikey) for apikey in wordnik_api_keys] a_b_testing_wordnik = len(wordnik_translators) > 1 logger.info("Number of wordnik api keys: %s" % len(wordnik_translators)) api_mux_worddefs = APIMultiplexer( api_list=wordnik_translators, config_filepath=os.environ.get("API_MUX_CONFIG__EN2EN", '')) def get_all_translations(data): if data["from_lang_code"] == data["to_lang_code"] == "en": # Wordnik case, get only the top result response = get_next_results(data, number_of_results=1) else: response = get_next_results(data, number_of_results=-1) logger.debug(f"Zeeguu-API - Request data: {data}") return response