利用zabbix api计算流量月95值
Reverse Lv4

直接提供脚本

虽然这个脚本是让 AI 帮忙写的,但经过实际验证,是有效的,并且我一直在使用

注意这的 1,2,3 步骤中的一些参数需要自己配置,比如时间范围,单位换算,文件路径等.

根据你实际情况改改,另外就是一些必备软件包得装一下,比如jq bc curl什么的.

最重要的是ITEM_ID这个变量,你需要从 zabbix 中找到你想计算 95 的流量图,然后把他的 itemid 拿出来.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#!/usr/bin/env bash

# 1. Zabbix API 参数配置
ZABBIX_URL="https://xxxxxxx/api_jsonrpc.php" # 你的 zabbix API 地址
ITEM_ID=" 123456" # 你的 item id,这个是流量图的 itemid,可以自己找一下
AUTH_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # 你的 auth token

# 2. 时间范围,假设(2025-04-01 ~ 2025-04-30)
TIME_FROM=1743436800 # 2025-04-01 00:00:00 转换到对应的秒级时间戳
TIME_TILL=1746028799 # 2025-04-30 23:59:59 转换到对应的秒级时间戳

# 3. 单位换算:字节 => MB
BYTES_TO_MB=1048576
JSON_PATH="/tmp/zabbix.json" # zbx 请求数据
CSV_PATH="/tmp/zabbix_full_data.csv" # 全量数据(每 5 分钟一个点的 95 值)
ROW_PATH="/tmp/zabbix_row_data.csv" # 每分钟数据

# 4. 获取历史数据(只下载一次)
if [ ! -f "$JSON_PATH" ]; then
echo "拉取 Zabbix 数据中..."
RESPONSE=$(curl -s "$ZABBIX_URL" \
-H "Content-Type: application/json" \
--data-raw "{
\"jsonrpc\": \"2.0\",
\"id\": 1,
\"method\": \"history.get\",
\"params\": {
\"output\": \"extend\",
\"itemids\": \"$ITEM_ID\",
\"history\": 3,
\"time_from\": $TIME_FROM,
\"time_till\": $TIME_TILL,
\"sortfield\": [\"clock\", \"itemid\"],
\"sortorder\": \"ASC\"
},
\"auth\": \"$AUTH_TOKEN\"
}")

echo "$RESPONSE" >"$JSON_PATH"
else
RESPONSE=$(awk 'NF' "$JSON_PATH")
fi

# 5. 提取时间和流量,转换为 MB,并写入 CSV 文件
echo "Timestamp,Datetime,Value(MB)" >"$CSV_PATH"

# 暂存 5 分钟分组后的最大值
declare -A GROUPED_MAX

# 从 JSON 中提取数据并分组
while IFS= read -r line; do
CLOCK=$(echo "$line" | jq -r '.clock')
VALUE=$(echo "$line" | jq -r '.value')
VALUE_MB=$(awk -v val="$VALUE" -v factor="$BYTES_TO_MB" 'BEGIN {printf "%.2f", val / factor}')

# 向下取整到 5 分钟(300 秒)粒度
GROUP_TS=$((CLOCK / 300 * 300))

# 如果该组未存在,或当前值更大,更新最大值
if [[ -z "${GROUPED_MAX[$GROUP_TS]}" ]] || (($(echo "$VALUE_MB > ${GROUPED_MAX[$GROUP_TS]}" | bc -l))); then
GROUPED_MAX[$GROUP_TS]=$VALUE_MB
fi

# 写出每分钟的数据,带可读时间
DATETIME=$(date -d "@$CLOCK" "+%Y-%m-%d %H:%M:%S")
echo "$DATETIME,$VALUE_MB" >>"$ROW_PATH"

done < <(echo "$RESPONSE" | jq -c '.result | sort_by(.clock)[]')

# 提取排序后的 5 分钟粒度时间点和值,写入 CSV,并保存值用于 P95
VALUES_MB=()
for ts in $(printf "%s\n" "${!GROUPED_MAX[@]}" | sort -n); do
val="${GROUPED_MAX[$ts]}"
datetime=$(date -d "@$ts" "+%Y-%m-%d %H:%M:%S")
echo "$ts,$datetime,$val" >>"$CSV_PATH"
VALUES_MB+=("$val")
done

# 6. 统计与 P95 计算
TOTAL=${#VALUES_MB[@]}
if [[ $TOTAL -lt 1 ]]; then
echo "未获取到有效数据,退出。"
exit 1
fi

SORTED=($(printf "%s\n" "${VALUES_MB[@]}" | sort -n))
PERCENTILE_INDEX=$(echo "$TOTAL * 0.95" | bc | awk '{printf("%d", ($1==int($1))?$1:$1+1)}')
P95_VALUE=${SORTED[$((PERCENTILE_INDEX - 1))]}

# 7. 输出汇总
echo "✅ 数据点总数: $TOTAL"
echo "📈 95 百分位位置: $PERCENTILE_INDEX"
echo "📊 整体 95 值: $P95_VALUE MB"
echo "📁 CSV 文件已生成: $CSV_PATH"

最终结果

大概长这样:

1
2
3
4
5
6
7
✅ 数据点总数: 3589

📈 95 百分位位置: 3410

📊 整体 95 值: 1955.15 MB

📁 CSV 文件已生成: /tmp/zabbix_full_data.csv