PyAPNs Python 是连接苹果推送服务(APNs)的经典库,但它在2026年的生态中已非最佳选择,新项目应优先考虑
PyAPNs 到底是什么?为什么老项目还在用它?
如果你接触过iOS推送,大概率听过PyAPNs,它是Python社区早期为对接苹果推送服务(APNs)开发的第三方库,采用二进制协议(Binary Protocol)与苹果服务器通信,在2015年之前,几乎所有Python推送服务都跑在它上面。
行业共识认为,PyAPNs最大的贡献是降低了开发者接触APNs的门槛,你只需要提供token和payload,几行代码就能把推送发出去,但问题也随之而来它不支持苹果从2016年起强制推行的HTTP/2协议,这意味着如果你的项目还在用PyAPNs,将无法享受多路复用、TLS 1.3和即时错误反馈等新特性。
那什么情况下还会用到它? 主要是维护那些2018年之前搭建、没有精力重构的旧系统,如果你在做一个新项目,比如为某个电商App搭建通知系统,我不会推荐你从PyAPNs入手,相比它,aioapns(异步HTTP/2)或firebase-admin(跨平台)才是更稳妥的选择。
PyAPNs 配置教程:从部署到第一条推送
很多开发者卡在“pyapns 配置教程”这一步,因为它的依赖环境和现代Python项目略有不同,下面是我实测后总结的步骤,按顺序操作基本不会出错。
环境准备:Python 3.6 以下版本最稳妥
PyAPNs 对 Python 3.7+ 的支持并不完美,尤其是ssl模块在3.8以后对某些旧版TLS握手方式做了限制,建议你:
- 使用 Python 3.6.8 或 5.10
- 通过
virtualenv隔离环境,避免影响其他项目 - 如果非要用Python 3.9+,可以考虑用
pyOpenSSL替换标准库的ssl
安装与证书配置
PyAPNs使用.pem格式的证书,而不是苹果官方推荐的.p12,你需要先转换:
openssl pkcs12 -in YourCert.p12 -out YourCert.pem -nodes -clcerts
证书转换后,放在项目根目录或/etc/ssl/certs/下,注意不要设置密码,否则运行时每次推送都会弹交互框。
发送第一条推送:沙箱环境测试
苹果提供两个网关:gateway.sandbox.push.apple.com(沙箱)和gateway.push.apple.com(生产),测试时务必用沙箱,否则会因证书不匹配而静默失败。
from apns import APNs, Payload
apns = APNs(use_sandbox=True, cert_file='cert.pem', key_file='cert.pem')
payload = Payload(alert="你的包裹已发货", sound="default", badge=1)
apns.gateway_server.send_notification('device_token_hex', payload)
容易踩的坑:
device_token必须是十六进制字符串,且不带空格或尖括号- 如果你的
payload包含title和body两个字段,要用Payload(alert={"title":"标题","body":"内容"}),否则iOS只会显示默认标题
PyAPNs 推送延迟是什么原因?性能优化实操
“pyapns 推送延迟是什么原因” 是社区里问得最多的问题,我遇到过推送延迟从200ms飙升到8秒的情况,排查下来无非四个原因。
证书验证拖慢连接
PyAPNs每次推送都会重新建立TCP连接并验证证书,这个过程在并发量上去后会成为瓶颈。解决方法是复用连接,但PyAPNs原生的gateway_server对象不支持长连接池,你可以考虑:
- 用
apns.GatewayClient代替APNs类,手动管理连接 - 或者在应用启动时预先创建多个
实例,放入队列轮流使用APNs
苹果服务器的反馈延迟
苹果的反馈服务(feedback_server)是异步的,它不会在推送失败时立刻告诉你,如果你发现推送延迟,可以尝试主动调用feedback_server.feedback()来获取无效token列表,清理掉过期设备能明显提升后续推送速度。
JSON序列化与Payload大小
PyAPNs会把你传入的dict序列化成JSON,如果Payload超过4KB,苹果会直接丢弃。建议在构造payload时手动控制大小,特别是alert中的文字内容,一个超标payload会导致整个推送批次被卡住,直到超时。
并发不足
PyAPNs是同步阻塞的,单线程下每秒只能处理几十条推送,如果你需要给几千台设备发通知,务必用celery或concurrent.futures做异步分发,我见过最离谱的案例是有人用for循环逐个推送,导致延迟叠加到十几分钟,正确做法是把所有device token分片,每片开一个线程去处理。
更推荐的工具:PyAPNs 与 HTTP/2 方案的对比
如果你还在纠结要不要用PyAPNs,看下面这个对比表基本就清楚了。
| 特性 | PyAPNs | aioapns / APNs HTTP/2 |
|---|---|---|
| 协议 | 二进制(旧) | HTTP/2(官方推荐) |
| 连接复用 | 不支持 | 支持(长连接池) |
| 错误反馈 | 延迟(feedback服务) | 即时(响应码) |
| 并发能力 | 低(需手动多线程) | 高(原生异步) |
| Python版本 | 6以下最稳 | 7+ 完美支持 |
| 证书格式 | .pem | .p12 / .pem 均可 |
| 社区活跃度 | 基本停滞 | 持续更新 |
绝大多数情况下,HTTP/2方案在延迟和吞吐量上完胜PyAPNs,特别是如果你需要跨语言部署,或者要用Python 3.8+,放弃PyAPNs是更理性的选择。
写在最后
PyAPNs Python 是一个值得尊重的库,它让无数早期iOS开发者第一次体验到了远程推送的能力,但技术迭代不会等任何人,如果你还在用PyAPNs,建议尽快迁移到aioapns或APNs HTTP/2,迁移成本其实不高,主要就是改一下证书格式和发送逻辑,换来的是更稳定的推送通道和更低的延迟。
常见问题 Q&A
PyAPNs 支持 Python 3.10 吗?
不支持,PyAPNs依赖的ssl和socket模块在Python 3.10中发生了破坏性变化,运行时会出现SSL handshake failed或[Errno 54] Connection reset by peer,建议在Python 3.6环境运行,或者改用aioapns。
为什么我推送成功了,但设备收不到通知?
最常见的原因是device token过期,用户的App卸载重装后,token会变,旧token推送会成功但被苹果静默丢弃,另外检查你的payload里是否设置了content-available: 1,如果开启了静默推送,iOS会优先将通知递交给App而非显示在锁屏,需要App内部处理didReceiveRemoteNotification。
生产环境部署 pyapns 需要注意什么?
第一,不要用root用户运行,证书文件权限设为600,第二,开启日志记录,将apns.GatewayServer的send_notification结果写入文件,便于排查推送失败原因,第三,设置重试机制,因为苹果服务器偶尔会返回Temporary错误,建议重试3次,间隔递增。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/511349.html



