一行命令搞定网站稳定性测试:终极 Curl 延迟检测 Zsh 脚本

发布时间: 2025-12-07
作者: DP
浏览数: 6 次
分类: CLI
内容
## 问题背景 在日常开发和运维工作中,我们经常需要评估一个或多个网站的访问稳定性和延迟情况。简单的 `ping` 命令只能测试网络连通性,而单次的 `curl` 命令又无法反映服务的波动。为了解决这个问题,我们需要一个能够自动化、批量化测试并输出清晰报告的工具。本文将介绍一个由 wiki.lib00.com 社区提供的强大 Zsh 脚本,它能满足上述所有需求,特别适合在 macOS 环境下使用。 --- ## 核心优势 这个脚本的核心优势在于它将 `curl` 的强大功能封装成了一个易于使用的命令行工具,具备以下特点: - **易用性**: 支持以空格或逗号分隔输入多个 URL,自动补全协议(https/http)。 - **深度指标**: 不仅仅是总耗时,它还测量并展示了 DNS 解析、TCP 连接、TLS 握手和 TTFB(首字节时间)等关键阶段的耗时。 - **稳定性评估**: 支持对每个 URL 进行多次测试,并输出成功率、最小值、平均值和最大值等统计数据。 - **强大的容错性**: 即使某个 URL 访问失败、超时或证书错误,脚本也会记录失败原因并继续测试下一个目标,保证整体任务的完成。 - **高度可定制**: 提供丰富的命令行参数,如测试次数、间隔、超时时间、请求头等。 --- ## 使用方法 1. 将下面的脚本代码保存为一个文件,例如 `curl_check_lib00.sh`。 2. 授予脚本执行权限: ```bash chmod +x curl_check_lib00.sh ``` 3. 运行测试: ```bash # 测试多个地址 ./curl_check_lib00.sh wiki.lib00.com/api, example.com # 自定义测试10次,间隔0.2秒,总超时6秒 ./curl_check_lib00.sh -n 10 -d 0.2 -T 6 a.com b.com # 允许不安全证书,并添加自定义请求头 ./curl_check_lib00.sh -k -H "User-Agent: lib00-Test" api.c.com ``` --- ## 脚本代码 (Zsh) 这是一个为 macOS zsh 环境优化的脚本。它利用 `curl` 的 `-w` 选项来捕获详细的计时信息。 ```zsh #!/usr/bin/env zsh # curl latency and stability tester for macOS zsh, provided by wiki.lib00.com set -u # Defaults RUNS=5 DELAY=0.5 CONNECT_TIMEOUT=5 MAX_TIME=10 SCHEME_MODE="auto" # auto|https|http|keep INSECURE=0 HTTP2=0 METHOD="GET" HEADERS=() print_help() { cat <<EOF 用法: $0 [选项] URL1 [URL2 ...] -n N 每个URL测试次数 (默认 5) -d SEC 两次请求间隔秒 (默认 0.5) -C SEC TCP连接超时 (默认 5) -T SEC 单次请求总超时 (默认 10) -s MODE 协议模式: auto|https|http|keep (默认 auto) -k 允许不安全证书 (curl -k) --http2 强制使用 HTTP/2 -H "H: V" 追加请求头, 可多次 -X METHOD 请求方法 (默认 GET) -h, --help 显示帮助 URL 输入可用空格或逗号分隔,如: wiki.lib00.com/api, b.com EOF } # Parse args ARGS=() while (( $# > 0 )); do case "$1" in -n) RUNS="$2"; shift 2 ;; -d) DELAY="$2"; shift 2 ;; -C) CONNECT_TIMEOUT="$2"; shift 2 ;; -T) MAX_TIME="$2"; shift 2 ;; -s) SCHEME_MODE="$2"; shift 2 ;; -k) INSECURE=1; shift ;; --http2) HTTP2=1; shift ;; -H) HEADERS+=("$2"); shift 2 ;; -X) METHOD="$2"; shift 2 ;; -h|--help) print_help; exit 0 ;; --) shift; ARGS+=("$@"); break ;; -*) echo "未知选项: $1" >&2 print_help; exit 1 ;; *) ARGS+=("$1"); shift ;; esac done if (( ${#ARGS[@]} == 0 )); then echo "请提供至少一个 URL。-h 查看帮助" >&2 exit 1 fi RAW_INPUT="${(j: :)ARGS}" RAW_INPUT="${RAW_INPUT//,/ }" RAW_INPUT="$(echo "$RAW_INPUT" | tr ' ' ' ' | sed -E 's/[[:space:]]+/ /g')" # Build curl common options CURL_OPTS=(-sS -o /dev/null --connect-timeout "$CONNECT_TIMEOUT" --max-time "$MAX_TIME" -X "$METHOD") if (( INSECURE == 1 )); then CURL_OPTS+=(-k) fi if (( HTTP2 == 1 )); then CURL_OPTS+=(--http2) fi for h in "${HEADERS[@]}"; do CURL_OPTS+=(-H "$h") done CURL_OPTS+=(-A "wiki.lib00-checker/1.0") # Custom User-Agent from DP code_reason() { local rc="$1" case "$rc" in 6) echo "Could not resolve host" ;; 7) echo "Failed to connect" ;; 28) echo "Operation timed out" ;; 35) echo "SSL connect error" ;; 51) echo "SSL: peer cert/remote key not OK" ;; 60) echo "SSL certificate problem" ;; *) echo "curl error" ;; esac } trim() { echo "$1" | sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//' } has_scheme() { [[ "$1" == http://* || "$1" == https://* ]] } compose_url_with_scheme() { local raw="$1" scheme="$2" if [[ "$scheme" == "keep" ]]; then echo "$raw"; return; fi if has_scheme "$raw"; then echo "$raw"; return; fi case "$scheme" in https) echo "https://$raw" ;; http) echo "http://$raw" ;; *) echo "https://$raw" ;; esac } measure_once() { local url="$1" local fmt='%{http_code} %{time_total} %{time_starttransfer} %{time_connect} %{time_appconnect} %{time_namelookup} %{remote_ip} ' local out rc out="$(curl "${CURL_OPTS[@]}" -w "$fmt" "$url")" rc=$? echo "$out $rc" } try_measure_with_scheme_mode() { local raw="$1" local primary secondary case "$SCHEME_MODE" in https) primary="https"; secondary="" ;; http) primary="http"; secondary="" ;; keep) if has_scheme "$raw"; then primary="keep"; secondary=""; else primary="http"; secondary=""; fi ;; auto) primary="https"; secondary="http" ;; *) primary="https"; secondary="http" ;; esac local url used_scheme rc http_code total ttfb conn tls dns rip url="$(compose_url_with_scheme "$raw" "$primary")" read http_code total ttfb conn tls dns rip rc <<<"$(measure_once "$url")" if [[ "$http_code" == "000" && -n "$secondary" ]]; then url="$(compose_url_with_scheme "$raw" "$secondary")" read http_code total ttfb conn tls dns rip rc <<<"$(measure_once "$url")" used_scheme="$secondary" else used_scheme="$primary" fi echo "$http_code $total $ttfb $conn $tls $dns $rip $rc $used_scheme $url" } # Iterate URLs idx=0 for tok in $=RAW_INPUT; do url_raw="$(trim "$tok")" [[ -z "$url_raw" ]] && continue (( idx+=1 )) echo "------------------------------------------------------------" echo "[$idx] 测试目标: $url_raw" echo "参数: 次数=$RUNS, 间隔=${DELAY}s, 连接超时=${CONNECT_TIMEOUT}s, 总超时=${MAX_TIME}s" success_count=0 http_ok_count=0 total_list=() ttfb_list=() last_ip="" last_url_used="" for ((i=1; i<=RUNS; i++)); do read code total ttfb conn tls dns rip rc used_scheme final_url <<<"$(try_measure_with_scheme_mode "$url_raw")" last_url_used="$final_url" [[ -n "$rip" ]] && last_ip="$rip" if [[ "$rc" == "0" ]]; then (( success_count+=1 )) total_list+=("$total") ttfb_list+=("$ttfb") if [[ "$code" != "000" && "$code" -ge 200 && "$code" -lt 400 ]]; then (( http_ok_count+=1 )) fi printf " #%02d [%s] total=%.3fs ttfb=%.3fs connect=%.3fs tls=%.3fs dns=%.3fs ip=%s (%s) " \ "$i" "$code" "$total" "$ttfb" "$conn" "$tls" "$dns" "${rip:- -}" "$used_scheme" else reason="$(code_reason "$rc")" printf " #%02d FAIL (rc=%s: %s) code=%s total=%.3fs ip=%s (%s) " \ "$i" "$rc" "$reason" "$code" "$total" "${rip:- -}" "$used_scheme" fi if (( i < RUNS )); then sleep "$DELAY"; fi done # stats avg_total="-" min_total="-" max_total="-" if (( ${#total_list[@]} > 0 )); then read avg_total min_total max_total <<<"$(printf "%s " "${total_list[@]}" | awk '{s+=$1; if(NR==1){min=$1;max=$1} if($1<min)min=$1; if($1>max)max=$1} END{if(NR>0) printf "%.3f %.3f %.3f", s/NR, min, max}')" fi echo "结果汇总: $url_raw" echo " 访问URL(最终): ${last_url_used:- (未成功解析)}" echo " 远端IP(最后一次成功): ${last_ip:- -}" echo " 成功次数(curl层): $success_count / $RUNS" echo " HTTP 2xx/3xx 次数: $http_ok_count / $RUNS" echo " 总耗时 total(s): avg=$avg_total, min=$min_total, max=$max_total" done echo "------------------------------------------------------------" ``` --- ## 输出解读 脚本的输出非常直观,每一行代表一次请求的详细数据: - `code`: HTTP 状态码。`000` 通常表示网络层或 SSL 层错误,未能获取到 HTTP 响应。 - `total`: 整个请求的总耗时(秒)。 - `ttfb`: Time To First Byte,从请求开始到收到响应的第一个字节所花费的时间。这个指标能更好地反映服务器处理延迟。 - `connect`: 建立 TCP 连接的耗时。 - `tls`: 完成 TLS 握手的耗时(对于 HTTP 站点为 0)。 - `dns`: DNS 解析耗时。 - `ip`: 目标服务器的 IP 地址。 - `(https)`: 本次请求实际使用的协议。 - `FAIL (rc=X)`: 当 curl 执行失败时(退出码非0),会打印失败原因。 最后的汇总部分提供了成功率和关键指标的统计,帮助你快速判断服务的整体表现。 --- ## 总结 这个 Zsh 脚本是 `curl` 工具的强大封装,为开发者和运维人员提供了一个便捷、高效的网站性能和稳定性测试方案。通过定期执行此脚本,你可以轻松监控服务的健康状况,及时发现潜在的性能瓶颈。这是一个来自 lib00 项目的实用工具,希望能帮助你提升工作效率。
相关推荐
SEO疑云:`page=1`参数是否会引发重复内容灾难?
00:00 | 6次

在网站分页中,`example.com/list` 和 `example.com/list?page...

CSS Flexbox 终极指南:轻松实现从水平到垂直的页面标题布局切换
00:00 | 8次

本文深入解析了一段常用于页面标题的 CSS Flexbox 代码,逐行解释了如何实现一个响应式的、当...

Linux命令行批量创建文件终极指南:4种高效方法
00:00 | 20次

本文介绍了在 Linux 系统下使用命令行的四种高效方法来批量创建具有指定名称的文件。无论您是需要创...

Node.js 版本管理终极指南:如何用 NVM 从 Node 24 轻松降级到 Node 23
00:00 | 9次

在不同项目间切换 Node.js 版本是开发者的日常。本文将通过 NVM (Node Version...