{"id":4714,"date":"2021-12-23T15:55:34","date_gmt":"2021-12-23T07:55:34","guid":{"rendered":"http:\/\/123.57.164.21\/?p=4714"},"modified":"2022-09-22T21:33:08","modified_gmt":"2022-09-22T13:33:08","slug":"%e4%bd%bf%e7%94%a8pushy%e8%bf%9b%e8%a1%8capns%e6%b6%88%e6%81%af%e6%8e%a8%e9%80%81","status":"publish","type":"post","link":"https:\/\/92it.top\/?p=4714","title":{"rendered":"\u4f7f\u7528Pushy\u8fdb\u884cAPNs\u6d88\u606f\u63a8\u9001"},"content":{"rendered":"\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/123.57.164.21\/wp-content\/uploads\/2021\/12\/image-245-1024x527.png\" alt=\"\" class=\"wp-image-4715\" width=\"482\" height=\"248\" srcset=\"https:\/\/92it.top\/wp-content\/uploads\/2021\/12\/image-245-1024x527.png 1024w, https:\/\/92it.top\/wp-content\/uploads\/2021\/12\/image-245-300x154.png 300w, https:\/\/92it.top\/wp-content\/uploads\/2021\/12\/image-245-768x395.png 768w, https:\/\/92it.top\/wp-content\/uploads\/2021\/12\/image-245-830x427.png 830w, https:\/\/92it.top\/wp-content\/uploads\/2021\/12\/image-245-230x118.png 230w, https:\/\/92it.top\/wp-content\/uploads\/2021\/12\/image-245-350x180.png 350w, https:\/\/92it.top\/wp-content\/uploads\/2021\/12\/image-245-480x247.png 480w, https:\/\/92it.top\/wp-content\/uploads\/2021\/12\/image-245.png 1372w\" sizes=\"(max-width: 482px) 100vw, 482px\" \/><\/figure><\/div>\n\n\n\n<p>\u6700\u8fd1\u5bf9\u9879\u76ee\u7ec4\u7684\u8001\u7684\u82f9\u679cIOS\u63a8\u9001\u8fdb\u884c\u4e86\u5347\u7ea7\u4fee\u6539\uff0c\u8c03\u7814\u4e86\u4e00\u4e9b\u5f00\u6e90\u5e38\u7528\u7684\u5e93\u4e4b\u540e\uff0c\u9009\u62e9\u4e86Turo\u56e2\u961f\u5f00\u53d1\u548c\u7ef4\u62a4\u7684<a rel=\"noreferrer noopener\" href=\"https:\/\/link.jianshu.com?t=https:\/\/github.com\/relayrides\/pushy\" target=\"_blank\">pushy<\/a>\u3002<\/p>\n\n\n\n<p>\u4ee3\u7801\uff1a<a href=\"https:\/\/github.com\/jchambers\/pushy\">https:\/\/github.com\/jchambers\/pushy<\/a><\/p>\n\n\n\n<h5 class=\"wp-block-heading\">APNs\u548cPushy<\/h5>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>\u82f9\u679c\u8bbe\u5907\u7684\u6d88\u606f\u63a8\u9001\u662f\u4f9d\u9760\u82f9\u679c\u7684APNs\uff08Apple Push Notification service\uff09\u670d\u52a1\u7684\uff0c<a rel=\"noreferrer noopener\" href=\"https:\/\/link.jianshu.com\/?t=https:\/\/developer.apple.com\/library\/content\/documentation\/NetworkingInternet\/Conceptual\/RemoteNotificationsPG\/APNSOverview.html#\/\/apple_ref\/doc\/uid\/TP40008194-CH8-SW1\" target=\"_blank\">APNs<\/a>\u7684\u5b98\u65b9\u7b80\u4ecb\u5982\u4e0b\uff1a<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p>Apple Push Notification service (APNs) is the centerpiece of the remote notifications feature. It is a robust, secure, and highly efficient service for app developers to propagate information to iOS (and, indirectly, watchOS), tvOS, and macOS devices.<\/p><\/blockquote>\n\n\n\n<p>IOS\u8bbe\u5907\uff08tvOS\u3001macOS\uff09\u4e0a\u7684\u6240\u6709\u6d88\u606f\u63a8\u9001\u90fd\u9700\u8981\u7ecf\u8fc7APNs\uff0cAPNs\u670d\u52a1\u786e\u5b9e\u975e\u5e38\u5389\u5bb3\uff0c\u6bcf\u5929\u9700\u8981\u63a8\u9001\u4e0a\u767e\u4ebf\u7684\u6d88\u606f\uff0c\u53ef\u9760\u3001\u5b89\u5168\u3001\u9ad8\u6548\u3002\u5c31\u7b97\u662f\u5fae\u4fe1\u548cQQ\u8fd9\u79cd\u7528\u6237\u7ea7\u522b\u7684\u5373\u65f6\u901a\u8bafapp\u5728\u7a0b\u5e8f\u6ca1\u6709\u542f\u52a8\u6216\u8005\u540e\u53f0\u8fd0\u884c\u8fc7\u7a0b\u4e2d\u4e5f\u662f\u9700\u8981\u4f7f\u7528APNs\u7684\uff08\u5f53\u7a0b\u5e8f\u542f\u52a8\u65f6\uff0c\u4f7f\u7528\u81ea\u5df1\u5efa\u7acb\u7684\u957f\u8fde\u63a5\uff09\uff0c\u53ea\u4e0d\u8fc7\u817e\u8baf\u4f18\u5316\u4e86\u6574\u6761\u4ece\u4ed6\u4eec\u670d\u52a1\u5668\u5230\u82f9\u679c\u670d\u52a1\u5668\u7684\u7ebf\u8def\u800c\u5df2\uff0c\u6240\u4ee5\u89c9\u5f97\u63a8\u9001\u8981\u5feb\uff08\u53c2\u8003<a href=\"https:\/\/link.jianshu.com?t=https:\/\/www.zhihu.com\/question\/20654687\" target=\"_blank\" rel=\"noreferrer noopener\">\u77e5\u4e4e<\/a>\uff09\u3002<\/p>\n\n\n\n<p>\u9879\u76ee\u7ec4\u8001\u7684\u82f9\u679c\u63a8\u9001\u670d\u52a1\u4f7f\u7528\u7684\u662f\u82f9\u679c\u4ee5\u524d\u7684\u57fa\u4e8e\u4e8c\u8fdb\u5236socket\u7684APNs\uff0c\u540c\u65f6\u4f7f\u7528\u7684\u662f\u4e00\u4e2a<a href=\"https:\/\/link.jianshu.com?t=https:\/\/github.com\/fernandospr\/javapns-jdk16\" target=\"_blank\" rel=\"noreferrer noopener\">javapns<\/a>\u7684\u5f00\u6e90\u5e93\uff0c\u8fd9\u4e2ajavapns\u8c8c\u4f3c\u6548\u679c\u4e0d\u662f\u5f88\u597d\uff0c\u5728\u7f51\u4e0a\u4e5f\u6709\u4eba\u6709\u8fc7\u8ba8\u8bba\u3002javapns\u73b0\u5728\u4e5f\u505c\u6b62\u7ef4\u62a4DEPRECATED\u6389\u4e86\u3002\u4f5c\u8005\u5efa\u8bae\u8f6c\u5411\u57fa\u4e8e\u82f9\u679c\u65b0APNs\u670d\u52a1\u7684\u5e93\u3002<\/p>\n\n\n\n<p>\u82f9\u679c\u65b0APNs\u57fa\u4e8eHTTP\/2\uff0c\u901a\u8fc7\u8fde\u63a5\u590d\u7528\uff0c\u66f4\u52a0\u9ad8\u6548\uff0c\u5f53\u7136\u8fd8\u6709\u5176\u5b83\u65b9\u9762\u7684\u4f18\u5316\u548c\u6539\u5584\uff0c\u53ef\u4ee5\u53c2\u8003<a rel=\"noreferrer noopener\" href=\"https:\/\/www.jianshu.com\/p\/ace1b422bad4\" target=\"_blank\">APNs\u7684\u4e00\u7bc7\u4ecb\u7ecd<\/a>\uff0c\u8bb2\u89e3\u7684\u6bd4\u8f83\u6e05\u695a\u3002<\/p>\n\n\n\n<p>\u518d\u8bf4\u4e00\u4e0b\u6211\u4eec\u4f7f\u7528\u7684<a href=\"https:\/\/link.jianshu.com\/?t=https:\/\/github.com\/relayrides\/pushy\" target=\"_blank\" rel=\"noreferrer noopener\">Pushy<\/a>\uff0c\u5b98\u65b9\u7b80\u4ecb\u5982\u4e0b\uff1a<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p>Pushy is a Java library for sending APNs (iOS, macOS, and Safari) push notifications. It is written and maintained by the engineers at Turo&#8230;&#8230;<strong>We believe that Pushy is already the best tool for sending APNs push notifications from Java applications<\/strong>, and we hope you&#8217;ll help us make it even better via bug reports and pull requests.<\/p><\/blockquote>\n\n\n\n<p>Pushy\u7684\u6587\u6863\u548c\u8bf4\u660e\u5f88\u5168\uff0c\u8ba8\u8bba\u4e5f\u5f88\u6d3b\u8dc3\uff0c\u4f5c\u8005\u57fa\u672c\u6709\u95ee\u5fc5\u7b54\uff0c\u5927\u90e8\u5206\u7591\u95ee\u90fd\u53ef\u4ee5\u627e\u5230\u7b54\u6848\uff0c\u4f7f\u7528\u96be\u5ea6\u4e5f\u4e0d\u5927\u3002<\/p>\n\n\n\n<h5 class=\"wp-block-heading\">\u4f7f\u7528Pushy\u8fdb\u884cAPNs\u6d88\u606f\u63a8\u9001<\/h5>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<ul><li><strong>\u9996\u5148\u52a0\u5165\u5305<\/strong><\/li><\/ul>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">    &lt;dependency>\n    \t&lt;groupId>com.eatthepath&lt;\/groupId>\n    \t&lt;artifactId>pushy&lt;\/artifactId>\n    \t&lt;version>0.15.0&lt;\/version>\n    &lt;\/dependency><\/pre>\n\n\n\n<ul><li><strong>\u8eab\u4efd\u8ba4\u8bc1<\/strong><\/li><\/ul>\n\n\n\n<p>\u82f9\u679cAPNs\u63d0\u4f9b\u4e86\u4e24\u79cd\u8ba4\u8bc1\u7684\u65b9\u5f0f\uff1a\u57fa\u4e8eJWT\u7684\u8eab\u4efd\u4fe1\u606ftoken\u8ba4\u8bc1\u548c\u57fa\u4e8e\u8bc1\u4e66\u7684\u8eab\u4efd\u8ba4\u8bc1\u3002Pushy\u4e5f\u540c\u6837\u652f\u6301\u8fd9\u4e24\u79cd\u8ba4\u8bc1\u65b9\u5f0f\uff0c\u8fd9\u91cc\u6211\u4eec\u4f7f\u7528\u8bc1\u4e66\u8ba4\u8bc1\u65b9\u5f0f\uff0c\u5173\u4e8etoken\u8ba4\u8bc1\u65b9\u5f0f\u53ef\u4ee5\u67e5\u770bPushy\u7684\u6587\u6863\u3002<\/p>\n\n\n\n<p>\u5982\u4f55\u83b7\u53d6\u82f9\u679cAPNs\u8eab\u4efd\u8ba4\u8bc1\u8bc1\u4e66\u53ef\u4ee5\u67e5\u8003<a rel=\"noreferrer noopener\" href=\"https:\/\/link.jianshu.com?t=https:\/\/developer.apple.com\/library\/content\/documentation\/NetworkingInternet\/Conceptual\/RemoteNotificationsPG\/APNSOverview.html\" target=\"_blank\">\u5b98\u65b9\u6587\u6863<\/a>\u3002<\/p>\n\n\n\n<ul><li><strong>Pushy\u4f7f\u7528<\/strong><\/li><\/ul>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">ApnsClient apnsClient = new ApnsClientBuilder()\n    .setClientCredentials(new File(\"\/path\/to\/certificate.p12\"), \"p12-file-password\")\n    .build();<\/pre>\n\n\n\n<p>ps. \u8fd9\u91cc\u7684setClientCredentials\u51fd\u6570\u4e5f\u53ef\u4ee5\u652f\u6301\u4f20\u5165\u4e00\u4e2aInputStream\u548c\u8bc1\u4e66\u5bc6\u7801\u3002<\/p>\n\n\n\n<p>\u540c\u65f6\u4e5f\u53ef\u4ee5\u901a\u8fc7setApnsServer\u51fd\u6570\u6765\u6307\u5b9a\u662f\u5f00\u53d1\u73af\u5883\u8fd8\u662f\u751f\u4ea7\u73af\u5883\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">ApnsClient apnsClient = new ApnsClientBuilder().setApnsServer(ApnsClientBuilder.DEVELOPMENT_APNS_HOST)\n    .setClientCredentials(new File(\"\/path\/to\/certificate.p12\"), \"p12-file-password\")\n    .build();\n<\/pre>\n\n\n\n<p>Pushy\u662f\u57fa\u4e8eNetty\u7684\uff0c\u901a\u8fc7ApnsClientBuilder\u6211\u4eec\u53ef\u4ee5\u6839\u636e\u9700\u8981\u6765\u4fee\u6539ApnsClient\u7684\u8fde\u63a5\u6570\u548cEventLoopGroups\u7684\u7ebf\u7a0b\u6570\u3002<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">EventLoopGroup eventLoopGroup = new NioEventLoopGroup(4);\nApnsClient apnsClient = new ApnsClientBuilder()\n        .setClientCredentials(new File(\"\/path\/to\/certificate.p12\"), \"p12-file-password\")\n        .setConcurrentConnections(4).setEventLoopGroup(eventLoopGroup).build();\n<\/pre>\n\n\n\n<p><br>\u5173\u4e8e\u8fde\u63a5\u6570\u548cEventLoopGroup\u7ebf\u7a0b\u6570\u5b98\u7f51\u6709\u5982\u4e0b\u7684\u8bf4\u660e\uff0c\u7b80\u5355\u6765\u8bf4\uff0c\u4e0d\u8981\u914d\u7f6eEventLoopGroups\u7684\u7ebf\u7a0b\u6570\u8d85\u8fc7APNs\u8fde\u63a5\u6570\u3002<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p>Because connections are bound to a single event loop (which is bound to a single thread), it never makes sense to give an ApnsClient more threads in an event loop than concurrent connections. A client with an eight-thread EventLoopGroup that is configured to maintain only one connection will use one thread from the group, but the other seven will remain idle. Opening a large number of connections on a small number of threads will likely reduce overall efficiency by increasing competition for CPU time.<\/p><\/blockquote>\n\n\n\n<p>\u5173\u4e8e\u6d88\u606f\u7684\u63a8\u9001\uff0c\u6ce8\u610f\u4e00\u5b9a\u8981\u4f7f\u7528<strong>\u5f02\u6b65\u64cd\u4f5c<\/strong>\uff0cPushy\u53d1\u9001\u6d88\u606f\u4f1a\u8fd4\u56de\u4e00\u4e2aNetty Future\u5bf9\u8c61\uff0c\u901a\u8fc7\u5b83\u53ef\u4ee5\u62ff\u5230\u6d88\u606f\u53d1\u9001\u7684\u60c5\u51b5\u3002<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">for (final ApnsPushNotification pushNotification : collectionOfPushNotifications) {\n    final Future sendNotificationFuture = apnsClient.sendNotification(pushNotification);\n\n    sendNotificationFuture.addListener(new GenericFutureListener&lt;Future&lt;PushNotificationResponse>>() {\n        \n        @Override\n        public void operationComplete(final Future&lt;PushNotificationResponse> future) throws Exception {\n            \/\/ This will get called when the sever has replied and returns immediately\n            final PushNotificationResponse response = future.getNow();\n        }\n    });\n}\n\n<\/pre>\n\n\n\n<p>APNs\u670d\u52a1\u5668\u53ef\u4ee5\u4fdd\u8bc1\u540c\u65f6\u53d1\u90011500\u6761\u6d88\u606f\uff0c\u5f53\u8d85\u8fc7\u8fd9\u4e2a\u9650\u5236\u65f6\uff0cPushy\u4f1a\u7f13\u5b58\u6d88\u606f\uff0c\u6240\u4ee5\u6211\u4eec\u4e0d\u5fc5\u62c5\u5fc3\u5f02\u6b65\u64cd\u4f5c\u53d1\u9001\u7684\u6d88\u606f\u8fc7\u591a\uff08\u5f53\u6211\u4eec\u7684\u6d88\u606f\u975e\u5e38\u591a\uff0c\u8fbe\u5230\u4e0a\u4ebf\u65f6\uff0c\u6211\u4eec\u4e5f\u5f97\u505a\u4e00\u4e9b\u63a7\u5236\uff0c\u907f\u514d\u7f13\u5b58\u8fc7\u5927\uff0c\u5185\u5b58\u4e0d\u8db3\uff0cPushy\u7ed9\u51fa\u4e86\u4f7f\u7528Semaphore\u7684\u89e3\u51b3\u65b9\u6cd5\uff09<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p>The APNs server allows for (at the time of this writing) 1,500 notifications in flight at any time. If we hit that limit, Pushy will buffer notifications automatically behind the scenes and send them to the server as in-flight notifications are resolved.<\/p><\/blockquote>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p>In short, asynchronous operation allows Pushy to make the most of local resources (especially CPU time) by sending notifications as quickly as possible.<\/p><\/blockquote>\n\n\n\n<p>\u4ee5\u4e0a\u4ec5\u662fPushy\u7684\u57fa\u672c\u7528\u6cd5\uff0c\u5728\u6211\u4eec\u7684\u751f\u4ea7\u73af\u5883\u4e2d\u60c5\u51b5\u53ef\u80fd\u4f1a\u66f4\u52a0\u590d\u6742\uff0c\u6211\u4eec\u53ef\u80fd\u9700\u8981\u77e5\u9053\u4ec0\u4e48\u65f6\u5019\u6240\u6709\u63a8\u9001\u90fd\u5b8c\u6210\u4e86\uff0c\u53ef\u80fd\u9700\u8981\u5bf9\u63a8\u9001\u6210\u529f\u6d88\u606f\u8fdb\u884c\u8ba1\u6570<br>\uff0c\u53ef\u80fd\u9700\u8981\u9632\u6b62\u5185\u5b58\u4e0d\u8db3\uff0c\u4e5f\u53ef\u80fd\u9700\u8981\u5bf9\u4e0d\u540c\u7684\u53d1\u9001\u7ed3\u679c\u8fdb\u884c\u4e0d\u540c\u5904\u7406&#8230;.\u4e0d\u591a\u8bf4\uff0c\u4e0a\u4ee3\u7801\u3002<\/p>\n\n\n\n<h5 class=\"wp-block-heading\">\u6700\u4f73\u5b9e\u8df5<\/h5>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>\u53c2\u8003Pushy\u7684<a href=\"https:\/\/link.jianshu.com?t=https:\/\/github.com\/relayrides\/pushy\/wiki\/Best-practices\" target=\"_blank\" rel=\"noreferrer noopener\">\u5b98\u65b9\u6700\u4f73\u5b9e\u8df5<\/a>\uff0c\u6211\u4eec\u52a0\u5165\u4e86\u5982\u4e0b\u64cd\u4f5c\uff1a<\/p>\n\n\n\n<ul><li>\u901a\u8fc7Semaphore\u6765\u8fdb\u884c\u6d41\u63a7\uff0c\u9632\u6b62\u7f13\u5b58\u8fc7\u5927\uff0c\u5185\u5b58\u4e0d\u8db3<\/li><li>\u901a\u8fc7CountDownLatch\u6765\u6807\u8bb0\u6d88\u606f\u662f\u5426\u53d1\u9001\u5b8c\u6210<\/li><li>\u4f7f\u7528AtomicLong\u5b8c\u6210\u533f\u540d\u5185\u90e8\u7c7boperationComplete\u65b9\u6cd5\u4e2d\u7684\u8ba1\u6570<\/li><li>\u4f7f\u7528Netty\u7684Future\u5bf9\u8c61\u8fdb\u884c\u6d88\u606f\u63a8\u9001\u7ed3\u679c\u7684\u5224\u65ad<\/li><\/ul>\n\n\n\n<ul><li><strong>\u5177\u4f53\u7528\u6cd5\u53c2\u8003\u5982\u4e0b\u4ee3\u7801\uff1a<\/strong><\/li><\/ul>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">public class IOSPush {\n\n    private static final Logger logger = LoggerFactory.getLogger(IOSPush.class);\n\n    private static final ApnsClient apnsClient = null;\n\n    private static final Semaphore semaphore = new Semaphore(10000);\n\n    public void push(final List&lt;String> deviceTokens, String alertTitle, String alertBody) {\n\n        long startTime = System.currentTimeMillis();\n\n        if (apnsClient == null) {\n            try {\n                EventLoopGroup eventLoopGroup = new NioEventLoopGroup(4);\n                apnsClient = new ApnsClientBuilder().setApnsServer(ApnsClientBuilder.DEVELOPMENT_APNS_HOST)\n                        .setClientCredentials(new File(\"\/path\/to\/certificate.p12\"), \"p12-file-password\")\n                        .setConcurrentConnections(4).setEventLoopGroup(eventLoopGroup).build();\n            } catch (Exception e) {\n                logger.error(\"ios get pushy apns client failed!\");\n                e.printStackTrace();\n            }\n        }\n\n        long total = deviceTokens.size();\n\n        final CountDownLatch latch = new CountDownLatch(deviceTokens.size());\n\n        final AtomicLong successCnt = new AtomicLong(0);\n\n        long startPushTime =  System.currentTimeMillis();\n\n        for (String deviceToken : deviceTokens) {\n            ApnsPayloadBuilder payloadBuilder = new ApnsPayloadBuilder();\n            payloadBuilder.setAlertBody(alertBody);\n            payloadBuilder.setAlertTitle(alertTitle);\n            \n            String payload = payloadBuilder.buildWithDefaultMaximumLength();\n            final String token = TokenUtil.sanitizeTokenString(deviceToken);\n            SimpleApnsPushNotification pushNotification = new SimpleApnsPushNotification(token, \"com.example.myApp\", payload);\n\n            try {\n                semaphore.acquire();\n            } catch (InterruptedException e) {\n                logger.error(\"ios push get semaphore failed, deviceToken:{}\", deviceToken);\n                e.printStackTrace();\n            }\n            final Future&lt;PushNotificationResponse&lt;SimpleApnsPushNotification>> future = apnsClient.sendNotification(pushNotification);\n\n            future.addListener(new GenericFutureListener&lt;Future&lt;PushNotificationResponse>>() {\n                @Override\n                public void operationComplete(Future&lt;PushNotificationResponse> pushNotificationResponseFuture) throws Exception {\n                    if (future.isSuccess()) {\n                        final PushNotificationResponse&lt;SimpleApnsPushNotification> response = future.getNow();\n                        if (response.isAccepted()) {\n                            successCnt.incrementAndGet();\n                        } else {\n                            Date invalidTime = response.getTokenInvalidationTimestamp();\n                            logger.error(\"Notification rejected by the APNs gateway: \" + response.getRejectionReason());\n                            if (invalidTime != null) {\n                                logger.error(\"\\t\u2026and the token is invalid as of \" + response.getTokenInvalidationTimestamp());\n                            }\n                        }\n                    } else {\n                        logger.error(\"send notification device token={} is failed {} \", token, future.cause().getMessage());\n                    }\n                    latch.countDown();\n                    semaphore.release();\n                }\n            });\n        }\n\n        try {\n            latch.await(20, TimeUnit.SECONDS);\n        } catch (InterruptedException e) {\n            logger.error(\"ios push latch await failed!\");\n            e.printStackTrace();\n        }\n\n        long endPushTime = System.currentTimeMillis();\n\n        logger.info(\"test pushMessage success. [\u5171\u63a8\u9001\" + total + \"\u4e2a][\u6210\u529f\" + (successCnt.get()) + \"\u4e2a], \n            totalcost= \" + (endPushTime - startTime) + \", pushCost=\" + (endPushTime - startPushTime));\n    }\n}\n<\/pre>\n\n\n\n<ul><li><strong>\u5173\u4e8e\u591a\u7ebf\u7a0b\u8c03\u7528client<\/strong><\/li><\/ul>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p>Pushy ApnsClient\u662f\u7ebf\u7a0b\u5b89\u5168\u7684\uff0c\u53ef\u4ee5\u4f7f\u7528\u591a\u7ebf\u7a0b\u6765\u8c03\u7528<\/p><\/blockquote>\n\n\n\n<ul><li><strong>\u5173\u4e8e\u521b\u5efa\u591a\u4e2aclient<\/strong><\/li><\/ul>\n\n\n\n<p>\u521b\u5efa\u591a\u4e2aclient\u662f\u53ef\u4ee5\u52a0\u5feb\u53d1\u9001\u901f\u5ea6\u7684\uff0c\u4f46\u662f\u63d0\u5347\u5e76\u4e0d\u5927\uff0c\u4f5c\u8005\u5efa\u8bae\uff1a<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p>ApnsClient instances are designed to stick around for a long time. They&#8217;re thread-safe and can be shared between many threads in a large application. We recommend creating a single client (per APNs certificate\/key), then keeping that client around for the lifetime of your application.<\/p><\/blockquote>\n\n\n\n<ul><li><strong>\u5173\u4e8eAPNs\u54cd\u5e94\u4fe1\u606f\uff08\u9519\u8bef\u4fe1\u606f\uff09<\/strong><\/li><\/ul>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p>\u53ef\u4ee5\u67e5\u770b\u5b98\u7f51\u7684error code\u8868\u683c\uff08<a href=\"https:\/\/link.jianshu.com\/?t=https:\/\/developer.apple.com\/library\/content\/documentation\/NetworkingInternet\/Conceptual\/RemoteNotificationsPG\/CommunicatingwithAPNs.html#\/\/apple_ref\/doc\/uid\/TP40008194-CH11-SW1\" target=\"_blank\" rel=\"noreferrer noopener\">\u94fe\u63a5<\/a>\uff09\uff0c\u4e86\u89e3\u51fa\u9519\u60c5\u51b5\uff0c\u53ca\u65f6\u8c03\u6574\u3002<\/p><\/blockquote>\n\n\n\n<h5 class=\"wp-block-heading\">Pushy\u6027\u80fd<br><\/h5>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>\u4f5c\u8005\u5728Google\u8ba8\u8bba\u7ec4\u4e2d\u8bf4Pushy\u63a8\u9001\u53ef\u4ee5\u5355\u6838\u5355\u7ebf\u7a0b\u8fbe\u523010k\/s-20k\/s<\/p>\n\n\n\n<p>\u4f46\u662f\u53ef\u80fd\u662f\u7f51\u7edc\u6216\u5176\u4ed6\u539f\u56e0\uff0c\u6211\u7684\u6d4b\u8bd5\u7ed3\u679c\u6ca1\u6709\u8fd9\u4e48\u597d\uff0c\u628a\u6d4b\u8bd5\u7ed3\u679c\u8d34\u51fa\u6765\uff0c\u4ec5\u4f9b\u53c2\u8003\uff08\u65f6\u95f4ms\uff09\uff1a<\/p>\n\n\n\n<p>ps. \u7531\u4e8e\u662f\u6d4b\u8bd5\uff0c\u6ca1\u6709\u5927\u91cf\u7684\u8bbe\u5907\u53ef\u4ee5\u7528\u4e8e\u7fa4\u53d1\u63a8\u9001\u6d4b\u8bd5\uff0c\u6240\u4ee5\u4ee5\u5f80\u4e00\u4e2a\u8bbe\u5907\u53d1\u9001\u591a\u6761\u63a8\u9001\u66ff\u4ee3\u3002\u8fd9\u91cc\u77ed\u65f6\u95f4\u5f80\u4e00\u4e2a\u8bbe\u5907\u53d1\u9001\u5927\u91cf\u7684\u63a8\u9001\uff0cAPNs\u4f1a\u62a5TooManyRequests\u9519\u8bef\uff0cToo many requests were made consecutively to the same device token\u3002\u6240\u4ee5\u4f1a\u6709\u5c11\u91cf\u6d88\u606f\u65e0\u6cd5\u53d1\u51fa\u3002<\/p>\n\n\n\n<p>ps. \u8fd9\u91cc\u7684\u63a8\u9001\u65f6\u95f4\uff0c\u6ca1\u6709\u52a0\u4e0aclient\u521d\u59cb\u5316\u7684\u65f6\u95f4\u3002<\/p>\n\n\n\n<p>ps. \u6d88\u606f\u63a8\u9001\u65f6\u95f4\u4e0e\u88ab\u63a8\u6d88\u606f\u7684\u5927\u5c0f\u6709\u5173\u7cfb\uff0c\u8fd9\u91cc\u6211\u5728\u6d4b\u8bd5\u65f6\u6ca1\u6709\u63a7\u5236\u6d88\u606f\u53d8\u91cf\uff08\u90fd\u662f\u6211\u778e\u586b\u7684\uff0c\u90fd\u662f\u5f88\u77ed\u7684\u6d88\u606f\uff09\u6240\u4ee5\u6570\u636e<strong>\u4ec5\u4f9b\u53c2\u8003<\/strong>\u3002<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/123.57.164.21\/wp-content\/uploads\/2021\/12\/image-247-1024x641.png\" alt=\"\" class=\"wp-image-4721\" width=\"581\" height=\"363\" srcset=\"https:\/\/92it.top\/wp-content\/uploads\/2021\/12\/image-247-1024x641.png 1024w, https:\/\/92it.top\/wp-content\/uploads\/2021\/12\/image-247-300x188.png 300w, https:\/\/92it.top\/wp-content\/uploads\/2021\/12\/image-247-768x481.png 768w, https:\/\/92it.top\/wp-content\/uploads\/2021\/12\/image-247-830x519.png 830w, https:\/\/92it.top\/wp-content\/uploads\/2021\/12\/image-247-230x144.png 230w, https:\/\/92it.top\/wp-content\/uploads\/2021\/12\/image-247-350x219.png 350w, https:\/\/92it.top\/wp-content\/uploads\/2021\/12\/image-247-480x300.png 480w, https:\/\/92it.top\/wp-content\/uploads\/2021\/12\/image-247.png 1406w\" sizes=\"(max-width: 581px) 100vw, 581px\" \/><\/figure><\/div>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/123.57.164.21\/wp-content\/uploads\/2021\/12\/image-248-1024x340.png\" alt=\"\" class=\"wp-image-4722\" width=\"556\" height=\"184\" srcset=\"https:\/\/92it.top\/wp-content\/uploads\/2021\/12\/image-248-1024x340.png 1024w, https:\/\/92it.top\/wp-content\/uploads\/2021\/12\/image-248-300x100.png 300w, https:\/\/92it.top\/wp-content\/uploads\/2021\/12\/image-248-768x255.png 768w, https:\/\/92it.top\/wp-content\/uploads\/2021\/12\/image-248-830x276.png 830w, https:\/\/92it.top\/wp-content\/uploads\/2021\/12\/image-248-230x76.png 230w, https:\/\/92it.top\/wp-content\/uploads\/2021\/12\/image-248-350x116.png 350w, https:\/\/92it.top\/wp-content\/uploads\/2021\/12\/image-248-480x160.png 480w, https:\/\/92it.top\/wp-content\/uploads\/2021\/12\/image-248.png 1396w\" sizes=\"(max-width: 556px) 100vw, 556px\" \/><\/figure><\/div>\n\n\n\n<p>\u5173\u4e8e\u6027\u80fd\u4f18\u5316\u4e5f\u53ef\u4ee5\u770b\u770b\u5b98\u7f51\u4f5c\u8005\u7684\u5efa\u8bae\uff1a<a rel=\"noreferrer noopener\" href=\"https:\/\/link.jianshu.com\/?t=https:\/\/github.com\/relayrides\/pushy\/wiki\/Best-practices\" target=\"_blank\">Threads, concurrent connections, and performance<\/a><\/p>\n\n\n\n<h5 class=\"wp-block-heading\">\u601d\u8003<\/h5>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>\u82f9\u679cAPNs\u4e00\u76f4\u5728\u66f4\u65b0\u4f18\u5316\uff0c\u4e00\u81f4\u5728\u62e5\u62b1\u65b0\u6280\u672f\uff08HTTP\/2\uff0cJWT\u7b49\uff09\uff0c\u662f\u4e00\u4e2a\u975e\u5e38\u4e86\u4e0d\u8d77\u7684\u670d\u52a1\u3002<\/p>\n\n\n\n<p>\u81ea\u5df1\u6765\u76f4\u63a5\u8c03\u7528APNs\u670d\u52a1\u6765\u8fbe\u5230\u751f\u6210\u73af\u5883\u8981\u6c42\u8fd8\u662f\u6709\u70b9\u56f0\u96be\u3002Turo\u7ed9\u6211\u4eec\u63d0\u4f9b\u4e86\u4e00\u4e2a\u5f88\u597d\u7684Java\u5e93\uff1aPushy\u3002Pushy\u8fd8\u6709\u4e00\u4e9b\u5176\u4ed6\u7684\u529f\u80fd\u4e0e\u7528\u6cd5\uff08Metrics\u3001proxy\u3001Logging&#8230;\uff09\uff0c\u603b\u4f53\u6765\u8bf4\u8fd8\u662f\u975e\u5e38\u4e0d\u9519\u7684\u3002<\/p>\n\n\n\n<h5 class=\"wp-block-heading\">\u9644\uff1a\u81ea\u5df1\u5199\u7684\u7b80\u5355Demo\u4ee3\u7801<\/h5>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import java.io.File;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.core.io.ResourceLoader;\nimport org.springframework.stereotype.Service;\n\nimport com.eatthepath.pushy.apns.ApnsClient;\nimport com.eatthepath.pushy.apns.ApnsClientBuilder;\nimport com.eatthepath.pushy.apns.PushNotificationResponse;\nimport com.eatthepath.pushy.apns.util.ApnsPayloadBuilder;\nimport com.eatthepath.pushy.apns.util.SimpleApnsPayloadBuilder;\nimport com.eatthepath.pushy.apns.util.SimpleApnsPushNotification;\nimport com.eatthepath.pushy.apns.util.concurrent.PushNotificationFuture;\n\nimport io.netty.channel.EventLoopGroup;\nimport io.netty.channel.nio.NioEventLoopGroup;\n\n@Service\npublic class ApnsDemoImpl implements ApnsDemoService {\n\n\t@Autowired\n\tResourceLoader resourceLoader;\n\n\t@Value(\"${pushCertPassword}\")\n\tprivate String pushCertPassword;\n\n\t@Value(\"${appBundleId}\")\n\tprivate String appBundleId;\n\n\tprivate static ApnsClient apnsClient = null;\n\n\tprivate File apnsFile;\n\n\tpublic void pushRemoteMessage(String[] deviceTokens, String alertTitle, String alertBody) {\n\n\t\tif (apnsClient == null) {\n\t\t\ttry {\n\t\t\t\tif (apnsFile == null || !apnsFile.exists()) {\n\t\t\t\t\t\/\/ \u6ce8\u610f\u8fd9\u90e8\u5206\u903b\u8f91\n\t\t\t\t\tInputStream stream = getClass().getClassLoader().getResourceAsStream(\"push.p12\");\n\t\t\t\t\tapnsFile = new File(\"push.p12\");\n\t\t\t\t\tFileUtils.copyInputStreamToFile(stream, apnsFile);\n\t\t\t\t}\n\n\t\t\t\tEventLoopGroup eventLoopGroup = new NioEventLoopGroup(4);\n\t\t\t\tapnsClient = new ApnsClientBuilder().setApnsServer(ApnsClientBuilder.DEVELOPMENT_APNS_HOST)\n\t\t\t\t\t\t.setClientCredentials(apnsFile, pushCertPassword).setConcurrentConnections(4)\n\t\t\t\t\t\t.setEventLoopGroup(eventLoopGroup).build();\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\n\t\t}\n\n\t\tfor (String deviceToken : deviceTokens) {\n\t\t\tApnsPayloadBuilder payloadBuilder = new SimpleApnsPayloadBuilder();\n\t\t\tpayloadBuilder.setAlertBody(alertBody);\n\t\t\tpayloadBuilder.setAlertTitle(alertTitle);\n\n\t\t\tString payload = payloadBuilder.build();\n\t\t\tSimpleApnsPushNotification pushNotification = new SimpleApnsPushNotification(deviceToken, appBundleId,\n\t\t\t\t\tpayload);\n\n\t\t\tfinal PushNotificationFuture&lt;SimpleApnsPushNotification, PushNotificationResponse&lt;SimpleApnsPushNotification>> sendNotificationFuture = apnsClient\n\t\t\t\t\t.sendNotification(pushNotification);\n\n\t\t\ttry {\n\t\t\t\tfinal PushNotificationResponse&lt;SimpleApnsPushNotification> pushNotificationResponse = sendNotificationFuture\n\t\t\t\t\t\t.get();\n\n\t\t\t\tif (pushNotificationResponse.isAccepted()) {\n\t\t\t\t\tSystem.out.println(\"Push notification accepted by APNs gateway.\");\n\t\t\t\t} else {\n\t\t\t\t\tSystem.out.println(\"Notification rejected by the APNs gateway: \"\n\t\t\t\t\t\t\t+ pushNotificationResponse.getRejectionReason());\n\t\t\t\t}\n\t\t\t} catch (final Exception e) {\n\t\t\t\tSystem.err.println(\"Failed to send push notification.\");\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>\u6700\u8fd1\u5bf9\u9879\u76ee\u7ec4\u7684\u8001\u7684\u82f9\u679cIOS\u63a8\u9001\u8fdb\u884c\u4e86\u5347\u7ea7\u4fee\u6539\uff0c\u8c03\u7814\u4e86\u4e00\u4e9b\u5f00\u6e90\u5e38\u7528\u7684\u5e93\u4e4b\u540e\uff0c\u9009\u62e9\u4e86Turo\u56e2\u961f\u5f00\u53d1\u548c\u7ef4\u62a4\u7684pu [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[26],"tags":[],"_links":{"self":[{"href":"https:\/\/92it.top\/index.php?rest_route=\/wp\/v2\/posts\/4714"}],"collection":[{"href":"https:\/\/92it.top\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/92it.top\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/92it.top\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/92it.top\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=4714"}],"version-history":[{"count":8,"href":"https:\/\/92it.top\/index.php?rest_route=\/wp\/v2\/posts\/4714\/revisions"}],"predecessor-version":[{"id":7414,"href":"https:\/\/92it.top\/index.php?rest_route=\/wp\/v2\/posts\/4714\/revisions\/7414"}],"wp:attachment":[{"href":"https:\/\/92it.top\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=4714"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/92it.top\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=4714"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/92it.top\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=4714"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}