ios apns 推送服务

246 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第15天,点击查看活动详情

ios apns 推送服务

前言

公司内部有一套自己的消息推送系统, 是通过WebSocket的方式实现的, Android应用早已接入使用了, 但是ios应用迟迟没有接入, 因为ios应用程序在退到后台一段时间后线程会被挂起, 系统会收回WebSocket资源, 为了更好的支持ios推送, 决定接入ios apns服务, 其原理也很简单我们的消息先推送到ios的apns服务, 然后由apns服务再推送到用户的设备

准备

找到了javapnsnotnoop两个工具, 看到有大佬说javapns会存在内存泄漏问题, 没有深入研究, 我们直接选择notnoop

maven

<dependency>
     <groupId>com.notnoop.apns</groupId>
     <artifactId>apns</artifactId>
     <version>1.0.0.Beta6</version>
</dependency>

notnoop代码结构

源码.jpg

可以看到源码非常精简

源码中总共有三种消息发送方式

微信图片_20220608101514.jpg

  • ApnsServiceImpl 来一个处理一个
  • BatchApnsService 批消息发送 这个看源码有点疑惑, 里面用的一个ScheduledThreadPoolExecutor线程池处理的任务, 这个应该叫延迟消息吧, 而且继承的AbstractApnsService本身就实现的有批处理消息

微信图片_20220608101550.jpg

  • QueuedApnsService 消息先进入队列(默认是LinkedBlockingQueue), 然后创建了一个线程死循环处理队列消息 这里我们选择通过队列方式发送, 避免消息发送过于频繁导致被封, 如果想扩展也很简单, 只要继承AbstractApnsService然后自定义自己的实现就可以了

快速开始

定义消息

@Data
public class Msg implements Serializable {
    /**
     * 消息来源
     */
    private String from;
    /**
     * 消息去向
     */
    private String to;
    /**
     * 消息类型
     */
    private int msgType;
    /**
     * 消息体
     */
    private Object text;
    /**
     * 设备deviceId
     */
    private String deviceId;
    /**
     * 苹果deviceToken
     */
    private String deviceToken;
}

初始化ApnsService

  @Autowired
  private ApnsConfigMapper apnsConfigMapper;
  @Value("${push.server.ios.sandbox}")
  private boolean isSandbox;
  @Value("${push.server.ios.cert.path}")
  private String p12Path;

  private Map<String, ApnsService> apnsServiceMap;

  /**
   * 初始化apns服务
   */
  @PostConstruct
  public void initApnsService() {
      List<ApnsConfig> apnsConfigs = apnsConfigMapper.getApnsConfig();
      apnsConfigs.forEach(e -> apnsServiceMap.put(e.getAppName(),
              APNS.newService()
                      // 加载证书
                      .withCert(p12Path + (isSandbox ? "dev/" + e.getSandboxP12() : "prod/" + e.getProdP12()),
                              isSandbox ? e.getSandboxPwd() : e.getProdPwd())
                      // 使用沙箱
                      .withSandboxDestination()
                      // 消息通过队列方式发送
                      .asQueued()
                      // 关闭错误检测
                      .withNoErrorDetection()
                      .build()));
 }

发送消息

 /**
  * 发送消息到apns服务
  *
  * @param appName 应用名称
  * @param msg     消息
  */
 public void push(String appName, Msg msg) {
     if (Objects.isNull(msg)) {
         return;
     }
     try {
         String text = String.valueOf(msg.getText());
         JSONObject pushJson = JSONObject.parseObject(text);
         String payload = APNS.newPayload()
                 // 设置消息标题
                 .alertTitle(pushJson.getString("_mediaTitle"))
                 // 设置消息内容
                 .alertBody(pushJson.getString("_mediaSummary"))
                 .alertAction(text)
                 .build();
         // 发送消息
         apnsServiceMap.get(appName).push(msg.getDeviceToken(), payload);
     } catch (Exception ex) {
         log.error("ios msg push error, msg = {}, error = {}", msg, ex.getMessage());
     }
 }