Python消息推送实战:从轻量通道到统一HTTP服务
发布时间:2026/9/3 3:48:48 作者:尧图编辑部 阅读量:1,286

你是否遇到过这样的情况凌晨两点服务器告警邮件已经躺在邮箱里等你第二天早上打开电脑才发现服务早就不可用又或者写了一个监控脚本结果只能每天手动点开网页看结果。许多自动化任务到最后都会卡在同一个环节怎么把消息主动送到手机上让人第一时间知道。这个话题听起来不大但真正做起来会发现选择特别多iOS、Android、厂商限制、系统省电策略、Token 有效期……每一个都可能影响你的消息能不能到达。这篇文章不会只抛出几个概念而是会完整梳理“推送消息到手机”的主流技术路线再带你把一条轻量级链路跑通用 Python 脚本调用现成通道把消息直接送到手机通知栏。接着会设计一个统一推送的小型 HTTP 服务最后补充 APNs、FCM、厂商通道这类系统级推送的选型思路和常见排错方法。无论你是后端开发、运维还是写爬虫和自动化脚本的开发者都可以按本文一步步落地。1. 把消息推到手机上先理解技术选型1.1 通知栏消息背后的通道机制很多刚接触推送的开发者会想手机上都装了 App服务器直接和 App 建立网络连接不就能把消息发给用户了吗理论上可以但真的这么做会碰到两个现实问题。第一移动端网络不像服务器那样固定。用户可能从 Wi-Fi 切到 4G/5GIP 地址会变普通 TCP 长连接很容易断开。App 如果退到后台操作系统为了省电会在几分钟内冻结应用进程连接自然也就没了。第二应用不可能永远在前台运行。如果服务器只能通过“应用进程里的连接”发消息那么用户一旦清掉 App后续消息就全部丢失。真正成熟的推送能力依赖的是手机操作系统层面的通知通道手机系统本身保持一个长连接或者接收厂商推送服务器的消息再弹出通知栏。这样 App 就算没在运行用户也能看到通知。这里就引出了几种不同层次的方案有 iOS 的 APNs、Android 的 FCM、国内手机厂商自己的通道也有 Bark、Server酱这类面向个人开发者的轻量通道。选哪一种取决于你要解决什么问题。1.2 主流方案选型对比为了更直观地理解区别可以看下面的对比表方案类型代表优点劣势适用场景轻量 Webhook 通道Bark、Server酱、PushPlus、钉钉/企业微信机器人接入快几分钟跑通不需要上架应用依赖第三方服务功能和到达率不可控个人提醒、服务器告警、自动化脚本通知iOS 系统通道APNs苹果官方通道离线消息可靠需要 Apple 开发者账号需要 App 集成iOS 正式 App 推送Android 官方通道FCMGoogle 官方服务跨平台支持好终端依赖 Google 服务国内使用受限海外应用、跨平台应用Android 厂商通道小米推送、华为推送、OPPO 推送、vivo 推送等系统级通道省电且到达率高各厂商规则不一致接入成本高国内 Android App第三方聚合推送极光、个推、友盟等一套 SDK 对接多个厂商通道商业化限制多底层仍依赖厂商通道中小型 App 快速接入自建长连接WebSocket、MQTT可控性强适合实时业务成本高省电策略下仍然无法保证后台到达IM、实时互动类业务从接入成本来看个人项目、自动化通知、服务器告警这类场景不需要做 App也不需要申请推送证书更适合用 Bark、Server酱这类消息工具。它们本质上扮演的是一个“中转站”角色你的服务通过 HTTP 请求把消息发给中转服务中转服务再调用 iOS APNs 或微信/企业微信的能力把消息推到你的设备上。1.3 本文的推荐路线本文后面会分成两段实战路线第一段编写 Python 脚本直接调用轻量通道把消息推送到手机。这一段代码可以立刻用于服务器告警、定时任务通知。第二段把脚本升级为一个 HTTP 推送服务统一接口、写入发送记录、加上简单鉴权方便团队内部多个系统共用。在动手之前你只要知道一个核心结论轻量通道适合“把消息发到你自己的手机上”系统级通道适合“把消息发给所有 App 用户”。两者不冲突很多项目初期用前者等真正做产品了再去接后者。2. 环境准备与接收端搭建2.1 开发环境本文示例以 Python 为主依赖不多核心只有requests。操作系统Windows / macOS / Linux 均可。Python建议 3.8 或更高版本。开发工具任意 IDE 或文本编辑器能跑.py文件即可。测试环境可以用本机终端直接运行不需要真实服务器。版本不需要刻意追求最新重点是代码逻辑和请求方式与你本机环境匹配。检查 Python 版本python --version如果你用的是 Windows 且安装了多个 Python 版本可能需要用python3或py -3代替python命令。下面所有命令都以 Linux/macOS 风格的python为例Windows 用户按终端环境替换即可。2.2 接收端准备要让消息最终出现在手机通知栏接收端必须先行准备好。下面按不同方案说明使用 Bark需要一台 iPhone在 App Store 安装 Bark。打开应用后会看到一个属于你的设备 Key例如xxxxxxxxxxxxxxxx。这个 Key 是调用推送接口的核心凭证不要泄露给别人。使用 Server酱电脑端访问 Server酱官网使用 GitHub 或微信登录后在“SendKey”页面复制你的密钥。Server酱会把消息推送到你绑定的微信上。使用 PushPlus同样属于微信消息推送工具登录官网后获取 Token扫码关注公众号后即可接收。如果你是 Android 用户又不想使用微信相关通道还可以考虑通过“企业微信群机器人”接收告警或者安装 Bark 的 Android 替代品、使用 PushPlus 等。这些工具的操作思路一致先拿到一个唯一标识然后在请求参数里带上它。2.3 安装 Python 依赖先进到一个新建目录创建虚拟环境再安装依赖mkdir push-demo cd push-demo python -m venv venv source venv/bin/activate # Windows PowerShell 使用 venv\Scripts\Activate.ps1 pip install requestsrequests是目前 Python 中最常用的 HTTP 请求库用它发起 POST/GET 请求很稳定。在后面的 FastAPI 实战中还会用到fastapi、uvicorn到那一节再安装即可。3. 实战一用 Python 快速实现“推送消息到手机”3.1 接入轻量通道的基本流程不管是 Bark、Server酱还是 PushPlus接入流程都是下面几步准备接收端的唯一标识设备 Key / SendKey / Token。构造推送内容通常包含标题、正文、跳转地址等字段。用 HTTP 请求把内容发送到指定 API 地址。解析返回结果判断是否发送成功。不同工具之间只是 API 地址和字段名不同完全可以用一个 Python 脚本统一封装。下面以 Bark 和 Server酱两者为例写一个可以直接运行的push.py。选择这两个示例是因为 Bark 能直达 iPhone 通知栏Server酱能推送到微信基本覆盖了大多数个人开发者场景。3.2 编写第一个发送脚本 push.py在push-demo目录下创建文件push.py粘贴以下代码#!/usr/bin/env python3 # 文件路径push-demo/push.py import os import sys import argparse import requests BARK_API_BASE os.getenv(BARK_API_BASE, https://api.day.app) BARK_DEVICE_KEY os.getenv(BARK_DEVICE_KEY, ) SERVERCHAN_SEND_KEY os.getenv(SERVERCHAN_SEND_KEY, ) def send_bark(title: str, content: str, url: str ) - requests.Response: 通过 Bark 推送到 iPhone 通知栏。 if not BARK_DEVICE_KEY: raise RuntimeError(缺少 BARK_DEVICE_KEY请先设置环境变量) payload { title: title, body: content, } if url: payload[url] url # Bark 支持 POST JSON 请求比 GET 拼 URL 更不容易出现编码问题 resp requests.post( f{BARK_API_BASE}/{BARK_DEVICE_KEY}, jsonpayload, timeout10, ) return resp def send_serverchan(title: str, content: str, url: str ) - requests.Response: 通过 Server酱推送到微信。 if not SERVERCHAN_SEND_KEY: raise RuntimeError(缺少 SERVERCHAN_SEND_KEY请先设置环境变量) # 如果消息里有跳转链接可以和正文拼在一起发送 markdown_content content if url: markdown_content f{content}\n\n[查看详情]({url}) resp requests.post( fhttps://sctapi.ftqq.com/{SERVERCHAN_SEND_KEY}.send, data{ title: title, desp: markdown_content, }, timeout10, ) return resp def main(): parser argparse.ArgumentParser(description把消息推送到手机) parser.add_argument(--channel, choices[bark, serverchan], defaultbark) parser.add_argument(--title, default消息通知) parser.add_argument(--content, default这是一条测试推送) parser.add_argument(--url, default) args parser.parse_args() try: if args.channel bark: resp send_bark(args.title, args.content, args.url) else: resp send_serverchan(args.title, args.content, args.url) print(HTTP 状态码:, resp.status_code) print(返回内容:, resp.text[:500]) # 不同服务商判断成功的方式不同有的看 HTTP 状态码有的看返回体里的业务码 # 如果只是脚本告警可以先用状态码判断生产环境建议进一步解析业务码 if resp.status_code 200: print(发送请求已成功提交) else: sys.exit(1) except requests.RequestException as exc: print(请求异常:, exc) sys.exit(1) except RuntimeError as exc: print(配置错误:, exc) sys.exit(1) if __name__ __main__: main()代码里有两处需要特别说明。第一密钥信息不要硬编码在文件里而是通过环境变量读取。这样你以后把代码提交到 Git 仓库时不会把密钥一起提交上去。第二Bark 使用 POST JSON 请求Server酱使用 POST 表单请求。两者的字段名不同Bark 是title和bodyServer酱是title和desp。如果你换成 PushPlus 或其他工具只需要调整请求地址和字段名。3.3 运行并验证推送先设置你需要的环境变量。Linux/macOSexport BARK_DEVICE_KEY你的Bark设备Key export SERVERCHAN_SEND_KEY你的Server酱SendKeyWindows PowerShell$env:BARK_DEVICE_KEY你的Bark设备Key $env:SERVERCHAN_SEND_KEY你的Server酱SendKey然后运行python push.py --channel bark --title 第一次推送 --content 你好手机收到这条消息了吗如果 Bark 配置正确iPhone 通知栏会立刻出现一条标题为“第一次推送”的通知。终端输出类似HTTP 状态码: 200 返回内容: {code:200,message:success} 发送请求已成功提交再测试 Server酱python push.py --channel serverchan --title 微信推送测试 --content 这是一条来自 Server酱 的消息这时你绑定的微信里会收到一条服务通知标题和正文就是你传入的内容。这里有一个新手容易踩的坑如果在终端里直接手写中文 URL 测试 Bark 的 GET 接口中文往往需要做 URL 编码不加编码就可能失败。解决办法就是代码中使用的 POST JSON 方式把标题和正文放在 body 里由requests自动处理编码这也是推荐写法。3.4 在监控告警中调用推送脚本脚本本身不解决问题真正有价值的是把它接入到你的自动化流程里。举一个实际的服务器磁盘监控例子。假设你有一台 Linux 服务器希望磁盘使用率超过 85% 时自动推送消息。可以新建disk_monitor.py#!/usr/bin/env python3 # 文件路径push-demo/disk_monitor.py import shutil from push import send_bark def check_disk(path/): usage shutil.disk_usage(path) used_percent usage.used / usage.total * 100 print(f当前磁盘使用率: {used_percent:.1f}%) if used_percent 85: send_bark( title服务器磁盘告警, contentf路径 {path} 当前使用率已达 {used_percent:.1f}%请及时清理磁盘。, urlhttp://your-monitor.example.com/disk, ) print(已发送告警通知) else: print(磁盘使用率正常不发送通知) if __name__ __main__: check_disk()把它加入 crontab*/30 * * * * cd /path/to/push-demo /path/to/push-demo/venv/bin/python disk_monitor.py这样每隔半小时检查一次磁盘超过阈值就推到手机。你会发现原本需要时刻关注服务器状态的工作变成了一条可以直接在手机通知栏完成闭环的消息。4. 实战二搭建统一推送 HTTP 服务4.1 需求设计与接口定义当你只有一台服务器、一个脚本时直接用上面的push.py就够了。但脚本和通道一旦多起来问题就出现了每个脚本都要复制一份推送代码改通道时要到处改。没有发送记录出了问题不知道消息到底发出去没有。密钥分散在各个脚本的环境变量里不便于统一管理。这时可以把“推送”这件事抽象成一个独立服务提供一个 HTTP 接口。其他脚本只需要往这个接口 POST 一条消息由服务统一决定走哪个通道、记录日志、返回结果。接口设计如下POST /push Content-Type: application/json X-Token: 自行设置的访问令牌 { title: 公告标题, content: 消息正文, url: https://example.com/detail, channel: serverchan }同时提供一个查询历史记录的接口GET /history这个服务本身不实现真正的“推到手机”它只负责把消息转发到前面的轻量通道。好处是团队内部所有系统都只对接它不需要知道 Bark、Server酱这些工具的存在。4.2 项目结构和核心代码下面用 FastAPI 来搭建服务。先安装依赖pip install fastapi uvicorn创建notify_service目录并在里面新建app.py# 文件路径notify_service/app.py import datetime import os import sqlite3 import uuid import requests import uvicorn from fastapi import FastAPI, Header, HTTPException from pydantic import BaseModel app FastAPI(title统一推送服务) DB_PATH os.getenv(DB_PATH, notify.db) API_TOKEN os.getenv(API_TOKEN, change-me) BARK_DEVICE_KEY os.getenv(BARK_DEVICE_KEY, ) SERVERCHAN_SEND_KEY os.getenv(SERVERCHAN_SEND_KEY, ) class PushMessage(BaseModel): title: str content: str url: str channel: str serverchan def init_db(): 初始化 SQLite保存消息发送记录。 conn sql