2017/09/13 September 13th, 2017
理解和测量HTTP时序帮助我们去发现客户端与服务器、服务器与服务器之间通信的性能瓶颈。本文阐述了在一次HTTP请求中的时序,并展示了如何在Node.js中进行测量。
在我们谈及到HTTP时序之前,让我们看一下基本的概念:
- IP(Internet Protocol) : IP是网络层的协议,处理网络地址和路由。IP的责任是通过包的头部跨过一个或更多的IP网络,将源主机的包传送到目的主机,它还定义了封装要传递的数据的数据包结构。
- DNS(Domain Name Servers) : DNS是一种分层分散式的命名系统。用于将类似于risingstack.com的人类可读的域名解析为机器可读的IP地址。
- TCP(Transmission Control Protocol): TCP标准定义了在应用交换数据时,如何去建立和保持网络会话。TCP为运行在IP网络请求的应用程序提供了可靠、有序、和错误检查的八位字节流。HTTP的客户端通过建立TCP连接来发起请求。
- SSL/TLS(Transport Layer Security):TLS是一种通过计算机网络提供通信安全的加密协议。SSL(Secure Sockets Layer)是TLS的不推荐使用的前身。 TLS和SSL都使用证书建立安全连接。 SSL证书不依赖于加密协议(如TLS),证书包含密钥对:公钥和私钥。这些密钥一起工作,建立一个加密的连接。
现在,让我们看一下一次普通的HTTP请求时间轴
时间段的解释:
- DNS Lookup: DNS的查询时间。DNS查询解决了域名到IP的解析。每一个新的域名需要一个完整的往返来完成域名的查询。当目的地已经在IP地址时,DNS的查询便结束了。
- TCP Connection: TCP连接源主机和目的主机的时间。连接必须正确地建立在多次握手过程中。TCP的连接被操作系统所管理,如果在TCP之下的连接无法被连接,操作系统范围内的TCP连接超时将超出我们应用范围内的超时配置。
- TLS handshake:TLS的 握手时间。在握手过程中,端点交换认证和密钥来建立和恢复安全的会话。没有HTTPS的请求就没有TLS握手。
- Time to First Byte: 初始响应的时间。这个时间除了等待服务器处理请求和返回响应的时间之外,还可以捕获往返服务器的延迟。
- Content Transfer: 接受数据的时间。他的长度取决于返回数据的大小和可用的网络带宽。
HTTP时序是如何去发现瓶颈的?
举个例子:如果你的DNS查询比你期望的时间更长,这个问题可能是因为你的DNS供应商或者DNS缓存引起的。
当时间比Time to First Byte更长时,应该检查端点之间的延迟,还有当前服务器的负载。
Content Transfer过慢可能是由于返回的数据太大不够高效(无用的JSON属性值等)或者过慢的网络连接等。
使用Node.js测量HTTP的请求时序
使用Node.js测量HTTP的时序,我们需要订阅一个特定的HTTP请求、响应和socket事件。这里有一个只关注时序的简短Node.js代码片段。
const timings = {
// use process.hrtime() as it's not a subject of clock drift
startAt: process.hrtime(),
dnsLookupAt: undefined,
tcpConnectionAt: undefined,
tlsHandshakeAt: undefined,
firstByteAt: undefined,
endAt: undefined
}
const req = http.request({ ... }, (res) => {
res.once('readable', () => {
timings.firstByteAt = process.hrtime()
})
res.on('data', (chunk) => { responseBody += chunk })
res.on('end', () => {
timings.endAt = process.hrtime()
})
})
req.on('socket', (socket) => {
socket.on('lookup', () => {
timings.dnsLookupAt = process.hrtime()
})
socket.on('connect', () => {
timings.tcpConnectionAt = process.hrtime()
})
socket.on('secureConnect', () => {
timings.tlsHandshakeAt = process.hrtime()
})
})
DNS Lookup(DNS查询) 只在有域名的情况才有。
// There is no DNS lookup with IP address
const dnsLookup = dnsLookupAt !== undefined ?
getDuration(startAt, dnsLookupAt) : undefined
TCP Connectio(TCP连接)当host的问题解决后会立刻建立连接:
const tcpConnection = getDuration((dnsLookupAt || startAt), tcpConnectionAt)
TLS handshake (SSL) 只发生在HTTPS的请求协议中:
// There is no TLS handshake without https
const tlsHandshake = tlsHandshakeAt !== undefined ?
getDuration(tcpConnectionAt, tlsHandshakeAt) : undefined
我们等待服务器去发送 First Byte(第一个字节):
const firstByte = getDuration((tlsHandshakeAt || tcpConnectionAt), firstByteAt)
Content Transfer(内容传送) 开始于第一个字节流:
const contentTransfer = getDuration(firstByteAt, endAt)
Total Duration(总共持续时长) 由开始到最后的计算:
const total = getDuration(startAt, endAt)
在GitHub上完整的例子:example
使用工具测量时序
现在我们已经知道了Node去测量HTTP时序,让我们看一下现有的工具去帮助你理解HTTP请求。
request
模块
流行的request模块具有内置的HTTP时序测量方法。您可以使用time
属性启用它。
const request = require('request')
request({
uri: 'https://risingstack.com',
method: 'GET',
time: true
}, (err, resp) => {
console.log(err || resp.timings)
})
分布式追踪
可以使用分布式跟踪工具收集HTTP时序,并在时间轴上可视化它们。这样,您可以全面了解后台发生的情况,以及构建分布式系统的实际成本是多少。
RisingStack的opentracing-auto库具有内置的标志,可以通过OpenTracing收集所有的HTTP时序。
Jaeger使用opentracing-auto测量HTTP请求。
总结
使用Node.js测量HTTP的时序可以帮助发现性能瓶颈。Node的生态系统提供了大量的好的工具去从你的应用中提取这些应用指标。
Péter Márton
RisingStack的首席技术官,使用Node.js打造微型服务和酿造啤酒
阅读更多来自我们的文章:
及时地获取我们的发布:
请使用Javascript查看 comments powered by Disqus. comments powered by Disqus