replace (
golang.org/x/crypto => github.com/golang/crypto v0.0.0-20190313024323-a1f597ede03a
golang.org/x/sys => github.com/golang/sys v0.0.0-20190318195719-6c81ef8f67ca
golang.org/x/text => github.com/golang/text v0.3.0
golang.org/x/lint => github.com/golang/lint v0.0.0-20190409202823-959b441ac422
golang.org/x/time => github.com/golang/time v0.0.0-20190308202827-9d24e82272b4
golang.org/x/tools => github.com/golang/tools v0.0.0-20190529010454-aa71c3f32488
golang.org/x/oauth2 => github.com/golang/oauth2 v0.0.0-20190523182746-aaccbc9213b0
golang.org/x/net => github.com/golang/net v0.0.0-20190318221613-d196dffd7c2b
golang.org/x/exp => github.com/golang/exp master
cloud.google.com/go => github.com/googleapis/google-cloud-go master
google.golang.org/genproto => github.com/google/go-genproto v0.0.0-20190522204451-c2c4e71fbf69
google.golang.org/grpc => github.com/grpc/grpc-go v1.21.0
google.golang.org/appengine => github.com/golang/appengine v1.6.1-0.20190515044707-311d3c5cf937
golang.org/x/sync => github.com/golang/sync v0.0.0-20190227155943-e225da77a7e6
)
了解一下DPDK
DPDK(Data Plane Development Kit),是一组快速处理数据包的开发平台及接口。运行于Intel X86与arm平台上(最新版本也开始支持PowerPC)。
概述
在X86结构中,处理数据包的传统方式是CPU中断方式,即网卡驱动接收到数据包后通过中断通知CPU处理,然后由CPU拷贝数据并交给协议栈。在数据量大时,这种方式会产生大量CPU中断,导致CPU无法运行其他程序。
而DPDK则采用轮询方式实现数据包处理过程:DPDK重载了网卡驱动,该驱动在收到数据包后不中断通知CPU,而是将数据包通过零拷贝技术存入内存,这时应用层程序就可以通过DPDK提供的接口,直接从内存读取数据包。
这种处理方式节省了CPU中断时间、内存拷贝时间,并向应用层提供了简单易行且高效的数据包处理方式,使得网络应用的开发更加方便。但同时,由于需要重载网卡驱动,因此该开发包目前只能用在部分采用Intel网络处理芯片的网卡中。
x86网络io瓶颈
- 传统的收发报文方式都必须采用硬中断来做通讯,每次硬中断大约消耗100微秒,这还不算因为终止上下文所带来的Cache Miss。
- 数据必须从内核态用户态之间切换拷贝带来大量CPU消耗,全局锁竞争。
- 收发包都有系统调用的开销。
- 内核工作在多核上,为可全局一致,即使采用Lock Free,也避免不了锁总线、内存屏障带来的性能损耗。
- 从网卡到业务进程,经过的路径太长,有些其实未必要的,例如netfilter框架,这些都带来一定的消耗,而且容易Cache Miss。
DPDK生态
腾讯云开源的F-Stack
Seastar也很强大和灵活,内核态和DPDK都随意切换,也有自己的传输协议Seastar Native TCP/IP Stack支持,但是目前还未看到有大型项目在使用Seastar,可能需要填的坑比较多。
http2 over tls1.2 tls1.3 quic RTT对比
http2 over tls1.2 tls1.3 quic RTT对比
method | HTTP/2 OVER TLS1.2首次连接 | HTTP/2 OVER TLS1.2连接恢复 | HTTP/2 OVER TLS1.3首次连接 | HTTP/2 OVER TLS1.3连接恢复 | HTTP/2 OVER QUIC首次连接 | HTTP/2 OVER QUIC连接恢复 |
---|---|---|---|---|---|---|
DNS解析 | 1RTT | 0RTT | 1RTT | 0RTT | 1RTT | 0RTT |
TCP握手 | 1.5RTT | 1.5RTT | 1.5RTT | 1.5RTT | - | - |
TLS握手 | 2RTT | 1RTT | 1RTT | 0RTT | - | - |
QUIC握手 | - | - | - | - | 1RTT | 0RTT |
HTTP Request | 1RTT | 1RTT | 1RTT | 1RTT | 1RTT | 1RTT |
总计 | 5.5RTT | 3.5RTT | 4.5RTT | 2.5RTT | 3RTT | 1RTT |
TLS1.3 VS TLS1.2 握手阶段对比
TLS 1.0 RFC https://www.ietf.org/rfc/rfc2246.txt
TLS 1.1 RFC https://www.ietf.org/rfc/rfc4346.txt
TLS 1.2 RFC https://www.ietf.org/rfc/rfc5246.txt
TLS 1.3 RFC https://www.ietf.org/rfc/rfc8446.txt
Client Server
ClientHello -------->
ServerHello
Certificate*
ServerKeyExchange*
CertificateRequest*
<-------- ServerHelloDone
Certificate*
ClientKeyExchange
CertificateVerify*
[ChangeCipherSpec]
Finished -------->
[ChangeCipherSpec]
<-------- Finished
Application Data <-------> Application Data
TLS 1.2 完整握手过程( 来自 RFC 5246 )
Client Server
Key ^ ClientHello
Exch | + key_share*
| + signature_algorithms*
| + psk_key_exchange_modes*
v + pre_shared_key* -------->
ServerHello ^ Key
+ key_share* | Exch
+ pre_shared_key* v
{EncryptedExtensions} ^ Server
{CertificateRequest*} v Params
{Certificate*} ^
{CertificateVerify*} | Auth
{Finished} v
<-------- [Application Data*]
^ {Certificate*}
Auth | {CertificateVerify*}
v {Finished} -------->
[Application Data] <-------> [Application Data]
TLS 1.3 完整握手过程( 来自 RFC 8446 )
- +表示该报文中值得注意的extension
- *表示该内容也可能不被发送
- {} 表示该内容使用handshake_key加密
- [] 表示该内容使用application_key加密
在完全握手情况下,TLS 1.3需要1-RTT建立连接。与TLS1.2有两点不同:握手过程中移除了ServerKeyExchange和ClientKeyExchange, DH (Diffie-Hellman) 参数通过 key_share 传输。
golang 切片扩容规则
如果切片的容量小于1024个元素,那么扩容的时候slice的cap就翻番,乘以2;一旦元素个数超过1024个元素,增长因子就变成1.25,即每次增加原来容量的四分之一。
如果扩容之后,还没有触及原数组的容量,那么,切片中的指针指向的位置,就还是原数组,如果扩容之后,超过了原数组的容量,那么,Go就会开辟一块新的内存,把原来的值拷贝过来,这种情况丝毫不会影响到原数组。