Pushy 是一個用於發送 APN(iOS、macOS 和 Safari)推播通知的 Java 函式庫。
Pushy 使用 Apple 基於 HTTP/2 的 APNs 協定發送推播通知,並支援 TLS 和基於令牌的身份驗證。它與其他推播通知庫的區別在於,它專注於完整的文件、非同步操作和工業規模操作的設計。借助 Pushy,可以輕鬆有效地維護與 APNs 網關的多個並行連接,以向許多不同的應用程式(「主題」)發送大量通知。
我們相信 Pushy 已經是從 Java 應用程式發送 APN 推播通知的最佳工具,我們希望您能透過錯誤報告和拉取請求幫助我們讓它變得更好。
如果您需要一個簡單的 GUI 應用程式來發送推播通知以用於開發或測試目的,您可能也會對 Pushy 的姊妹專案 Pushy Console 感興趣。
如果您使用 Maven,則可以透過將以下相依性聲明新增至 POM 來將 Pushy 新增至您的專案:
< dependency >
< groupId >com.eatthepath</ groupId >
< artifactId >pushy</ artifactId >
< version >0.15.4</ version >
</ dependency >
如果您不使用 Maven(或其他了解 Maven 依賴項的工具,如 Gradle),您可以將 Pushy 下載為.jar
檔案並直接新增至您的專案。您還需要確保您的類別路徑上有 Pushy 的執行時間依賴項。他們是:
Pushy 本身需要 Java 8 或更高版本才能建置和運行。雖然不是必要的,但使用者可以選擇使用 netty-native 作為 SSL 提供者來增強效能。要使用本機提供程序,請確保 netty-tcnative 在您的類路徑上。 Maven 使用者可以向其專案新增依賴項,如下所示:
< dependency >
< groupId >io.netty</ groupId >
< artifactId >netty-tcnative-boringssl-static</ artifactId >
< version >2.0.62.Final</ version >
< scope >runtime</ scope >
</ dependency >
在開始使用 Pushy 之前,您需要向 Apple 進行一些設定工作,以註冊您的應用程式並取得所需的憑證或簽署金鑰(稍後將詳細介紹)。有關此過程的詳細信息,請參閱 Apple UserNotifications 文件的「使用 APNs 註冊您的應用程式」部分。請注意,有一些警告,特別是在 macOS 10.13 (El Capitan) 下。
一般來說,APNs 用戶端必須透過某種方式向 APNs 伺服器進行身份驗證,然後才能發送推播通知。目前,APNs(和 Pushy)支援兩種身份驗證方法:基於 TLS 的身份驗證和基於令牌的身份驗證。這兩種方法是互斥的;您需要為每個客戶選擇一個或另一個。
在基於 TLS 的身份驗證中,用戶端在連線時向伺服器提供 TLS 證書,並且可以向證書中指定的任何「主題」發送通知。一般來說,這意味著單一客戶端只能將推播通知傳送到單一接收應用程式。
註冊應用程式並擁有必要的憑證後,要開始使用 Pushy 發送推播通知,您需要做的第一件事就是建立一個ApnsClient
。使用 TLS 驗證的用戶端需要憑證和私鑰才能與 APNs 伺服器進行驗證。儲存憑證和金鑰的最常見方法是儲存在受密碼保護的 PKCS#12 檔案中(如果您在撰寫本文時按照 Apple 的說明進行操作,您將獲得一個受密碼保護的 .p12 檔案)。若要建立將使用基於 TLS 的身份驗證的用戶端:
final ApnsClient apnsClient = new ApnsClientBuilder ()
. setApnsServer ( ApnsClientBuilder . DEVELOPMENT_APNS_HOST )
. setClientCredentials ( new File ( "/path/to/certificate.p12" ), "p12-file-password" )
. build ();
在基於令牌的身份驗證中,用戶端仍然使用 TLS 安全連線來連接到伺服器,但在連線時不會向伺服器提供憑證。相反,客戶端在發送的每個通知中都包含一個加密簽署的令牌(不用擔心,Pushy 會自動為您處理這個問題)。客戶端可以向他們擁有有效簽名金鑰的任何「主題」發送推播通知。
要開始使用基於令牌的用戶端,您需要從 Apple 取得簽署金鑰(在某些情況下也稱為私鑰)。獲得簽名金鑰後,您可以建立新客戶端:
final ApnsClient apnsClient = new ApnsClientBuilder ()
. setApnsServer ( ApnsClientBuilder . DEVELOPMENT_APNS_HOST )
. setSigningKey ( ApnsSigningKey . loadFromPkcs8File ( new File ( "/path/to/key.p8" ),
"TEAMID1234" , "KEYID67890" ))
. build ();
Pushy 的 APNs 用戶端維護與 APNs 伺服器的內部連線池,並根據需要建立新連線。因此,客戶端不需要明確啟動。無論您選擇哪種身份驗證方法,建立用戶端後,就可以開始發送推播通知。推播通知至少需要一個設備令牌(它標識通知的目標設備,並且與身份驗證令牌不同)、一個主題和一個有效負載。
final SimpleApnsPushNotification pushNotification ;
{
final ApnsPayloadBuilder payloadBuilder = new SimpleApnsPayloadBuilder ();
payloadBuilder . setAlertBody ( "Example!" );
final String payload = payloadBuilder . build ();
final String token = TokenUtil . sanitizeTokenString ( "<efc7492 bdbd8209>" );
pushNotification = new SimpleApnsPushNotification ( token , "com.example.myApp" , payload );
}
Pushy 包含SimpleApnsPayloadBuilder
,並且基於 Gson 和 Jackson 的有效負載建構器可作為單獨的模組使用。 APNs 有效負載只是 JSON 字串,呼叫者可以透過自己選擇的方法產生有效負載;雖然 Pushy 的有效負載建構器可能很方便,但呼叫者沒有義務使用它們。
發送推播通知的過程是異步的;儘管發送通知和從伺服器取得回覆的過程可能需要一些時間,但客戶端會立即傳回一個CompletableFuture
。您可以使用CompletableFuture
來追蹤發送操作的進度和最終結果。請注意,發送通知會傳回PushNotificationFuture
,它是CompletableFuture
的子類,並且始終保存已傳送通知的參考。
final PushNotificationFuture < SimpleApnsPushNotification , PushNotificationResponse < SimpleApnsPushNotification >>
sendNotificationFuture = apnsClient . sendNotification ( pushNotification );
CompletableFuture
將在以下三種情況之一下完成:
CompletableFuture
因異常而失敗。這通常應被視為暫時故障,呼叫者應在問題解決後嘗試再次發送通知。一個例子:
try {
final PushNotificationResponse < SimpleApnsPushNotification > pushNotificationResponse =
sendNotificationFuture . get ();
if ( pushNotificationResponse . isAccepted ()) {
System . out . println ( "Push notification accepted by APNs gateway." );
} else {
System . out . println ( "Notification rejected by the APNs gateway: " +
pushNotificationResponse . getRejectionReason ());
pushNotificationResponse . getTokenInvalidationTimestamp (). ifPresent ( timestamp -> {
System . out . println ( " t …and the token is invalid as of " + timestamp );
});
}
} catch ( final ExecutionException e ) {
System . err . println ( "Failed to send push notification." );
e . printStackTrace ();
}
值得注意的是, CompletableFuture
可以在操作完成時安排額外的任務運行。在實踐中,等待每個單獨的推播通知效率很低,透過向CompletableFuture
添加後續任務而不是阻塞直到任務完成,可以更好地為大多數用戶提供服務。舉個例子:
sendNotificationFuture . whenComplete (( response , cause ) -> {
if ( response != null ) {
// Handle the push notification response as before from here.
} else {
// Something went wrong when trying to send the notification to the
// APNs server. Note that this is distinct from a rejection from
// the server, and indicates that something went wrong when actually
// sending the notification or waiting for a reply.
cause . printStackTrace ();
}
});
所有 APNs 用戶端(即使是那些從未發送過訊息的用戶端)都可能分配並保留系統資源,釋放它們非常重要。 APNs 用戶端旨在成為持久的、長期存在的資源;您絕對不需要在發送通知(甚至大量通知)後關閉客戶端,但您需要在應用程式關閉時關閉客戶端(或多個客戶端):
final CompletableFuture < Void > closeFuture = apnsClient . close ();
關閉時,用戶端將等待所有已發送但未確認的通知以接收伺服器的回覆。已傳遞給sendNotification
但尚未傳送到伺服器的通知(即在內部佇列中等待的通知)在斷開連線時將立即失敗。呼叫者通常應確保在關閉之前伺服器已確認所有發送的通知。
充分利用系統資源來實現高吞吐量應用程式總是需要付出一些努力。為了引導您完成整個過程,我們整理了一個 wiki 頁面,其中涵蓋了使用 Pushy 的一些最佳實踐。所有這些要點在 wiki 上都有更詳細的介紹,但總的來說,我們的建議是:
ApnsClient
實例視為長期資源CompletableFutures
Pushy 包含一個用於監控指標的介面,可深入了解客戶的行為和績效。您可以編寫自己的ApnsClientMetricsListener
介面實作來記錄和報告指標。我們還提供指標監聽器,使用 Dropwizard Metrics 庫收集和報告指標,並使用 Micrometer 應用程式監控外觀作為單獨的模組。若要開始接收指標,請在建置新用戶端時設定偵聽器:
final ApnsClient apnsClient = new ApnsClientBuilder ()
. setApnsServer ( ApnsClientBuilder . DEVELOPMENT_APNS_HOST )
. setSigningKey ( ApnsSigningKey . loadFromPkcs8File ( new File ( "/path/to/key.p8" ),
"TEAMID1234" , "KEYID67890" ))
. setMetricsListener ( new MyCustomMetricsListener ())
. build ();
請注意,偵聽器實作中的指標處理方法不應呼叫阻塞程式碼。直接在處理程序方法中增加計數器是合適的,但對資料庫或遠端監視端點的呼叫應分派到單獨的執行緒。
如果您需要使用代理進行出站連接,您可以在建置ApnsClient
實例時指定ProxyHandlerFactory
。為 HTTP、SOCKS4 和 SOCKS5 代理提供了ProxyHandlerFactory
的具體實作。
一個例子:
final ApnsClient apnsClient = new ApnsClientBuilder ()
. setApnsServer ( ApnsClientBuilder . DEVELOPMENT_APNS_HOST )
. setSigningKey ( ApnsSigningKey . loadFromPkcs8File ( new File ( "/path/to/key.p8" ),
"TEAMID1234" , "KEYID67890" ))
. setProxyHandlerFactory ( new Socks5ProxyHandlerFactory (
new InetSocketAddress ( "my.proxy.com" , 1080 ), "username" , "password" ))
. build ();
如果使用透過 JVM 系統屬性配置的 HTTP 代理,您也可以使用:
final ApnsClient apnsClient = new ApnsClientBuilder ()
. setApnsServer ( ApnsClientBuilder . DEVELOPMENT_APNS_HOST )
. setSigningKey ( ApnsSigningKey . loadFromPkcs8File ( new File ( "/path/to/key.p8" ),
"TEAMID1234" , "KEYID67890" ))
. setProxyHandlerFactory ( HttpProxyHandlerFactory . fromSystemProxies (
ApnsClientBuilder . DEVELOPMENT_APNS_HOST ))
. build ();
Pushy 使用 SLF4J 進行日誌記錄。如果您還不熟悉它,SLF4J 是一個外觀,允許使用者透過向類別路徑添加特定的「綁定」來選擇在部署時使用哪個日誌庫。為了避免為您做出選擇,Pushy 本身不依賴任何 SLF4J 綁定;您需要自行新增一個(透過將其新增為您自己的專案中的依賴項或直接安裝)。如果您的類別路徑上沒有 SLF4J 綁定,您可能會看到如下所示的警告:
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
有關更多信息,請參閱 SLF4J 使用手冊。
Pushy 使用日誌記錄等級如下:
等級 | 記錄的事件 |
---|---|
error | 嚴重的、不可恢復的錯誤;可恢復的錯誤可能表示 Pushy 中存在錯誤 |
warn | 嚴重但可恢復的錯誤;可能表示呼叫者程式碼中存在錯誤的錯誤 |
info | 重要的生命週期事件 |
debug | 較小的生命週期事件;預期的例外情況 |
trace | 單獨的IO操作 |
Pushy 包含一個模擬 APNs 伺服器,呼叫者可以在整合測試和基準測試中使用它。在正常操作中沒有必要使用模擬伺服器(或任何相關類別)。
要建立模擬伺服器,呼叫者應使用MockApnsServerBuilder
。所有伺服器都需要一個PushNotificationHandler
(由提供給建構器的PushNotificationHandlerFactory
建置)來決定模擬伺服器是否接受或拒絕每個傳入的推播通知。 Pushy 包含一個有助於基準測試的AcceptAllPushNotificationHandlerFactory
和一個可能有助於整合測試的ValidatingPushNotificationHandlerFactory
。
呼叫者也可以在建構模擬伺服器時提供MockApnsServerListener
;每當模擬伺服器接受或拒絕來自客戶端的通知時,偵聽器都會收到通知。
Pushy 可在 MIT 許可證下使用。
Pushy 的當前版本是 0.15.4。它功能齊全並廣泛應用於生產環境,但公共 API 在 1.0 版本發布之前可能會發生重大變化。