RustNet - 一个查看当前服务进程流量的神器
你是否遇到过这样的困惑：服务器突然带宽飙升，却不知道是哪个进程在作怪？想看实时网络连接，netstat 输出一堆 IP 地址让人眼花缭乱，ss 命令只能看到端口信息，用 Wireshark 抓包又太重了，还得在服务器上转发 X11 才能看到界面？今天给大家介绍一个填补了这个空白的神器 —— RustNet。它解决了什么问题？传统的网络监控工具各有短板：netstat / ss：能看到连接，但不知道是哪个进程发起的，而且无法实时监控状态变化Wireshark / tcpdump：能抓包分析，但看不到进程归属，因为是工作在数据包层面而非 socket 层面iftop：能看带宽，但同样不知道进程信息RustNet 的定位很明确：每一条 TCP、UDP、QUIC 连接都能追溯到所属进程。让你一眼就能看清：谁在使用网络、走的是什么协议、消耗了多少带宽。核心能力一览1. 进程级归属识别这是 RustNet 最核心的能力。在不同平台上采用了不同的技术方案：Linux：使用 eBPF 技术，性能极高，开销极低macOS：使用 PKTAPWindows / FreeBSD：使用原生 API你再也不用去 lsof -i 或者 netstat -tunlp 按进程名搜索了，RustNet 直接把进程信息展示在每条连接旁边。2. 深度包检测（DPI）不需要额外解析器，就能自动识别：HTTP / HTTPS（带 SNI）DNS、SSH、FTPQUIC、MQTT、BitTorrentSTUN、NTP、mDNS、DHCP、SNMP 等这意味着你不仅能看到连接，还能知道这条连接是用来干嘛的。3. 安全沙箱机制这是个很贴心的设计。抓包工具通常需要 root 权限，这带来安全风险。RustNet 采用的策略是：Linux 5.13+：使用 Landlock 沙箱macOS：使用 SeatbeltWindows：token 降权 + job-object在 libpcap 初始化完成后立即丢弃特权，大大降低了风险。4. TCP 网络分析实时统计重传、乱序包、快重传，帮你发现网络质量问题。既有每条连接的细节，也有全局汇总视图。5. 智能连接生命周期按协议设置不同的超时时间：HTTP/HTTPS：10 分钟SSH：30 分钟（适配长会话）DNS：30 秒QUIC 已连接：3 分钟颜色指示过期程度：白色（活跃）→ 黄色（开始过期）→ 红色（即将过期）。按 t 可以保留已关闭的历史连接，方便事后追溯。6. 强大的过滤能力支持 Vim / fzf 风格的过滤语法：/port:443 # 按端口过滤 /process:firefox # 按进程过滤 /sni:google.com # 按 SNI 过滤 /state:established # 按状态过滤 /dport:443 sni:github.com # 组合过滤还支持正则表达式：/(?i)pattern/7. GeoIP 增强基于本地 MaxMind GeoLite2 数据库查询国家信息，不发起任何网络请求，完全本地化。实战场景举例场景一：排查带宽异常服务器突然带宽飙升，SSH 进去后直接运行：sudo rustnet然后按 s 多次，切换到按带宽排序，立刻就能看到哪个进程在疯狂发包。场景二：监控可疑连接怀疑有恶意进程对外连接？过滤一下：/process:suspect_name或者看所有非 HTTPS 的对外连接：/proto:tcp !port:443场景三：SSH 远程运维在远程服务器上直接运行 RustNet，TUI 界面完美适配 SSH，不需要转发 X11，也不需要把抓包文件传回本地分析。场景四：QUIC 协议分析QUIC 是 HTTP/3 的底层协议，传统工具很难分析。RustNet 可以直接识别 QUIC 连接并显示状态：/proto:quic安装与使用安装各平台都有原生包：# macOS / Linux (Homebrew) brew tap domcyrus/rustnet brew install rustnet # Ubuntu 25.10+ sudo add-apt-repository ppa:domcyrus/rustnet sudo apt update && sudo apt install rustnet # Arch Linux sudo pacman -S rustnet # Fedora 42+ sudo dnf copr enable domcyrus/rustnet sudo dnf install rustnet # Windows (需要先安装 Npcap) choco install rustnet也可以通过 crates.io 安装：cargo install rustnet-monitorLinux 免 sudo 运行推荐做法是赋予 capabilities 而不是每次都用 sudo：sudo setcap 'cap_net_raw,cap_bpf,cap_perfmon+eip' $(which rustnet) rustnet # 现在可以直接运行了常用参数rustnet -i eth0 # 指定网络接口 rustnet --show-localhost # 显示 localhost 连接 rustnet --no-resolve-dns # 关闭 DNS 反向解析 rustnet -r 500 # 设置刷新间隔（毫秒）快捷键速览按键功能Tab切换标签页i切换接口统计↑/k ↓/j上下移动Enter查看连接详情/进入过滤模式s S切换排序列 / 排序方向t显示/隐藏历史连接c复制远端地址q退出与 Wireshark 的协作RustNet 和 Wireshark 不是替代关系，而是互补：用 RustNet 快速定位哪个进程在发起连接用 --pcap-export 导出带进程信息的数据包用脚本 scripts/pcap_enrich.py 富化数据在 Wireshark 中结合 PID 和进程上下文深度分析技术栈RustNet 基于 Rust 构建，核心依赖：ratatui：终端 UIlibpcap：抓包eBPF (libbpf-rs)：Linux 进程识别Landlock：Linux 沙箱MaxMind GeoLite2：GeoIP 数据库总结RustNet 填补了一个长期存在的空白：在不需要复杂抓包分析的情况下，快速看清每条网络连接的进程归属。它的价值在于：快速定位问题：带宽异常、可疑连接，几秒钟就能找到源头SSH 友好：远程服务器运维的利器安全设计：沙箱隔离，最小化特权跨平台：Linux、macOS、Windows、FreeBSD 全覆盖如果你经常需要排查网络问题，RustNet 绝对值得一试。项目开源在 GitHub：https://github.com/domcyrus/rustnet
一、项目背景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(` %s 月份: %s 95计费值: %.2f Gbps 峰值: %.2f Gbps `, 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值。报告自动化：最大的价值在于节省人工，一键生成报告并发送邮件。系统功能相对简单，但覆盖了完整的业务闭环：数据采集 → 计算 → 报告 → 通知。后续可以根据需求扩展，比如增加历史趋势分析、异常告警等功能。
从零搭建一个手机号过滤平台：PBlack 架构全解析
今天聊聊 PBlack——一个开源的手机号黑名单/白名单检测平台。我会从架构设计的角度，拆解它是如何解决这些痛点的，以及为什么选择 Vue + Django + Go 这样的技术组合。为什么需要手机号过滤？先说说背景。在金融风控、电商防刷、社交平台注册这些场景里，手机号是最基础的身份标识。但问题来了：骚扰电话营销：用户被各种推广电话轰炸，体验极差羊毛党批量注册：用虚拟号、接码平台批量薅羊毛欺诈风险：黑名单号码反复作案，平台损失巨大传统的解决方案要么简单粗暴（直接拒接所有陌生号），要么成本太高（每次都要调用第三方接口）。PBlack 的设计目标就是：既要快，又要准，还要省钱。整体架构：三层分离PBlack 采用前后端分离的微服务架构，核心分为三层：┌─────────────────────────────────────────────────────────┐ │ 前端层 (pfront) │ │ Vue 3 + TypeScript + Vite │ │ 管理界面、API 密钥管理、消费统计看板 │ └─────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────┐ │ API 服务层 (papi) │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ phone-filter │ │ teddy-api │ │ pfront-api │ │ │ │ -api (Go) │ │ -proxy (Go) │ │ (Go) │ │ │ │ 黑名单检测 │ │ 上游代理 │ │ 认证/消息 │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ └─────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────┐ │ 数据与管理后台层 │ │ Django + PostgreSQL + Redis │ │ 用户管理、号码库、访问记录、缓存加速 │ └─────────────────────────────────────────────────────────┘这个分层不是拍脑袋决定的。每一层都有自己的职责，而且可以独立部署、独立扩展。技术选型：为什么这样组合？前端：Vue 3 + Vite选择 Vue 3 而不是 React，主要是考虑开发效率和团队熟悉度。Vite 的冷启动速度比 Webpack 快一个数量级，本地开发体验很好。TypeScript 是必须的——手机号过滤涉及大量的 API 接口对接，类型安全能避免很多低级错误。管理后台：DjangoDjango 的 admin 界面是开箱即用的。用户管理、API 密钥管理、黑白名单维护这些功能，用 Django 的 ModelAdmin 几行代码就能搞定。如果用 Go 写后台，光 CRUD 接口就要写一堆。Django 的 ORM 和 admin 在这种场景下是降维打击。核心检测服务：Go这是整个系统的性能瓶颈所在。手机号检测是高频操作，QPS 可能达到几千甚至上万。Go 的 goroutine + channel 模型非常适合这种高并发、低延迟的场景。实测下来，单机可以轻松支撑 10k+ QPS。// 核心检测逻辑：Redis Set 的 O(1) 查询 func (s *BlacklistService) CheckSingleNumber(phone string) (bool, error) { isBlacklisted, err := database.IsPhoneInBlacklistSet(phone) if err != nil { return false, err } s.updateStats(isBlacklisted) return isBlacklisted, nil }数据流转：一次检测请求的全流程来看看一个黑名单检测请求是怎么处理的：客户端 → phone-filter-api → 签名验证 → 用户鉴权 → Redis 查询 ↓ 客户端 ← 返回结果 ← 组装响应 ← 命中判定 ← 黑名单 Set关键设计点：Redis Set 存储：黑名单和白名单分别用 Redis 的 Set 数据结构存储，SISMEMBER 命令是 O(1) 复杂度，百万级号码也能毫秒级响应多级检测策略：level=1：只查黑名单，命中即拦截level=2：只查白名单，用于"只允许特定号码"的场景level=3：黑名单优先，未命中时调用上游 API 二次确认批处理优化：访问记录和消费记录不实时写入数据库，而是先放入内存队列，每 5 秒批量 flush 一次，大幅降低数据库压力// 后台批处理器，降低主链路延迟 type BatchProcessor struct { accessRecords []*models.AccessRecord consumptionRecords []*models.ConsumptionRecord mutex sync.Mutex batchSize int // 默认 100 条批量写入 } func (bp *BatchProcessor) startBackgroundProcessing() { ticker := time.NewTicker(5 * time.Second) for range ticker.C { bp.flushRecords() // 定时批量刷盘 } }安全设计：不只是黑名单PBlack 的安全机制是多层防护：1. 签名验证每个请求必须携带 appId、timestamp、sign 三个参数。签名算法如下：sign = MD5(appId + timestamp + appSecret)timestamp 必须在 5 分钟内，防止重放攻击。2. IP 白名单每个 API 密钥可以绑定允许的 IP 列表，即使密钥泄露，攻击者也无法从其他 IP 调用。3. 余额与配额控制每个用户有独立的余额和单价设置，每次检测按单价扣费。余额不足时自动拒绝服务。部署架构：从开发到生产本地开发时，用 Docker Compose 一键启动所有依赖：# postgres/docker-compose.yaml services: postgres: image: postgres:15-alpine ports: - "5432:5432" environment: POSTGRES_DB: phonedb POSTGRES_PASSWORD: uWNZugjBqbcf8dxC生产环境建议：Nginx 反向代理：SSL 终止、静态资源缓存、限流systemd 进程守护：API 服务和 Worker 分别托管PostgreSQL 主从：读写分离，查询走从库Redis Cluster：支持横向扩展性能数据在 4C8G 的云服务器上实测：指标数值单机 QPS12,000+平均响应时间3-5msRedis 命中率99.8%批处理延迟< 5s总结PBlack 的架构设计遵循几个核心原则：职责分离：Django 做管理、Go 做性能、Vue 做交互，各取所长缓存优先：Redis Set 是核心，数据库只是持久化备份批处理降载：非关键路径异步化，主链路保持轻量安全第一：签名 + IP 白名单 + 余额控制，多层防护这套架构已经在实际业务中跑了半年多，处理了几千万次检测请求。如果你也在做类似的风控系统，希望这些经验对你有帮助。下篇预告：《30 分钟跑起来：PBlack 本地开发环境搭建实战》
从零搭建一个短信平台：LESMS 架构全解析
短信服务看似简单，但真要自己搭一个能支撑业务运转的平台，坑比想象中多得多。去年有个朋友找我吐槽：公司每个月短信费用好几万，想自建平台省点钱。我反问了他三个问题——高并发怎么扛？通道故障怎么切？账单对不上怎么办？ 他沉默了。这就是短信平台的真相：发一条短信容易，发一亿条还能稳定、安全、可追踪，完全是另一回事。今天聊聊我自己写的 LESMS这个项目，看看一个轻量级企业短信平台是怎么从 0 到 1 搭建起来的。一、为什么要自己搭短信平台？先泼点冷水——大部分公司不需要自建短信平台。直接接阿里云、腾讯云，API 调一下就能用，省心省力。但以下几种情况，自建就有意义了：成本敏感：月发送量过百万，第三方平台的单价和附加费会吃掉不少利润数据合规：金融、医疗行业，用户手机号不能外流到第三方功能定制：需要特殊的签名审核流程、定时发送策略、精细化的通道调度技术储备：团队有能力维护，且业务长期依赖短信触达LESMS 的定位很明确：中小型企业的自建方案，不求大而全，但求核心链路稳、二次开发易。二、三模块架构设计整个系统拆成三大块，每块只干一件事：1. Django 管理后台：业务的"大脑"Django 在这里扮演两个角色：一是客户中心 API。用户注册、登录、余额查询、发送记录查看，这些面向客户的接口都由 Django 提供。用 Django REST Framework（DRF）写起来很快，自带序列化、权限控制、限流。二是运营管理后台。基于 Django Admin 改造的管理界面，运营人员可以在里面审核签名、配置通道、查看统计报表。SimpleUI 美化了一下，至少不像原生 Admin 那么丑。关键模块包括：用户管理：实名认证、企业认证、余额与计费消息管理：模板审核、签名报备、上下行记录通道管理：多运营商通道配置、黑名单管理充值管理：套餐、订单、卡密核销2. Vue 3 前端：用户的"门面"前端用的是 Vue 3 + TypeScript + Element Plus，基于 Vben Admin 框架二次开发。为什么不用 Django 自带的模板？因为现代前端工程化已经成熟了，Vue 的组件复用、状态管理、构建优化都比传统后端渲染强太多。前端主要承载：客户自助服务：发送短信、查看记录、管理通讯录可视化数据：发送成功率、消费趋势、通道质量开发者工具：API 密钥管理、SDK 下载、调试控制台3. Go API 服务：性能的"发动机"这是整个系统的性能担当，用 Go + Gin 框架实现。短信发送有几个特点：高并发、低延迟、重可靠。Django 是同步阻塞模型，处理这种场景比较吃力；Go 的 goroutine + channel 天然适合高并发网络 IO。Go 服务负责的核心功能：短信发送接口：接收请求、校验签名、写入队列Worker 调度：从 Redis 队列取任务，分发到不同运营商通道状态回执：接收运营商的送达报告，更新数据库上行处理：处理用户回复的短信（MO）三、技术选型背后的思考有人可能会问：为什么搞这么复杂，用一种技术栈不行吗？当然行，但会有代价。模块选型理由管理后台Django快速开发、生态成熟、Admin 开箱即用核心 APIGo高并发性能、部署简单、资源占用低前端Vue 3组件化、TypeScript 支持好、团队熟悉数据库PostgreSQLACID 保障、JSON 字段支持、运维友好队列/缓存Redis原子操作丰富、持久化可选、生态完善一句话总结：让合适的工具做合适的事。Django 适合业务逻辑复杂、变更频繁的场景；Go 适合性能敏感、高并发的场景。两者通过 REST API + Redis 队列协作，既保留了各自的优势，又避免了过度耦合。四、一条短信的完整旅程光讲架构有点抽象，我们跟踪一条短信从用户点击"发送"到手机收到的全过程：第一步：前端提交用户在 Vue 界面填写手机号、选择模板、点击发送。前端组装请求体，带上时间戳、nonce、签名，调用 /api/v1/sms/send。第二步：Go 服务接入Go 服务收到请求，依次执行：中间件链：CORS → 日志 → 签名校验 → 限流检查参数校验：手机号格式、模板是否存在、余额是否充足写入队列：将任务序列化后推入 Redis 队列第三步：Worker 调度Worker 进程从队列取出任务：解析手机号前 7 位，识别归属运营商（移动/联通/电信）查询用户的通道配置策略选择最优通道，HTTP 调用运营商网关第四步：运营商处理运营商网关返回提交结果（成功/失败），Worker 记录提交状态。真正的送达是异步的——运营商会在短信送达后推送状态报告。第五步：状态回执运营商回调 Go 服务的 /callback/status 接口，携带手机号、状态、时间戳。Go 服务更新数据库中的发送明细，同时触发计费扣减。第六步：用户感知用户在前端刷新页面，看到"已送达"状态。如果失败了，能看到失败原因（余额不足、黑名单拦截、通道故障等）。整个过程通常在 3-5 秒内完成，其中大部分时间花在运营商网络上。五、写在最后LESMS 的架构没有炫技，就是一个务实的企业级方案：用 Django 快速搞定业务和管理后台用 Go 保证核心链路的性能和稳定性用 Redis 解耦同步压力和异步处理用 Vue 提供现代化的用户体验这套架构支撑过日发送量百万级的业务，也经历过双 11 的流量洪峰。它不是最完美的，但在开发效率、运维成本、性能表现之间找到了一个不错的平衡点。下篇文章，我会带你 15 分钟把这套环境在本地跑起来，从安装依赖到发出第一条测试短信，手把手教你踩完所有的坑。本文基于 LESMS 开源项目撰写，如需了解更多实现细节，可参考项目文档或源码。
手机号黑名单库查询逻辑与性能优化分享
在高并发场景下，如何实现毫秒级的手机号黑名单校验？本文将深入剖析一个生产级黑名单系统的核心架构与技术实现。一、总体介绍在短信通道、语音呼叫、风控审核等业务场景中，手机号黑名单校验是一项高频且关键的能力。想象一下，当用户发起呼叫或发送短信时，系统需要在毫秒级时间内判断目标号码是否在黑名单中——这直接关系到业务合规性和用户体验。本文介绍的手机号黑名单库是一个面向运营/风控场景的完整解决方案，具备以下核心特性：双通道架构：Django 管理后台 + Go 高性能 API 服务毫秒级响应：依托 Redis 缓存实现单次查询 < 5ms百万级数据支持：轻松支撑千万级黑名单数据量灵活接入方式：支持单号查询、批量查询（最多 500 个）二、技术要点1. Redis Set 数据结构：空间换时间的极致实践是什么：使用 Redis 的 Set（集合）数据结构存储黑名单手机号，而非传统的 Hash 或 String。为什么这么做：Set 的 SISMEMBER 命令时间复杂度为 O(1)，无论数据量多大，查询性能恒定内存占用优化：存储 1000 万个 11 位手机号仅需约 400MB 内存天然去重：Set 自动处理重复号码，简化业务逻辑核心价值：单机 Redis 可支撑 10 万+ QPS 的查询压力，满足绝大多数业务场景。2. 冷热分离架构：PostgreSQL + Redis 双层存储是什么：PostgreSQL 作为持久化存储（冷数据），Redis 作为查询缓存（热数据）。为什么这么做：数据安全：PostgreSQL 保证数据不丢失，支持事务和备份查询性能：Redis 避免频繁访问数据库，减轻 DB 压力水平扩展：Redis 可部署集群模式，支持数据分片核心价值：兼顾数据可靠性和查询性能，实现"写入慢、读取快"的最优解。3. 原子切换机制：零停机数据更新是什么：使用临时 Set + RENAME 命令实现黑名单数据的无缝切换。为什么这么做：避免更新过程中的数据不一致问题切换操作是原子性的，微秒级完成业务层无感知，零停机时间核心价值：支持百万级数据的全量更新，而不影响线上查询服务。4. Pipeline 批量查询：网络延迟优化是什么：使用 Redis Pipeline 技术批量发送查询命令，减少网络往返次数。为什么这么做：单次网络 RTT 约 1-5ms，批量查询可将 500 个号码的查询时间从 2500ms 降至 10ms减少 Redis 服务器处理开销核心价值：批量查询接口支持 500 个号码一次性校验，响应时间 < 50ms。5. 签名验证机制：API 安全防护是什么：基于 MD5 的参数签名验证，防止接口被恶意调用。为什么这么做：防止请求被篡改（如修改查询号码）防止重放攻击（时间戳有效期 5 分钟）身份认证（AppId + AppSecret 模式）核心价值：确保只有授权用户才能访问黑名单查询服务。三、核心代码片段1. Redis 批量查询实现// BatchIsPhoneInBlacklistSet 使用 Pipeline 批量查询号码是否在黑名单中 func BatchIsPhoneInBlacklistSet(phones []string) (map[string]bool, error) { if len(phones) == 0 { return make(map[string]bool), nil } pipe := RedisClient.Pipeline() cmds := make(map[string]*redis.BoolCmd) // 批量发送 SISMEMBER 命令 for _, phone := range phones { cmds[phone] = pipe.SIsMember(ctx, BlacklistSetKey, phone) } // 执行所有命令（一次网络往返） _, err := pipe.Exec(ctx) if err != nil { return nil, err } // 处理结果 results := make(map[string]bool) for phone, cmd := range cmds { isMember, err := cmd.Result() if err != nil { // 单个查询出错，默认不在黑名单 results[phone] = false } else { results[phone] = isMember } } return results, nil }核心逻辑解读：使用 Pipeline 将多个 SISMEMBER 命令打包发送所有查询共享一次网络往返，大幅降低延迟结果逐个解析，单个失败不影响整体2. 原子切换实现// AtomicSwitchBlacklist 原子切换黑名单数据 func AtomicSwitchBlacklist() error { // 检查临时 Set 是否存在 exists, err := RedisClient.Exists(ctx, BlacklistTempSetKey).Result() if err != nil { return err } if exists == 0 { return fmt.Errorf("temporary blacklist set does not exist") } // 原子操作：重命名临时 Set 为主 Set pipe := RedisClient.Pipeline() pipe.Rename(ctx, BlacklistTempSetKey, BlacklistSetKey) pipe.Incr(ctx, BlacklistVersionKey) _, err = pipe.Exec(ctx) return err }核心逻辑解读：RENAME 命令是原子操作，微秒级完成版本号递增，便于追踪数据更新状态切换期间查询不中断，业务零感知3. 签名验证实现// GenerateSignature 生成请求签名 func GenerateSignature(params SignParams) string { // 构建参数字典 paramMap := make(map[string]string) paramMap["appId"] = params.AppId paramMap["callee"] = params.Callee paramMap["level"] = strconv.Itoa(params.Level) paramMap["timestamp"] = params.Timestamp // 按字典序排序（确保签名一致性） keys := make([]string, 0, len(paramMap)) for k := range paramMap { keys = append(keys, k) } sort.Strings(keys) // 拼接签名字符串 var signStr strings.Builder for i, key := range keys { if i > 0 { signStr.WriteString("&") } signStr.WriteString(key) signStr.WriteString("=") signStr.WriteString(paramMap[key]) } signStr.WriteString("&appSecret=") signStr.WriteString(params.AppSecret) // MD5 加密 hash := md5.Sum([]byte(signStr.String())) return hex.EncodeToString(hash[:]) }核心逻辑解读：参数按字典序排序，确保客户端和服务端生成相同签名AppSecret 仅参与签名计算，不传输，防止泄露MD5 算法兼顾安全性和计算性能4. 批量查询接口处理// ProcessBatchCheck 处理批量号码检查请求 func (s *BlacklistService) ProcessBatchCheck(req *models.BatchCheckRequest, clientIP string) (*models.BatchCheckResponse, error) { // 1. 验证 API 密钥和签名 params := map[string]interface{}{ "appId": req.AppId, "callee": req.Callee, "level": req.Level, "timestamp": req.Timestamp, } apiKey, err := s.ValidateAndGetApiKey(req.AppId, req.Sign, params) if err != nil { return &models.BatchCheckResponse{ Code: 405, // 签名验证失败 Msg: "签名验证失败", }, nil } // 2. 检查访问权限（IP 白名单等） allowed, err := database.ValidateAccess(apiKey.CUserId, req.AppId, apiKey.ID, clientIP) if !allowed { return &models.BatchCheckResponse{ Code: 403, Msg: "访问被拒绝", }, nil } // 3. 解析并去重电话号码 phones := strings.Split(req.Callee, ",") uniquePhones := make(map[string]bool) var validPhones []string for _, phone := range phones { phone = strings.TrimSpace(phone) if phone != "" && utils.IsValidPhoneNumber(phone) { if !uniquePhones[phone] { uniquePhones[phone] = true validPhones = append(validPhones, phone) } } } // 4. 限制批量查询数量 if len(validPhones) > 500 { return &models.BatchCheckResponse{ Code: 400, Msg: "批量查询数量不能超过500个", }, nil } // 5. 执行批量查询（Level 1=黑名单, 2=白名单, 3=混合模式） results, hitCount, err := s.checkBatchNumbersWithResults(validPhones) // 6. 记录访问日志和消费记录（异步） s.recordAccessAndConsumption(apiKey, validPhones, hitCount) return &models.BatchCheckResponse{ Code: 200, Msg: "success", Data: results, }, nil }核心逻辑解读：六层校验：签名验证 → 权限检查 → 参数解析 → 数量限制 → 黑名单查询 → 日志记录支持三种查询模式：黑名单、白名单、混合模式异步记录访问日志，不阻塞查询响应四、总结手机号黑名单库的核心设计思想可以概括为："冷热分离保安全，Redis 缓存保性能，原子切换保稳定，签名验证保安全"。通过本文介绍的技术方案，我们实现了：单次查询延迟 < 5ms批量 500 个号码查询 < 50ms支持千万级黑名单数据零停机数据更新这套方案已在生产环境稳定运行，日均处理查询请求数百万次。希望本文的技术分享能为你的黑名单系统设计提供参考。本文基于 PBlack 手机号黑名单管理系统（Django + Go + Redis + PostgreSQL）生产实践整理。
短信通道管理平台之多层架构与Worker异步处理设计分享
今天来聊聊我们在构建企业级短信平台时的一些架构设计心得。短信服务看似简单，但要在高并发场景下保证稳定性、可观测性和可扩展性，背后的技术选型与架构设计才是关键。一、总体介绍1.1 解决的问题在短信业务中，我们面临几个核心挑战：高并发写入：营销活动期间，短时间内可能有数万条短信发送请求涌入实时性与可靠性的平衡：用户希望短信立即发出，但下游通道往往有速率限制多维度统计：需要实时统计发送量、成功率、计费信息风控合规：黑名单过滤、敏感词检测必须在发送前完成1.2 在平台中的位置本文介绍的架构设计是整个短信平台的"中枢神经"，位于 API 接入层与下游通道之间，承担着请求调度、异步处理、数据持久化的核心职责。1.3 核心价值通过"分层架构 + Worker 异步处理"的设计，我们实现了：解耦：API 层只负责接收请求，具体处理交给 Worker削峰：利用队列缓冲突发流量可扩展：Worker 可以水平扩容可观测：每个组件都有独立的日志输出二、技术要点2.1 三层架构设计我们的平台采用经典的三层架构：┌─────────────────────────────────────────────────────────────┐ │ 前端 (Vue 3) │ │ customer-web - Vben Admin 框架 │ └───────────────────────────┬─────────────────────────────────┘ │ ┌───────────────────────────▼─────────────────────────────────┐ │ Nginx 反向代理 │ └───────────────┬───────────────────────────┬─────────────────┘ │ │ ┌───────────────▼───────────┐ ┌───────────▼─────────────────┐ │ Django 管理后台 │ │ Go API 服务 │ │ (ladmin) │ │ (lsms-api) │ │ - 用户管理 │ │ - 短信发送 API │ │ - 模板/签名审核 │ │ - 调度器 │ │ - 充值管理 │ │ - Worker 集群 │ │ - 统计报表 │ │ - 计费 Worker │ │ │ │ - 统计 Worker │ │ │ │ - 黑名单 Worker │ └───────────────┬───────────┘ └───────────┬─────────────────┘ │ │ ┌───────────────▼───────────────────────────▼─────────────────┐ │ PostgreSQL + Redis │ │ 数据持久化 + 缓存/队列 │ └─────────────────────────────────────────────────────────────┘技术选型考量：Vue 3 + Vben Admin：现代化前端生态，组件丰富，适合构建复杂的运营后台Django：成熟的企业级后台框架，自带 Admin 界面，适合快速开发管理功能Go API 服务：高并发、低延迟，适合短信发送这类 IO 密集型场景PostgreSQL：ACID 事务、JSONB 支持，适合存储复杂的业务数据Redis：高性能缓存与队列，支撑高并发与解耦2.2 Worker 异步处理机制Worker 是我们架构的核心创新点。当 API 接收到发送请求后，不会立即处理，而是：写入队列：将任务推入 Redis 队列记录日志：在 PostgreSQL 中创建发送记录异步处理：多个 Worker 从队列中拉取任务并行处理flowchart TD Start(["接收发送请求"]) --> Validate["校验签名/限流/鉴权"] Validate --> Enqueue["写入发送队列(Redis)"] Enqueue --> Record["记录发送记录(PostgreSQL)"] Record --> Dispatch["调度器分发任务"] Dispatch --> Billing["计费 Worker 处理"] Dispatch --> Stats["统计 Worker 处理"] Dispatch --> Blacklist["黑名单过滤 Worker 处理"] Billing --> Update["更新计费/余额"] Stats --> UpdateStats["更新统计指标"] Blacklist --> FilterOK{"命中黑名单?"} FilterOK --> |是| Drop["丢弃/标记失败"] FilterOK --> |否| Send["下发到通道"] Send --> Done(["完成"]) Drop --> Done Update --> Done UpdateStats --> DoneWorker 类型说明：Worker 类型职责配置参数调度 Worker扫描定时任务、分发消息dispatcher_count: 2计费 Worker计算费用、扣减余额worker_count: 2, batch_size: 500统计 Worker汇总发送数据、生成报表scan_interval: 60s, batch_size: 1000黑名单 Worker过滤黑名单号码worker_count: 4, cache_ttl: 600s2.3 Nginx 反向代理与负载均衡Nginx 作为入口网关，承担着流量分发、SSL 终止、静态资源缓存等职责：server { listen 443 ssl; server_name sms.hongboxinhua.com; # API requests proxy to ladmin backend location /basic-api/ { proxy_pass http://172.17.0.1:3038/api/v1/customer/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # Increase buffer sizes for API responses proxy_buffer_size 128k; proxy_buffers 4 256k; proxy_busy_buffers_size 256k; } # Static files caching location ~* \.(css|js)$ { expires 1y; add_header Cache-Control "public, immutable"; } }关键配置解析：proxy_buffer_size：增大缓冲区，避免大响应被截断expires 1y：静态资源长期缓存，减轻服务器压力X-Forwarded-For：传递真实客户端 IP，便于后端限流统计2.4 限流与安全防护我们在 Django 和 Go API 两层都实现了限流：# Django REST Framework 限流配置 REST_FRAMEWORK = { 'DEFAULT_THROTTLE_RATES': { 'anon': '100/minute', 'user': '1000/minute', 'login': '5/minute', 'register': '3/minute', 'send_code': '1/minute', 'customer_api': '60/minute', 'sms_api': '30/minute', }, }限流策略根据接口类型差异化设置：登录注册类接口严格限流，防止暴力破解；短信发送接口限流较宽松，但仍有上限保护。三、核心代码片段3.1 Go API 服务配置# config.yaml - 服务核心配置 server: host: "0.0.0.0" port: 8080 mode: "debug" database: host: "127.0.0.1" port: 5432 user: "postgres" password: "***" dbname: "lesms" max_open_conns: 100 max_idle_conns: 10 redis: host: "localhost" port: 6379 db: 6 pool_size: 100 min_idle_conns: 10 # Worker 配置 worker: dispatcher_count: 2 queue_timeout: 5 schedule_scan_interval: 10 billing: worker_count: 2 batch_size: 500 scan_interval: 300 stats: batch_size: 1000 scan_interval: 60 blacklist: worker_count: 4 cache_ttl: 600配置说明：max_open_conns: 100：数据库连接池大小，避免连接耗尽batch_size：Worker 批量处理数量，平衡吞吐与延迟cache_ttl: 600：黑名单缓存 10 分钟，减少数据库查询3.2 日志轮转配置log: level: "debug" dir: "logs" max_size: 100 # 单个日志文件最大 100MB max_backups: 30 # 保留 30 个历史文件 max_age: 7 # 保留 7 天 compress: true # 压缩旧日志每个组件都有独立的日志文件：lsms-api.log - API 服务日志dispatcher.log - 调度 Worker 日志billing.log - 计费 Worker 日志stats.log - 统计 Worker 日志blacklist.log - 黑名单 Worker 日志【放一张日志目录结构截图】3.3 数据库连接池配置# Django 数据库配置 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'lesms', 'USER': 'postgres', 'PASSWORD': '***', 'HOST': '127.0.0.1', 'PORT': '5432', } }四、实际效果通过这套架构，我们在生产环境实现了：峰值处理能力：单机可处理 5000+ TPS 的发送请求平均延迟：从请求接收到入队 < 50ms系统可用性：99.9%（过去 6 个月数据）故障隔离：单个 Worker 故障不影响整体服务五、总结与展望本文介绍了短信通道管理平台的核心架构设计，重点包括：三层架构：前端 + Django 后台 + Go API 服务，职责清晰Worker 异步处理：解耦、削峰、可扩展完善的可观测性：多维度日志、监控大盘安全防护：多层限流、黑名单过滤后续文章我们将深入探讨：短信通道的智能路由与故障转移黑名单系统的设计与优化高并发场景下的数据库优化实践
CVOICE语音质检系统：一个看板掌控全局，管理看板深度解析
总体介绍在语音质检系统中，运营管理者面临的最大痛点之一就是信息分散：用户数据、录音状态、敏感词告警、财务消费……分布在不同页面，想要全面了解系统运行状况，需要反复切换、手动汇总。CVOICE语音质检系统的管理看板（Dashboard）正是为解决这一问题而生。它将系统中最核心的运营指标——用户数量、节点状态、录音处理进度、告警消息、财务概览、质检规则排行——全部汇聚在一个页面上，配合可视化图表和实时统计，让管理者打开浏览器就能"一眼看穿"系统全貌。无论是日常巡检还是异常排查，看板都能成为你的"数字驾驶舱"。技术要点1. 后端架构：自定义 Django AdminSiteCVOICE的管理看板并非使用第三方BI工具，而是基于Django Admin框架进行了深度定制。通过继承 AdminSite 类，注册自定义URL，将看板页面无缝嵌入到后台管理系统中：class CustomAdminSite(AdminSite): site_header = '语音质检平台' def get_urls(self): urls = super().get_urls() custom_urls = [ path('dashboard/', self.admin_view(self.dashboard_view), name='dashboard'), path('realtime-stats/', self.admin_view(self.realtime_stats_view), name='realtime_stats'), ] return custom_urls + urls这种方式的好处是：看板天然继承了Django Admin的认证和权限体系，只有经过身份验证的管理员才能访问，无需额外实现鉴权逻辑。2. 多维度数据聚合看板页面在一次请求中完成了超过30项指标的实时计算，覆盖6大维度：维度核心指标数据来源用户管理总用户数、活跃/禁用/欠费分布CUser模型节点状态在线/离线、审核状态分布CUserAgent模型录音处理总量、今日新增、检测状态分布PhoneRecording模型语音转写转写总量、审核状态、质检结果SpeechToText模型告警消息总量、待处理数AlertMessage模型财务统计总余额、累计消费、今日消费ConsumptionRecord模型所有查询均使用Django ORM的 count()、aggregate(Sum()) 和 filter() 链式调用，保持了代码的简洁性和可维护性。3. 可视化图表：Chart.js驱动看板中集成了两类动态图表，均由 Chart.js 4.x 渲染：近7天录音趋势折线图：展示系统整体的录音采集量走势，帮助发现异常波动。节点维度录音趋势：取录音量TOP5的节点，按天展示各自的录音增长曲线，便于定位"高产"或"沉默"节点。图表数据在后端通过循环计算7天内每天的录音数量，序列化为JSON后传递给前端模板：for i in range(6, -1, -1): day = today - timedelta(days=i) day_start = timezone.make_aware(timezone.datetime.combine(day, timezone.datetime.min.time())) day_end = day_start + timedelta(days=1) count = PhoneRecording.objects.filter( created_at__gte=day_start, created_at__lt=day_end ).count() daily_recordings.append(count)4. 质检规则排行榜看板底部设计了质检规则排行榜，从两个角度排名：按录音条数排序：哪条规则命中的录音最多？按总音频时长排序：哪条规则覆盖的通话总时长最长？这为规则优化和资源分配提供了直观的数据支撑。核心代码/配置片段看板的前端采用 Bootstrap 5 构建响应式布局，使用CSS渐变变量统一视觉风格：:root { --primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%); --success-gradient: linear-gradient(135deg, #11998e 0%, #38ef7d 100%); --warning-gradient: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); }每个指标卡片通过统一的 stat-card 组件展示，支持趋势标识（如"今日+N"）： {{ total_recordings }} 电话录音 今日 +{{ today_recordings }} 生产环境使用 Gunicorn 作为WSGI服务器，配合 systemd 实现进程管理和自动重启：ExecStart=/opt/miniconda3/envs/cvoice/bin/gunicorn \ --workers 2 \ --bind 0.0.0.0:3008 \ --worker-class sync \ --max-requests 1000 \ cvoice.wsgi:application总结CVOICE管理看板通过Django Admin深度定制 + Chart.js可视化 + Bootstrap响应式布局，在一个页面内实现了30+核心指标的实时聚合展示。它不仅是运维人员的"第一屏"，更是整个语音质检系统运行状态的晴雨表。下一篇，我们将深入探讨看板中"实时数据统计"页面的48小时逐时分析功能——它如何按节点、按规则维度呈现细粒度的录音分布，帮你精准把控每一个时段的系统脉搏。敬请期待！
为了在 antom-payments 插件中集成 Boost 支付通道，同时保持原有目录结构不变，请按以下步骤操作：1. 添加前端资源文件1.1 在 assets/blocks/ 下创建 boost 目录：assets/blocks/boost/ ├── boost.asset.php └── boost.js从现有通道（如 alipay_cn）复制模板文件，修改类名和标识符为 boost。1.2 在 assets/images/ 下添加 Boost 图标：assets/images/Boost-A+.svg # 符合命名规范（如 Alipay-A+.svg）2. 添加后端网关类在 includes/gateways/ 下创建文件：includes/gateways/class-wc-gateway-antom-boost.php文件内容示例（基于 class-wc-gateway-antom-alipay-cn.php 修改）：3. 添加区块支持类在 includes/blocks/ 下创建文件：includes/blocks/class-wc-gateway-antom-boost-block-support.php文件内容示例（基于其他通道修改）：4. 添加资源目录脚本在 resource/ 下创建文件：resource/boost.js从 resource/alipay_cn.js 复制模板，替换内容标识为 boost。5. 注册网关到核心系统修改主文件 includes/antom-payment-gateway-settings.php： array( 'gateway_file' => $dir . '/gateways/class-wc-gateway-antom-boost.php', 'gateway_class' => 'WC_Gateway_Antom_Boost', 'block_file' => $dir . '/blocks/class-wc-gateway-antom-boost-block-support.php', 'block_support_class' => 'WC_Gateway_Antom_Boost_Block_Support', 'slug' => 'antom_gcash', 'default_display_name' => 'Boost', 'menu_title' => __( 'Boost', 'antom-payments' ) . ' ' . __( 'Settings', 'antom-payments' ), 'pay_name' => __( 'Boost by Antom', 'antom-payments' ), 'payment_method_type' => 'BOOST', 'support_currencies' => array( 'AED', 'CHF', 'HKD', 'QAR', 'EUR', 'DKK', 'USD', 'CAD', 'CNY', 'THB', 'AUD', 'SGD', 'JPY', 'PLN', 'GBP', 'NZD', 'PHP', 'TRY' ), 'icon' => ANTOM_PAYMENT_GATEWAYS_URL . 'assets/images/Boost-A+.svg', ),关键注意事项ID 一致性：网关类中的 $id（antom_boost）区块支持类中的 $gateway_id（antom_boost）资源文件名（boost.js）图标规范：SVG 文件命名为 Boost-A+.svg，与其他通道统一。区块注册：确保 includes/class-antom-frontend.php 中的区块注册逻辑自动加载 boost 目录（已有逻辑通常无需修改）。测试验证：在 WooCommerce 后台启用 Boost 支付方式。测试下单流程，检查支付表单是否正常渲染。通过以上步骤，Boost 通道即可无缝集成到现有插件中，且完全符合原有目录结构。
用deepseek，我花一天手撸了一个自媒体管理平台
前言最近有一段时间事情不多，打算好好做一下自媒体方向。把自己平时积累的技术文档整理整理，然后发到各个平台。同时测试一下那种形式更熟欢迎，所以有做图文和视频内容。各个平台的账号也有多个，为了保证内容垂直度，不同的内容发在不同的账号下。自己替换下来的旧手机设备也有四五台。结果问题来了，每天在不同设备，不同账号之间切来切去。制作好的内容,过几天也记不得是不是发过，又烦又乱。就打算写一个系统，将设备，多媒体账号和内容管理起来。设计思路一台手机设备对应一到两个手机号，是一对多的关系。一个多媒体账号需要一个手机号，一个手机号可以注册多个平台。一篇内容可以发到多个平台上，没发一次生成一个发布记录。代码实现直接问deepseek，怎么写。我对django框架比较熟悉，所以实现就用django实现{card-default label="代码目录" width="80%"}{/card-default}模型设计让deepseek帮我设计模型。{card-default label="模型" width="80%"}{/card-default}数据展示数据模型设计完成之后，后台需要展示数据。还是问deepseek。{card-default label="展示" width="80%"}{/card-default}报错处理遇到程序提示错误，直接把错误提交给deepseek，让它给出解决方案。{card-default label="调试" width="80%"}{/card-default}系统展示就是一直问deepseek，经过一天的时间，大功告成。功能正常，界面如下。{card-default label="首页" width="80%"}{/card-default}{card-default label="设备列表" width="80%"}{/card-default}{card-default label="号码管理" width="80%"}{/card-default}{card-default label="媒体管理" width="80%"}{/card-default}{card-default label="内容管理" width="80%"}{/card-default}{card-default label="发布记录" width="80%"}{/card-default}小节最后看一下关于整个系统代码完成后，deepseek的api的消耗情况。用量3.25+我的一天时间，就是全部成本。这么看来，deepseek确实提高了我的工作效率。
CDN是什么CDN的全称是Content Delivery Network，即内容分发网络。其基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节，使内容传输得更快、更稳定。通过在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网络，CDN系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。其目的是使用户可就近取得所需内容，解决 Internet网络拥挤的状况，提高用户访问网站的响应速度。简单地说，内容分发网络是一个经策略性部署的整体系统，包括分布式存储、负载均衡、网络请求的重定向和内容管理4个要求，而内容管理和全局的网络流量管理是CDN的核心所在。通过用户就近性和服务器负载的判断，CDN确保内容以一种极为高效的方式为用户的请求提供服务。DNS是什么域名系统（英文：Domain Name System，缩写：DNS）是互联网的一项服务，是Internet上解决网上机器命名的一种系统，它作为将域名和IP地址相互映射的一个分布式数据库，能够使人更方便地访问互联网。DNS使用UDP端口53。当前，对于每一级域名长度的限制是63个字符，域名总长度则不能超过253个字符。就像拜访朋友要先知道别人家怎么走一样，Internet上当一台主机要访问另外一台主机时，必须首先获知其地址，TCP/IP中的IP地址是由四段以“.”分开的数字组成(此处以IPv4的地址为例，IPv6的地址同理)，记起来不如名字那么方便，所以，就采用了域名系统来管理名字和IP的对应关系。虽然因特网上的节点都可以用IP地址标识，并且可以通过IP地址被访问，但即使是将32位的二进制IP地址写成4个0～255的十位数形式，也依然太长、太难记。因此，人们发明了域名(Domain Name)，域名可将一个IP地址关联到一组有意义的字符上去。用户访问一个网站的时候，既可以输入该网站的IP地址，也可以输入其域名，对访问而言，两者是等价的。一个公司的Web网站可看作是它在网上的门户，而域名就相当于其门牌地址，通常域名都使用该公司的名称或简称。一句话简述：DNS调度是CDN常见的调度方式之一。ADN是什么应用交付网络（Application Delivery Network，简称ADN），它利用相应的网络优化/加速设备，确保用户的业务应用能够快速、安全、可靠地交付给内部员工和外部服务群。从定义中可以看出应用交付的宗旨是保证企业关键业务的可靠性、可用性与安全性。应用交付应是多种技术的殊途同归，比如广域网加速、负载均衡、Web应用防火墙…针对不同的应用需求有不同的产品依托和侧重。ADC是传统的网络负载均衡的升级、扩展，它是一种综合的交付平台设备，其综合了负载平衡、TCP优化管理、链接管理、SSL VPN、压缩优化、智能网络地址转换、高级路由、智能端口镜像等各种技术手段的综合平台。一句话简述：ADN和CDN的区别是一个是侧重于内容分发，一个侧重于应用交付。SCDN是什么SCDN（Secure Content Delivery Network），即拥有安全防护能力的CDN服务，提供稳定加速的同时，智能预判攻击行为，通过智能的调度系统将DDoS攻击请求切换至高防IP完成清洗，而真正用户的请求则正常从加速节点获取资源。加速节点的分布式架构还同时具备防CC攻击的能力，达到加速和安全兼顾。SCDN的架构与全站加速基本一致，区别就是在CDN节点不仅具有加速功能，还配有防火墙，所有请求都是发到CDN节点，这样的好处第一隐藏了源站服务器IP，让攻击者不能直接攻击源站，第二因为CDN节点配有防火墙，可以抵御大部分DDOS和CC攻击。使用高抗性IP保护源站是无法兼具加速能力的，但如网络游戏、金融服务、政企安全、电子商务、医疗服务等不仅易受攻击还要兼备加速的业务场景等领域，则必须要兼备高防御能力和高效化的安全CDN服务。一句话简述：SCDN和CDN的差异在于增加了CDN的安全防护能力。DCDN是什么全站加速DCDN（Dynamic Route for Content Delivery Network）是一项根据CDN加速技术的云技术升级，明智地区分动态和静态内容的浏览。对静态內容直接使用CDN开展加速；对动态內容则开展高效回源拉取，如路由决策优化、协议优化等。全站加速DCDN不仅能提供基础的CDN静态资源加速，而且还进一步提供了动态加速、TCP和UDP四层加速、Websocket七层加速等能力，可以快速地将安全、边缘计算等能力集成到全站加速的全球节点，提升全站性能和用户体验，实现业务提效。一句话简述：DCDN和CDN的差异在于增强了动态加速功能。ECDN是什么ECDN（Enterprise Content Delivery Network）全站加速是腾讯云2019年推出的全能加速平台，由原有DSA动态加速产品升级而来.ECDN 适用于动、静态资源混合型资源的一站式加速，同一平台上可实现站内所有类型资源同时加速，自动识别动静态资源。静态资源可根据腾讯自研的GSLB系统智能分配最优服务节点；动态资源基于回源路由优化技术实现快速回源。一句话简述：ECDN还是普通CDN技术，和DCDN的基本逻辑差不多，无非腾讯给他命名方式和其他家不一样。什么是PCDNPCDN的英文全称是P2P CDN，中文名叫P2P内容分发网络，是以P2P技术为基础，通过挖掘利用边缘网络海量碎片化闲置资源而构建的低成本高品质内容分发网络服务。你可以通过集成PCDN SDK（以下简称SDK）接入该服务后能获得等同（或略高于）CDN的分发质量，同时显著降低分发成本。客户通过集成 PCDN SDK（以下简称 SDK）接入该服务后能获得等同（或略高于）CDN 的分发质量，同时显著降低分发成本。适用于视频点播、直播、大文件下载等业务场景。一句话简述：PCDN=P2P+CDN的融合体。什么是融合CDN融合CDN是在传统CDN基础上，通过技术手段融合各主流CDN厂商的优质节点，以实现全业务处理能力的智能调度加速管理服务。融合CDN打破了单个CDN厂商的节点资源以及调度能力有限的困境，突破了地域时间以及不同运营商的限制，通过强大的智能调度策略来综合利用上述资源来解决实际场景中的问题，可以带来更加优质的服务效果、更加稳定的质量和相对降低的服务成本。一句话简述：融合CDN=多接入几家CDN，采用智能监控，哪家加速效果好用哪个。