Разница между опросом с использованием ajax-запроса и веб-сокетов - PullRequest
0 голосов
/ 12 октября 2018

Я пытаюсь непрерывно передавать потоковое изображение в моем приложении фляги с моего сервера на клиент (веб-приложение).Я могу успешно транслировать вывод, используя, открыв ajax-запрос и оставив его открытым следующим образом:

//Open up a stream
let url = '/stream';
xhr.open('GET', url);
xhr.send(fd);

//Display it like this
data = xhr.responseText;
$(".myTextArea").val(data);

Я могу закрыть этот ajax-запрос, выполнив это (но я оставляю его открытым):

xhr.readyState == XMLHttpRequest.DONE
//do stuff

Но я также могу использовать веб-сокеты (flask_socketio) для непрерывного опроса потока вывода сервера клиенту.

Являются ли эти два метода опроса одинаковыми с точки зрения производительности?

Вот как я собираю поток AJAX с удаленного сервера:

let streamTimer;
function stream(){
    let xhr;
    let streamData;
    let lastStreamData;
    xhr = new XMLHttpRequest();
    const url = '/stream';
    xhr.open('GET', url);
    xhr.send();
    let streamTimer = setInterval(function() {
        streamData = xhr.responseText;
        if (lastStreamData !== streamData) {
            console.log(streamData)
            lastStreamData = streamData;
        }
    }, 1000);
}

Мои вызовы ajaxмой маршрут фляги stream(), который в основном ведет журнал с опцией -f, таким образом, он будет держать соединение открытым.Если в журнал есть вывод, он отправит его обратно на мой вызов ajax и отобразит на веб-странице.

def stream():
    def generate():
        if request.method == "POST":
            hostname = request.data.decode('utf-8')

            # ssh set up
            client = set_up_client()

            # Connect to hostname
            client.connect(hostname, username=USERNAME,
                           password=PASSWORD)

            cmd = ('tail -f -n0 /home/server.log')

            stdin, stdout, stderr = client.exec_command(cmd)

            for line in iter(lambda: stdout.readline(2048), ""):
                print(line, end="")
                yield line
                if re.search(r'keyword1|keyword2', line):
                    print('change detected')
                    yield 'change detected\n'

    return Response(stream_with_context(generate()), mimetype='text/html')

Ответы [ 2 ]

0 голосов
/ 03 ноября 2018

Являются ли эти два метода опроса одинаковыми с точки зрения производительности?

Нет, они не одинаковы.Возможно, вы не отправляете запрос на сервер каждые X миллисекунд, но вы опрашиваете XMLHttpRequest#responseText геттер каждые X миллисекунд, что является непроизводительным расходом независимо от того, как вы на это смотрите.

Хорошей новостью является то, что вам вообще не нужен интервал, потому что обработчик события XMLHttpRequest#onprogress вызывается каждый раз, когда данные отправляются с сервера.Просто переместите содержимое вашего интервала в обработчик событий onprogress для запроса, и все готово.

let data;
const request = new XMLHttpRequest();
request.open('GET', '/stream');
request.onprogress = function() {
    if(data === request.response) return;
    data = request.response;
    console.log(data);
};
request.send();

В этом примере опрос не выполняется.Клиент просто обрабатывает события progress, соответственно обновляя целевой элемент.Если какое-то время обновления не поступают, мы не запускаем интервал каждую секунду, просто чтобы проверить, так что никакого вреда не будет.

Является ли это такой же производительностью, как у сокета?У меня нет ответа на этот вопрос, но я готов поспорить, что это чертовски близко.Достаточно близко, чтобы я мог сказать «достаточно похоже».

Теперь я вижу одно явное различие между этим методом и сокетом в том, что сокеты обеспечивают двустороннюю связь, тогда как я не смог реплицировать отправку данных насервер более одного раза, используя один и тот же запрос.Это может быть возможно, но я еще не понял.


Дальнейшая абстракция

Вы можете абстрагировать это, реализовав класс, расширяющий XMLHttpRequest вот так:

class ChunkedResponseRequest extends XMLHttpRequest {
    constructor(...args) {
        super(...args);
        this.data = null;
        this.addEventListener('progress', _ => {
            if(this.data === this.response) return;
            const chunk = this.data === null ? this.response 
                                             : this.response.substr(this.data.length);
            this.data = this.response;
            this.dispatchEvent(new MessageEvent('message', { data: chunk }));
        });
    }
}

Тогда вы можете использовать это так:

const request = new ChunkedResponseRequest();
request.addEventListener('message', function({ data }) {
    console.log(data);
});
request.open('GET', '/stream');
request.send();

Значение свойства data события сообщения будет последним ответом от сервера.


Реализация фрагментированного ответа

Для тех, кто хотел бы воспроизвести это поведение с помощью Node.js (лично я не работаю с Python или Flask)Вот краткий пример сервера, который отвечает бесконечным количеством фрагментов с интервалом в одну секунду:

const http = require('http')
const requestHandler = (request, response) => {
    response.setHeader('Content-Type', 'text/html; charset=UTF-8');
    response.setHeader('Access-Control-Allow-Origin', '*'); // so we can access locally
    response.setHeader('Transfer-Encoding', 'chunked');
    (function send(i, delay) {
        setTimeout(_ => {
            response.write(i + '\r\n');
            send(i + 1, 1000);
        }, delay)
    })(0, 0);
}
const server = http.createServer(requestHandler)
server.listen(3000, (error) => error && console.log(error));

Убедитесь, что для URL XMLHttpRequest в коде на стороне клиента установлено значение http://localhost:3000где 3000 - это порт, который прослушивает сервер.Очевидно, что если у вас уже есть что-то, прослушивающее порт 3000, вам нужно выбрать неиспользуемый порт.

0 голосов
/ 12 октября 2018

Нет, для опроса ajax вы должны постоянно подключаться к серверу, чтобы получить данные, но в случае веб-сокета вы можете подключиться к вам с помощью сервера двусторонней связи.Если что-то есть, он напрямую отправит вам данные.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...