1. 程式人生 > >IOS APNS訊息推送框架介紹(pushy)以及詳細使用方法

IOS APNS訊息推送框架介紹(pushy)以及詳細使用方法

最近公司需要做IOS訊息推送的功能,我負責後臺推送,IOS端資料處理以及回撥我不負責,本篇文章主要介紹基於java的apns訊息推送,使用框架為pushy。
宣告:我先前也沒有接觸過這個IOS推送,自己研究了兩天,通過百度,對比各個框架的優缺點,最後選擇了這個框架,有說的不對的地方,還希望指出來,當然現在很多公司都是採用第三方平臺的方式來推送,這裡不介紹。
目前比較流行的框架在github上可以直接看到,通過star的多少,可以判斷其流行程度,原本準備使用notnoop框架,但是考慮到其基於http1框架,沒有返回值,而且框架比較老,很久沒有維護了,所以沒用。

pushy框架介紹

Pushy是用於傳送APN(iOS,MacOS和Safari)推送通知的Java庫。它由Turo的工程師編寫和維護。

Pushy使用Apple的基於HTTP / 2的APN協議傳送推送通知,並支援TLS和基於令牌的身份驗證。它與其他推送通知庫區別開來,重點在於全面的文件記錄,非同步操作和用於工業規模操作的設計; 通過Pushy,維護到APN閘道器的多個並行連線可以輕鬆高效地向大量不同的應用程式(“主題”)傳送大量通知。

我們相信Pushy已經是從Java應用程式傳送APN推送通知的最佳工具,並且我們希望您能通過錯誤報告和拉取請求幫助我們更好地實現它。如果您對使用Pushy有任何疑問,請加入我們的Pushy郵件列表或檢視wiki。謝謝!

上面摘抄於git文件
留一個網址,具體原始碼大家自己去看吧https://github.com/relayrides/pushy

程式碼詳解

我用的maven專案,需要先加入jar

        <dependency>
            <groupId>com.turo</groupId>
            <artifactId>pushy</artifactId>
            <version>0.13.1</version>
        </dependency>

直接先上程式碼

package com.huali.excutemq.util;

import java.util.Date;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.turo.pushy.apns.ApnsClient;
import com.turo.pushy.apns.PushNotificationResponse;
import com.turo.pushy.apns.util.ApnsPayloadBuilder;
import com.turo.pushy.apns.util.SimpleApnsPushNotification;
import com.turo.pushy.apns.util.TokenUtil;

import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;

public class IOSPush {

    private static final Logger logger = LoggerFactory.getLogger(IOSPush.class);

   // private static ApnsClient apnsClient = null;

    //private static final Semaphore semaphore = new Semaphore(10000);//Semaphore又稱訊號量,是作業系統中的一個概念,在Java併發程式設計中,訊號量控制的是執行緒併發的數量。

    @SuppressWarnings("rawtypes")
    public void push(final String deviceToken, String alertTitle, String alertBody,boolean contentAvailable,Map<String, Object> customProperty,int badge) {

       // long startTime = System.currentTimeMillis();

        ApnsClient apnsClient = APNSConnect.getAPNSConnect(); 

       // long total = deviceTokens.size();

      //  final CountDownLatch latch = new CountDownLatch(deviceTokens.size());//每次完成一個任務(不一定需要執行緒走完),latch減1,直到所有的任務都完成,就可以執行下一階段任務,可提高效能

        final AtomicLong successCnt = new AtomicLong(0);//執行緒安全的計數器

       // long startPushTime =  System.currentTimeMillis();

       // for (String deviceToken : deviceTokens) { 

             ApnsPayloadBuilder payloadBuilder = new ApnsPayloadBuilder();

             if(alertBody!=null&&alertTitle!=null) {
                 payloadBuilder.setAlertBody(alertBody);
                 payloadBuilder.setAlertTitle(alertTitle);
             }

             //如果badge小於0,則不推送這個右上角的角標,主要用於訊息盒子新增或者已讀時,更新此狀態
             if(badge>0) {
                payloadBuilder.setBadgeNumber(badge); 
             }

             //將所有的附加引數全部放進去
             if(customProperty!=null) {
                 for(Map.Entry<String, Object> map:customProperty.entrySet()) {
                     payloadBuilder.addCustomProperty(map.getKey(),map.getValue()); 
                 }
             }



             payloadBuilder.setContentAvailable(contentAvailable); 

            String payload = payloadBuilder.buildWithDefaultMaximumLength();
            final String token = TokenUtil.sanitizeTokenString(deviceToken);
            SimpleApnsPushNotification pushNotification = new SimpleApnsPushNotification(token, "com.Huali-tec.HLSmartWay", payload);

//            try {
//                semaphore.acquire();//從訊號量中獲取一個允許機會
//            } catch (Exception e) {
//                logger.error("ios push get semaphore failed, deviceToken:{}", deviceToken);//執行緒太多了,沒有多餘的訊號量可以獲取了
//                e.printStackTrace();
//            }

            final Future<PushNotificationResponse<SimpleApnsPushNotification>> future = apnsClient.sendNotification(pushNotification);

            future.addListener(new GenericFutureListener<Future<PushNotificationResponse>>() { 
                @Override
                public void operationComplete(Future<PushNotificationResponse> pushNotificationResponseFuture) throws Exception {
                    if (future.isSuccess()) {
                        final PushNotificationResponse<SimpleApnsPushNotification> response = future.getNow();
                        if (response.isAccepted()) {
                            successCnt.incrementAndGet();
                        } else {
                            Date invalidTime = response.getTokenInvalidationTimestamp();
                            logger.error("Notification rejected by the APNs gateway: " + response.getRejectionReason());
                            if (invalidTime != null) {
                                logger.error("\t…and the token is invalid as of " + response.getTokenInvalidationTimestamp());
                            }
                        }
                    } else {
                        logger.error("send notification device token={} is failed {} ", token, future.cause().getMessage());
                    }
                  //  latch.countDown();
                   // semaphore.release();//釋放允許,將佔有的訊號量歸還
                }
            });

        //}

//        try {
//            latch.await(20, TimeUnit.SECONDS);
//        } catch (Exception e) { 
//            logger.error("ios push latch await failed!");
//            e.printStackTrace();
//        }

        //long endPushTime = System.currentTimeMillis();

      //  logger.info("test pushMessage success. [共推送" + total + "個][成功" + (successCnt.get()) + "個],totalcost= " + (endPushTime - startTime) + ", pushCost=" + (endPushTime - startPushTime));
    }
}

蘋果訊息推送大致流程:
1、首先通過上面這段程式碼,將訊息推送到蘋果伺服器(蘋果公司買了幾十萬臺伺服器專門用於做這個事情),然後蘋果伺服器收到訊息以後,根據devicetoken將對應的內容推送到對應的裝置(ps:而安卓手機就是在系統後臺有一個監聽程式,一直在耗電,這也是為什麼蘋果手機比較省電的原因,而安卓手機比較費電,你下載的app越多,這個差異就越明顯)

2、先前在網上看資料說如果你推送的devicetoken有錯誤,連結就會中斷或者暫停一段時間,這樣就會造成延遲,目前我使用這個框架測試十萬條資料,暫時沒有發現這個情況。

3、由於本案例使用的是訊息佇列加多個消費者推送訊息,所以程式碼中每次只推送了一條訊息,註釋部分開啟,devicetoken可以存放多個使用者裝置標誌用於多次推送,這裡用了多個消費者。
4、此段程式碼封裝了一個方法getAPNSConnect(),程式碼如下

package com.huali.excutemq.util;

import java.io.File;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.turo.pushy.apns.ApnsClient;
import com.turo.pushy.apns.ApnsClientBuilder;

import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;

public class APNSConnect {

    private static final Logger logger = LoggerFactory.getLogger(APNSConnect.class);

    private static ApnsClient apnsClient = null;

    public static ApnsClient getAPNSConnect() { 

        if (apnsClient == null) {  
            try {
                EventLoopGroup eventLoopGroup = new NioEventLoopGroup(4);
                apnsClient = new ApnsClientBuilder().setApnsServer(ApnsClientBuilder.DEVELOPMENT_APNS_HOST)
                        .setClientCredentials(new File("C:\\Users\\Administrator\\Desktop\\PushCertificate-HualiSmartWay.p12"), "#edcvfr4%t")
                        .setConcurrentConnections(4).setEventLoopGroup(eventLoopGroup).build();
            } catch (Exception e) {
                logger.error("ios get pushy apns client failed!"); 
                e.printStackTrace();
            } 
        }
        return apnsClient;

    }
}

這個方法用於伺服器與蘋果伺服器的連結,為什麼要單獨寫出來呢?主要是總不能每消費一個devicetoken就重新連結一次蘋果伺服器吧,每次執行推送訊息程式的時候,會去檢查一下這個連結是否還在(與蘋果伺服器的連線長時間沒有資料發過去的話,蘋果伺服器會將它主動斷掉),如果還在的話,就直接用了。
5、這裡主要介紹一下setConcurrentConnectionssetEventLoopGroup
setConcurrentConnections用於設定伺服器與蘋果伺服器建立幾個連結通道,這裡是建立了四個,連結通道並不是越多越好的,具體速度自己百度
setEventLoopGroup的作用是建立幾個執行緒來處理,說白了就是多執行緒,我這裡設定的都是4,相當於16個執行緒同時處理。
6、關於推送速度,我沒有測試這種4乘以4的速度,我測試了十萬條資料,分別是2、10、50消費者的時候的消費速度,2個消費者速度大概600個推送每秒,10個消費者3000個推送每秒。50個消費者6000個推送每秒,大致上跟網上說的速度差不多。
7、上面程式碼中涉及到一些多執行緒的知識,我在後面有簡單的標註,具體的還請大家自己再網上學習。
8、關於蘋果伺服器返回的訊息(返回訊息是否推送成功),第一個返回的訊息是Future<PushNotificationResponse<SimpleApnsPushNotification>> future
裡面包含伺服器傳送訊息到蘋果伺服器是否成功。這個成功的話還需要知道蘋果伺服器是否將訊息成功推送到使用者的手機上,這個是final PushNotificationResponse<SimpleApnsPushNotification> response = future.getNow();
這裡獲取的response是是否將訊息成功推送到使用者手機上,這裡是一個監聽,即在伺服器將訊息傳送以後,就設定了一個監聽,用來監聽蘋果伺服器是否將訊息成功推送到使用者手機上,當然,監聽不會等到訊息返回以後才繼續往下執行程式碼,而是直接往下執行程式碼。所以這裡有一個程式碼latch.await(20, TimeUnit.SECONDS);
這句程式碼的意思就是等待當前執行緒執行完畢(即有返回訊息以後),再繼續往下執行,目的是為了統計訊息傳送成功的數量,當然我這裡是一個個執行的,所以就沒有必要去統計了,不然會有延遲的。

額,順便貼上一張圖:
這裡寫圖片描述
這個圖片是訊息訊息推送的主要引數。
這裡說一下,蘋果推送分為靜默推送(contentAvailable值為0,app端沒有回撥函式)和非靜態推送(contentAvailable值為1,app端有回撥函式,比如 對資料進行一些處理什麼的。),如果你設定title和body為null,則app不會推送(就是下拉沒有資料,這個情況一般用來更新badge數量)
2、還有一點順帶提一下:payloadBuilder.addCustomProperty(map.getKey(),map.getValue());
這句程式碼是後臺伺服器傳給app的附加引數,這個需要根據具體需求,具體分析。

對了還有一個證書的問題:自己去蘋果官網申請,只需要一個.p12格式的證書,以及證書的密碼就可以了。具體申請步驟請自己百度

上面大概就是我知道的所有有關apns的知識,有什麼問題可以評論,能回答的我儘量回答