You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Perl LWP获取HTTPS请求各阶段耗时:DNS解析、SSL连接等

拆分HTTPS请求各阶段耗时的实现方案

要统计DNS解析、SSL连接等各阶段的耗时,LWP::UserAgent的默认get方法无法直接拆分这些阶段,需要手动拆解请求流程并在每个关键步骤前后记录时间。以下是基于底层模块的实现方案,使用Time::HiRes获取高精度计时,拆分每个请求阶段:

实现代码

use strict;
use warnings;
use Time::HiRes qw(gettimeofday tv_interval);
use Net::DNS;
use IO::Socket::INET;
use IO::Socket::SSL;
use HTTP::Request;
use HTTP::Response;
use URI;

sub get_url_with_timing {
    my ($url, $alltimeout) = @_;
    $alltimeout ||= 3; # 默认超时3秒

    # 解析URL提取关键信息
    my $uri = URI->new($url) or return { error => "URL解析失败" };
    my $host = $uri->host or return { error => "无法提取主机名" };
    my $port = $uri->port || ($uri->scheme eq 'https' ? 443 : 80);
    my $path = $uri->path_query || '/';

    my %timings;
    my $start;

    # 1. DNS解析阶段计时
    $start = [gettimeofday];
    my $dns_resolver = Net::DNS::Resolver->new;
    my $dns_query = $dns_resolver->search($host);
    unless ($dns_query) {
        return { error => "DNS解析失败: " . $dns_resolver->errorstring, timings => \%timings };
    }
    $timings{dns解析} = tv_interval($start) * 1000; # 转换为毫秒

    # 2. TCP连接阶段计时
    $start = [gettimeofday];
    my $tcp_socket = IO::Socket::INET->new(
        PeerHost => $host,
        PeerPort => $port,
        Timeout  => $alltimeout,
    );
    unless ($tcp_socket) {
        return { error => "TCP连接失败: $!", timings => \%timings };
    }
    $timings{TCP连接} = tv_interval($start) * 1000;

    # 3. SSL握手阶段计时(仅HTTPS)
    if ($uri->scheme eq 'https') {
        $start = [gettimeofday];
        IO::Socket::SSL->start_SSL($tcp_socket,
            SSL_hostname => $host,
            SSL_verify_mode => SSL_VERIFY_NONE, # 如需证书验证,改为SSL_VERIFY_PEER并配置CA路径
        );
        unless ($tcp_socket->is_SSL) {
            return { error => "SSL握手失败: " . IO::Socket::SSL::errstr(), timings => \%timings };
        }
        $timings{SSL握手} = tv_interval($start) * 1000;
    }

    # 4. HTTP请求/响应阶段计时
    $start = [gettimeofday];
    my $http_request = HTTP::Request->new(GET => $path);
    $http_request->header('User-Agent' => 'CHECKER (Win98; U)');
    $http_request->header('Accept-Charset' => 'utf-8');
    $http_request->protocol('HTTP/1.1');
    $http_request->header('Host' => $host);

    # 发送请求并接收响应
    $tcp_socket->print($http_request->as_string("\r\n"));
    my $http_response = HTTP::Response->parse(do { local $/; $tcp_socket->getline });
    $timings{HTTP请求响应} = tv_interval($start) * 1000;

    $tcp_socket->close;

    # 整理返回结果
    if ($http_response->is_success) {
        return {
            status => 200,
            content => $http_response->content,
            timings => \%timings,
        };
    } else {
        return {
            error => "ERROR" . $http_response->status_line,
            timings => \%timings,
        };
    }
}

# 使用示例
my $result = get_url_with_timing('https://example.com', 3);
if ($result->{error}) {
    print "请求失败: $result->{error}\n";
} else {
    print "请求成功,状态码: $result->{status}\n";
    print "各阶段耗时(毫秒):\n";
    foreach my $stage (sort keys %{$result->{timings}}) {
        printf "  %-12s: %.2f ms\n", $stage, $result->{timings}->{$stage};
    }
}

关键说明

  • DNS解析: 用Net::DNS单独解析域名,避免和TCP连接阶段的时间重叠,确保计时准确。
  • TCP连接: 用IO::Socket::INET建立裸TCP连接,单独计时连接耗时。
  • SSL握手: 对HTTPS请求,调用IO::Socket::SSL的start_SSL方法完成握手,单独计时该阶段。
  • HTTP请求响应: 构造并发送HTTP请求、接收响应,计时整个交互过程。
  • 精度控制: 使用Time::HiRes的tv_interval计算时间差,转换为毫秒便于查看。

注意事项

  • SSL证书验证:如果需要验证服务器证书,将SSL_verify_mode改为SSL_VERIFY_PEER,并通过SSL_ca_path或SSL_ca_file配置CA证书路径。
  • 超时处理:每个阶段的超时通过对应模块的参数控制(如IO::Socket::INET的Timeout),确保整体超时符合需求。

内容的提问来源于stack exchange,提问作者thepromo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 06:18:24