Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
wecloud_im_server
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
hewei
wecloud_im_server
Commits
ad1069e1
Commit
ad1069e1
authored
Sep 18, 2021
by
giaogiao
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
完成apns推送第一版
parent
7ef58732
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
276 additions
and
1 deletions
+276
-1
bootstrap/src/test/java/io/geekidea/springbootplus/test/IosApnsPushTest.java
+42
-0
common/pom.xml
+30
-1
common/src/main/java/com/wecloud/im/ws/sender/IosPush.java
+204
-0
common/src/main/resources/frogsell_push.p12
+0
-0
common/src/main/resources/frogsell_push_dev.p12
+0
-0
No files found.
bootstrap/src/test/java/io/geekidea/springbootplus/test/IosApnsPushTest.java
0 → 100644
View file @
ad1069e1
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"
);
}
}
common/pom.xml
View file @
ad1069e1
...
@@ -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
common/src/main/java/com/wecloud/im/ws/sender/IosPush.java
0 → 100644
View file @
ad1069e1
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
;
}
}
common/src/main/resources/frogsell_push.p12
0 → 100644
View file @
ad1069e1
File added
common/src/main/resources/frogsell_push_dev.p12
0 → 100644
View file @
ad1069e1
File added
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment