在Openfire上弄一个简单的推送系统
推送系统
说是推送系统有点大,其实就是一个消息广播功能吧。作用其实也就是由服务端接收到消息然后推送到订阅的客户端。
思路
对于推送最关键的是服务端向客户端发送数据,客户端向服务端订阅自己想要的消息。这样的好处就是有消息后才向客户端推送,相比于拉取数据不会产生许多无效的查询,实时性也高。
xmpp这种即时通信协议基于TCP长连接还是比较符合这种场景的。只需要在服务端增加一个模块用于接收用户订阅与数据的推送就完成了主体功能。
在xmpp协议里可以扩展组件,这样我们写一个组件,然后连接到xmpp服务器,这样就可以应用于不同的xmpp服务器。
准备工作
主要的环境
因为我比较熟悉openfire的体系,所以自然就用它。客户端暂时没有特别的需求,只是用于接收数据,所以用smack或者任何一款xmpp 客户端都可以。我为了简单就用smack写一个简单的代码。
需要用到的jar包
用到的了whack的core,在maven工程里直接引用即可,相关的依赖包会自动加载进来
<dependency>
<groupId>org.igniterealtime.whack</groupId>
<artifactId>core</artifactId>
<version>2.0.1-SNAPSHOT</version>
<type>jar</type>
</dependency>
核心模块
推送服务
推送服务就是等待或者获得需要推送的消息数据后向用户广播出去的服务。因为这里暂时没有设定数据的场景,所以就简单的用一个阻塞队列来表示。步骤:
- 数据通过推送接口写入到推送服务
- 推送服务将数据写入到消息队列
- 发送线程检测到消息后取出并发给订阅的客户端
在此我写了一个PushServer的类用于表示推送服务,这个类里包含了:
- 一个消息队列
- 一个发送线程
- 一个订阅列表
- 以及一些发送相关的xmpp组件
消息队列
//消息列表
private BlockingQueue<Packet> packetQueue;
使用到了生产者消费者模式,所以用了一个阻塞队列,用于存放等待发送的消息数据。
发送线程
private class PacketSenderThread extends Thread {
private volatile Boolean shutdown = false;
private BlockingQueue<Packet> queue;
private Component component;
private ComponentManager componentManager;
public PacketSenderThread(ComponentManager componentManager, Component component, BlockingQueue<Packet> queue) {
this.componentManager = componentManager;
this.component = component;
this.queue = queue;
}
public void run() {
while (!shutdown) {
Packet p;
try {
p = queue.take();
componentManager.sendPacket(component, p);
} catch (InterruptedException e1) {
System.err.println(e1.getStackTrace());
} catch (ComponentException e) {
e.printStackTrace();
}
}
}
public void shutdown() {
shutdown = true;
this.interrupt();
}
}
这个线程继承了Thread,线程的功能很简单,就是一直从queue中获得消息,因为是阻塞的队列,所以没有消息时会阻塞,一旦有消息就会执行发送sendPacket将包发送出去。
这里使用到了componentManager,这个是openfire实现的一个组件管理类,通过这个类的对象可以发送xmpp数据包。
增加shutdown方法,使得线程可以在外部进行退出操作。
订阅列表
//订阅列表
private Set<JID> subscriptions;
public synchronized void subscription(JID jid) {
subscriptions.add(jid);
}
public synchronized void unsubscription(JID jid) {
subscriptions.remove(jid);
}
只有订阅了这个推送服务的客户端才会进行推送操作,这里的代码就是用于订阅与退订操作。用了一个HashSet来存储。
xmpp组件
public class PushComponent extends AbstractComponent{
public PushComponent() {
}
@Override
public String getDescription() {
return "用于消息推送服务组件,主要功能就是将消息转发给具体的客户端,实现消息中转的功能";
}
@Override
public String getName() {
return "pusher";
}
@Override
protected void handleMessage(Message message) {
}
}
public class PushManager {
private static PushManager _instance = new PushManager();
private Map<String, PushServer> pushServers;
private ExternalComponentManager manager;
private PushManager() {
pushServers = new ConcurrentHashMap<String, PushServer>();
manager = new ExternalComponentManager("192.168.149.214", 5275);
manager.setSecretKey("push", "test");
manager.setMultipleAllowed("push", true);
}
public static PushManager getInstance() {
return _instance;
}
public void init() {
try {
//初始化PushServer
PushServer pushSvr = new PushServer("push", manager);
pushServers.put("push", pushSvr);
//注册Component到xmpp服务器
manager.addComponent(pushSvr.getPushDomain(), pushSvr.getComp());
} catch (ComponentException e) {
e.printStackTrace();
}
}
public PushServer getPushServer(String pushDomain) {
return pushServers.get(pushDomain);
}
}
这里的PushComponent就是一个xmpp组件,相当于一个扩展模块,可以接收消息并处理消息,也就是自己写一些和xmpp相关的业务功能。
PushManager就是管理组件并连接到xmpp服务器的一个类。
服务端启动
public class App
{
public static void main( String[] args )
{
PushManager.getInstance().init();
//推送消息
PushServer ps = PushManager.getInstance().getPushServer("push");
ps.start();
JID client1 = new JID("1twja8e8yr@domain/1twja8e8yr");
ps.subscription(client1);
try {
for (Integer i = 0; i< 200; i++) {
ps.putPacket("推送消息200:" + i.toString());
Thread.sleep(1);
}
Thread.sleep(5000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
ps.stop();
System.out.println("go die");
}
}
这段代码模拟了服务的启动,同时为了简化功能这里直接添加了一个订阅用户。
客户端
public class TestAnonymous {
public static void main(String[] args) {
AbstractXMPPConnection connection = SesseionHelper.newConn("192.168.149.214", 5223, "domain");
try {
connection.login();//匿名登录
connection.addAsyncStanzaListener(new StanzaListener() {
@Override
public void processPacket(Stanza packet) throws NotConnectedException {
System.out.println((new Date()).toString()+ ":" + packet.toXML());
}
}, new StanzaFilter() {
@Override
public boolean accept(Stanza stanza) {
return stanza instanceof Message;
}
});
} catch (XMPPException | SmackException | IOException e) {
e.printStackTrace();
}
while (true) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
客户端代码启动一个xmpp连接,然后登录到服务器,同时订阅消息,将收到的消息print出来。
整个过程就完成了。
在Openfire上弄一个简单的推送系统的更多相关文章
- 用C写一个简单的推箱子游戏(二)
下面接着上一篇随笔<用C写一个简单的推箱子游戏(一)>来写 tuidong()函数是用来判断游戏人物前方情况的函数,是推箱子游戏中非常重要的一个函数,下面从它开始继续介绍推箱子的小程序怎么 ...
- 用C写一个简单的推箱子游戏(一)
我现在在读大二,我们有一门课程叫<操作系统>,课程考查要求我们可以写一段程序或者写Windows.iOS.Mac的发展历程.后面我结合网上的资料参考,就想用自己之前简单学过的C写一关的推箱 ...
- 使用SignalR ASP.NET Core来简单实现一个后台实时推送数据给Echarts展示图表的功能
什么是 SignalR ASP.NET Core ASP.NET Core SignalR 是一种开放源代码库,可简化将实时 web 功能添加到应用程序的功能. 实时 web 功能使服务器端代码可以立 ...
- 用Pomelo 搭建一个简易的推送平台
前言 实际上,个人感觉,pomelo 目前提供的两个默认sioconnector和hybridconnector 使用的协议并不适合用于做手机推送平台,在pomelo的一份公开ppt里面,有提到过, ...
- Android高效率编码-第三方SDK详解系列(二)——Bmob后端云开发,实现登录注册,更改资料,修改密码,邮箱验证,上传,下载,推送消息,缩略图加载等功能
Android高效率编码-第三方SDK详解系列(二)--Bmob后端云开发,实现登录注册,更改资料,修改密码,邮箱验证,上传,下载,推送消息,缩略图加载等功能 我的本意是第二篇写Mob的shareSD ...
- 【译】建立属于你的个人高效系统——效率专家 Mike Vardy 教你如何设置一个简单的个人高效系统
原文:http://mux.baidu.com/?p=5300 百度MUX 已经有太多的高效系统供人使用,而对于那些刚刚开始,想寻求更好方法完成他们任务,项目,目标的人来说,要做一个高效系统却是相当艰 ...
- 最简单的推送--uexGetui
个推插件使用指南 配置方法这里不再复述,详情请参见插件接入指引 怎样创建一个最简单的推送? //只需要两个方法 uexGetui.initialize(data); uexGetui.onInitia ...
- 基于page的简单页面推送技术
我们可以先看下简单效果,打开2个页面可以看到推送效果 服务端我们只需要下面一个方法 using System; using System.Collections.Generic; using Syst ...
- MPush开源消息推送系统:简洁、安全、支持集群
引言由于之前自己团队需要一个消息推送系统来替换JPUSH,一直找了很久基本没有真正可用的开源系统所有就直接造了个轮子,造轮子的时候就奔着开源做打算的,只是后来创业项目失败一直没时间整理这一套代码,最近 ...
随机推荐
- Java多线程
一:进程与线程 概述:几乎任何的操作系统都支持运行多个任务,通常一个任务就是一个程序,而一个程序就是一个进程.当一个进程运行时,内部可能包括多个顺序执行流,每个顺序执行流就是一个线程. 进程:进程 ...
- 【原】nodejs全局安装和本地安装的区别
来微信支付有2年多了,从2年前的互联网模式转变为O2O模式,主要的场景是跟线下的商户去打交道,不像以往的互联网模式,有产品经理提需求,我们帮忙去解决问题. 转型后是这样的,团队成员更多需要去寻找业务的 ...
- CSS HTML元素布局及Display属性
本篇文章主要介绍HTML的内联元素.块级元素的分类与布局,以及dispaly属性对布局的影响. 目录 1. HTML 元素分类:介绍内联元素.块级元素的分类. 2. HTML 元素布局:介绍内联元素. ...
- JavaScript Math和Number对象
目录 1. Math 对象:数学对象,提供对数据的数学计算.如:获取绝对值.向上取整等.无构造函数,无法被初始化,只提供静态属性和方法. 2. Number 对象 :Js中提供数字的对象.包含整数.浮 ...
- javascript中的继承与深度拷贝
前言 本篇适合前端新人,下面开始...... 对于前端新手来说(比如博主),每当对js的对象做操作时,都是一种痛苦,原因就是在于对象的赋值是引用的传递,并非值的传递,虽然看上去后者赋值给了前者,他们就 ...
- C#数组,List,Dictionary的相互转换
本篇文章会向大家实例讲述以下内容: 将数组转换为List 将List转换为数组 将数组转换为Dictionary 将Dictionary 转换为数组 将List转换为Dictionary 将Dicti ...
- [C#] C# 知识回顾 - 特性 Attribute
C# 知识回顾 - 特性 Attribute [博主]反骨仔 [原文地址]http://www.cnblogs.com/liqingwen/p/5911289.html 目录 特性简介 使用特性 特性 ...
- Python自然语言处理工具小结
Python自然语言处理工具小结 作者:白宁超 2016年11月21日21:45:26 目录 [Python NLP]干货!详述Python NLTK下如何使用stanford NLP工具包(1) [ ...
- 微信小程序(微信应用号)组件讲解
这篇文章主要讲解微信小程序的组件. 首先,讲解新建项目.现在有句话:招聘三天以上微信小程序开发,这个估计只能去挖微信的工程师了.技术新,既然讲解,那我们就从开始建项目讲解. 打开微信web开发者工具, ...
- 基于Composer Player 模型加载和相关属性设置
主要是基于达索软件Composer Player.的基础上做些二次开发. public class ComposerToolBarSetting { public bool AntiAliasingO ...