http1.1和http2.0

729 阅读3分钟

http1.1

keep-alive

  • http请求都要经过3次握手,慢启动的算法,所以每次请求都要建立一个TCP连接的话,比较耗时,所以http1.1浏览器中,默认开启了connection:keep-alive
let request = require('request');
router.get('/http1', (req, res, next) => {
    request({
        method: 'GET',
        uri: 'http://xxx:8887/xxx'
    }, (error, response, body) => {
        console.log('response', response);
    });
});

请求如上图所示,在请求完成后,TCP连接就会断开

  • 如果我们使用kepp-alive,则需要如下所示,编写代码
request({
    method: 'GET',
    uri: 'http://xxx:8887/xxx',
    headers: {
        Connection: 'keep-alive'
    }
}, (error, response, body) => {
    console.log('response', response);
});

  • 但是上图所示写法是错误的,如果做实验的话,发现是没有办法实现TCP复用的 应该用forever: true这个设置
request({
    method: 'GET',
    uri: 'http://xxx:8887/xxx',
    forever: true // 这个很重要 开启keep-alive
}, (error, response, body) => {
    console.log('response', response);
});

这个连接保持时间,在nginx中有设置,默认为65s

router.get('/http1', (req, res, next) => {
    async function fn() {
        for (let i = 0; i < 10; i ++) {
            await new Promise((resolve, reject) => {
                request({
                    method: 'GET',
                    uri: 'http://xxx:8887/xxx',
                    time: true, // 配置这个属性可以看到时间信息
                    forever: true
                }, (error, response, body) => {
                    console.log('timingPhases', response.timingPhases);
                    resolve();
                });
            });
        }
        return 'success';
    }
    fn().then(()=>{
        res.json({
            msg: 'end'
        });
    });
});

如果串行请求的话,确实复用的同一个TCP连接

  • 但是如果并行发送多个请求呢
router.get('/http1con', (req, res, next) => {
    let promiseArr = [];
    for (let i = 0; i < 10; i ++) {
        let newP = new Promise((resolve, reject) => {
            request({
                method: 'GET',
                uri: 'http://xxx:8887/xxx',
                time: true,
                forever: true
            }, (error, response, body) => {
                resolve();
            });
        });
        promiseArr.push(newP);
    }
    Promise.all(promiseArr).then(() => {
        res.json({
            'msg': 'end'
        });
    });
});

http1.1在并行请求的时候,会建立多个连接,规定,浏览器对一个域名,最多同时可以建立6个连接。

如何让并行请求也用同一个TCP连接呢,就要涉及到http2.0的单一长连接了

http2.0

多路复用

  1. 在keep-alive中,请求必须要等上一请求结束完后,才可以复用这个TCP发出下一个请求,所以会收到前面请求的阻塞

  2. 使用pop-line可以同时发送多个请求,但是请求响应的顺序必须和请求发送的顺序一致,所以如果这个期间有某个响应延迟了,那么,后面的响应就算完成了,也要等阻塞的请求响应的返回,也就是线头阻塞

  3. http2.0的多路复用就解决了以上问题 http2的传输是基于二进制帧的,每个TCP连接中,都有多个双向流通的流,每个流都有独一无二的标识和优先级,而流就是由二进制帧组成的。二进制帧的头部会标识自己是属于哪一个流的,所以这些流可以交错传输,在接收端根据帧头组装成完整的信息,这就解决了线头传输的问题,也提高了网络的利用率

带来的好处:http性能优化的点不在于高带宽而在于低延迟,比如慢启动,就会让原来具有突发性和短时性的http连接变得十分低效,http2通过让所有请求共用一个连接,可以更有效的使用TCP连接,让高带宽真正的服务于http的性能提升

压缩头部

如果一个页面有100个请求,而每一个请求的头部可能会达到1k(因为有cookie和引用),那这样只是头部的传输就会达到100kb,http2.0可以维护一个头部字典,可以差异化更新头部,大大减少因头部传输产生的流量