Python下的苹果推送服务(APNS)模块选择

APNS 是苹果为IOS设备提供的推送服务,全称是(Apple Push Notification service)。 如果你有接触移动互联网相关的开发的话,应该对它很熟悉。

接下来我会给大家简单介绍一下Python下的一些APNS相关的模块以及其特点。

模块介绍

PyAPNs

项目地址:https://github.com/djacobs/PyAPNs

PyAPNs是我最早使用的APNS模块,它应该是我要介绍的所有模块里面最简单的,最新的源码 只有384行,实现了APNS的基本功能,包括发送推送、使用Frame群发推送、feedback 接口等。

它的所有验证都是在客户端做的,比如每一个Payload不超过256字节。

但是它有一个最大的缺点,我们都知道苹果官方文档对发送推送的连接建议是:

Best Practices for Managing Connections

You may establish multiple connections to the same gateway or to multiple gateway instances. If you need to send a large number of push notifications, spread them out over connections to several different gateways. This improves performance compared to using a single connection: it lets you send the push notifications faster, and it lets APNs deliver them faster.

Keep your connections with APNs open across multiple notifications; don’t repeatedly open and close connections. APNs treats rapid connection and disconnection as a denial-of-service attack. You should leave a connection open unless you know it will be idle for an extended period of time—for example, if you only send notifications to your users once a day it is ok to use a new connection each day.

简单来说,就是尽量复用你的链接,不要频繁的建立和断开,不然会被当做DoS攻击处理。所以 我们使用它来发送推送时应该这么干:

... ...

# 复用这个gateway_server
apns.gateway_server.send_notification(token_hex, payload)

复用这个gateway_server也就是连接,但是到APNS Server的链接是很不稳定的,很多情况下 都会被断开,比如网络原因、发送了非法的token等。所以我们还需要一个重连的机制。

但PyAPNs模块没有为你处理这些,所以你需要自己去处理那些出错的情况,这也是使用 这个模块最不方便的地方。

所以我的建议是,除非你自己需要去写一个APNS的Provider,那你可以以这个模块作为起点。 否则,如果你想在你的项目里面快速用上推送服务的话,建议还是选择别的模块。

pyapns(twisted)

项目地址:https://github.com/samuraisam/pyapns

第一次知道这个东西实在instagram的 一篇博客 里面,这篇博客里面有提到这么一句话:

For doing push notifications, the most cost-effective solution we found was https://github.com/samuraisam/pyapns, an open-source Twisted service that has handled over a billion push notifications for us, and has been rock-solid.

他们使用的就是这个项目作为他们的推送服务的provider,所以我之后把推送从PyAPNs迁移到了这个项目, 使用下来其实还是挺不错的,这个项目的主要特点是:

  • 它其实是一个基于twisted的server,所有发送推送的请求都通过它来和苹果的服务器交互。
  • 对Django和Pylons有原生支持。
  • 支持多个APP。

因为和苹果的推送服务器是由这个provider维持的长连接,所以你每次发送推送的时候都直接 这个provier进行叫交互,这样的的好处是每一次的接口调用返回都很快,真正推送到苹果服务器的过程 则是由这个provider异步来完成。

但是这个模块很长时间都没有维护了,其实Apple那边的协议在这段时间里已经进行了一些更新。 但这个模块没有跟上。

我使用这个模块碰到的最大的问题就是 群发推送的效果得不到保证

虽然这个模块的demo里面有对批量发送推送进行支持,但是我的使用经验是,这个模块的群发 推送效果比较差,而且缺少从苹果Server拿到错误反馈的逻辑。

因为Twisted的代码风格实在不怎么喜欢,所以我群发碰到问题后开始寻找别的解决方案。

apns-client

项目地址:https://bitbucket.org/sardarnl/apns-client/

这是另外一个和APNS交互的模块,它在自己的主页上列出了和其他模块之前最大的区别:

  • Keep connections persistent. An SSL handshaking round is slow. Once connection is established, it should remain open for at least few minutes, waiting for the next batch.
  • Support enhanced format. Apple developers have designed a notoriously bad push protocol. They have upgraded it to enhanced version, which makes it possible to detect which messages in the batch have failed.
  • Clean pythonic API. No need for lots of classes, long lists of exceptions etc.
  • Do not hard-code validation, let APNs fail. This decision makes library a little bit more future proof.

总结一下就是:

  • 维持持久链接。SSL协议的握手环节是很慢的。当每一个连接被建立之后,它应该一直保持最少几分钟来等待 下一次的推送。
  • 支持改进过的的协议格式。Apple的程序员们设计了一个臭名昭著的推送协议。他们更新了一个版本,这个版本可以让你知道 每一次群发推送里面到底是哪一个单独的消息出了问题。
  • 清晰的Python API
  • 没有把验证这块写进代码里,而是直接返回APNS的错误信息

使用这个模块来发送推送也很简单:

from apnsclient import *

# 可以使用Session对象来维持连接池
session = Session()
con = session.get_connection("push_sandbox", cert_file="sandbox.pem")

# 发送推送和得到反馈
messge = Message(["my", "device", "tokens"], alert="My message", badge=10)

# Send the message.
srv = APNs(con)
res = srv.send(message)

# Check failures. Check codes in APNs reference docs.
for token, reason in res.failed.items():
    code, errmsg = reason
    print "Device faled: {0}, reason: {1}".format(token, errmsg)

# Check failures not related to devices.
for code, errmsg in res.errors:
    print "Error: ", errmsg

对于我来说,这个模块最大的优点就是为你处理了连接有可能被异常断开重连的情况。而且代码不像 pyapns这样晦涩,更直观,可读性更高。所以你如果要在它的基础上做一些修改也没有任何问题。

经过我的使用经验,使用apns-client来处理百万级别这种量级的推送没有任何问题,到达率也很好。

所以如果你没有特殊的需求的话,apns-client应该是你最好的选择。