@givemoneybiatch
Немного веб, немного гейм

Почему событие 'data' срабатывает лишь после полной загрузки данных?

Задача состоит в том, чтобы при загрузке данных на сервере срабатывал некоторый триггер, который бы регистрировал сколько данных поступило за единицу времени. Т.е. клиент отправляет бинарные данные через POST. Как только данные (даже если частично) достигают сервера, он считает сколько байт пришло и уведомляет об этом другого клиента (например через вебсокеты).

Как сделать так, чтобы информация о поступлении данных на сервере появлялась в режиме реального времени (или максимально близко к этому)? Вебсокеты для отправки данных не подходят, только http.

Сделал следующим образом. Есть простая отправка данных POST-ом на сервер NodeJS. Я хочу начать получать данные на сервере как только они туда начнут поступать, как можно скорее, не дожидаясь всего тела запроса.

Для этого использую событие 'data' .
https://nodejs.org/uk/docs/guides/anatomy-of-an-ht...
https://nodejs.org/api/stream.html#event-data

Ожидаю, что как только запрос достигнет сервера, данные сразу же появятся в событии 'data'.
Проблема в том, что событие 'data' триггерится с задержкой и чем больше размер отправляемых данных, тем больше задержка. Например, если отправляю 1Мб, то задержки практически не видно, но если отправляю 50Мб, то задержка становится очень существенной (порядка 10секунд). Речь идет о реальном сервере, потому что на локалхосте это не так заметно.

Такое впечатление, что событие 'data' триггерится только после того как данные полностью поступили на сервер.
Не могу разобраться почему так происходит? Ниже простой пример, который наглядно показывает, что данные в 'data' на сервере появляются с задержкой.

Код серверной части.
spoiler
const http = require('http');

var firstByte = null;
var serverLog = {};
var server = http.createServer((request, response) => {
    const { headers, method, url } = request;
    let body = [];
    request.on('error', (err) => {
    }).on('data', (chunk) => {
        if (!firstByte) {
            firstByte = Date.now();
            serverLog.backendReceivedFirstData = firstByte;
        }
    }).on('end', () => {
        var end = Date.now();
        serverLog.backendReceivedAllData = end;

        if (url === '/') {
            response.statusCode = 200;
            response.setHeader('Content-Type', 'text/html');
            response.write('<h1>Hello World</h1>');
        } else {
            response.statusCode = 200;
            response.setHeader('Content-Type', 'application/json');
            response.write(JSON.stringify(serverLog));
        }

        firstByte = null;
        serverLog = {};
        response.end();
    });
    var requestReceived = Date.now();
    serverLog.backendRequestReceived = requestReceived;
});

server.listen(4000);


Код браузерной части:
spoiler
// send 50Mb
            var dataToSend = prepareDataBlob(50 * 1024 * 1024);
            var start = Date.now();

            fetch("http://localhost:4000/upload?a=" + Date.now(), {
                body: dataToSend,
                method: "post"
            }).then(async response => {
                const data = await response.json();
                console.log(`difference between start and server received a request: ${data.backendRequestReceived - start}`);
                console.log(`difference between start and first byte received on server: ${data.backendReceivedFirstData - start}`);
                console.log(`difference between start and all data received on server: ${data.backendReceivedAllData - start}`);
            });

function prepareDataBlob(fileSize) {
            const DEFAULT_UPLOAD_DATA = "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890";
            var data = '';
            var count = fileSize / DEFAULT_UPLOAD_DATA.length;
            for (var j = 0; j < count; j++) data += DEFAULT_UPLOAD_DATA;
            var dataBlob = new Blob([data], {
                type: 'application/octet-stream'
            });
            return data;
        }
  • Вопрос задан
  • 94 просмотра
Решения вопроса 1
Lynn
@Lynn
nginx, js, css
У вас точно никакого проксирования нет? Или может это браузер как-то умудряется чудить.

Я проверил на таком скрипте
const http = require('http');

const server = http.createServer((request, response) => {
    let size = 0;
    let n = 0;
    request.on('error', (err) => {
    }).on('data', (chunk) => {
        size += chunk.length;
        n++;
        console.log('chunk', n, size);
    }).on('end', () => {
        console.log('end', size);
        response.statusCode = 200;
        response.setHeader('Content-Type', 'text/plain');
        response.end('done\n');
    });
});

server.listen(4000);


$ truncate --size 1000000 data
$ curl localhost:4000 --data-binary @data


и вижу вот такой вывод:
chunk 1 32768
chunk 2 98304
chunk 3 163840
chunk 4 229376
chunk 5 294912
chunk 6 360448
chunk 7 425984
chunk 8 491520
chunk 9 557056
chunk 10 622592
chunk 11 688128
chunk 12 753664
chunk 13 819200
chunk 14 884736
chunk 15 950272
chunk 16 1000000
end 1000000
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы