Уменьшение запросов get-state к api, добавлен метод get-state-finish

This commit is contained in:
Book Pauk
2020-01-12 18:51:12 +07:00
parent f8481413c9
commit 6faa7b2efe
3 changed files with 93 additions and 25 deletions

View File

@@ -1,7 +1,5 @@
import axios from 'axios'; import axios from 'axios';
import * as utils from '../share/utils';
const api = axios.create({ const api = axios.create({
baseURL: '/api/reader' baseURL: '/api/reader'
}); });
@@ -12,7 +10,6 @@ const workerApi = axios.create({
class Reader { class Reader {
async loadBook(opts, callback) { async loadBook(opts, callback) {
const refreshPause = 300;
if (!callback) callback = () => {}; if (!callback) callback = () => {};
let response = await api.post('/load-book', opts); let response = await api.post('/load-book', opts);
@@ -22,37 +19,52 @@ class Reader {
throw new Error('Неверный ответ api'); throw new Error('Неверный ответ api');
callback({totalSteps: 4}); callback({totalSteps: 4});
let i = 0;
while (1) {// eslint-disable-line no-constant-condition
callback(response.data); callback(response.data);
if (response.data.state == 'finish') {//воркер закончил работу, можно скачивать кешированный на сервере файл //присылается текст, состоящий из json-объектов state каждые 300ms, с разделителем splitter между ними
callback({step: 4}); const splitter = '-- aod2t5hDXU32bUFyqlFE next status --';
const book = await this.loadCachedBook(response.data.path, callback); let lastIndex = 0;
return Object.assign({}, response.data, {data: book.data}); response = await workerApi.post('/get-state-finish', {workerId}, {
onDownloadProgress: progress => {
//небольая оптимизация, вместо простого responseText.split
const xhr = progress.target;
let currIndex = xhr.responseText.length;
if (lastIndex == currIndex)
return;
const last = xhr.responseText.substring(lastIndex, currIndex);
lastIndex = currIndex;
//быстрее будет last.split
const res = last.split(splitter).pop();
if (res) {
callback(JSON.parse(res));
} }
if (response.data.state == 'error') { }
let errMes = response.data.error; });
//берем последний state
response = response.data.split(splitter).pop();
if (response) {
response = JSON.parse(response);
if (response.state == 'finish') {//воркер закончил работу, можно скачивать кешированный на сервере файл
callback({step: 4});
const book = await this.loadCachedBook(response.path, callback);
return Object.assign({}, response, {data: book.data});
}
if (response.state == 'error') {
let errMes = response.error;
if (errMes.indexOf('getaddrinfo') >= 0 || if (errMes.indexOf('getaddrinfo') >= 0 ||
errMes.indexOf('ECONNRESET') >= 0 || errMes.indexOf('ECONNRESET') >= 0 ||
errMes.indexOf('EINVAL') >= 0 || errMes.indexOf('EINVAL') >= 0 ||
errMes.indexOf('404') >= 0) errMes.indexOf('404') >= 0)
errMes = `Ресурс не найден по адресу: ${response.data.url}`; errMes = `Ресурс не найден по адресу: ${response.url}`;
throw new Error(errMes); throw new Error(errMes);
} }
if (i > 0) } else {
await utils.sleep(refreshPause); throw new Error('Пустой ответ сервера');
i++;
if (i > 120*1000/refreshPause) {//2 мин ждем телодвижений воркера
throw new Error('Слишком долгое время ожидания');
}
//проверка воркера
const prevProgress = response.data.progress;
const prevState = response.data.state;
response = await workerApi.post('/get-state', {workerId});
i = (prevProgress != response.data.progress || prevState != response.data.state ? 1 : i);
} }
} }

View File

@@ -1,5 +1,6 @@
const BaseController = require('./BaseController'); const BaseController = require('./BaseController');
const WorkerState = require('../core/WorkerState');//singleton const WorkerState = require('../core/WorkerState');//singleton
const utils = require('../core/utils');
class WorkerController extends BaseController { class WorkerController extends BaseController {
constructor(config) { constructor(config) {
@@ -15,6 +16,7 @@ class WorkerController extends BaseController {
throw new Error(`key 'workerId' is wrong`); throw new Error(`key 'workerId' is wrong`);
const state = this.workerState.getState(request.workerId); const state = this.workerState.getState(request.workerId);
return (state ? state : {}); return (state ? state : {});
} catch (e) { } catch (e) {
error = e.message; error = e.message;
@@ -23,6 +25,59 @@ class WorkerController extends BaseController {
res.status(400).send({error}); res.status(400).send({error});
return false; return false;
} }
async getStateFinish(req, res) {
const request = req.body;
let error = '';
try {
if (!request.workerId)
throw new Error(`key 'workerId' is wrong`);
res.writeHead(200, {
'Content-Type': 'text/json; charset=utf-8',
});
const splitter = '-- aod2t5hDXU32bUFyqlFE next status --';
const refreshPause = 300;
let i = 0;
let prevProgress = -1;
let prevState = '';
let state;
while (1) {// eslint-disable-line no-constant-condition
state = this.workerState.getState(request.workerId);
if (!state) break;
res.write(splitter + JSON.stringify(state));
res.flush();
if (state.state != 'finish')
await utils.sleep(refreshPause);
else
break;
i++;
if (i > 2*60*1000/refreshPause) {//2 мин ждем телодвижений воркера
res.write(splitter + JSON.stringify({state: 'error', error: 'Слишком долгое время ожидания'}));
break;
}
i = (prevProgress != state.progress || prevState != state.state ? 1 : i);
prevProgress = state.progress;
prevState = state.state;
}
if (!state) {
res.write(splitter + JSON.stringify({}));
}
res.end();
return false;
} catch (e) {
error = e.message;
}
//bad request
res.status(400).send({error});
return false;
}
} }
module.exports = WorkerController; module.exports = WorkerController;

View File

@@ -29,6 +29,7 @@ function initRoutes(app, config) {
['POST', '/api/reader/storage', reader.storage.bind(reader), [aAll], {}], ['POST', '/api/reader/storage', reader.storage.bind(reader), [aAll], {}],
['POST', '/api/reader/upload-file', [upload.single('file'), reader.uploadFile.bind(reader)], [aAll], {}], ['POST', '/api/reader/upload-file', [upload.single('file'), reader.uploadFile.bind(reader)], [aAll], {}],
['POST', '/api/worker/get-state', worker.getState.bind(worker), [aAll], {}], ['POST', '/api/worker/get-state', worker.getState.bind(worker), [aAll], {}],
['POST', '/api/worker/get-state-finish', worker.getStateFinish.bind(worker), [aAll], {}],
]; ];
//to app //to app