Description
自2013年HTTP/2推出以来,HTTP/2已经得到了长足发展,浏览器平台基本提供了HTTP/2的支持,知乎,豆瓣等国内大型平台也已经部分切换到HTTP/2了。
HTTP/2优点
1.多路复用请求
2.对请求划分优先级
3.压缩HTTP头
4.服务器推送流
HTTP/2支持情况:
chrome,firefox,ie11(win10版)都已经支持
详情可查看:https://caniuse.com/#search=http2
HTTP/2缓存情况:
HTTP/2并不像我们所在网上搜索到的那样不能缓存,只是需要在服务器端配置缓存,如:"expires":"Tue, 09 Oct 2018 11:27:20 GMT"。
测试方法:
这里我们使用node.js自己写前后端测试
在地图中应用时,加载图片有两种方式,一种是直接在img实例上设置url,让浏览器自己去触发访问图片,另一种是通过ajax加载(xhr.responseType = 'blob';,因为ajax请求过来的都是二进制字符串,所以要解析,后端读取文件和返回文件内容时同样要设置参数为binary),请求的结果通过img.src = window.URL.createObjectURL(data);解析成图片url。
根据两种加载方式,我构造了四组测试代码,并分别选取了每个图片大小为237KB和2KB的大小两组图片进行测试:
- HTTP/2&&AJAX加载
- HTTP/2&&浏览器加载
- http1.1&&AJAX加载
- http1.1&&浏览器加载
其它注意事项
使用浏览器访问的时候一定要用https协议访问,http协议访问不到
chrome允许本地https,进入chrome://flags/#allow-insecure-localhost设置其值为enabled
chrome里network内列标题上右键可打开protocol列查看请求的协议是不是h2
初次加载时会自动加载favicon.ico文件,可能会导致出错
代码实现
虽然http/2标准支持在http和https两种请求下使用,localhost-cert.pem,localhost-privkey.pem,但浏览器实际实现时只实现了基于https的,所以测试时需要自己生成证书
index.js
const http2 = require('http2');
const fs = require('fs');
const server = http2.createSecureServer({
key: fs.readFileSync('localhost-privkey.pem'),
cert: fs.readFileSync('localhost-cert.pem')
});
server.on('error', (err) => console.error(err));
server.on('socketError', (err) => console.error(err));
server.on('request', (request, response) => {
console.log('./images'+request.url);
//格式必须为 binary 否则会出错'./images'+request.url
var content = fs.readFileSync('./images'+request.url,"binary");
response.writeHead(200, {
"Content-Type": "image/png",
"expires":"Tue, 09 Oct 2018 11:27:20 GMT",
"Access-Control-Allow-Origin": "http://localhost:8080"
});
response.write(content,"binary"); //格式必须为 binary,否则会出错
response.end();
});
server.listen(8443);
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>HTTP/2测试</title>
<script src="./http.js"></script>
</head>
<body>
123
<div id="imgContainer"></div>
</body>
<script>
for(var i=1; i<16; i++){
// GET('https://localhost:8443/'+i+'.png',function(response){
// drawImg(response);
// });
// GET('http://localhost:8080/http2test/images/'+i+'.png',function(response){
// drawImg(response);
// });
// var myImage = new Image();
// myImage.src = 'https://localhost:8443/'+i+'.png';
// document.body.appendChild(myImage);
var myImage = new Image();
myImage.src = 'http://localhost:8080/http2test/images/'+i+'.png';
document.body.appendChild(myImage);
}
function drawImg(data){
var img = new Image();
img.src = window.URL.createObjectURL(data);
img.onload = function(){
window.URL.revokeObjectURL(img.src);
}
document.body.appendChild(img);
}
</script>
</html>
http.js
/**
* 发起异步请求的库
*/
function GET(url,callback){
var xhr = new XMLHttpRequest();
xhr.open('GET',url,true);
xhr.responseType = 'blob';
xhr.onload = function(){
if(xhr.readyState === 4 && xhr.status >= 200 && xhr.status <300 && xhr.response){
callback(xhr.response);
}
}
xhr.send();
}
rename.js(用于快速生成大量不同名的相同文件)
const fs = require('fs');
var path = './images';
var dirs = fs.readdirSync(path);
var n = 0;
for(let i in dirs){
var filename = dirs[i].split('.');
var type = filename[filename.length-1];
if(type=='png'){
fs.renameSync(path+'/'+dirs[i],path+'/'+n+'.png');
n +=1;
}else{
fs.unlinkSync(path+'/'+dirs[i]);
}
console.log('处理完成:'+dirs[i]);
}
完整代码地址:
可以直接跑起来的http/2与http/1.1的对比测试
测试结果:
因为本地加载实在太快,为尽量模拟生产环境网络情况,以下测试都是使用Chrome的fast3G模式。
类型 | 请求方式 | 大(237kb)小(2kb)文件 | 文件数 | 加载时间 |
---|---|---|---|---|
HTTP/2 | 浏览器加载 | 小 | 40 | 2.81s |
HTTP/1.1 | 浏览器加载 | 小 | 40 | 5.89s |
HTTP/2 | AJAX | 小 | 40 | 2.49s |
HTTP/1.1 | AJAX | 小 | 40 | 5.82s |
HTTP/2 | 浏览器加载 | 大 | 40 | 52.59s |
HTTP/1.1 | 浏览器加载 | 大 | 40 | 52.57s |
HTTP/2 | AJAX | 大 | 40 | 52.66s |
HTTP/1.1 | AJAX | 大 | 40 | 52.58s |
由此可见 :
HTTP/2的优势主要体现在大量小图片的加载上,对于少量大图片的加载并不占太大优势,并且,网络条件如果非常好的时候,由于加载非常快,HTTP/2也不能体现出优势,但是由于头部压缩和多路复用确实是执行了的,所以传输的总数据量理论上应该是减少了的。
HTTP/2在WebGIS等地图应用上的设想:
WebGIS以瓦片技术得以普及,瓦片即请求和文件,每加载一个瓦片都需要请求一次服务器上的图片或其它格式的文件。地图瓦片大多为40kb左右的png文件,且一次加载数量都在20左右,由于这种传输模式没有取消机制,所以,如果快速拖动地图由于未能及时传输会积累更多请求。二维瓦片请求数量如此庞大,三维请求数量更甚。由于http1.1本身的限制,浏览器只能同时加载6个文件(不同的浏览器策略额细微的差别),当地图浏览窗口移动时,前端会同时发送大量AJAX请求来加载瓦片。 此时浏览器就会受到阻塞,需要先等待队列前面的文件加载完成才能加载后面的文件。
HTTP/2的并发特性正好能解决此问题,也正好能对应这种场景。
高德地图部分使用了HTTP/2
谷歌地图全部使用了HTTP/2
百度地图还没有使用HTTP/2
mapbox没有使用HTTP/2
目前唯一的问题是需要统计ie11的市场占用率,若占用率还是太高切换之后处理起来是个麻烦事。
更新
webgis应用场景下chrome浏览器市场占有率最高,ie几乎绝迹,可以暂时不考虑ie的问题,而且ie也在切换为chiume内核。所以我搭建了环境弄了套三维数据开始测试。
实验证明三维框架上加载3dtile时http/2与http1.1相比效率几乎没有提升。所以开始根据猜测排查问题:
1.生成的ssh证书不合法,不被chrome信任,所以http/2实际是失效的。
2.换用mkcert工具生成可被chrome信任的合法证书后问题还在。怀疑是三维框架与二维框架资源加载机制和算法不太一样,这不是http/2慢不慢的问题,而是前端有没有向后端请求这个资源的问题。
3.搭建了二维框架的测试环境测试后仍然没效果。再从网上下载一批影像数据并搭建二维地图测试环境研究后觉得不是框架问题,应该是chrome浏览器又做了优化,去掉了http1.1的单域名下请求数量限制。
4.把单次瓦片请求量增大,仔细观察chrome的network请求时间信息后发现也不是chrome去掉了限制。很可能是本地测试时网络比较好的原因,虽然通过浏览器我们可以限制网速,但我们无法限制网络使它需要通过多个交换机,路由器,来增加数据传输和tcp连接握手的时间。简单讲就是http/2解决的就是tcp通过很多路由器实现多次握手造成的无用时间开销,但在本地测试时这些时间开销几乎是忽略不计的,而且由于http/2是基于https的,它还有一个加密的时间开销,所以就比http1.1慢了那么几毫秒。
5.租了个阿里云来验证是不是本地网络的问题。实践证明,在阿里云上部署好后从我的笔记本上访问地图时http/2和http1.1的速度还是相差无几。难道单个图片还是太大了,不够小么?
6.翻看了以前的测试数据,拿小数据集测试的时候小的图片都只有几kb,现在每一张图都有上百kb,在之前的测试里面它应该算是大的图片了。重新编写了测试,发现在同时加载40张240kb大图时http/2与http1.1差异不明显,同时加载40个只有4kb的小图时http/2速度差不多是http1.1的两倍,如果换到400个小图同时并发时http/2的速度差不多是http1.1的4倍。由此可见是单个资源大小导致http/2和http1.1的差异不明显。
在webgis中,一个影像瓦片至少有100kb,如果是mvt的矢量瓦片的话,小的倒是可以小到几kb,所以当请求的瓦片数量一致时,http/2应用在矢量瓦片传输上应该会有比较明显的效果。
参考:
Node.js中HTTP/2的使用:https://nodejs.org/api/http2.html
PHP中HTTP/2深入分析:http://www.phpchina.com/article-40293-1.html
生成密钥的工具:https://github.com/FiloSottile/mkcert/blob/master/README.md#supported-root-stores
http/2官方规范:https://httpwg.org/specs/rfc7540.html#top
Activity