APNS导致消息丢失和发送效率原因

2023-05-16

http://blog.csdn.net/tlq1988/article/details/9612237


  首先说明一下,本文只是介绍一些容易被开发者忽视,而导致性能低下问题。并不是介绍如何向苹果设备成功发送一条消息,这里假设所有阅读者已经能够向苹果服务器发送消息,并且成功接收,只是发送效率比较低,并且丢失率很高。如果你不是此类情况,那么绕道吧。PS:伸手党可以直接看标红部分(结论)

    最近参与并且完成了公司1000W级的消息推送服务平台重建。此次重构级别解决了消息丢失,并且大幅度提升了推送效率。有些东西我想很多开发者也会碰到,并且难以被开发者所意识到。

    先先扫下盲哈。如果你发送消息是一次连接发送一条,那么请你先改成长连接发送--一次连接发送多条数据。粘下PHP代码吧:)

[php]  view plain copy
  1. $pass = ''; // $pass是你在建立证书的时候输入的密码  
  2. $ctx = stream_context_create();  
  3. // apns.pem就是你的证书的路径了,最好写绝对路径  
  4. stream_context_set_option($ctx, 'ssl', 'local_cert', 'apns.pem');  
  5. stream_context_set_option($ctx, 'ssl', 'passphrase', $pass);  
  6. $fp = stream_socket_client('ssl://gateway.sandbox.push.apple.com:2195', $err, $errstr, 60, STREAM_CLIENT_CONNECT, $ctx);  
  7. if(!$fp) {  
  8.     print "Failed to connect $err $errstr";  
  9.     exit();  
  10. } else {  
  11.     print "Connection OK\n";  
  12. }  
  13. $body = array('aps' => array('badge' => 1));  
  14. for($i = 0; $i <= 10000; $i++) {  
  15.     $deviceToken = md5(time() . rand(0, 9999999)) . md5(time() . rand(0, 9999999)); // 模拟一个Device Token  
  16.     $body['aps']['alert'] = md5(time() . rand(0, 9999999)); // 随便模拟点数据  
  17.     $payload = json_encode($body);  
  18.     // 这里是简单的消息结构,如果想多发几个但是不要返回错误,可以用这个  
  19.     /* 
  20.     $msg = chr(0) . pack("n", 32) 
  21.         . pack('H*', str_replace(' ', '', $deviceToken)) 
  22.         . pack("n", strlen($payload)) . $payload; 
  23.     */  
  24.     // 这个是增强型消息格式,$i就是Identifier,864000就是Expiry了  
  25.     $msg = pack('CNNnH*', self::COMMAND_PUSH, $i, 864000, 32, $deviceToken)  
  26.         . pack('n', strlen($payload))  
  27.         . $payload;  
  28.     print "sending message :" . $payload . "\n";  
  29.     fwrite($fp, $msg);  
  30.     // 这里是读取错误信息,不要没发一条就读取一次,这样苹果会认为攻击而终止连接  
  31.     //fread($fp, 6);  
  32. }  

    要往下面说我先解释一下这个东东--Broken Pipe,如果你有过大量的数据推送,并且看下你的错误日志那么Writen Broken Pipe你一定不陌生。这个错误产生的原因通常是当管道读端没有在读,而管道的写端继续有线程在写,就会造成管道中断。可以简单的理解为你在向一个已经关闭的连接写数据就会抛出这个错误。

    由于Broken Pipe的关系,我们不得不重新和苹果服务器建立连接,这个连接耗时在国内.....(你们懂的3sec+),这个应该是我们推送速度最大的瓶颈了。有很多开发者也许会认为这个是由于国内的网络环境导致,因为他们习惯的“traceroute>你就错了。

    我们用大量(10W左右)能保证基本正确的Device Token来做测试,平均一次连接的能写入3W左右的数据,好的情况下能一次写完这10W数据!!!这个测试也就证明平凡出现Broken Pipe不是由于网络原因。既然不是由于网络原因,那么我做个大胆的假设:这个连接是由APNs主动断开的

    那么假设这个猜想是正确的,那苹果什么时候会断开连接了?解释这个问题,我们又做了一个测试:往这10W的Device Token里面均匀插入1000个错误的Device Token。神奇的事情发生了,发送期间平均断开连接900次+。这个实验正好验证了我之前的猜测:产生Broken Pipe是因为APNs服务器主动断开了连接,并且是由于错误的Device Token引起的(或者其他的错误)。苹果的错误类型和代码编号:

Status code

Description

0

No errors encountered

1

Processing error

2

Missing device token

3

Missing topic

4

Missing payload

5

Invalid token size

6

Invalid topic size

7

Invalid payload size

8

Invalid token

10

Shutdown

255

None (unknown)

    我们进一步跟进测试,我们发现一个奇怪的现象,断开连接的时的前一个Token并不是我们所特意设置的错误Token。同时我们也发现消息送达率也变得非常的低(偶尔有设备能收到)。这个很好解释,之前我就有文章提到过(官方也有相应说明)当一次连接先发送一个错误的Token,之后的有效Token的消息是无法送达的(http://blog.csdn.net/hjq_tlq/article/details/8131115),这就导致了错误的Token后面的正确的Token全部没有收到,从而送达率也就明显下降了。

    经过上面的测试,当APNs接收到错误的Token的时候会主动断开连接,但是断开连接之前会有1sec左右的延迟。那么你可以有下面这个例子理解:

        你要发送1000条数据并且第20个Token是错误的

        当此次连接发到第20个Token的时候苹果认为此次连接终止(但是连接并没有断开,只是APNs将抛弃之后的内容),并且不处理此次连接之后的消息

        1sec左右的时间之后苹果主动断开SSL连接,如果你继续忘此连接写数据,你将可以捕捉到Broken Pipe错误

        此时由于1sec左右的延迟,你已经发送到了第123个消息

        此时从20以后直至123的消息将全部没有送达

    太可怕了.....你竟然不知道是从哪一个错了!!!苹果是SB啊!先不要做这样的结论,我们先看一下苹果官方文档所给出的东东:

Figure 5-1  Notification format

The first byte in the notification format is a command value of 1. The remaining fields are as follows:

  • Identifier—An arbitrary value that identifies this notification. This same identifier is returned in a error-response packet if APNs cannot interpret a notification.

  • Expiry—A fixed UNIX epoch date expressed in seconds (UTC) that identifies when the notification is no longer valid and can be discarded. The expiry value uses network byte order (big endian). If the expiry value is positive, APNs tries to deliver the notification at least once. Specify zero (or a value less than zero) to request that APNs not store the notification at all.

  • Token length—The length of the device token in network order (that is, big endian)

  • Device token—The device token in binary form.

  • Payload length—The length of the payload in network order (that is, big endian). The payload must not exceed 256 bytes and must not be null-terminated.

  • Payload—The>    PS:这里苹果到是做了件好事,这个消息结构在早些的文档中显示的是5-2 Enhanced Notification Format,而之前的5-1是Notification Format。区别在于之前的5-1中的消息结构为简单消息结构,没有Identifier和Expiry字段并且Command为0。现在直接把简单的消息体结构给去掉了,这样可以强制开发者加上Identifier,从而得到返回值。

        为了方便我直接把官方文档粘过来了哈:)我们需要注意的是Identifier这个东东。没错,这个就是苹果用来提供的给第三方的4唯一标示,如果鸟语不是很好的话他后面的那个注释大致就是说:一个消息的唯一标识。如果苹果服务器不能解释这个消息,那么将在错误中返回这个唯一标示。

        可恶的苹果并没有说明这个会有延迟,以及怎么确保我们能收到这个错误。我们现在采用的是每发送100条消息,就检查一下(read)是否有失败的。如果你抓到这个错误,那么果断断开连接,并且重新发送这条错误以后的Token,这样就能保证消息基本能送达。

        哦,顺便说一下如何得到错误反馈,如果你发送的时候加上了Identifier,那么此时你一定有一个和APNs的连接吧(废话,没连接怎么write),那么你只要read就好了,如果有就能读到一个二进制数据:)

        有一个也需要提一下,就是APNs的FeedBack功能也一定要用上,这个能帮助你更好的剔除错误的Token。

        当你的Token基本为正确的时候,如果还有大量的Broken Pipe出现,你可以给我留言,我们一起研究到底哪里出问题了:)

        附录:苹果推送官方文档

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

APNS导致消息丢失和发送效率原因 的相关文章

  • Shell 脚本监控磁盘空间

    df 命令可以展示文件系统的磁盘有效空间信息 如果不指定文件名 xff0c 则当前所有挂载的文件系统有效空间信息 实现步骤 使用 df 查看磁盘信息使用 grep命令 过滤文件系统 xff0c 获取空间使用百分比通过Shell 脚本进行监控
  • R 实现熵权法计算权重

    按照信息论基本原理的解释 xff0c 信息是系统有序程度的一个度量 xff0c 熵是系统无序程度的一个度量 xff1b 根据信息熵的定义 xff0c 对于某项指标 xff0c 可以用熵值来判断某个指标的离散程度 xff0c 其信息熵值越小
  • R实现KMeans聚类算法教程

    本文和你一起学习无监督机器学习算法 kmeans算法 xff0c 并在R中给详细的实现示例和步骤 什么是k means聚类算法 聚类是从数据集中对观测值进行聚类的机器学习方法 它的目标是聚类相似观测值 xff0c 不同类别之间差异较大 聚类
  • 如何在Java中调用Python

    Python语言有丰富的系统管理 数据处理 统计类软件包 xff0c 因此从java应用中调用Python代码的需求很常见 实用 DataX 是阿里开源的一个异构数据源离线同步工具 xff0c 致力于实现包括关系型数据库 MySQL Ora
  • 集群多机ROS通信中间件:swarm_ros_bridge

    最近写了一个无线网络环境下 xff08 比如WIFI xff09 多机ROS通信的ROS包 swarm ros bridge xff1a https gitee com shu peixuan swarm ros bridge 该项目已被R
  • ClickHouse 基于角色访问控制(RBAC)最佳实践

    本文介绍ClickHouse RBAC访问控制模型 包括如何启用SQL管理 xff0c 创建管理员用户 xff0c 创建角色 xff0c 授权 xff0c 细粒度列和行级授权 并通过示例进行验证实现过程 启用RBAC 在users xml中
  • ClickHouse服务端配置最佳实践

    安装好ClickHouse xff0c 需要对服务和用户进行配置 本文介绍ClickHouse建议配置方式 xff0c 配置项修改后是否需要重启 xff0c 另外还提供一些实例配置加深理解 独立自定义配置 Clickhouse 服务端配置包
  • 2014年度总结——软件产品化的简要理解

    2014年度总结 软件产品化的简要理解 2014年转瞬即逝 xff0c 真是让人感慨 xff0c 岁月不是一天天在逝去 xff0c 而是一年年 xff1b 总结一年的工作非常有意义 xff0c 觉得今年最大的变化就是从定制软件到产品化的过度
  • R语言中mean函数

    mean函数是求算术平均值 用法 xff1a mean x trim 61 0 na rm 61 FALSE x是数值型 逻辑向量 trim表示截尾平均数 xff0c 0 0 5之间的数值 xff0c 如 xff1a 0 10表示丢弃最大1
  • 使用Spring @DependsOn控制bean加载顺序

    使用Spring 64 DependsOn控制bean加载顺序 spring容器载入bean顺序是不确定的 xff0c spring框架没有约定特定顺序逻辑规范 但spring保证如果A依赖B 如beanA中有 64 Autowired B
  • 使用R中merge()函数合并数据

    使用R中merge 函数合并数据 在R中可以使用merge 函数去合并数据框 xff0c 其强大之处在于在两个不同的数据框中标识共同的列或行 如何使用merge 获取数据集中交叉部分 merge 最简单的形式为获取两个不同数据框中交叉部分
  • 介绍java中Pair

    介绍java中Pair 在这篇文章中 xff0c 我们讨论了一个非常有用的编程概念 xff0c 配对 Pair 配对提供了一种方便方式来处理简单的键值关联 xff0c 当我们想从方法返回两个值时特别有用 在核心Java库中可以使用配对 Pa
  • python numpy 中linspace函数

    python numpy 中linspace函数 numpy提供linspace函数 有时也称为np linspace 是python中创建数值序列工具 与Numpy arange函数类似 xff0c 生成结构与Numpy 数组类似的均匀分
  • 把文件夹里的文本批量替换内容

    下面的例子 xff0c 把 tmp task文件夹里 xff0c 所有文本文件中的 10 10 10 10 替换为 20 20 20 20 sed i 34 s 10 10 10 10 20 20 20 20 g 34 96 grep rl
  • openssl生成RSA私钥

    openssl OpenSSL gt genrsa out app private key pem 2048 默认生成PKCS 1的私钥 xff0c 2048表示私钥的长度 xff0c 我们建议是2048位 xff0c 这样安全 xff0c
  • ubuntu Ad-Hoc组网通信

    目录 WIFI通信的多种组网方式 1 AP模式 2 Ad hoc模式 ubuntu18配置ad hoc模式 WIFI通信的多种组网方式 1 AP模式 最常用的模式 xff0c 需要一个节点 xff08 一般是路由器 xff09 作为AP x
  • github中误上传,文件夹中包含.git,导致没法上传里面文件的方法

    假设包含 gi的文件夹为vendor github com 360EntSecGroup Skylar excelize xff0c 则可以 xff1a span class token function git span span cla
  • bluehost虚拟主机的301重定向<原创>

    http www bluehost cn com bluehost domain redirects 出于SEO PR值传递 网址转换的目的 xff0c 在网站初建和网站迁移时我们都需要使用301重定向 xff0c 通常包括域名对域名 xf
  • 一个很全部的验证手机号码的函数

    验证手机号是否正确 64 author 曾健生 64 param unknown type mobile public function validateMobile mobile 手机号码 移动 xff1a 134 0 8 135 136
  • .htaccess:正则表达式、重定向代码

    http codeyun com zonghe 235 html 位于行首时表示注释 F Forbidden xff08 禁止 xff09 命令服务器返回 403 Forbidden错误给用户浏览器 L Last rule xff08 最后

随机推荐

  • .htaccess技巧: URL重写(Rewrite)与重定向(Redirect)

    http lesca me archives htaccess rewrite html comment page 1 comment 16045 目录 Table of Contents 一 准备开始 xff1a mod rewrite
  • es实现mysql like的查询

    es中需要先安装中文分词插件ik 如果之前有这个索引 xff0c 需要先删除索引 DELETE lepeng 创建一个索引 PUT lepeng 指定索引的格式 ik max word 将文本做最细粒度的拆分 ik smart 会做最粗粒度
  • mysql错误:Error 1045: Access denied for user 的解决

    golang中 xff0c 在程序中配置的mysql地址是 xff1a 10 10 10 131 但是程序链接mysql时返回的错误是如下 xff1a Error 1045 Access denied for user 39 root 39
  • php代码解决跨域问题

    header 34 Access Control Allow Origin 34 header 34 Access Control Allow Methods OPTIONS GET POST DELETE PUT 34 header 34
  • 解决“Let‘s Encrypt“ 证书过期的错误消息

    https www cnblogs com tutuye p 11589546 html https manuals gfi com en kerio connect content server configuration ssl cer
  • 转:ubuntu22.04桌面版开启root用户登陆并开启root用户远程ssh连接

    https yunml blog csdn net article details 127145272
  • ROG配置ubuntu20.04.5双系统要点

    win11 43 ubuntu20 04 5 1 BIOS设置 开机长按F2进入bios设置 xff0c 修改advanced参数 xff1a boot gt 关闭fast bootsecurity gt 关闭secure boot设置VM
  • 《自己动手写Docker》书摘之一: Linux Namespace

    Linux Namespace 介绍 我们经常听到说Docker 是一个使用了Linux Namespace 和 Cgroups 的虚拟化工具 xff0c 但是什么是Linux Namespace 它在Docker内是怎么被使用的 xff0
  • app后端设计(1)--api(2014.01.31更新)

    1 Restful设计原则 Restful风格 xff1a RESTfu设计原则 它被Roy Felding提出 xff08 在他的 基于网络的软件架构 论文中第五章 xff09 而REST的核心原则是将你的API拆分为逻辑上的资源 这些资
  • [How To] Remove deleted records from Main Index in Sphinx

    http www sanisoft com blog 2013 06 28 remove deleted records in sphinx The problem You probably landed here searching fo
  • 左右值无限分类实现算法[转]

    http www jcwcn com archiver tid 227253 html 一 引言 产品分类 xff0c 多级的树状结构的论坛 xff0c 邮件列表等许多地方我们都会遇到这样的问题 xff1a 如何存储多级结构的数据 xff1
  • 给xmpphp添加了几个常用的方法

    给xmpphp添加给了以下的常用方法 xff1a registerNewUser 注册一个新用户 addRosterContact 发送添加好友的请求 accept friend request 接受好友请求 deleteRosterCon
  • app后端设计(2)--xmpp的使用(2014.01.14更新)

    在app中有时候是需要添加聊天服务 xff0c 在这里谈谈曾经开发聊天服务的经验 xff1a xff08 1 xff09 聊天服务端选的openfire xff0c 这是一个基于xmpp协议的聊天服务器 xff08 XMPP是一种基于XML
  • Nginx Location配置总结

    http blog sina com cn s blog 97688f8e0100zws5 html 语法规则 xff1a location 61 uri 61 开头表示精确匹配 开头表示uri以某个常规字符串开头 xff0c 理解为匹配
  • Nginx 下配置SSL证书的方法

    http www jb51 net article 24629 htm 默认 Nginx 是没有 ssl 模块的 xff0c 而我的 VPS 默认装的是 Nginx 0 7 63 xff0c 顺带把 Nginx 升级到 0 7 64 并且
  • 使用nginx搭建https服务器

    http www cnblogs com tintin1926 archive 2012 07 12 2587311 html 最近在研究nginx xff0c 整好遇到一个需求就是希望服务器与客户端之间传输内容是加密的 xff0c 防止中
  • Nginx与Lua

    http www cnblogs com xd502djj archive 2012 11 20 2779598 html 今天安装lua试试 xff0c 这个从开始装的 xff0c 发现一篇文字 xff0c 字数虽少 xff0c 但是却讲
  • MYSQL的空间查询

    http blog sina com cn s blog a48af8c001018q1p html 本文将向各位介绍如何使用MySql5 x中的空间数据库 xff0c 并展示一下它高效的性能 xff08 前提是正确使用 xff09 本文适
  • APM飞控使用动捕等外部定位

    本文初次写于2023 03 03 xff0c pixhawk飞控应该是刷写了ArduPilot 4 1以上的版本 机载计算机通过WIFI和vrpn ros client获取动捕系统 xff08 vicon或者nokov xff09 的无人机
  • APNS导致消息丢失和发送效率原因

    http blog csdn net tlq1988 article details 9612237 首先说明一下 xff0c 本文只是介绍一些容易被开发者忽视 xff0c 而导致性能低下问题 并不是介绍如何向苹果设备成功发送一条消息 xf