移动互联网实战--Apple的APNS桩推送服务的实现(2)

前记:
  相信大家在搞IOS推送服务的开发时, 会直接使用javapns api来简单实现, 调试也直连Apple的APNS服务(产品/测试版)来实现. 很少有人会写个APNS的桩服务, 事实也是如此. 只是当时我所面临的应用场景有些特殊, 为了测试服务的性能和调试功能方便, 特地写了APNS的桩服务(其实主要原因是当时的iphone测试机, 被小组长"霸占"占为己用, ⊙﹏⊙b汗). 在此写一篇关于APNS桩服务的文章, 以此纪念逝去的"青春", 也希望对读者有所帮助.
  有些事情回想起来, 历历在目, 清晰异常, 仿佛发生在昨天. 犹记一年前, 开发IOS的APNS推送后端服务, 吐血三升...

秉承:
  上一篇文章, 详见: Apple的APNS桩推送服务的实现(1) 
  文章简要介绍了APNS协议/证书管理/JSSE的API的一些基础概念和实现原理.

需求分析:
  从开发者的角度去理解和分析需求. 对于IOS的消息推送的功能和性能测试, 大致可分为如下几种:
  1). 推送平台QPS/RT的性能指标
  2). Bad Device Token对服务的影响评估
  3). 证书对服务的影响评估
  4). 推送可靠性评估(推送弱ack机制, 丢消息和消息重复之间的折中)
  通过记录日志来评估QPS/RT和服务的质量, 引入黑名单机制来模拟bad device token对推送服务的影响评估.技术分析:

技术分析:
  对APNS服务的理解和自身的功能/性能测试需求:
  1). APNS推送服务采用SSL长连接/二进制流/异步的方式来实现, 追求推送消息的吞吐量.
  2). APNS支持V1/V2两种二进制协议, 遇到异常时, APNS的默认行为不一样.
  3). APNS客户端是TSL(SSL)的单向认证(客户端携带证书, 服务端信任检查).
  结合桩的定位, 我们采用One Connection Per Thread的线程池模式去模拟处理它, 一方面能基本满足需求, 另一方面实现高效简单. 同时为了方便测试, 服务端关闭对客户端认证机制.

服务实现:
  APNS模拟桩的实现过程
  1). 服务端证书引入

keytool -genkey -v -alias ssl-server -keyalg RSA -keystore ./server_ks -storepass server -keypass 123456 -dname "CN=Unknown"

  生成效果如图

  2). 自定义TrustManager的实现类

private class MyX509TrustManager implements X509TrustManager {

  @Override
  public X509Certificate[] getAcceptedIssuers() {
    return null;
  }
  // 对服务端证书的认证, 抛出异常表示不通过
  @Override
  public void checkServerTrusted(X509Certificate[] chain, String authType)
      throws CertificateException {
  }
  // 对客户端证书进行认证, 抛出异常表示不通过
  @Override
  public void checkClientTrusted(X509Certificate[] chain, String authType)
      throws CertificateException {
  }

}; 

  评注: 这边提供空实现, 对客户端的证书不进行任何校验
  3). 创建SSLServerSocket代码片段

SSLContext ctx = SSLContext.getInstance("SSL");

// *) 创建KeyManagerFactory类实例
KeyManagerFactory kmf = KeyManagerFactory
    .getInstance(KeyManagerFactory.getDefaultAlgorithm());

// *) 初始化KeyStore, 这边的KEY_PASSWORD为"123456"
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(ApnsStubServer.class.getResourceAsStream("/server_ks"), null);
kmf.init(ks, KEY_PASSWOR.toCharArray());

// *) 进行SSLContext类的初始化工作
ctx.init(kmf.getKeyManagers(),
    new TrustManager[] { new MyX509TrustManager() },
    new SecureRandom());

// *) SSLServerSocket的生成
SSLServerSocket serverSocket = (SSLServerSocket) ctx
    .getServerSocketFactory()
    .createServerSocket(serverPort);
serverSocket.setReuseAddress(true);
// *) 设置Client认证开关关闭
serverSocket.setNeedClientAuth(false);

  评注: 把server_ks代码放到classpath目录下, 同时这边的key_password为"123456", 而不是keystore的密码"server", 注意keypass和storepass的区别
  4). 服务端连接处理代码

// One Connection Per Thread的机制
ExecutorService workerPools = Executors.newCachedThreadPool();

while ( true ) {
  final Socket cliSocket = sslServerSocket.accept();
  workerPools.execute(new Runnable() {
    @Override
    public void run() {
      onHandle(cliSocket);
    }
  });
}

  评注: serverSocket用于接受客户端连接, 并放入工作池去处理
  onHandle函数定义如下:

private void onHandle(Socket cliSocket) {

  DeviceTokenManager dtm = apnsStubController.getDeviceTokenManager();
  DataInputStream dis = new DataInputStream(cliSocket.getInputStream());
  int ch = 0;
  while ( (ch = dis.read()) != -1 ) {
    IVResult<ApnsMessage> result = ApnsProtocolUtility.readMessage(ch, dis);
    ApnsMessage message = result.getValue();
    // 属于不同版本的消息, 进行分发和处理
    if ( ApnsVersion.APNS_BINARY_PROTOCOL_V1 == message.getVersion() ) {
      // *) 如果属于bad device token黑名单
      if ( dtm.judgeBadDeviceToken(message.getDeviceToken()) ) {
        break;
      }
      printMessage(message);
    } else if ( ApnsVersion.APNS_BINARY_PROTOCOL_V2 == message.getVersion() ) {
      // *) 如果属于bad device token黑名单
      if ( dtm.judgeBadDeviceToken(message.getDeviceToken()) ) {
        // 8 为令牌错误, 就是bad device token
        ApnsProtocolUtility. writeResponse(8, message.getIdentifier(), cliSocket.getOutputStream());
        break;
      }
      printMessage(message);
    }
  }
}

  5). 黑名单的引入和接口定义

  由外置黑名单文件来指定

public class DeviceTokenManager {

  // 用于配置参数的初始化
  public void init(Properties prop);

  // 判断是否为bad device token
  public boolean judgeBadDeviceToken(String deviceToken);

  // 载入bad device token 名单
  private void loadBadDeviceTokenFile(String filename);

}

  总结: 难点在于JSSE和证书的理解, 至于APNS的网络协议, 处理还是相对简单的.

APNS推送服务平台:
  对于APNS推送服务平台, 还是有些优化技巧, 这边并不深入展开, 仅从个人的观点描述几个.
  1). 对gateway.push.apple.com的ip地址解析, 挑选rrt时间最短的服务ip进行连接
  
  2). 引入队列异步化/构建DadDeviceToken仓库
  作为推送服务, 对于推送请求, 作简单的校验(消息格式合法/是否属于bad device token). 然后储存消息于队列中, 由后端worker慢慢消化. 不再等待真实的APNS服务反馈(APNS服务弱ACK机制), 立即返回结果(允许一定程度的推送成功误报率).
  构建自己的Bad Device Token仓库, 一方来自Feedback服务接受, 一方来自APNS推送的响应结果收集.

后记:
  感觉自己还是没有把自己想表达的事情说清楚, 甚是遗憾. 无论如何, 还是希望对读者有些帮助, 对我而言, 则是充满了回忆. Fighting, Let it go!!!

时间: 2024-10-25 04:22:17

移动互联网实战--Apple的APNS桩推送服务的实现(2)的相关文章

移动互联网实战--Apple的APNS桩推送服务的实现(1)

前记: 相信大家在搞IOS推送服务的开发时, 会直接使用javapns api来简单实现, 调试也直连Apple的APNS服务(产品/测试版)来实现. 很少有人会写个APNS的桩服务, 事实也是如此. 只是当时我所面临的应用场景有些特殊, 为了测试服务的性能和调试功能方便, 特地写了APNS的桩服务(其实主要原因是当时的iphone测试机, 被小组长"霸占"占为己用, ⊙﹏⊙b汗). 在此写一篇关于APNS桩服务的文章, 以此纪念逝去的"青春", 也希望对读者有所帮

2014年国内常用移动客户端推送服务介绍和比较

经过5年移动互联网的迅速发展,现在推送服务方面国内已经出现了很多产品,例如极光推送,个推,一推,百度推送,友盟推送等,我们在选择推送服务时,首先排除了付费的推送服务,重点调查了免费的推送服务. 极光推送是我们最早在产品中使用的推送服务,特点是极光只做推送,比较专业,这点从文档和SDK可以看出来.文档比较好,支持平台多,支持Android,iOS,Windows Phone等,并且有Android和iOS平台的插件,对于HybridApp的开发模式比较方便,这是我们最早使用极光推送的一个重要原因.

APNs 远程推送

APNs 远程推送 生成推送证书     1.登陆开发者中心: https://developer.apple.com     2.点开 certificates identifiers 和 proversionprofiles 里面     3.创建APPID. 勾选 push notification servers服务     4.搜索刚才创建的APPID, 点击edit, 配置证书, 下载双击    5.证书创建完成, 钥匙链内右键导出p12     1.App key     2.真机

IOS本地,APNS远程推送(具体过程)

添加本地推送 ///本地添加 -(void)addLocalPushNotification:(UIButton*)sender; { NSLog(@"%s",__FUNCTION__); UILocalNotification* localNotification=[[UILocalNotification alloc]init]; if (localNotification) { //设置时间当前加20秒 NSDate* pushDate=[NSDate dateWithTimeI

IOS 基于APNS消息推送原理与实现(JAVA后台)--转

Push的原理: Push 的工作机制可以简单的概括为下图 图中,Provider是指某个iPhone软件的Push服务器,这篇文章我将使用.net作为Provider. APNS 是Apple Push Notification Service(Apple Push服务器)的缩写,是苹果的服务器. 上图可以分为三个阶段. 第一阶段:Push服务器应用程序把要发送的消息.目的iPhone的标识打包,发给APNS. 第二阶段:APNS在自身的已注册Push服务的iPhone列表中,查找有相应标识的

转:IOS 基于APNS消息推送原理与实现(JAVA后台)

Push的原理: Push 的工作机制可以简单的概括为下图 图中,Provider是指某个iPhone软件的Push服务器,这篇文章我将使用.net作为Provider. APNS 是Apple Push Notification Service(Apple Push服务器)的缩写,是苹果的服务器. 上图可以分为三个阶段. 第一阶段:Push服务器应用程序把要发送的消息.目的iPhone的标识打包,发给APNS. 第二阶段:APNS在自身的已注册Push服务的iPhone列表中,查找有相应标识的

ZPush--基于netty4实现的苹果通知推送服务(APNs)Javaclient

简单说下实现苹果通知推送服务(APNs)client的一些要注意的地方: 使用长连接: sanboxserver是无用的,调试时直接用"gateway.push.apple.com"域名: 对于错误的Notification.苹果会回应一个Error response.里面有个identifier,在这个identifier之后的Notification全都失败. 因此发送者要缓存已经发送的Notification,最好设置Notification identifier为增长的整数序列

手把手教你配置苹果APNS推送服务|钿畑的博客 | 钿畑的博客

http://www.360doc.com/content/15/0118/17/1073512_441822850.shtml# 钿畑的文章索引 1. 什么是推送通知 2. 什么是APNS? 3. 推送流程 3.1 获取设备device_token阶段 3.2 消息推送过程 3.3 完整流程介绍 4. Push机制类型 5. 正式开工 5.1 准备工作 5.2 证书生成 6. 客户端制作 7. php服务器端配置 8. 测试 8. 附录: 8.1 JSON示例 8.2 检验证书是否正确的方法:

苹果通知推送服务(APNS)关键特性摘要

1. If APNs attempts to deliver a notification but the device is offline, the notification is stored for a limited period of time, and delivered to the device when it becomes available. 假如用户手机不在线,可能没有信号或者关机吧,APNs会存储转发,等用户在线时再发送 2.Only one recent notif