Javascript.RU

Создать новую тему Ответ
 
Опции темы Искать в теме
  #1 (permalink)  
Старый 03.10.2017, 13:46
Кандидат Javascript-наук
Отправить личное сообщение для Signal Посмотреть профиль Найти все сообщения от Signal
 
Регистрация: 03.05.2016
Сообщений: 137

Помогите с алгоритмом
собственно задача, есть некий сайт с которым работаю по API
разрешено делать не более 8 запросов в секунду
есть у меня несколько API ключей

вот никак не соображу как делать запросы по каждому API ключу и так, чтобы не более 8 в секунду
загрузка работает скачалось и забылось

база для запросов содержит около 10 тысяч записей

сейчас это реализовано тупо формирую блоки по API.length+(API.length*8) но бывает так, что 1 API уже отработал 8 запросов и ждет пока отработает весь блок

помогите с алгоритмом, что-то уже башка не соображает
заранее благодарен вкусным морковным соком
Ответить с цитированием
  #2 (permalink)  
Старый 03.10.2017, 14:53
Аватар для Alexandroppolus
Профессор
Отправить личное сообщение для Alexandroppolus Посмотреть профиль Найти все сообщения от Alexandroppolus
 
Регистрация: 25.10.2016
Сообщений: 1,005

Сообщение от Signal
база для запросов содержит около 10 тысяч записей
Тебе надо сделать 10000 запросов, ограничение не более 8 в секунду на один ключ, у тебя есть N ключей, и надо придумать как организовать запросы? я правильно понял?
Ответить с цитированием
  #3 (permalink)  
Старый 03.10.2017, 15:13
Кандидат Javascript-наук
Отправить личное сообщение для Signal Посмотреть профиль Найти все сообщения от Signal
 
Регистрация: 03.05.2016
Сообщений: 137

Сообщение от Alexandroppolus Посмотреть сообщение
Тебе надо сделать 10000 запросов, ограничение не более 8 в секунду на один ключ, у тебя есть N ключей, и надо придумать как организовать запросы? я правильно понял?
да, все правильно
Ответить с цитированием
  #4 (permalink)  
Старый 03.10.2017, 16:36
Аватар для Alexandroppolus
Профессор
Отправить личное сообщение для Alexandroppolus Посмотреть профиль Найти все сообщения от Alexandroppolus
 
Регистрация: 25.10.2016
Сообщений: 1,005

Signal,
какие параметры передаются в каждый запрос? эти параметры уже есть в коде? или они приходят от предыдущих запросов?

можно ли слать запросы одновременно (в пределах ограничений)? т.е. можно ли просто взять и отправить с одного ключа сразу 8 запросов?
Ответить с цитированием
  #5 (permalink)  
Старый 03.10.2017, 17:08
Кандидат Javascript-наук
Отправить личное сообщение для Signal Посмотреть профиль Найти все сообщения от Signal
 
Регистрация: 03.05.2016
Сообщений: 137

да с каждого ключа 8 запросов в секунду можно делать (одновременно)
посылается только 1 параметр в запросе
Ответить с цитированием
  #6 (permalink)  
Старый 04.10.2017, 13:08
Аватар для Alexandroppolus
Профессор
Отправить личное сообщение для Alexandroppolus Посмотреть профиль Найти все сообщения от Alexandroppolus
 
Регистрация: 25.10.2016
Сообщений: 1,005

Навскидку видится такой вариант (сотворено на коленке, не дебажил):

function request(key, param, callback) { ... } // делает запрос в API

function APIClient(key, data) {
	this._key = key;
	this._data = data;
	this.nextTime = 0;
	this.loading = false;
	this._finish = this._finish.bind(this);
}

APIClient.prototype.start = function() {
	if (this._data.params.length) {
		var param = this._data.params.pop();
		request(this._key, param, this._finish);
		this._data.requests++;
		this.loading = true;
		this.param = param;
		this.nextTime = Date.now() + 1000; // следующий запрос через секунду, не ранее
	}
};
APIClient.prototype._finish = function(error) {
	if (error) {
		// запрос был с ошибкой, будем пробовать ещё раз для этого параметра
		this._data.params.push(this.param);
	}
	this.loading = false;
	this.param = null;
	this._data.requests--;
	if (!this._data.requests && !this._data.params.length && this._data.timer) {
		clearInterval(this._data.timer);
		this._data.timer = null;
		this._data.callback();
	}
};

function loadAll(keys, limit, params, callback) {
	var data = {
		params: params,
		requests: 0,
		timer: null,
		callback: callback
	};
	var clients = [];
	for (var i = 0; i < keys.length; ++i) {
		for (var j = 0; j < limit; ++j) {
			clients.push(new APIClient(keys[i], data));
		}
	}
	data.timer = setInterval(function () {
		var time = Date.now();
		for (var i = 0; i < clients.length; ++i) {
			var client = clients[i];
			if (time >= client.nextTime && !client.loading) {
				client.start();
			}
		}
	}, 300);
}

// запуск
loadAll([...], 8, [...], function() {
    ...
});


для каждого ключа создаем по 8 клиентов, все складываем в массив. Каждый клиент делает запрос не чаще раза в секунду. Отмечает время старта запроса (точнее, время следующего старта). И есть один общий таймер, который проверяет всех клиентов и стартует тех, которые уже успели "отдохнуть". Последний клиент, завершившись, уничтожает таймер и вызывает общий колбэк.

при такой схеме никто никого не ждет.

если требуется минутный интервал между финишем предыдущего запроса и стартом нового, то установку nextTime перенеси в _finish

Последний раз редактировалось Alexandroppolus, 04.10.2017 в 13:32.
Ответить с цитированием
  #7 (permalink)  
Старый 06.10.2017, 14:13
Аватар для EmperioAf
Профессор
Отправить личное сообщение для EmperioAf Посмотреть профиль Найти все сообщения от EmperioAf
 
Регистрация: 15.01.2015
Сообщений: 622

const queue = require('async').queue;
const request = require('request');

/** Class control free keys. */
function ApiKeys(apiKeys) {
    this.apiKeys = apiKeys;
    this.locked = {};
}

/**
 * Get a free key and lock it
 * @return {string} Key.
 */
ApiKeys.prototype.getKey = function () {
    for (const key of this.apiKeys) {
        if (!this.locked[key]) {
            this.locked[key] = true;
            return key;
        }
    }
}

/**
 * Method free key for next request
 * @param {string} key - Locked key.
 */
ApiKeys.prototype.freeKey = function (key) {
    this.locked[key] = false;
}

const API_KEYS = ['key1', 'key2', 'key3', 'key4', 'key5', 'key6', 'ke7', 'key8'];
const MAX_WORKERS_IN_QUEUE = API_KEYS.length;

const apiKeys = new ApiKeys(API_KEYS);


const requestQueue = queue(function (task, callback) {
    const key = apiKeys.getKey();
    task.options.url += '&key=' + key;
    request(task.options, (err, res, body) => {
        apiKeys.freeKey(key);
        callback(err, res, body);
    })
}, MAX_WORKERS_IN_QUEUE);


const requestUrls = [];
for (const url of requestUrls) {
    const options = {
        url: url,
        method: 'GET'
    };
    requestQueue.push({ options: options }, function (err, res, body) {
        // process response here
    });
}
requestQueue.drain = function () {
    console.log('all items have been processed');
};

Последний раз редактировалось EmperioAf, 06.10.2017 в 14:23.
Ответить с цитированием
  #8 (permalink)  
Старый 09.10.2017, 17:03
Кандидат Javascript-наук
Отправить личное сообщение для Signal Посмотреть профиль Найти все сообщения от Signal
 
Регистрация: 03.05.2016
Сообщений: 137

Alexandroppolus,
Благодарствую!!! работает, с меня вкусный морковный сок!!!

EmperioAf,
Тоже благодарю попробую и твой вариант, таких задач приходится делать много разных по ходу работы, тоже его опробую.
Ответить с цитированием
Ответ



Опции темы Искать в теме
Искать в теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Помогите с алгоритмом скрипта обработки select-ов shelby1992 Элементы интерфейса 5 05.06.2016 22:08
Помогите разобраться с алгоритмом argab Общие вопросы Javascript 11 21.11.2015 15:00
Помогите разобраться с алгоритмом паралакса nesfiraty Общие вопросы Javascript 1 23.09.2014 20:46
Помогите с алгоритмом OlegALL Ваши сайты и скрипты 4 18.07.2011 01:34
Помогите! Многоуровневые вкладки! sergeeeeee Элементы интерфейса 2 02.08.2010 23:50