Back Pressure on fetch() не работает в Google Chrome

У меня возникла проблема с ответом от моего сервера WebFlux с помощью нового API Streams.

Я вижу через Curl (с помощью --limit-rate), что сервер замедляется, как ожидалось, но когда я пытаюсь уничтожить тело в Google Chrome (64.0.3282.140), он не замедляется, как он должен. На самом деле Chrome загружает и буферизирует около 32 мегабайт с сервера, хотя только около 187 кБ передается на write().

Что-то не так с моим JavaScript?

async function fetchStream(url, consumer) {
    const response = await fetch(url, {
        headers: {
            "Accept": "application/stream+json"
        }
    });
    const decoder = new TextDecoder("utf-8");
    let buffer = "";
    await response.body.pipeTo(new WritableStream({
        async write(chunk) {
            buffer += decoder.decode(chunk);
            const blocks = buffer.split("\n");
            if (blocks.length === 1) {
                return;
            }
            const indexOfLastBlock = blocks.length - 1;
            for (let index = 0; index < indexOfLastBlock; index ++) {
                const block = blocks[index];
                const item = JSON.parse(block);
                await consumer(item);
            }
            buffer = blocks[indexOfLastBlock];
        }
    }));
}

В соответствии с спецификация для потоков,

Если стратегия не указана, поведение по умолчанию будет таким же, как и CountQueuingStrategy с отметкой высокой воды 1.

Так что это должно замедлить обещание, возвращаемое consumer(item), разрешается очень медленно, правильно?

Ответ 1

Глядя на поддержку противодавления в API Streams, кажется, что информация о противодавлении передается в цепочке потоков, а не по сети. В этом случае мы можем предполагать неограниченную очередь где-то, и это объясняет поведение, которое вы видите.

Этот другой вопрос github предполагает, что информация о противодавлении действительно останавливается на уровне TCP - они просто прекращают чтение из сокета TCP, который, в зависимости от в текущем размере TCP-TCP/конфигурации TCP означает, что буферы будут заполнены, а затем будет выполняться управление потоком TCP. Как говорится в этой проблеме, они не могут установить размер окна вручную, и они должны позволить TCP-стекю обрабатывать вещи оттуда.

HTTP/2 поддерживает управление потоками на уровне протокола, но я не знаю, используют ли реализации браузера это с помощью API Streams.

Я не могу объяснить разницу в поведении, которую вы видите, но я думаю, что вы, возможно, слишком много читаете в поддержке Backpressure, и это работает как ожидается в соответствии со спецификацией.