使用Ajax请求和WebSocket进行轮询之间的区别

时间:2018-10-11 23:38:28

标签: ajax python-3.x flask websocket socket.io

我正在尝试将我的flask应用程序上的输出从服务器连续流式传输到客户端(webapp)。通过打开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

但是我也可以使用websockets(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 :(得分:1)

  

这两种轮询方法在性能方面是否相同?

不,他们不一样。您可能不会每隔X毫秒就向服务器发送一个请求,但是您正在 每隔X毫秒对XMLHttpRequest#responseText吸气剂进行轮询,无论您如何看,这都是开销。

好消息是,您根本不需要时间间隔,因为每次从服务器发送数据时都会调用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事件。如果一段时间内未收到任何更新,则我们不会每秒检查一次间隔,因此不会造成任何危害。

this 与套接字的性能相同吗?我没有答案,但是我敢打赌,这还算不错。足够接近,我会说“合理地相似”。

现在,我在此方法和套接字之间看到的一个明显区别是,套接字支持双向通信,而我使用同一请求无法多次向服务器复制发送数据。有可能,但是我还没有弄清楚。


进一步的抽象

您可以通过实现扩展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),这是一个服务器的快速示例,该服务器以无数个间隔为一秒的块进行响应:< / p>

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));

确保将客户端代码中的XMLHttpRequest的URL设置为http://localhost:3000,其中3000是服务器正在侦听的端口。显然,如果您已经在端口3000上监听了某些内容,则需要选择一个未使用的端口。

答案 1 :(得分:-1)

否,对于ajax轮询,您必须连续命中服务器才能获取数据,但是如果使用Web套接字,则通信服务器也可以通过两种方式与您连接。 如果有,它将直接向您发送数据。