作为新一代的HTTP协议,HTTP/2可以提高网站性能,优化用户体验,Fundebug也是时候升级HTTP/2了,虽然已经有点晚了。

升级HTTP/2是一件很简单的事情,改1行Nginx配置就好了,但是,工程师只知道How是远远不够的,还需要理解Why,这就要求我们需要足够的事先调研(1. 什么是HTTP/2?)以及事后分析(4. 升级HTTP/2真的提高性能了吗?)。

1. 什么是HTTP/2?

HTTP/2是新一代的HTTP协议,于2015正式发布。

与其他众多Web技术标准一样,推动HTTP/2标准的依然是Google。发布Chrome的时候Google说过要推动Web技术的发展,然后它真的做到了。(JavaScript深入浅出第5课:Chrome是如何成功的?

根据W3Techs的统计,截止2019年10月26日,全世界41.3%的网站已经使用了HTTP/2。

根据Can I use,绝大多数浏览器都支持了HTTP/2:

HTTP/2主要有以下几个特性:

  • HTTP/2为二进制协议



图片来源:Valentin V. Bartenev

由上图可知,HTTP/1.1传输的是文本数据,而HTTP/2传输的是二进制数据,提高了数据传输效率。

  • HTTP/2支持TCP连接多路复用



图片来源:Factory.hr

由上图可知,HTTP 1.1需要为不同的HTTP请求建立单独的TCP连接,而HTTP/2的多个HTTP请求可以复用同一个TCP连接。

要知道,建立TCP连接时需要3次握手,再加上TLS的4次握手,加起来就是7次握手,如果可以复用TCP连接的话,则可以减少这些多余的开销。

  • HTTP/2会压缩请求Header



图片来源:运维实谈

如上图所示,第2个请求的Header只有:path不一样,因此压缩空间非常可观。

Headers压缩的算法HPACK本身似乎很复杂(其实也不难),但是算法思想其实非常简单的,假设我们在浏览器发起100个请求,它们的user-agent是不会变的,那我们为什么需要重复传输这个长长的字符串呢?用dictionary记录一次不就行了!

  • HTTP/2支持服务器推送(Server Push)

图片来源:lujjjh

由上图可知,当客服端向服务端请求HTML时,Server Push服务端可以提前返回HTML所依赖的css、js等资源,这样可以节省解析HTML以及请求资源的时间,从而缩短页面的加载时间。

2. 如何升级HTTP/2?

我们使用了Nginx作为前端页面与后端接口的反向代理服务器(Reverse Proxy),只需要修改一下Nginx配置文件就可以升级HTTP/2了,非常简单。

注意,在 Nginx 上 开启 HTTP/2 需要 Nginx 1.9.5 以上版本(包括1.9.5),并且需要 OpenSSL 1.0.2 以上版本(包括1.0.2)。使用nginx -V命令可以查看Nginx的版本信息:

nginx -V
nginx version: nginx/1.12.1
built by gcc 6.3.0 20170516 (Debian 6.3.0-18)
built with OpenSSL 1.1.0f 25 May 2017
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin3-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-g -O2 -fdebug-prefix-map=/data/builder/debuild/nginx-1.12.1/debian/debuild-base/nginx-1.12.1=. -specs=/usr/share/dpkg/no-pie-compile.specs -fstack-protector-strong -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fPIC' --with-ld-opt='-specs=/usr/share/dpkg/no-pie-link.specs -Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie'

可知,我们使用的Nginx版本为1.12.1,OpenSSL版本为1.1.0f,符合要求。

还有一点,虽然HTTP/2标准并没有要求加密,但是所有浏览器都要求HTTP/2必须加密,这样的话,只有HTTPS才能升级HTTP/2。

如果你还没用过HTTPS的话,不妨看看我的博客:教你快速撸一个免费HTTPS证书,其实也很简单。

一切前提没问题的话(Nginx>=1.9.5,OpenSSL>=1.0.2,HTTPS),只需要修改1行配置,在listen指令后面添加http2:

server
{
listen 443 ssl http2;
server_name www.fundebug.com;
}

重启Nginx,升级HTTP/2就成功了,可以使用curl命令检查:

curl -sI https://www.fundebug.com
HTTP/2 200
server: nginx/1.12.1
date: Mon, 07 Oct 2019 00:12:53 GMT
content-type: text/html; charset=UTF-8
content-length: 4892
x-powered-by: Express
accept-ranges: bytes
cache-control: public, max-age=0
last-modified: Sun, 06 Oct 2019 23:07:25 GMT
etag: W/"131c-16da353dbc8"
vary: Accept-Encoding
strict-transport-security: max-age=15768001

3. HTTP/2导致Safari浏览器OPTIONS请求失败

升级HTTP/2之后,使用Safari的用户发现无法登陆Fundebug了:

我们的前端异常监控插件捕获了这个报错:

可知,是/api/members/login接口出错了。

经过排查发现是OPTIONS请求失败了:

curl -X OPTIONS https://api.fundebug.com/api/members/login -v
* Trying 120.77.45.162...
* TCP_NODELAY set
* Connected to api.fundebug.com (120.77.45.162) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
* CAfile: /etc/ssl/cert.pem
CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
* subject: CN=api.fundebug.com
* start date: Sep 15 16:38:43 2019 GMT
* expire date: Dec 14 16:38:43 2019 GMT
* subjectAltName: host "api.fundebug.com" matched cert's "api.fundebug.com"
* issuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3
* SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7fcfbb80ce00)
> OPTIONS /api/members/login HTTP/2
> Host: api.fundebug.com
> User-Agent: curl/7.54.0
> Accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS updated)!
* http2 error: Invalid HTTP header field was received: frame type: 1, stream: 1, name: [content-length], value: [0]
* HTTP/2 stream 1 was not closed cleanly: PROTOCOL_ERROR (err 1)
* Closing connection 0
* TLSv1.2 (OUT), TLS alert, Client hello (1):
curl: (92) HTTP/2 stream 1 was not closed cleanly: PROTOCOL_ERROR (err 1)

根据curl的报错信息,可知是Header中content-length有问题:

* http2 error: Invalid HTTP header field was received: frame type: 1, stream: 1, name: [content-length], value: [0]

将Nginx配置文件中OPTIONS请求的Content-Length配置注释掉,问题就解决了:

if ($request_method = "OPTIONS")
{
add_header Access-Control-Allow-Origin *;
add_header 'Access-Control-Max-Age' 86400;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, OPTIONS, DELETE';
add_header 'Access-Control-Allow-Headers' 'token, reqid, nid, host, x-real-ip, x-forwarded-ip, event-type, event-id, accept, content-type';
# add_header 'Content-Length' 0; // 必须注释,否则HTTP/2会报错
add_header 'Content-Type' 'text/plain, charset=utf-8';
return 200;
}

HTTP/2中对于Header有特殊处理,这应该是导致出错的根本原因,关于这一个问题,我会在下一篇博客中详细介绍。

4. 升级HTTP/2真的提高性能了吗?

理论上来说,HTTP/2应该可以提高网站性能,但是实际情况是怎样呢?HTTP/2真的可以提高性能了吗?如果有的话,究竟提高了多少呢?

于是,我使用Chrome记录了升级HTTP/2前后Fundebug首页的加载时间,计算了5次加载的平均时间(单位为妙),如下表:

HTTP版本 DOMContentLoaded Load Finish
HTTP/1.1 1.572 4.342 5.138
HTTP/2 1.0004 4.102 4.288

可知,HTTP/2明显提高了首页加载时间,DOMContentLoaded、Load与Finish时间均有明显提高。

一共也就改了2行Nginx配置,就可以提高页面访问性能,多好啊!

参考

Fundebug网站升级HTTP/2,真的变快了!的更多相关文章

  1. freopen stdout 真的更快?

    freopen stdout 真的更快? 在一次数独作业中,我发现大部分同学提交的代码中都使用 freopen 来将 stdout 重新指向目标文件进行文件输出操作.我感到十分好奇,关于 freope ...

  2. Redis是不是真的变慢了?

    大家好,今天我们来学习一下如何确定Redis是不是真的变慢了. 我们在使用redis时一定会遇到变慢的时候,那我们如何来判断Redis是否真的变慢了呢, 一个最直接的方法就是查看Redis的响应延迟, ...

  3. Vue3.0系列——「vue3.0性能是如何变快的?」

    前言 先学习vue2.x,很多2.x内容依然保留: 先学习TypeScript,vue3.0是用TS重写的,想知其然知其所以然必须学习TS. 为什么学习vue3.0? 性能比vue2.x快1.2-2倍 ...

  4. 通过设计让APP变快的6个方法

    我们都知道不管网页还是移动应用,响应速度都是最重要的体验指标之一,并且移动应用的网络环境不稳定,速度的体验显得尤为重要.其实速度优化不仅是程序员的事,设计,也能够让APP变得更快. 1. 后台执行 这 ...

  5. 发现一个国内牛逼的maven仓库,速度真的太快了

    前天网上下了一个项目,在公司还好,网络比较流畅,很快就把依赖下好了:回家的时候,想耍耍,结果下了一天也没把依赖下好,速度是几k每秒,甚至一k每秒,哎~心都碎了,网上一搜,结果发现了一个惊天的用nexu ...

  6. 为什么Sql Server的查询有时候第一次执行很慢,第二次,第三次执行就变快了

    老外提问: Hi, I have an sql query which takes 8 seconds in the first run. The next run there after takes ...

  7. iOS11有哪些新功能?旧iPhone是否真的变慢了

    1. [iOS 11] iOS 11十大实用新功能简介 2.[iOS 11] iPhone二维码扫描,通过内建相机就可以完成! 3. iOS 11内建屏幕录制功能!再也不需要通过第三方应用录屏 4.  ...

  8. 访问github太慢?我写了一个开源小工具一键变快

    前言 GitHub应该是广大开发者最常去的站点,这里面有大量的优秀项目,是广大开发者寻找资源,交友学习的好地方.尤其是前段时间GitHub公布了一项代码存档计划--Arctic Code Vault, ...

  9. Vue3.0是如何变快的

    1.diff算法优化 + Vue2中的虚拟dom是进行全量的对比 https://vue-next-template-explorer.netlify.app/ + Vue3新增了静态标记(Patch ...

随机推荐

  1. SQL SERVER查询数据库所有表的大小,按照记录数降序排列

    SELECT B.NAME,A.ROW_COUNT FROM SYS.DM_DB_PARTITION_STATS A,SYS.OBJECTS BWHERE A.OBJECT_ID=B.OBJECT_I ...

  2. Get提交数据过大,tomcat报错,信息: Error parsing HTTP request header Note: further occurrences of HTTP header parsing errors will be logged at DEBUG level

    原因 get提交的数据过大,超过了默认值. 解决办法 get提交配置 设置tomcat的server.xml.找到我们启动的端口,一般是8080,增加maxHttpHeaderSize= " ...

  3. node.js安装本地模块遇到的目录锁定问题【新手问题】

    昨天发过文字版的,后来以为问题没解决就删除,今天偷个懒,直接上图. 被书中的介绍给误导了,虽然和书中不一样,但实际上自己练习写的模块已经是可用了.也犯了个常识性的错误:Warn一般不会有太大的影响.

  4. com.alibaba.fastjson和net.sf.json的区别

    JSON有两种结构 json简单说就是javascript中的对象和数组,所以这两种结构就是对象和数组两种结构,通过这两种结构可以表示各种复杂的结构 1.对象:对象在js中表示为“{}”括起来的内容, ...

  5. VUE程序调试的方法

    目录 VUE程序调试的方法 1.写本文的背景 2.调试与测试 3.Console调试法 3.1 添加console.log指令 3.2 调出温度界面如下 3.3 Google浏览器的Console窗口 ...

  6. jQuery总结02_jq的dom操作+属性操作

    一:JQuery知识点 *:JQuery的dom操作 *:获取节点.给节点添加内容 *:动态创建dom节点 比如动态创建表格等,在js里面进行完成. *删除节点 这里面的删除就是将其放在了一个地方,并 ...

  7. HTML入门(转义字符、行内样式和块级元素、定位、锚点、跑马灯标签、图片标签、表格标签的讲解)

    一.转义字符由特殊字符包裹的文本 会当做标签去解析 对应不换行空格 对应全角空格 em是字体排印学的计量单位,相当于当前指定的点数.其占据的宽度正好是1个中文宽度,而且基本上不受字体影响.<对应 ...

  8. 今日资源帖-PPT逆袭秘籍72集+2000套模板,太经典了

    好资源不私藏,分享是一种态度 今日给大家分享的是PPT教程和2000套模板 如何让PPT成为你职场的利器 如何让你的PPT更具表现力 2000套模板随便选 PPT视频教程 链接 https://pan ...

  9. python-基础-def(*agrs,**kwagrs)

    1.*args,返回的数据类型为 tuple,使用方法如下图代码:**kwargs 返回的数据类型为 dict 使用方法如下图代码. def KeyWord_s(arg): print(arg,typ ...

  10. day97_11_29

    一.数据存储到mongodb 爬取的数据如果需要存储到mongodb中,要通过item,定义一个存储类.再yield一个类. 数据存储的时候需要进过pipelines,再到setting中配置. fr ...