APNs蘋果推送使用的是pushy框架+.p8證書(java後臺)(附上與.p12的不同)

因爲被這個困擾了好久,所以記錄一下…
網上大部分都是關於.p12的代碼,沒有看到有.p8的。
先上github:https://github.com/relayrides/pushy(如果有不懂,可以再來這裏看)

在Java端使用Pushy進行APNs消息推送

1 首先先加入這兩個包

<!-- ios推送  pushy框架-->
        <dependency>
            <groupId>com.turo</groupId>
            <artifactId>pushy</artifactId>
            <version>0.13.7</version>
        </dependency>

        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-tcnative-boringssl-static</artifactId>
            <version>2.0.20.Final</version>
            <scope>runtime</scope>
        </dependency>

2 身份認證
蘋果APNs提供了兩種認證的方式:基於JWT的身份信息token認證和基於證書的身份認證。Pushy也同樣支持這兩種認證方式。(待會會有區別)
如何獲取蘋果APNs身份認證證書可以查考官方文檔。

3 Pushy使用
這是.p8的:

package com.imagedt.message.util;
import java.io.File;

import com.turo.pushy.apns.auth.ApnsSigningKey;
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)
                        .setSigningKey(ApnsSigningKey.loadFromPkcs8File(new File("path/你的放置.p8證書的路徑"),
                                "你的teamid", "你的keyid"))
                        .setConcurrentConnections(4).setEventLoopGroup(eventLoopGroup).build();
            } catch (Exception e) {
                logger.error("ios get pushy apns client failed!");
                e.printStackTrace();
            }
        }
        return apnsClient;

    }

}

這是.p12證書的:

package com.imagedt.message.util;
import java.io.File;

import com.turo.pushy.apns.auth.ApnsSigningKey;
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("/path/to/certificate.p12"), "p12-file-password")
                        .setConcurrentConnections(4).setEventLoopGroup(eventLoopGroup).build();

            } catch (Exception e) {
                logger.error("ios get pushy apns client failed!");
                e.printStackTrace();
            }
        }
        return apnsClient;

    }

}

區別就是
.p8的:

.setSigningKey(ApnsSigningKey.loadFromPkcs8File(new File(".p8路徑"),
                        "teamid", "keyid"))

.p12的:

.setClientCredentials(new File("/path/to/certificate.p12"), "p12-file-password")

7、Pushy的最佳實踐
參考Pushy的官方最佳實踐,我們加入瞭如下操作:

通過Semaphore來進行流控,防止緩存過大,內存不足;

通過CountDownLatch來標記消息是否發送完成;

使用AtomicLong完成匿名內部類operationComplete方法中的計數;

使用Netty的Future對象進行消息推送結果的判斷。

具體用法參考如下代碼:

package com.imagedt.message.util;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import com.turo.pushy.apns.util.concurrent.PushNotificationFuture;
import com.turo.pushy.apns.util.concurrent.PushNotificationResponseListener;
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;

/**
 * ios消息推送
 */
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併發編程中,信號量控制的是線程併發的數量。

    /**
     *
     * ios的推送
     * @param deviceTokens
     * @param alertTitle
     * @param alertBody
     * @param contentAvailable true:表示的是產品發佈推送服務 false:表示的是產品測試推送服務
     * @param customProperty 附加參數
     * @param badge 如果badge小於0,則不推送這個右上角的角標,主要用於消息盒子新增或者已讀時,更新此狀態
     */
    @SuppressWarnings("rawtypes")
    public static void push(final List<String> deviceTokens, 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());
                }
            }

            // true:表示的是產品發佈推送服務 false:表示的是產品測試推送服務
            payloadBuilder.setContentAvailable(contentAvailable);

            String payload = payloadBuilder.buildWithDefaultMaximumLength();
            final String token = TokenUtil.sanitizeTokenString(deviceToken);
            SimpleApnsPushNotification pushNotification = new SimpleApnsPushNotification(token, "com.imagedt.basho.dev", payload);

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

            final PushNotificationFuture<SimpleApnsPushNotification, PushNotificationResponse<SimpleApnsPushNotification>>
                sendNotificationFuture = apnsClient.sendNotification(pushNotification);

            //---------------------------------------------------------------------------------------------------
            try {
                final PushNotificationResponse<SimpleApnsPushNotification> pushNotificationResponse = sendNotificationFuture.get();
//                System.out.println(sendNotificationFuture.isSuccess());
//                System.out.println(pushNotificationResponse.isAccepted());

                sendNotificationFuture.addListener(new PushNotificationResponseListener<SimpleApnsPushNotification>() {

                    @Override
                    public void operationComplete(final PushNotificationFuture<SimpleApnsPushNotification, PushNotificationResponse<SimpleApnsPushNotification>> future) throws Exception {
                        // When using a listener, callers should check for a failure to send a
                        // notification by checking whether the future itself was successful
                        // since an exception will not be thrown.
                        if (future.isSuccess()) {
                            final PushNotificationResponse<SimpleApnsPushNotification> pushNotificationResponse =
                                    sendNotificationFuture.getNow();
                            if (pushNotificationResponse.isAccepted()) {
                                successCnt.incrementAndGet();
                            } else {
                                Date invalidTime = pushNotificationResponse.getTokenInvalidationTimestamp();
                                logger.error("Notification rejected by the APNs gateway: " + pushNotificationResponse.getRejectionReason());
                                if (invalidTime != null) {
                                    logger.error("\t…and the token is invalid as of " + pushNotificationResponse.getTokenInvalidationTimestamp());
                                }
                            }
                            // Handle the push notification response as before from here.
                        } else {
                            // Something went wrong when trying to send the notification to the
                            // APNs gateway. We can find the exception that caused the failure
                            // by getting future.cause().
                            future.cause().printStackTrace();
                        }
                        latch.countDown();
                        semaphore.release();//釋放允許,將佔有的信號量歸還
                    }
                });
                //------------------------------------------------------------------

                /*if (pushNotificationResponse.isAccepted()) {
                    System.out.println("Push notification accepted by APNs gateway.");
                } else {
                    System.out.println("Notification rejected by the APNs gateway: " +
                            pushNotificationResponse.getRejectionReason());

                    if (pushNotificationResponse.getTokenInvalidationTimestamp() != null) {
                        System.out.println("\t…and the token is invalid as of " +
                                pushNotificationResponse.getTokenInvalidationTimestamp());
                    }
                }*/
            } catch (final ExecutionException e) {
                System.err.println("Failed to send push notification.");
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //---------------------------------------------------------------------------------------------------

        }

//        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));
    }

}

關於多線程調用client:

Pushy ApnsClient是線程安全的,可以使用多線程來調用。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章