http2

HTTP/2

{{:聚美优品:2.调研:pasted:20170930-113031.png?710|}}

官方协议

RFC 7540 - Hypertext Transfer Protocol Version 2(HTTP/2)
RFC 7541 - HPACK: Header Compression for HTTP/2

演示

HTTP/1.1 vs HTTP/2
{{ :聚美优品:0.分享:pasted:20170915-162423.png |}}

HTTP/1缺陷

  • 一个TCP链接上只能有一个请求/响应
    {{ :聚美优品:0.分享:pasted:20170915-162440.png |}}
  • 头部消耗
    HTTP/1 协议头部使用纯文本格式,没有任何压缩,且包含很多冗余信息(例如 Cookie、UserAgent 每次都会携带),所以一个页面的请求数越多,头部带来的额外开销就越大。

如打开http://www.oschina.net/question/1397765_172789?fromerr=VbVRMMxc
传输头部的开销超过了5%
{{ :聚美优品:0.分享:pasted:20170915-162459.png |}}

更有为了获得2字节的数据,在头部传输上花费了几十倍的流量:
{{ :聚美优品:0.分享:pasted:20170915-162512.png |}}

Why do we need header compression?

Mozilla的Patrick McManus 通过计算消息头对平均页面负载的影响,给出下面的说明:

假定一个页面有80个资源需要加载(这个数量对于今天的Web而言还是挺保守的), 而每一次请求都有1400字节的消息头(同样这也并不少见,因为Cookie和引用等东西的存在), 至少要7到8个来回去“在线”获得这些消息头。
这还不包括响应时间——那只是从客户端那里获取到它们所花的时间而已.
这全都由于TCP的慢启动机制,它会基于对已知有多少个包,来确定还要来回去获取哪些包 – 这很明显的限制了最初的几个来回可以发送的数据包的数量.
相比之下,即使是头部轻微的压缩也可以是让那些请求只需一个来回就能搞定——有时候甚至一个包就可以了。
这种开销是可以被节省下来的,特别是当你考虑移动客户端应用的时候,即使是良好条件下,一般也会看到几百毫秒的来回延迟。

HTTP/2改进

多路复用,只需一个连接即可实现并行

HTTP/2引入"流"概念支持多路复用。
流:

是服务器和客户端在HTTP/2连接内用于交换帧数据的独立双向序列,逻辑上可看做一个较为完整的交互处理单元,即表达一次完整的资源请求-响应数据交换流程。
一个业务处理单元,在一个流内进行处理完毕,这个流生命周期完结。

流与HTTP2连接关系
{{ :聚美优品:0.分享:pasted:20170915-162532.png |}}

实际传输可能是这样的
{{ :聚美优品:0.分享:pasted:20170915-162546.png |}}

流的一些属性:

  • 一个HTTP/2连接可同时保持多个打开的流,任一端点交换帧;
  • 流可被客户端或服务器单独或共享创建和使用;
  • 流可被任一端关闭;
  • 在流内发送和接收数据都要按照顺序;
  • 流的标识符自然数表示,1~2^31-1区间,由创建流的终端分配;
  • 流与流之间逻辑上是并行、独立存在;

服务器"推送"

"推送"是HTTP/2添加的一种新的交互模式,允许服务端“推送”那些它认为客户端将会需要的内容到客户端的缓存中,以此来避免往返的延迟。比如:客户端浏览页面请求page.html,服务器在发回page.html后,认为客户端还需要JavaScript和CSS,则把strict.js和style.css也推送给客户端。当然这个“推送”其实不是你想推就能推的,服务器要遵循请求-响应这个模型,只不过服务器对同一请求可以推送多个响应。客户端在交换 SETTINGS 帧时,设置字段 SETTINGS_ENABLE_PUSH(0x2) 为1显式允许服务器推送。
{{ :聚美优品:0.分享:pasted:20170915-162600.png |}}

头部压缩

为了减少传输头部在整个HTTP请求中所占的比重,HTTP/1 时代,有很多优化方案可以尝试,例如合并请求、启用 Cookie-Free 域名等等,但是这些方案或多或少会引入一些新的问题。

HTTP/2协议使用了HPACK算法压缩头部,能显著压缩头部大小。
{{ :聚美优品:0.分享:pasted:20170915-162611.png |}}

优先级

浏览器请求优先级与 HTTP 2.0

  • 浏览器在渲染页面时,并非所有资源都具有相同的优先级:HTML 文档本身对构建 DOM 不可或缺,CSS 对构建 CSSOM 不可或缺,而 DOM 和 CSSOM 的构 建都可能受到 JavaScript 资源的阻塞,其他资源(如图片)的优先级都可以降低。
  • 为加快页面加载速度,所有现代浏览器都会基于资源的类型以及它在页面中的位置排定请求的优先次序,甚至通过之前的访问来学习优先级模式——比如,之前的渲染如果被某些资源阻塞了,那么同样的资源在下一次访问时可能就会被赋予更高的优先级。
  • 在 HTTP 1.x 中,浏览器极少能利用上述优先级信息,因为协议本身并不支持多路复用,也没有办法向服务器通告请求的优先级。此时浏览器只能依赖并行连接,且最多只能同时向一个域名发送 6 个请求。于是,在等连接可用期间,请求只能在客户端排队,从而增加了不必要的网络延迟。理论上,HTTP 管道可以解决这 个问题,只是由于缺乏支持而无法付诸实践。
  • HTTP 2.0 一举解决了所有这些低效的问题:浏览器可以在发现资源时立即分派请求,指定每个流的优先级,让服务器决定最优的响应次序。这样请求就不必排队了,既节省了时间,也最大限度地利用了每个连接。

HTTP/2常见问题解答FAQ

HTTP/2实现情况

客户端对HTTP/2的支持

iOS

  • NSURLConnection
  • NSURLSession (HTTP/2 is only supported by NSURLSession. NSURLConnection has been deprecated in iOS 9. The HTTP/2 support is part of iOS 9, and thus won't be available on earlier OS releases.)

    • on iOS 9, via HTTP/2
    • on iOS 8, via HTTP/1.1 or SPDY
    • on iOS 7, via HTTP/1.1
  • AFNetworking 3.0版本已经支持NSURLSession(即 支持HTTP/2)
  • SDWebImage 要到4.0.0版本 才会引入 NSURLSession Refernce

Android

  • HttpUrlConnection 不支持
  • OkHttp 完整支持
  • Volley 可配置使用OkHttp传输数据

Windows

HTTP/2 support is present in Windows 10 and the Server 2016 Technical Preview

浏览器

{{ :聚美优品:2.调研:pasted:20171020-184529.png |}}
参见:CanIUse

服务端对HTTP/2的支持

Nginx

Nginx 1.9.5+ 完整支持:
{{ :聚美优品:0.分享:pasted:20170915-163406.png |}}

版本要求:

openssl 1.0.2+ (OpenSSL 1.0.2 开始支持 ALPN)
Nginx 1.9.5+

Nginx 编译安装:

./configure \
    --prefix=/usr/local/nginx \
    --with-openssl=/usr/local/openssl-1.0.2e \
    --with-pcre \
    --with-stream --with-stream_ssl_module \
    --with-http_ssl_module \
    --with-http_v2_module \
make 
make install

Moving to HTTP/2 with NGINX 1.9.5

Tengine

Tengine 2.1.2+ 支持 http2。

协议协商

在完全过度到 http/2 协议,http1.1 的服务器和客户端依然大量存在,这就注定新老协议的一个共存。这样,浏览器和服务器就需要协商使用何种协议进行通讯。

h2c

即:HTTP/2 ClearText。直接在标准TCP之上建立HTTP2,采用“h2c”作为协议标识符,在未知服务器是否提供HTTP/2支持之前,可以依赖现有HTTP/1.1进行试探。

GET /page HTTP/1.1
Host: server.example.com
Connection: Upgrade, HTTP2-Settings
Upgrade: h2c 
HTTP2-Settings: (SETTINGS payload) 

HTTP/1.1 200 OK 
Content-length: 243
Content-type: text/html

(... HTTP/1.1 response ...)

          (or)

HTTP/1.1 101 Switching Protocols 
Connection: Upgrade
Upgrade: h2c

(... HTTP/2 response ...)

h2

HTTP/2安全版本在TLS上构建,使用ALPN扩展协议进行协商,采用“h2”作为协议标识符,在TLS层协商过程中一并完成对是否支持HTTP2的试探。

ALPN 和 NPN 区别:
NPN(Next Protocol Negotiation) 是一个使 SPDY 在 TLS 服务器上对使用何种应用层协议进行协商的协议。ALPN(Application Layer Protocol Negotiation) 则是 IETF(h2的标准化组织)修改 NPN 而成的一个标准。两者区别在于谁持有会话协议的决定权,ALPN 是由客户端给服务器发送一个协议清单,由服务器来最终选择一个,而 NPN 则正好相反。

h2 和 h2c 的区别

{{ :聚美优品:0.分享:pasted:20170915-162648.png |}}

理论上来说,http2 可以架构在 tls 上,也可以架构在 tcp 上(运行于非加密通道之上的 http2)。协议文本也确实没有限定或者强制使用 tls 信道。但是 http2 的前身是 spdy,而 spdy 是在 tls 之上的。目前 chrome/firefox 也只支持 tls 。这就让架构于 tls 之上就成为 http2 的事实上的标准。

调试工具

Charles

Charles4.0+开始支持http2,如使用Charles4.2抓街电项目的包: \
{{ :聚美优品:2.调研:pasted:20171017-114143.png |}}

Wireshark

Wireshark 是一个无比强大的网络封包分析工具。新版 Wireshark 才增加了对 HTTP/2 的支持,下载地址:

https://www.wireshark.org/#download

安装后,输入过滤条件进行抓包,如:
{{ :聚美优品:2.调研:pasted:20171017-145717.png |}}

由于使用TLS,数据是加过密了的,看不到协议细节。为了解密,首先需要保存ssl key,然后 wireshark 通过 key 来解密数据包。具体操作请参考>>

解密后如图所示:
{{ :聚美优品:2.调研:pasted:20171017-154821.png |}}

CURL

执行命令 curl --version 查看curl是否支持 http2。如返回:

curl 7.54.0 (x86_64-apple-darwin16.0) libcurl/7.54.0 SecureTransport zlib/1.2.8
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: AsynchDNS IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz UnixSockets

可以看到当前 curl 的版本及支持的协议以及功能特性没有支持 HTTP2。为了让 curl 支持 HTTP2 需要安装 nghttp2(http2 的 C 语言库)。\
编译安装nghttp2(MAC 可以直接通过 brew install nghttp2 安装):

git clone https://github.com/tatsuhiro-t/nghttp2.git
cd nghttp2
autoreconf -i
automake
autoconf
./configure
make
sudo make install

升级curl版本:

wget https://curl.haxx.se/download/curl-7.56.0.tar.bz2
tar -xvjf curl-7.56.0.tar.bz2
cd curl-7.56.0
./configure --with-nghttp2 --with-ssl
sudo make && make install

检查是否安装成功:

curl --version

测试curl with http2:

$ curl --http2 -I https://www.jd.com
HTTP/2.0 200
server:JDWS/2.0
date:Tue, 17 Oct 2017 07:53:14 GMT
content-type:text/html; charset=utf-8
content-length:122981
vary:Accept-Encoding
vary:Accept-Encoding
expires:Tue, 17 Oct 2017 07:53:00 GMT
cache-control:max-age=30
ser:130.26
via:BJ-M-YZ-NX-76(HIT), http/1.1 CD-CT-2-JCS-25 ( [cRs f ])
age:30
strict-transport-security:max-age=360

nghttp

nghttp 做为一个功能完整的 HTTP/2 客户端,非常适合用来查看和调试 HTTP/2 流量。它支持的参数很多,通过官方文档或者 nghttp -h 都能查看。最常用几个参数如下:

  • -v, --verbose,输出完整的 debug 信息;
  • -n, --null-out,丢弃下载的数据;
  • -a, --get-assets,下载 html 中的 css、js、image 等外链资源;
  • -H, --header=<HEADER>,添加请求头部字段,如 -H':method: PUT';
  • -u, --upgrade,使用 HTTP 的 Upgrade 机制来协商 HTTP/2 协议,用于 h2c,详见下面的例子;
    以下是使用 nghttp 访问 nghttp2 官网的结果。从调试信息中可以清晰看到 h2c 协商以及 Server Push 的整个过程:
$ nghttp -nvu http://nghttp2.org
[  0.244] Connected
[  0.244] HTTP Upgrade request
GET / HTTP/1.1
host: nghttp2.org
connection: Upgrade, HTTP2-Settings
upgrade: h2c
http2-settings: AAMAAABkAAQAAP__
accept: */*
user-agent: nghttp2/1.24.0


[  1.833] HTTP Upgrade response
HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: h2c


[  1.833] HTTP Upgrade success
[  1.835] recv SETTINGS frame <length=18, flags=0x00, stream_id=0>
          (niv=3)
          [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
          [SETTINGS_INITIAL_WINDOW_SIZE(0x04):1048576]
          [SETTINGS_HEADER_TABLE_SIZE(0x01):8192]
[  1.835] recv (stream_id=1) :status: 200
[  1.835] recv (stream_id=1) date: Tue, 17 Oct 2017 08:07:13 GMT
[  1.835] recv (stream_id=1) content-type: text/html
[  1.835] recv (stream_id=1) last-modified: Wed, 20 Sep 2017 14:04:09 GMT
[  1.835] recv (stream_id=1) etag: "59c27559-19e1"
[  1.835] recv (stream_id=1) accept-ranges: bytes
[  1.835] recv (stream_id=1) content-length: 6625
[  1.835] recv (stream_id=1) x-backend-header-rtt: 0.001153
[  1.835] recv (stream_id=1) server: nghttpx
[  1.835] recv (stream_id=1) via: 2 nghttpx
[  1.835] recv (stream_id=1) x-frame-options: SAMEORIGIN
[  1.835] recv (stream_id=1) x-xss-protection: 1; mode=block
[  1.835] recv (stream_id=1) x-content-type-options: nosniff
[  1.835] recv HEADERS frame <length=198, flags=0x04, stream_id=1>
          ; END_HEADERS
          (padlen=0)
          ; First response header
[  1.835] recv DATA frame <length=6625, flags=0x01, stream_id=1>
          ; END_STREAM
[  1.835] send SETTINGS frame <length=12, flags=0x00, stream_id=0>
          (niv=2)
          [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
          [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
[  1.835] send GOAWAY frame <length=8, flags=0x00, stream_id=0>
          (last_stream_id=0, error_code=NO_ERROR(0x00), opaque_data(0)=[])

h2load

nghttp2 带有 h2load,它是一个支持 HTTP/2 的压测工具,可以用来测试 HTTP/2 服务的稳定性、QPS 等信息。它支持的参数也可以通过官网文档或者 h2load -h 来查看。下面是一个简单例子:

$ h2load https://example.com -n 100 -c 10
starting benchmark...
spawning thread #0: 10 total client(s). 100 total requests
TLS Protocol: TLSv1.2
Cipher: ECDHE-RSA-AES128-GCM-SHA256
Server Temp Key: ECDH P-256 256 bits
Application protocol: h2
progress: 10% done
progress: 20% done
progress: 30% done
progress: 40% done
progress: 50% done
progress: 60% done
progress: 70% done
progress: 80% done
progress: 90% done
progress: 100% done

finished in 2.67s, 37.52 req/s, 49.48KB/s
requests: 100 total, 100 started, 100 done, 100 succeeded, 0 failed, 0 errored, 0 timeout
status codes: 100 2xx, 0 3xx, 0 4xx, 0 5xx
traffic: 131.87KB (135038) total, 5.50KB (5628) headers (space savings 79.16%), 124.02KB (127000) data
                     min         max         mean         sd        +/- sd
time for request:   174.06ms    219.98ms    188.81ms      8.90ms    61.00%
time for connect:   558.91ms    606.67ms    572.44ms     14.39ms    80.00%
time to 1st byte:   737.95ms    813.75ms    762.35ms     22.60ms    60.00%
req/s           :       3.75        4.30        4.07        0.16    60.00%

Chrome开发者工具

Chrome 40版本就开始支持http2了,如图所示:
{{ :聚美优品:2.调研:pasted:20171017-120234.png |}}

Firefox开发者工具

Firefox 开发者工具也支持http2,如图所示:
{{ :聚美优品:2.调研:pasted:20171017-115856.png |}}

浏览器插件

HTTP/2 and SPDY indicator 是一款可以为每个网站提供HTTP/2,SPDY 和 QUIC 支持的指示按钮的插件,也有管理页面便于查看更详细的信息。

Chrome浏览器安装好插件后,在地址栏中输入 chrome://net-internals/#http2 会列出浏览器当前所有活跃的 HTTP/2 Session,点击具体的 Session ID,可以看到全部帧信息。,如图所示:
{{ :聚美优品:2.调研:pasted:20171017-114306.png |}}

参考

Go 完整实现版本比较 VersionCompare 函数

VersionCompare — 用于对比两个的版本数字字符串大小。

此函数首先在版本字符串里用一个点 . 替换 _、- 和 +,也会在任意非数字前后插入一个点 .,这样,类似 '4.3.2RC1' 将会变成 '4.3.2.RC.1'。 接下来它会分割结果,然后它会从左往右对比各个部分。如果某部分包含了特定的版本字符串,将会用以下顺序处理:列表中未找到的任意字符串 < dev < alpha = a < beta = b < RC = rc < # < pl = p。 这种方式不仅能够对比类似 '4.1' 和 '4.1.2' 那种不同的版本级别,同时也可以指定对比任何包含开发状态的版本。

PHP 中几种用于区分环境的方法

在项目开发过程中,我们一般至少会有三套环境:本地开发环境,测试环境、线上生产环境。而不同的环境一般它的配置文件内容是不一样的,比如环境不一样时数据库地址不一样。为了能在每次发布代码时不至于屡次改动代码来适配不同的环境,我们可以为每一套环境对应到各自的配置文件,这时就需要能有一种方法能识别当前代码所运行的环境来自动加载对应的配置文件。这样的自动识别环境的方法常用的有三种。

使用 nginx 的 proxy_pass 功能做域名转发导致一次事故

proxy 模块是 nginx 中非常实用的一个模块。可以方便地实现一个域名的流量转发到另一域名,也可以将一个端口的流量转发到另一个端口。而结合在 AWS 等云服务中的 elb(负载均衡器) 使用本功能时则要注意一下域名被缓存问题,否则可能导致整个域名无法提供服务。

一次 MongoDB 选主失败经历

MongoDB 选主机制较为复杂,需要考虑各种可能性,选举机制可参考>>。这样在某些特定情况下,则可能导致选主失败,从而使得整个集群中无主,也就无法提供写操作,进而影响到服务的可用性。本文便记录了一次在特殊场景下所导致的选主失败经历。

协程

“协程”(Coroutine)概念最早由 Melvin Conway 于1958年提出。协程可以理解为纯用户态的线程,其通过协作而不是抢占来进行切换。相对于进程或者线程,协程所有的操作都可以在用户态完成,创建和切换的消耗更低。总的来说,协程为协同任务提供了一种运行时抽象,这种抽象非常适合于协同多任务调度和数据流处理。在现代操作系统和编程语言中,因为用户态线程切换代价比内核态线程小,协程成为了一种轻量级的多任务模型。

从编程角度上看,协程的思想本质上就是控制流的主动让出(yield)和恢复(resume)机制,迭代器常被用来实现协程,所以大部分的语言实现的协程中都有 yield 关键字,比如 Python、PHP、Lua。但也有特殊比如 Go 就使用的是通道来通信。

复制集模式 MongoDB 从2.2升级到3.0

到目前最新 Release 版本的 Mongodb 已经是3.2了,对于比2.2更高的版本 2.4、2.6、3.0 来说,无论是支持更多的新特性还是在性能、安全、bug 修复方面也都有着巨大的改进。但也正是因为这些变动就会给升级工作带来困难和风险,比如兼容性问题。而我们必须遵守整个升级过程是数据要绝对安全,线上服务不能受影响的原则,这就需要在前期做好充足的准备工作。整个版本升级过程会逐步推进,从2.2到2.4,从2.4到2.6,php驱动升级,从2.6到3.0,升级3.0引擎为 WiredTiger。