持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第15天,点击查看活动详情
ios apns 推送服务
前言
公司内部有一套自己的消息推送系统, 是通过WebSocket的方式实现的, Android应用早已接入使用了,
但是ios应用迟迟没有接入, 因为ios应用程序在退到后台一段时间后线程会被挂起, 系统会收回WebSocket资源,
为了更好的支持ios推送, 决定接入ios apns服务, 其原理也很简单我们的消息先推送到ios的apns服务, 然后由apns服务再推送到用户的设备
准备
找到了javapns和notnoop两个工具, 看到有大佬说javapns会存在内存泄漏问题, 没有深入研究,
我们直接选择notnoop
maven
<dependency>
<groupId>com.notnoop.apns</groupId>
<artifactId>apns</artifactId>
<version>1.0.0.Beta6</version>
</dependency>
notnoop代码结构
可以看到源码非常精简
源码中总共有三种消息发送方式
ApnsServiceImpl来一个处理一个BatchApnsService批消息发送 这个看源码有点疑惑, 里面用的一个ScheduledThreadPoolExecutor线程池处理的任务, 这个应该叫延迟消息吧, 而且继承的AbstractApnsService本身就实现的有批处理消息
QueuedApnsService消息先进入队列(默认是LinkedBlockingQueue), 然后创建了一个线程死循环处理队列消息 这里我们选择通过队列方式发送, 避免消息发送过于频繁导致被封, 如果想扩展也很简单, 只要继承AbstractApnsService然后自定义自己的实现就可以了
快速开始
定义消息
@Data
public class Msg implements Serializable {
/**
* 消息来源
*/
private String from;
/**
* 消息去向
*/
private String to;
/**
* 消息类型
*/
private int msgType;
/**
* 消息体
*/
private Object text;
/**
* 设备deviceId
*/
private String deviceId;
/**
* 苹果deviceToken
*/
private String deviceToken;
}
初始化ApnsService
@Autowired
private ApnsConfigMapper apnsConfigMapper;
@Value("${push.server.ios.sandbox}")
private boolean isSandbox;
@Value("${push.server.ios.cert.path}")
private String p12Path;
private Map<String, ApnsService> apnsServiceMap;
/**
* 初始化apns服务
*/
@PostConstruct
public void initApnsService() {
List<ApnsConfig> apnsConfigs = apnsConfigMapper.getApnsConfig();
apnsConfigs.forEach(e -> apnsServiceMap.put(e.getAppName(),
APNS.newService()
// 加载证书
.withCert(p12Path + (isSandbox ? "dev/" + e.getSandboxP12() : "prod/" + e.getProdP12()),
isSandbox ? e.getSandboxPwd() : e.getProdPwd())
// 使用沙箱
.withSandboxDestination()
// 消息通过队列方式发送
.asQueued()
// 关闭错误检测
.withNoErrorDetection()
.build()));
}
发送消息
/**
* 发送消息到apns服务
*
* @param appName 应用名称
* @param msg 消息
*/
public void push(String appName, Msg msg) {
if (Objects.isNull(msg)) {
return;
}
try {
String text = String.valueOf(msg.getText());
JSONObject pushJson = JSONObject.parseObject(text);
String payload = APNS.newPayload()
// 设置消息标题
.alertTitle(pushJson.getString("_mediaTitle"))
// 设置消息内容
.alertBody(pushJson.getString("_mediaSummary"))
.alertAction(text)
.build();
// 发送消息
apnsServiceMap.get(appName).push(msg.getDeviceToken(), payload);
} catch (Exception ex) {
log.error("ios msg push error, msg = {}, error = {}", msg, ex.getMessage());
}
}