浏览代码

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

Book Pauk 5 年之前
父节点
当前提交
6faa7b2efe
共有 3 个文件被更改,包括 92 次插入24 次删除
  1. 36 24
      client/api/reader.js
  2. 55 0
      server/controllers/WorkerController.js
  3. 1 0
      server/routes.js

+ 36 - 24
client/api/reader.js

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

+ 55 - 0
server/controllers/WorkerController.js

@@ -1,5 +1,6 @@
 const BaseController = require('./BaseController');
 const WorkerState = require('../core/WorkerState');//singleton
+const utils = require('../core/utils');
 
 class WorkerController extends BaseController {
     constructor(config) {
@@ -15,6 +16,7 @@ class WorkerController extends BaseController {
                 throw new Error(`key 'workerId' is wrong`);
 
             const state = this.workerState.getState(request.workerId);
+
             return (state ? state : {});
         } catch (e) {
             error = e.message;
@@ -23,6 +25,59 @@ class WorkerController extends BaseController {
         res.status(400).send({error});
         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;

+ 1 - 0
server/routes.js

@@ -29,6 +29,7 @@ function initRoutes(app, config) {
         ['POST', '/api/reader/storage', reader.storage.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-finish', worker.getStateFinish.bind(worker), [aAll], {}],
     ];
 
     //to app