源码在:https://github.com/sskaje/apns
协议消息体使用的是APNS的简版消息结构,暂不支持Enhanced notification format,即不支持identifier和expiry,省了8个字节,不过稍后有空会加上。
大致的数据逻辑是:
1 |
Application --> Queue --> Daemon |
走了个异步队列。应用将推送数据按公用结构(适配非APNS的推送需求)带上推送的目标设备类型+客户端标识(支持同平台下多款应用的推送)所用的队列;后端程序阻塞或非阻塞的模式从队列里取数据,然后根据数据信息写APNS或者其他类型终端(例如Android)的推送服务。
开发过程中遇到的最大问题主要在 stream_socket_client() 的 STREAM_CLIENT_PERSISTENT 使用上:
最早需求上只有一个客户端证书的需求,不需要做切换,只需要创建一个连接,context配好证书,不论存不存静态变量,直接用就是了。
但是后来公司有第二个客户端的推送需求,简单地上了一个static变量,意图直接两个静态对象再根据数据参数选择连接,但是结果发现消息丢失率大大提升,而且两个客户端的推送消息还互串。
php源码的 ext/standard/streamfuncs.c 里
1 2 3 4 5 6 7 8 9 10 |
PHP_FUNCTION(stream_socket_client) ... if (flags & PHP_STREAM_CLIENT_PERSISTENT) { spprintf(&hashkey, 0, "stream_socket_client__%s", host); } ... stream = php_stream_xport_create(host, host_len, ENFORCE_SAFE_MODE | REPORT_ERRORS, STREAM_XPORT_CLIENT | (flags & PHP_STREAM_CLIENT_CONNECT ? STREAM_XPORT_CONNECT : 0) | (flags & PHP_STREAM_CLIENT_ASYNC_CONNECT ? STREAM_XPORT_CONNECT_ASYNC : 0), hashkey, &tv, context, &errstr, &err); |
而 main/streams/php_stream_transport.h
1 2 |
#define php_stream_xport_create(name, namelen, options, flags, persistent_id, timeout, context, estr, ecode) \ _php_stream_xport_create(name, namelen, options, flags, persistent_id, timeout, context, estr, ecode STREAMS_CC TSRMLS_CC) |
显然直接使用了 host 参数拼了个字符串就当persistent id了。而现在的需求又不得不使用两个证书创建不同的连接,于是解决方案似乎不外乎 干掉 STREAM_CLIENT_PERSISTENT 或者 拆分推送脚本 要不就得直接改源码了。
当然,直接使用IP(譬如一个使用域名一个使用IP,或者直接用上域名的一片A记录,每种客户端需求使用不同的IP连接),看似也是个解决方案,但是带来的灾难比拆分推送脚本还大。
偶然想到一个没准可行的解决方案:
ssl://gateway.push.apple.com:2195/?xxx
xxx部分可以用证书路径做个md5什么的,反正是个ssl socket连接,没有path和query的需求,应该能可用,而且还能区分成不同的’host’参数。
github里的代码是按这个想法本地实验过验证可行了的,只是生产环境的具体情况还得观察。