Уменьшение запросов get-state к api, добавлен метод get-state-finish
This commit is contained in:
@@ -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});
|
||||||
|
callback(response.data);
|
||||||
|
|
||||||
let i = 0;
|
//присылается текст, состоящий из json-объектов state каждые 300ms, с разделителем splitter между ними
|
||||||
while (1) {// eslint-disable-line no-constant-condition
|
const splitter = '-- aod2t5hDXU32bUFyqlFE next status --';
|
||||||
callback(response.data);
|
let lastIndex = 0;
|
||||||
|
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;
|
||||||
|
|
||||||
if (response.data.state == 'finish') {//воркер закончил работу, можно скачивать кешированный на сервере файл
|
//быстрее будет last.split
|
||||||
callback({step: 4});
|
const res = last.split(splitter).pop();
|
||||||
const book = await this.loadCachedBook(response.data.path, callback);
|
if (res) {
|
||||||
return Object.assign({}, response.data, {data: book.data});
|
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user