旨在做一个类似于极光推送,小米推送之类的Java程序开源实现。基于Netty + protobuf
github地址是 https://github.com/bootsrc/fpush
fpush-core 核心类库,protobuf原型类
fpush-server server端, 接受来自自己的应用服务器的http推送请求,并把请求转换成netty的socket发送给fpush-client
实现消息推送,即时通讯技术。
fpush-client 客户端,模拟App或者网页,或者桌面应用的客户端
[外链图片转存失败(img-fEu9lma5-1566535827161)(https://raw.githubusercontent.com/bootsrc/fpush/master/doc/arch-1.png)]
tcp通信图如下:
[外链图片转存失败(img-ZTfjgwWG-1566535827163)(https://raw.githubusercontent.com/bootsrc/fpush/master/doc/tcp.png)]
客户端使用PushConfirmHandler处理
服务端
RegisterResponseHandler中channelRead()里,如果客户端注册成功则把channel对象保存到NettyChannelMap这个Map里去
String clientId = header.getAlias();
ctx.channel().attr(ChannelAttrKey.KEY_CLIENT_ID).set(clientId);
NettyChannelMap.put(clientId, ctx.channel());
应用服务器通过访问 http接口 http://localhost:10200/api/**
后台ApiController把消息的内容写入缓存ToSendMap.aliasMap中去,如
ToSendMap.aliasMap.put(alias, list);
定时任务com.appjishu.fpush.server.boot.SendTask#scan
每隔一定的时间间隔,会扫描ToSendMap.aliasMap
里的待发送的消息. 遍历后,会通过NettyChannelMap.get(alias)
获取到Channel,然后 channel.writeAndFlush(message)
发送出去
@Scheduled(fixedRate = 5000)
public void scan() {
for (Map.Entry<String, List<MsgData>> entry: ToSendMap.aliasMap.entrySet()) {
String alias = entry.getKey();
List<MsgData> msgList = entry.getValue();
if (StringUtils.isNotEmpty(alias) && msgList != null && msgList.size() > 0) {
pushService.doPush(msgList, alias);
}
}
}
public void doPush(List<MsgData> msgList, String alias) {
FMessage fMessage = buildPushMessage(msgList, alias);
if (fMessage != null) {
log.info("---TringToDoPush()--->");
Channel channel = NettyChannelMap.get(alias);
if (channel == null) {
log.info("------channelIsNull---");
} else if (!channel.isWritable()) {
log.info("------channelIsNotWritable---");
} else {
ChannelFuture future = channel.writeAndFlush(fMessage);
log.info("------msgWriten!!!---");
future.addListener(new ChannelFutureListener() {
public void operationComplete(final ChannelFuture future)
throws Exception {
if (msgList.size() > 0) {
msgList.remove(0);
log.info("------removeAreadySentMsg!!!---");
}
}
});
}
}
}
eclipse/IDEA里
Step1 右键run as–java application-- FpushServerApp.java
Step2 右键run as–java application-- FpushClientApp.java
Step3 后台发送消息给fpush-client (用来模拟android,ios或者网页,或者java应用的消息客户端)
浏览器访问 http://localhost:10200
显示Welcome to fpush application!, 说明server运行起来了
然后浏览器请求
http://localhost:10200/api/pushTest
如果浏览器返回OK
并且fpush-client打印出下面的信息,说明推送消息成功
2018-11-19 14:28:44.792 INFO 27780 --- [ntLoopGroup-2-1] c.a.f.client.handler.PushConfirmHandler : --->>>这是推送到客户端的消息:title=fpush-Demo
2018-11-19 14:29:17.067 INFO 27780 --- [ntLoopGroup-2-1] c.a.f.client.handler.PushConfirmHandler : --->>>这是推送到客户端的消息:description=这是一条推送给lsm001的消息!
Step4 Android演示
[外链图片转存失败(img-UHnPbRdT-1566535827164)(https://raw.githubusercontent.com/bootsrc/fpush/master/doc/post.jpg)]
server效果图
[外链图片转存失败(img-rHKskFPk-1566535827165)(https://raw.githubusercontent.com/bootsrc/fpush/master/doc/server.png)]
client在eclipse上调试的效果图-eclipse console可以显示中文字符
注册一个应用账号,手机号是15600000000
http://localhost:10200/app/registerAccount?mobilePhone=15600000000
http://localhost:10200/app/secretToken?appId=517723931931574272&appSecretKey=cb2eb85b362941f1b3e1
netty+protobuf
protobuf的解码ProtobufVarint32FrameDecoder,ProtobufDecoder
protobuf的编码ProtobufVarint32LengthFieldPrepender,ProtobufEncoder
心跳机制的实现
client端经过HeartBeatResponseHandler新建线程,定期发出心跳请求
server端的HeartBeatResponseHandler监听心跳并作出响应
客户端长连接的鉴权
客户端(即fpush-client)发送appId + appKey,经后台鉴定权限通过后,获取到clientToken
fpush-client与fpush-server通信的时候, RegisterRequestHandler和HeartBeatRequestHandler里面需要带上
appId+clientToken
建立长连接后,最好所有的RequestHandler需要带上appId+clientToken
应用服务端的http连接的鉴权
应用服务端(即app server)发送appId + appSecretKey,经后台鉴定权限通过后,获取到appToken
应用服务端每次调用fpush-server的api都需要带上appId+appToken
github地址是 https://github.com/bootsrc/fpush
路过的帮忙点星星star,谢谢