Помогите с алгоритмом
собственно задача, есть некий сайт с которым работаю по API
разрешено делать не более 8 запросов в секунду есть у меня несколько API ключей вот никак не соображу как делать запросы по каждому API ключу и так, чтобы не более 8 в секунду загрузка работает скачалось и забылось база для запросов содержит около 10 тысяч записей сейчас это реализовано тупо формирую блоки по API.length+(API.length*8) но бывает так, что 1 API уже отработал 8 запросов и ждет пока отработает весь блок помогите с алгоритмом, что-то уже башка не соображает заранее благодарен вкусным морковным соком |
Цитата:
|
Цитата:
|
Signal,
какие параметры передаются в каждый запрос? эти параметры уже есть в коде? или они приходят от предыдущих запросов? можно ли слать запросы одновременно (в пределах ограничений)? т.е. можно ли просто взять и отправить с одного ключа сразу 8 запросов? |
да с каждого ключа 8 запросов в секунду можно делать (одновременно)
посылается только 1 параметр в запросе |
Навскидку видится такой вариант (сотворено на коленке, не дебажил):
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 |
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');
};
|
Alexandroppolus,
Благодарствую!!! работает, с меня вкусный морковный сок!!! EmperioAf, Тоже благодарю попробую и твой вариант, таких задач приходится делать много разных по ходу работы, тоже его опробую. |
| Часовой пояс GMT +3, время: 08:27. |