Pushy는 APN(iOS, macOS, Safari) 푸시 알림을 보내기 위한 Java 라이브러리입니다.
Pushy는 Apple의 HTTP/2 기반 APNs 프로토콜을 사용하여 푸시 알림을 보내고 TLS와 토큰 기반 인증을 모두 지원합니다. 철저한 문서화, 비동기 작업 및 산업 규모 작업을 위한 설계에 중점을 두고 다른 푸시 알림 라이브러리와 차별화됩니다. Pushy를 사용하면 APN 게이트웨이에 대한 여러 병렬 연결을 유지하여 다양한 애플리케이션("주제")에 많은 수의 알림을 보내는 것이 쉽고 효율적입니다.
우리는 Pushy가 이미 Java 애플리케이션에서 APN 푸시 알림을 보내는 최고의 도구라고 믿습니다. 버그 보고서와 풀 요청을 통해 Pushy를 더욱 개선하는 데 도움을 주시기를 바랍니다.
개발이나 테스트 목적으로 푸시 알림을 보내기 위한 간단한 GUI 애플리케이션이 필요하다면 Pushy의 자매 프로젝트인 Pushy Console에도 관심이 있을 수 있습니다.
Maven을 사용하는 경우 POM에 다음 종속성 선언을 추가하여 프로젝트에 Pushy를 추가할 수 있습니다.
< dependency >
< groupId >com.eatthepath</ groupId >
< artifactId >pushy</ artifactId >
< version >0.15.4</ version >
</ dependency >
Maven(또는 Gradle과 같이 Maven 종속성을 이해하는 다른 것)을 사용하지 않는 경우 Pushy를 .jar
파일로 다운로드하여 프로젝트에 직접 추가할 수 있습니다. 또한 클래스 경로에 Pushy의 런타임 종속성이 있는지 확인해야 합니다. 그들은:
Pushy 자체를 빌드하고 실행하려면 Java 8 이상이 필요합니다. 필수는 아니지만 사용자는 성능 향상을 위해 SSL 공급자로 netty-native를 사용하도록 선택할 수 있습니다. 기본 공급자를 사용하려면 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 설명서의 APN에 앱 등록 섹션을 참조하세요. 특히 macOS 10.13(El Capitan)에서는 몇 가지 주의 사항이 있습니다.
일반적으로 APN 클라이언트는 푸시 알림을 보내기 전에 어떤 방법으로 APN 서버에 인증해야 합니다. 현재 APN(및 Pushy)은 TLS 기반 인증과 토큰 기반 인증이라는 두 가지 인증 방법을 지원합니다. 두 가지 접근 방식은 상호 배타적입니다. 각 클라이언트에 대해 둘 중 하나를 선택해야 합니다.
TLS 기반 인증에서 클라이언트는 연결할 때 서버에 TLS 인증서를 제시하고 인증서에 명명된 "주제"에 알림을 보낼 수 있습니다. 일반적으로 이는 단일 클라이언트가 단일 수신 앱에만 푸시 알림을 보낼 수 있음을 의미합니다.
앱을 등록하고 필수 인증서를 확보한 후 Pushy로 푸시 알림 전송을 시작하기 위해 가장 먼저 해야 할 일은 ApnsClient
를 생성하는 것입니다. TLS 인증을 사용하는 클라이언트는 APN 서버에 인증하기 위해 인증서와 개인 키가 필요합니다. 인증서와 키를 저장하는 가장 일반적인 방법은 비밀번호로 보호된 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을 기반으로 하는 페이로드 빌더는 별도의 모듈로 제공됩니다. APN 페이로드는 JSON 문자열일 뿐이며 호출자는 선택한 방법으로 페이로드를 생성할 수 있습니다. Pushy의 페이로드 빌더는 편리할 수 있지만 호출자는 이를 사용할 의무가 없습니다 .
푸시 알림을 보내는 프로세스는 비동기식입니다. 알림을 보내고 서버에서 응답을 받는 과정에는 시간이 좀 걸릴 수 있지만 클라이언트는 즉시 CompletableFuture
를 반환합니다. 해당 CompletableFuture
사용하여 전송 작업의 진행 상황과 최종 결과를 추적할 수 있습니다. 알림을 보내면 전송된 알림에 대한 참조를 항상 보유하는 CompletableFuture
의 하위 클래스인 PushNotificationFuture
가 반환됩니다.
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 ();
}
});
메시지를 보낸 적이 없는 클라이언트라도 모든 APN 클라이언트는 시스템 리소스를 할당하고 보유할 수 있으므로 이를 해제하는 것이 중요합니다. APN 클라이언트는 지속적이고 수명이 긴 리소스로 만들어졌습니다. 알림(또는 일괄 알림)을 보낸 후 클라이언트를 종료할 필요는 없지만 애플리케이션이 종료될 때 클라이언트를 종료하는 것이 좋습니다.
final CompletableFuture < Void > closeFuture = apnsClient . close ();
종료되면 클라이언트는 전송되었지만 확인되지 않은 모든 알림이 서버로부터 응답을 받을 때까지 기다립니다. sendNotification
으로 전달되었지만 아직 서버로 전송되지 않은 알림(예: 내부 대기열에 대기 중인 알림)은 연결이 끊어지면 즉시 실패합니다. 호출자는 일반적으로 종료하기 전에 전송된 모든 알림이 서버에서 확인되었는지 확인해야 합니다.
처리량이 많은 애플리케이션을 위해 시스템 리소스를 최대한 활용하려면 항상 약간의 노력이 필요합니다. 프로세스를 안내하기 위해 Pushy 사용에 대한 몇 가지 모범 사례를 다루는 위키 페이지를 구성했습니다. 이러한 모든 사항은 위키에서 훨씬 더 자세히 다루고 있지만 일반적으로 권장 사항은 다음과 같습니다.
ApnsClient
인스턴스를 수명이 긴 리소스로 처리CompletableFutures
에 후속 작업을 추가하세요. Pushy에는 클라이언트의 행동과 성능에 대한 통찰력을 제공하는 측정항목을 모니터링하기 위한 인터페이스가 포함되어 있습니다. ApnsClientMetricsListener
인터페이스의 구현을 직접 작성하여 지표를 기록하고 보고할 수 있습니다. 또한 Dropwizard Metrics 라이브러리를 사용하고 Micrometer 애플리케이션 모니터링 Facade를 별도의 모듈로 사용하여 메트릭을 수집하고 보고하는 메트릭 리스너도 제공합니다. 측정항목 수신을 시작하려면 새 클라이언트를 구축할 때 리스너를 설정하세요.
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는 클래스 경로에 특정 "바인딩"을 추가하여 배포 시 사용할 로깅 라이브러리를 사용자가 선택할 수 있도록 하는 Facade입니다. 사용자가 선택하는 것을 피하기 위해 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에는 호출자가 통합 테스트 및 벤치마크에 사용할 수 있는 모의 APN 서버가 포함되어 있습니다. 정상적인 작업에서는 모의 서버(또는 관련 클래스)를 사용할 필요가 없습니다.
모의 서버를 구축하려면 호출자는 MockApnsServerBuilder
를 사용해야 합니다. 모든 서버에는 모의 서버가 들어오는 각 푸시 알림을 수락할지 거부할지 여부를 결정하는 PushNotificationHandler
(빌더에 제공되는 PushNotificationHandlerFactory
에 의해 구축됨)가 필요합니다. Pushy에는 벤치마킹에 도움이 되는 AcceptAllPushNotificationHandlerFactory
와 통합 테스트에 도움이 될 수 있는 ValidatingPushNotificationHandlerFactory
가 포함되어 있습니다.
호출자는 모의 서버를 구축할 때 MockApnsServerListener
를 제공할 수도 있습니다. 모의 서버가 클라이언트의 알림을 수락하거나 거부할 때마다 리스너에게 알림이 전달됩니다.
Pushy는 MIT 라이선스에 따라 사용할 수 있습니다.
Pushy의 현재 버전은 0.15.4입니다. 완벽하게 작동하고 프로덕션 환경에서 널리 사용되지만 공개 API는 1.0 릴리스 이전에 크게 변경될 수 있습니다.