Commit ad1069e1 by giaogiao

完成apns推送第一版

parent 7ef58732
package io.geekidea.springbootplus.test;
import com.turo.pushy.apns.DeliveryPriority;
import com.turo.pushy.apns.PushType;
import com.wecloud.im.ws.sender.IosPush;
import java.util.HashMap;
import java.util.Map;
public class IosApnsPushTest {
public static void main(String[] args) {
// * @param apnsCertificatePath 证书
// * @param productFlag 环境
// * @param deviceToken 设备token
// * @param alertTitle 标题
// * @param alertBody 副标题
// * @param contentAvailable Boolean.FALSE
// * @param customProperty 自定义属性
// * @param badge 角标数量
// * @param priority DeliveryPriority.IMMEDIATE
// * @param pushType PushType.ALERT
// * @param topicBundleId undleId
// * @param sound rtc= "call.caf"; 否则为default
Map<String, Object> customProperty = new HashMap<String, Object>(10);
String apnsCertificatePath = "frogsell_push_dev.p12";
String deviceToken = "27c93ca84bbf17d9ff8eb05df0576ac49822db2ae1c02aa0afea83b5c3861276";
String alertTitle = "你好";
String alertBody = "hi";
int badge = 1;
String topicBundleId = "com.jdw.frogsell";
boolean contentAvailable = false;
IosPush.push(apnsCertificatePath, Boolean.FALSE, deviceToken, alertTitle, alertBody,
contentAvailable, customProperty, badge
, DeliveryPriority.IMMEDIATE, PushType.ALERT, topicBundleId,
"default");
}
}
...@@ -115,6 +115,14 @@ ...@@ -115,6 +115,14 @@
<!-- 友盟 end --> <!-- 友盟 end -->
<!-- https://mvnrepository.com/artifact/com.turo/pushy -->
<!-- apns推送-->
<dependency>
<groupId>com.turo</groupId>
<artifactId>pushy</artifactId>
<version>0.13.10</version>
</dependency>
<!-- wecloud短信 start--> <!-- wecloud短信 start-->
<dependency> <dependency>
...@@ -131,5 +139,25 @@ ...@@ -131,5 +139,25 @@
<!-- wecloud短信 end--> <!-- wecloud短信 end-->
</dependencies> </dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<encoding>UTF-8</encoding>
<!-- 过滤后缀为pem、pfx的证书文件 -->
<!-- 打包编译是过滤掉这些证书文件,不再自动篡改-->
<nonFilteredFileExtensions>
<nonFilteredFileExtension>p12</nonFilteredFileExtension>
<nonFilteredFileExtension>pem</nonFilteredFileExtension>
<nonFilteredFileExtension>pfx</nonFilteredFileExtension>
</nonFilteredFileExtensions>
</configuration>
</plugin>
</plugins>
</build>
</project> </project>
\ No newline at end of file
package com.wecloud.im.ws.sender;
import com.turo.pushy.apns.ApnsClient;
import com.turo.pushy.apns.ApnsClientBuilder;
import com.turo.pushy.apns.DeliveryPriority;
import com.turo.pushy.apns.PushNotificationResponse;
import com.turo.pushy.apns.PushType;
import com.turo.pushy.apns.util.ApnsPayloadBuilder;
import com.turo.pushy.apns.util.SimpleApnsPushNotification;
import com.turo.pushy.apns.util.TokenUtil;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import java.io.InputStream;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
/**
* @Description 基于APNs最新HTTP/2接口实现iOS的高性能消息推送pushy(服务端篇)
* 通过Semaphore来进行流控,防止缓存过大,内存不足;
* 通过CountDownLatch来标记消息是否发送完成;
* 使用AtomicLong完成匿名内部类operationComplete方法中的计数;
* 使用Netty的Future对象进行消息推送结果的判断。
*/
public class IosPush {
// private static Map<String, String> APNS_CACHE = new HashMap<>();
private static final Logger logger = LoggerFactory.getLogger(IosPush.class);
/**
* Semaphore又称信号量,是操作系统中的一个概念,在Java并发编程中,信号量控制的是线程并发的数量。
*/
private static final Semaphore SEMAPHORE = new Semaphore(10000);
// private static ApnsClient apnsClient = null;
//
// public static ApnsClient getLocalAPNSConnect() {
// if (apnsClient == null) {
// try {
// EventLoopGroup eventLoopGroup = new NioEventLoopGroup(4);
// apnsClient = new ApnsClientBuilder().setApnsServer(ApnsClientBuilder.DEVELOPMENT_APNS_HOST)
// .setClientCredentials(new File("G:/java/push_file/xt_aillo_test_apns.p12"), "123456")
// .setConcurrentConnections(4).setEventLoopGroup(eventLoopGroup).build();
// } catch (Exception e) {
// logger.error("ios get pushy apns client failed!");
// e.printStackTrace();
// }
// }
// return apnsClient;
// }
// public static void push(String apnsCertificatePath, Boolean productFlag, final String deviceToken, String alertTitle, String alertBody,
// int badge, String topicBundleId,
// String sound) {
// }
/**
* @param apnsCertificatePath 证书
* @param productFlag 环境
* @param deviceToken 设备token
* @param alertTitle 标题
* @param alertBody 副标题
* @param contentAvailable Boolean.FALSE
* @param customProperty 自定义属性
* @param badge 角标数量
* @param priority DeliveryPriority.IMMEDIATE
* @param pushType PushType.ALERT
* @param topicBundleId undleId
* @param sound rtc= "call.caf"; 否则为default
*/
public static void push(String apnsCertificatePath, Boolean productFlag, final String deviceToken, String alertTitle, String alertBody,
boolean contentAvailable, Map<String, Object> customProperty,
int badge, DeliveryPriority priority, PushType pushType, String topicBundleId,
String sound) {
String certificatePassword = "123456";
if (deviceToken == null || "".equals(deviceToken)) {
logger.error("deviceToken=null");
return;
}
long startTime = System.currentTimeMillis();
//每次完成一个任务(不一定需要线程走完),latch减1,直到所有的任务都完成,就可以执行下一阶段任务,可提高性能
final CountDownLatch latch = new CountDownLatch(1);
//线程安全的计数器
final AtomicLong successCnt = new AtomicLong(0);
ApnsPayloadBuilder payloadBuilder = new ApnsPayloadBuilder();
if (alertTitle != null) {
payloadBuilder.setAlertTitle(alertTitle);
}
if (alertBody != null) {
payloadBuilder.setAlertBody(alertBody);
}
payloadBuilder.setMutableContent(true);
//如果badge小于0,则不推送这个右上角的角标,主要用于消息盒子新增或者已读时,更新此状态
if (badge > 0) {
payloadBuilder.setBadgeNumber(badge);
}
if (!StringUtils.isEmpty(sound)) {
payloadBuilder.setSound(sound);
}
//将所有的附加参数全部放进去
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, topicBundleId, payload, new Date(), priority, pushType);
try {
//从信号量中获取一个允许机会
SEMAPHORE.acquire();
} catch (Exception e) {
//线程太多了,没有多余的信号量可以获取了
logger.error("ios push get semaphore failed, deviceToken:{}", deviceToken);
logger.error("ios push get semaphore failed", e);
}
logger.debug("token={},payload={}", token, payload);
ApnsClient apnsClient = getAPNSConnect(apnsCertificatePath, productFlag, certificatePassword);
final Future<PushNotificationResponse<SimpleApnsPushNotification>> future = apnsClient.sendNotification(pushNotification);
future.addListener((GenericFutureListener<Future<PushNotificationResponse>>) pushNotificationResponseFuture -> {
if (future.isSuccess()) {
final PushNotificationResponse<SimpleApnsPushNotification> response = future.getNow();
if (response.isAccepted()) {
logger.debug("success token{}", token);
successCnt.incrementAndGet();
} else {
Date invalidTime = response.getTokenInvalidationTimestamp();
logger.error("Notification rejected by the APNs gateway: " + response.getRejectionReason() + ",payload:" + payload +
"\n,deviceToken:" + deviceToken
, ",alertBody:" + alertBody + ",sanitizeTokenString:" + token);
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 (InterruptedException e) {
logger.error("ios push latch await failed!", e);
}
long endPushTime = System.currentTimeMillis();
logger.debug("pushMessage success. [成功{}个,耗时{}ms]", successCnt.get(), endPushTime - startTime);
}
// public static void main(String[] args) {
// Map<String, Object> customProperty = new HashMap<String, Object>(5);
// //alert
// push(getLocalAPNSConnect(), "edb79e259a9523894da68328bca050c4e22f8188e45bcd6f15a5498cf1f0ab12", "123", "789",
// false, customProperty, 0, DeliveryPriority.IMMEDIATE, PushType.ALERT, "com.xteng.ailloTest", null);
// // 静默
// push(getLocalAPNSConnect(), "edb79e259a9523894da68328bca050c4e22f8188e45bcd6f15a5498cf1f0ab12", null, null,
// true, customProperty, 0, DeliveryPriority.CONSERVE_POWER, PushType.BACKGROUND, "com.xteng.ailloTest", null);
// }
/**
* 获取apns推送连接
*
* @param apnsCertificatePath 证书文件
* @param productFlag 环境: true=正式, false测试
* @param certificatePassword ios证书通用密码
* @return
*/
public static ApnsClient getAPNSConnect(String apnsCertificatePath, Boolean productFlag, String certificatePassword) {
ApnsClient apnsXTClient = null;
try {
EventLoopGroup eventLoopGroup = new NioEventLoopGroup(4);
String environmentHost = productFlag ? ApnsClientBuilder.PRODUCTION_APNS_HOST : ApnsClientBuilder.DEVELOPMENT_APNS_HOST;
InputStream resourceAsStream = ClassLoader.getSystemClassLoader().getResourceAsStream(apnsCertificatePath);
apnsXTClient = new ApnsClientBuilder().setApnsServer(environmentHost)
.setClientCredentials(resourceAsStream, certificatePassword)
.setConcurrentConnections(4).setEventLoopGroup(eventLoopGroup).build();
} catch (Exception e) {
logger.error("ios get push apns client failed!", e);
}
return apnsXTClient;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment