Помогите с алгоритмом
собственно задача, есть некий сайт с которым работаю по 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, время: 02:22. |