如何使用AsyncHttpClient正确度量HTTP请求各阶段耗时
结论先行
onRequestSend完全不适合作为请求起始时间的判定依据- 你要找的「客户端侧创建socket发起连接动作」对应的事件是
onTcpConnectAttempt;如果要覆盖DNS解析全流程,整个网络请求生命周期的第一个触发事件是onDnsResolutionAttempt
为什么onRequestSend不能用
onRequestSend的触发时机非常晚:只有当AHC拿到了可用连接(要么是连接池里复用的已有长连接,要么是已经走完DNS解析、TCP握手、HTTPS握手的全新连接),开始往网络通道写HTTP请求报文的时候,才会触发这个事件。
拿它当起始点会出两个致命问题:
- 碰到连接池复用的场景,DNS、TCP建连、TLS握手这几个步骤根本不会执行,对应事件也不会触发,你算出来的这几个阶段耗时要么是0要么是负数,完全不准
- 哪怕是新建连接的场景,这个事件触发的时候连接已经建完了,你根本统计不到前面DNS、TCP握手的耗时
各指标的正确时间点标记方法
你自定义实现AsyncHandler重写对应生命周期回调,在回调里打System.nanoTime()时间戳就行,各个指标的对应起止点列清楚:
- 总响应耗时:如果要算用户视角的端到端耗时,就从你代码里调用
executeRequest提交请求的那行开始记,结束点取onComplete或者onThrowable触发的时刻,这样连客户端内部排队、等连接池分配连接的时间都能覆盖到;如果只想算纯网络交互的耗时,就从onDnsResolutionAttempt触发开始记 - DNS解析耗时:起点是
onDnsResolutionAttempt触发时刻,终点是onDnsResolutionSuccess触发时刻 - TCP连接耗时:起点就是你要找的
onTcpConnectAttempt触发时刻(这时候客户端刚创建socket,开始发起TCP握手),终点是onTcpConnectSuccess触发时刻 - TLS握手耗时(HTTPS场景):起点是
onTlsHandshakeAttempt,终点是onTlsHandshakeSuccess - TTFB(首字节耗时):起点取
onRequestSend触发时刻(刚开始往网络写请求),终点取onStatusReceived触发时刻(刚收到响应行和响应头),这个口径比等第一个body字节到更准 - 请求发送耗时:
onRequestSend到onRequestSent的时间差,就是整个请求报文完全写入网络的耗时
几个注意点
- 连接复用场景下,DNS、TCP、TLS相关的attempt事件都不会触发,这几个阶段耗时直接记0就行,属于正常情况,不是bug
- 记时间戳一律用
System.nanoTime(),别用System.currentTimeMillis(),后者会受系统时间回拨影响,算出来的耗时可能出离谱的错 - 如果要区分连接复用和新建连接的场景,你可以判断有没有触发过
onTcpConnectSuccess事件,触发过就是新建连接,否则就是复用连接
内容的提问来源于stack exchange,提问作者Mary123
相关产品推荐
相关产品推荐

