首页
美图
服务
付费
树洞
云主机
推荐
邻居
支付
开发
书单
更多
我的足迹
罗盘时钟
圈小猫
工作打分
给我留言
本站统计
推荐
M商城
欣悦云店
txt阅读器
VPS监控
证书监控
网址导航
在线工具
Search
1
docker和docker-compose一键安装脚本
9,530 阅读
2
采用Prometheus+Grafana 监控H3C交换机状态
8,507 阅读
3
WooCommerce对接第三方支付插件开发
6,744 阅读
4
docker下运行grafana和grafana Image Renderer
5,817 阅读
5
服务器(vps)性能测试脚本汇总
5,487 阅读
大模型
虚拟化
数据库
运维
基础知识
监控预警
数据展示
运维工具
web安全
系统服务
开发
python
php
java
shell
go
项目
博客
电商
工具
娱乐
综合
VPS相关
规范文档
知识总结
经验分享
读书笔记
关于
Search
标签搜索
django
python
支付对接
运维工具
电商平台
Joe主题
docker
wordpress
woocommerce
支付通道
zabbix
蓝鲸智云
运维
grafana
监控
运维知识
typecho
php
mysql
nginx
行云流水
累计撰写
335
篇文章
累计收到
385
条评论
首页
栏目
大模型
虚拟化
数据库
运维
基础知识
监控预警
数据展示
运维工具
web安全
系统服务
开发
python
php
java
shell
go
项目
博客
电商
工具
娱乐
综合
VPS相关
规范文档
知识总结
经验分享
读书笔记
关于
页面
美图
服务
树洞
云主机
邻居
支付
书单
给我留言
本站统计
推荐
M商城
txt阅读器
网址导航
搜索到
1
篇与
的结果
2026-03-29
95计费系统开发过程总结
一、项目背景IDC带宽计费普遍采用95百分位计费法。原理很简单:一个月内每5分钟采集一次带宽数据,按值从大到小排序,去掉最高的5%,剩余数据中的最大值即为计费带宽。这种计费方式对客户有利——偶尔的流量突发不会被计入费用。但对运维来说,每月手动统计、制表、发邮件是重复劳动,以前每月我都需要拿出1天时间,来导出zabbix的监控数据,并计算95值,然后做报表,发邮件。开发这个系统的目的就是把这些事情自动化,提高效率节省时间。二、系统介绍首页首页展示系统概况,包括项目数量、报告统计、快捷入口等。项目管理项目是系统的核心概念,每个项目对应一个计费主体(比如一个客户或一条专线)。项目下挂载多个端口,端口信息从Zabbix同步过来。新建项目时配置基本信息:趋势方向(UP/DOWN,决定取流入还是流出)、数据源、Grafana Dashboard等。数据同步流量数据存储在Zabbix的MySQL数据库中。系统定时从Zabbix同步数据到本地表,避免直接查询生产库造成压力。95计费结果选择时间范围后,系统自动计算各项目的95值。支持一键复制为Markdown表格,方便粘贴到汇报文档。报告管理报告是月度统计的产出物,包含95值、峰值、Grafana截图、CSV数据文件。报告支持批量创建、状态管理、一键发送邮件。数据管理支持项目配置的导入导出、流量数据的导出,便于备份和迁移。系统设置配置通过环境变量管理,包括数据库连接、Grafana地址、SMTP信息等。邮件通知报告发送后,收件人会收到包含统计结果、图表附件的邮件。三、架构设计整体架构┌─────────────────────────────────────────────────────────────┐ │ 前端 (Vue 3) │ │ Element Plus + Vue Router + Axios │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 后端 (Go + Gin) │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ Handler │──│ Service │──│Repository│──│ Model │ │ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ └─────────────────────────────────────────────────────────────┘ │ ┌───────────────┼───────────────┐ ▼ ▼ ▼ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ MySQL │ │ Zabbix │ │ Grafana │ │ (zreport)│ │ (只读) │ │ (截图) │ └──────────┘ └──────────┘ └──────────┘目录结构aireport/ ├── cmd/server/main.go # 入口 ├── internal/ │ ├── config/ # 配置加载 │ ├── models/ # 数据模型 │ ├── repository/ # 数据访问层 │ ├── services/ # 业务逻辑层 │ ├── handlers/ # HTTP处理器 │ └── router/ # 路由配置 ├── pkg/ │ ├── utils/ # 工具函数(95计算) │ └── response/ # 统一响应格式 ├── web/ # 前端项目 │ └── src/ │ ├── api/ # 接口封装 │ ├── views/ # 页面组件 │ └── router/ # 前端路由 └── exports/ # 导出文件存储技术选型层面技术选择理由后端框架Gin轻量、性能好、社区活跃ORMGORM功能完善、开发效率高前端框架Vue 3组合式API、响应式设计方便UI组件库Element Plus组件丰富、文档完善数据库MySQL业务数据量小、关系型足够四、核心代码95计费算法95计费的核心是排序取百分位,实现并不复杂:// Calculate95thPercentile 计算95计费 // 排序后取第5%位置的值 func Calculate95thPercentile(data []DataPoint, unitBase int) BillingResult { if len(data) == 0 { return BillingResult{} } // 确定进制,影响Gbps转换 divisor := float64(1e9) // 1000进制 if unitBase == 1024 { divisor = 1073741824 // 1024进制 } // 按值降序排序 sort.Slice(data, func(i, j int) bool { return data[i].Value > data[j].Value }) // 计算要去掉的数据条数(5%) totalCount := len(data) removeCount := int(math.Round(float64(totalCount) / 20)) // 95值就是第removeCount位置的那个值 index95 := removeCount if index95 >= totalCount { index95 = totalCount - 1 } return BillingResult{ TotalCount: totalCount, RemoveCount: removeCount, Result95: data[index95].Value, Result95Gbps: float64(data[index95].Value) / divisor, PeakValue: data[0].Value, PeakGbps: float64(data[0].Value) / divisor, } }这里有个细节:进制选择。Grafana默认用1000进制(1Gbps = 10^9 bps),但有些场景用1024进制。系统支持在项目级别配置,保证计算结果和监控图表一致。多端口数据合并一个项目可能有多个端口,需要把所有端口的流量按时间点累加:// combineByTime 按时间点合并数据 func (s *AnalysisService) combineByTime(data []utils.DataPoint) []utils.DataPoint { timeMap := make(map[int]uint64) for _, d := range data { timeMap[d.Clock] += d.Value } result := make([]utils.DataPoint, 0, len(timeMap)) for clock, value := range timeMap { result = append(result, utils.DataPoint{ Clock: clock, Value: value, }) } return result }合并后再进行95计算,得到的是项目整体带宽。Grafana截图报告需要附带流量图表,直接调用Grafana的渲染接口:func (s *ReportService) downloadGrafanaImage(project *models.Project, startTs, endTs int) (string, error) { url := fmt.Sprintf("%s/render/d/%s?from=%d&to=%d&width=%d&height=%d", s.grafanaURL, project.GrafanaUID, startTs*1000, endTs*1000, project.ChartWidth, project.ChartHeight, ) req, _ := http.NewRequest("GET", url, nil) req.Header.Set("Authorization", s.grafanaAPIKey) client := &http.Client{Timeout: 30 * time.Second} resp, err := client.Do(req) // ... 保存图片 }邮件发送报告准备好后,调用SMTP发送:func (s *EmailService) SendReportWithAttachment( report *models.Report, imageData []byte, csvData []byte, csvFilename string, recipients []string, ) error { m := gomail.NewMessage() m.SetHeader("From", s.from) m.SetHeader("To", recipients...) m.SetHeader("Subject", report.Title) // HTML正文 body := fmt.Sprintf(` <h3>%s</h3> <p>月份: %s</p> <p>95计费值: %.2f Gbps</p> <p>峰值: %.2f Gbps</p> `, report.Title, report.Month, report.Result95, report.PeakValue) m.SetBody("text/html", body) // 附件 if imageData != nil { m.Attach("chart.png", gomail.SetCopyFunc(func(w io.Writer) error { _, err := w.Write(imageData) return err })) } // 发送 d := gomail.NewDialer(s.host, s.port, s.user, s.password) return d.DialAndSend(m) }五、数据模型主要表结构// Project 项目 type Project struct { ID uint `gorm:"primaryKey"` Name string `gorm:"uniqueIndex"` Host string // 交换机主机 Trend string // UP/DOWN Source string // 数据源表名 UnitBase int // 进制: 1000 或 1024 GrafanaUID string // Grafana Dashboard ID Ports []Port `gorm:"foreignKey:ProjectID"` } // Port 端口 type Port struct { ID uint `gorm:"primaryKey"` ProjectID uint Name string ItemIDIn uint64 // Zabbix流入监控项ID ItemIDOut uint64 // Zabbix流出监控项ID } // Report 报告 type Report struct { ID uint ProjectID uint Month string // 2026-02 Result95 float64 // Gbps PeakValue float64 // Gbps Status string // draft/sent/failed }Zabbix数据表系统直接读取Zabbix的history_uint表,结构如下:-- Zabbix原始表 CREATE TABLE history_uint ( itemid BIGINT UNSIGNED, clock INT, -- Unix时间戳 value BIGINT UNSIGNED, ns INT -- 纳秒 );每个itemid对应一个监控项(比如某端口的入流量)。我们关心的是itemid、clock、value三个字段。六、部署系统采用单机部署,前后端打包在一起:# 构建前端 cd web && npm run build # 构建后端 go build -o aireport ./cmd/server # 运行 ./aireport配置通过.env文件管理:# 数据库 DB_HOST=127.0.0.1 DB_PORT=3306 DB_USER=root DB_PASSWORD=xxx DB_NAME=zreport # Zabbix数据库(只读) ZABBIX_DB_HOST=127.0.0.1 ZABBIX_DB_NAME=zabbix # Grafana GRAFANA_URL=http://localhost:3000 GRAFANA_API_KEY=xxx # 邮件 SMTP_HOST=smtp.163.com SMTP_PORT=465 SMTP_USER=xxx@163.com SMTP_PASSWORD=xxx RECIPIENTS=xxx@qq.com,yyy@163.com七、总结这个系统解决了一个具体的运维痛点:每月的带宽统计和报告发送。开发过程中几个关键点:数据源选择:直接读Zabbix数据库比调API效率高,但要注意不要影响Zabbix自身性能,所以加了同步机制。进制问题:带宽单位换算有1000和1024两种标准,必须和监控平台保持一致,否则数据对不上。多端口聚合:一个客户多条线路是常见情况,需要按时间点累加后再计算95值。报告自动化:最大的价值在于节省人工,一键生成报告并发送邮件。系统功能相对简单,但覆盖了完整的业务闭环:数据采集 → 计算 → 报告 → 通知。后续可以根据需求扩展,比如增加历史趋势分析、异常告警等功能。
2026年03月29日
2 阅读
0 评论
0 点赞