Curl笔记
curl常用参数与示例
curl 结合代理使用
bash
curl -I --socks5-hostname localhost:2080 https://www.google.com/
curl -I socks5h://localhost:2080 https://www.google.com/
报错:
text
Protocol "socks5h" not supported
要先支持新版本才行,能用socks5h就优先用,更加安全。
-I 参数
使用:curl -I url http响应的头部信息(header)不需要知道返回内容body的时候使用大写-I参数
-i 参数
使用:curl -i url 显示响应头、返回内容body
-v 参数
-v:显示最详细的信息 包含请求头、响应头、返回内容
-o 参数
curl -o:保存为文件
-L 参数
curl -L 重定向301,追踪重定向
-D 参数
curl url -D 存放的文件名 将响应头存输出到文件中。-D可以放在url前面或后面都行
-X 参数
curl -X (POST、GET、PUT、PATCH、DELETE、HEAD) url -X指定请求方法
-H 参数
curl -H "name: value" 自定义请求头,只是请求,服务器并不一定返回。 curl -H "name:" 参数名后面留空,表示移除这个请求头
-k 参数
具体来说,-k 参数或 --insecure 参数的作用如下:
- 忽略SSL证书验证:在使用HTTPS协议进行数据传输时,curl 默认会验证服务器的SSL证书是否有效。如果证书无效或过期,curl 将拒绝连接。使用 -k 参数后,curl 会忽略这些证书错误,继续进行数据传输。
- 不安全:使用 -k 参数会降低安全性,因为它不再验证SSL证书的有效性。这意味着无法确保连接的安全性,可能会受到中间人攻击。
- 使用场景:在开发或测试环境中,如果需要快速验证功能而不需要关注安全性,可能会使用 -k 参数。但在生产环境中,出于安全考虑,通常不建议使用。
curl分析请求耗时
与耗时相关的参数
以下是一些常见的 curl --write-out 即:curl -w 参数及其解释:
text
%{http_code}:输出HTTP状态码。
%{time_starttransfer}:从开始到服务器开始发送数据的时间(秒)。等同于从请求开始到第一个字节开始传输的时间
%{time_connect}:从开始到建立TCP连接的时间(秒)。
%{time_appconnect}:从开始到SSL/SSH等应用层握手完成的时间(秒)。
%{time_pretransfer}:从开始到准备发送请求的时间(秒)。
%{time_redirect}:重定向所花费的总时间(秒)。
%{time_total}:完成请求所花费的总时间(秒)。
%{size_download}:下载的字节数。
%{size_upload}:上传的字节数。
%{url_effective}:请求的最终URL(可能包含重定向)。
%{response_rate}:数据传输速率(字节/秒)。
%{ssl_verify_result}:SSL证书验证结果。
%{filename_effective}:输出文件的名称(如果使用了-O选项)。
%{method}:请求使用的HTTP方法。
%{response_header}:输出整个响应头。
核心参数再理解:
- time_namelookup:DNS 域名解析的时候,就是把域名转换成 ip 地址的过程
- time_connect:TCP 连接建立的时间,就是三次握手的时间
- time_appconnect:SSL/SSH 等上层协议建立连接的时间,比如 connect/handshake 的时间
- time_pretransfer:是从 curl 开始执行到请求数据开始传输给服务器之前的准备时间。(还没正式发送请求- 体给服务器)
- time_starttransfer:从请求开始到第一个字节开始传输的时间(服务器处理完毕,开始给客户端发响应)
- time_total:这次请求花费的全部时间
以上参数全部都是从开始请求那一刻开始算起。所以要算单独一个阶段,都要减去前面无关的。
使用示例
可以将这些参数组合起来,创建一个自定义的输出格式。例如:
bash
curl -w "HTTP Status: %{http_code}\n" -w "Total Time: %{time_total} seconds\n" http://example.com/
我们先看看一个简单的请求,没有重定向,也没有 SSL 协议的时间:
bash
curl -o /dev/null -w "@curl-format.txt" -s -L "http://cizixs.com"
结果:
time_namelookup: 0.012
time_connect: 0.227
time_appconnect: 0.000
time_redirect: 0.000
time_pretransfer: 0.227
time_starttransfer: 0.443
----------
time_total: 0.867
可以看到这次请求各个步骤的时间都打印出来了,每个数字的单位都是秒(seconds),这样可以分析哪一步比较耗时,方便定位问题。这个命令各个参数的意义:
text
-w:从文件中读取要打印信息的格式
-o /dev/null:把响应的内容丢弃,因为我们这里并不关心它,只关心请求的耗时情况
-s:不要打印进度条
从这个输出,我们可以算出各个步骤的时间:
DNS 查询:12ms
TCP 连接时间:pretransfter(227) - namelookup(12) = 215ms
发送请求体+服务器处理时间:starttransfter(443) - pretransfer(227) = 216ms
内容传输时间:total(867) - starttransfer(443) = 424ms
来个比较复杂的,访问某度首页,带有中间有重定向和 SSL 协议:
bash
curl -w "@curl-format.txt" -o /dev/null -s -L "https://baidu.com"/
结果:
time_namelookup: 0.012
time_connect: 0.018
time_appconnect: 0.328
time_redirect: 0.356
time_pretransfer: 0.018
time_starttransfer: 0.027
----------
time_total: 0.384
可以看到 time_appconnect 和 time_redirect 都不是 0 了,其中 SSL 协议处理时间为 328-18=310ms。而且 pretransfer 和 starttransfer 的时间都缩短了,这是重定向之后请求的时间。(有重定向以后,需要考虑的比较多,实际拨测中,不要选取可能重定向的)
shell脚本使用指引
根据curl的-w参数功能,可以写脚本用来分析http请求耗时,以及tls是否超时,然后告警出来,是可行的拨测监控方式。设置定时方式,可以使用crontab,或者使用更加靠谱的python定时任务包:crond。
shell脚本示例:
sh
#!/bin/bash
echo '----------------------本次拨测开始----------------------'
# 需要检测的url列表 从文件获取 不同机器路径可能不一样
DOMAIN_URL_FILE="domain.txt"
# 获取本机ip
GETIP=`ip a | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | grep -v '127.0.0.1' | head -n 1`
# 设置超时的时间间隔
TIME_OUT=1.0
echo "拨测机器ip:$GETIP"
# 机器主机名
MACHINE=`hostname`
echo "机器名:$MACHINE"
# 当前时间 也就是发送告警时设置的告警时间 它比实际告警出来的时间要更早一些
ADATE=$(date +'%Y-%m-%d %H:%M:%S')
echo "本次拨测时间:$ADATE"
echo "超时设置:$TIME_OUT 秒"
# 生成随机IP函数 不发送随机ip 蓝鲸告警会收敛过滤
function generate_random_ip()
{
local ip1=$((RANDOM % 256))
local ip2=$((RANDOM % 256))
local ip3=$((RANDOM % 256))
local ip4=$((RANDOM % 256))
echo "${ip1}.${ip2}.${ip3}.${ip4}"
}
# 发送tls超时告警到蓝鲸
alarm()
{
curl -d '{
"ip": "'$(generate_random_ip)'",
"source_time": "'"${ADATE}"'",
"alarm_type": "tls告警",
"level": "fatal",
"alarm_name": "tls连接超时告警",
"alarm_content": "curl拨测告警明细: '$MACHINE' - '$GETIP' 访问url:'$3' 握手时长超过'$TIME_OUT'秒触发告警。具体数据:tcp建立连接耗时'$2'秒,tls建立连接耗时'$1'秒,客户端发送请求之前总耗时'$7'秒,客户端开始发送请求+服务器处理完请求后开始返回内容总耗时'$4'秒,服务器响应内容传输总耗时'$5'秒,本次http请求总的访问耗时'$6'秒,请核实确认tls/ssl相关。",
"action": "firing"
}' https://appops.haday.cn/o/cw_uac_saas/alarm/collect/event/api/34da4ffa-4158-41c9-a0c4-c4380f67c520/ -H 'X-Secret:AncecVal59WdMDr8KP0BivbJQLym4yu4' -k
echo "告警已发送"
}
# 计算tls与tcp握手时长,如果二者之差大于超时时间,则发送告警
# 注意 tls和tcp时间可能获取不到,比如请求失败或者超时 这些场景将不告警
print_tcp_tls_time()
{
cat $DOMAIN_URL_FILE | while read line
do
echo "拨测URL:$line"
res=`curl -w "%{time_connect},%{time_appconnect},%{time_namelookup},%{time_total},%{url_effective},%{http_code},%{time_redirect},%{time_pretransfer},%{time_starttransfer},%{size_download},%{speed_download}\n" -so /dev/null -L $line`
echo "拨测结果:$res"
url_effective=`echo $res | cut -d "," -f 5`
echo "最终访问的url-url_effective: $url_effective"
http_code=`echo $res | cut -d "," -f 6`
echo "HTTP状态码-http_code: $http_code"
size_download=`echo $res | cut -d "," -f 10`
echo "下载大小-size_download: $size_download bytes"
speed_download=`echo $res | cut -d "," -f 11`
echo "下载速率-speed_download: $speed_download bytes/s"
echo "耗时分析开始。============================================="
ns_time=`echo $res | cut -d "," -f 3 | bc`
echo "开始请求到DNS解析完成时间-time_namelookup: $ns_time"
tcp_time=`echo $res | cut -d "," -f 1 | bc`
echo "从开始请求到建立tcp连接完成时间-time_connect: $tcp_time"
# tcp建立时间 不包含dns解析
only_tcp_time=$(echo "$tcp_time - $ns_time" | bc)
echo "tcp建立时间 不包含dns解析-only_tcp_time: $only_tcp_time"
tls_time=`echo $res | cut -d "," -f 2 | bc`
echo "从开始请求到建立tls连接完成时间-time_appconnect: $tls_time"
# tls连接建立时间 不包含dns解析 不包含tcp连接
only_tls_time=$(echo "$tls_time - $tcp_time" | bc)
echo "tls连接建立时间 不包含dns解析 不包含tcp连接-only_tls_time: $only_tls_time"
time_redirect=`echo $res | cut -d "," -f 7 | bc`
echo "重定向耗时-time_redirect: $time_redirect"
time_pretransfer=`echo $res | cut -d "," -f 8 | bc`
echo "传输前准备时间-time_pretransfer: $time_pretransfer"
time_starttransfer=`echo $res | cut -d "," -f 9 | bc`
echo "开始传输时间-time_starttransfer: $time_starttransfer"
time_total=`echo $res | cut -d "," -f 4 | bc`
echo "----------总的耗时-也等于下面三项耗时之和---------: $time_total"
echo "发送请求体给服务器之前,耗时:$time_pretransfer"
# 请求体发送给服务器,并且服务器处理完毕开始返回第一个字节的时间
server_time=$(echo "$time_starttransfer - $time_pretransfer " | bc)
echo "发送请求体 + 服务器处理时间-server_time: $server_time"
# 内容传输时间 服务器开始返回第一个字节 到全部传输完毕的耗时
content_trans_time=$(echo "$time_total - $time_starttransfer " | bc)
echo "内容传输时间-content_trans_time: $content_trans_time"
echo "耗时分析结束。=============================================="
time_out=`echo $TIME_OUT|bc`
tls_timeout=$(echo "$tls_time - $tcp_time - $time_out" | bc)
if [ $(echo "$tls_timeout > 0" | bc) -eq 1 ]; then
echo "tls连接建立耗时,减去设置的超时timeout,结果 > 0 超时了 异常,发送告警"
# 此处alarm函数会获取后面的参数列表 $1 $2 $3等
alarm $only_tls_time $only_tcp_time $line $server_time $content_trans_time $time_total $time_pretransfer
else
echo "tls连接建立耗时,减去设置的超时timeout,结果 < 0 没超时 正常"
fi
done
}
print_tcp_tls_time
echo '----------------------本次拨测结束----------------------'
crontab脚本定时:
bash
* */5 * * * sh /app/tls_alarm.sh 2>&1 >> /app/log.txt
crontab这种方式实际运行中不稳定,尤其是涉及频繁修改执行间隔,可能有bug,运行有时不生效,所以改成python的定时包。
nohup python cron_sh.py > /dev/null 2>&1 &
同样也是依赖系统的crond服务,关闭crond后就无法工作。所以要确保crond正常工作。
sudo systemctl stop crond
sudo systemctl start crond
sudo systemctl status crond
python定时包crond使用示例:
py
import subprocess
from datetime import datetime
from apscheduler.schedulers.blocking import BlockingScheduler
def run_script():
# 获取当前日期并格式化为YYYY-MM-DD
current_date = datetime.now().strftime('%Y-%m-%d')
# 创建日志文件名,每天一个
log_filename = f'./logs/{current_date}.log'
with open(log_filename, 'a') as log_file:
try:
# 使用subprocess.Popen执行shell脚本
process = subprocess.Popen(['./tls.sh'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = process.communicate()
# 获取当前时间戳
current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
# 写入脚本输出到log文件
log_file.write(f'[{current_time}] Output:\n')
if stdout:
log_file.write(stdout.decode() + '\n')
if stderr:
log_file.write(f'[{current_time}] Error:\n')
log_file.write(stderr.decode() + '\n')
except Exception as e:
# 获取当前时间戳
current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
# 写入异常信息到log文件
log_file.write(f'[{current_time}] An error occurred: {e}\n')
# 创建一个调度器实例
scheduler = BlockingScheduler()
# 每分钟执行一次
scheduler.add_job(run_script, 'interval', minutes=1)
# 启动调度器
scheduler.start() # 这行代码在实际环境中会启动调度器,但在这里不会执行
参考: