在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,一直找了很久基本没有真正可用的开源系统所有就直接造了个轮子,造轮子的时候就奔着开源做打算的,只是后来创业项目失败一直没时间整理这一套代码,最近 ...
随机推荐
- 日期格式代码出现两次的错误 ORA-01810
错误的原因是使用了两次MM . 一.Oracle中使用to_date()时格式化日期需要注意格式码 如:select to_date('2005-01-01 11:11:21','yyyy-MM-dd ...
- 网页提交中文到WEB容器的经历了些什么过程....
先准备一个网页 <html><meta http-equiv="Content-Type" content="text/html; charset=gb ...
- RecyclerView使用大全
RecylerView介绍 RecylerView是support-v7包中的新组件,是一个强大的滑动组件,与经典的ListView相比,同样拥有item回收复用的功能,这一点从它的名字recyler ...
- Android 判断一个 View 是否可见 getLocalVisibleRect(rect) 与 getGlobalVisibleRect(rect)
Android 判断一个 View 是否可见 getLocalVisibleRect(rect) 与 getGlobalVisibleRect(rect) [TOC] 这两个方法的区别 View.ge ...
- 在Ubuntu 16.10 安装 git 并上传代码至 git.oschina.net
1. 注册一个账号和创建项目 先在git.oschina.net上注册一个账号和新建一个project ,如project name 是"myTest". 2.安装git sudo ...
- HTML块级元素
前面的话 在HTML5出现之前,人们一般把元素分为块级.内联和内联块元素.本文将详细介绍HTML块级元素 h 标题(Heading)元素有六个不同的级别,<h1>是最高级的,而&l ...
- UE4新手引导入门教程
请大家去这个地址下载:file:///D:/UE4%20Doc/虚幻4新手引导入门教程.pdf
- 在jekyll模板博客中添加网易云模块
最近使用GitHub Pages + Jekyll 搭建了个人博客,作为一名重度音乐患者,博客里面可以不配图,但是不能不配音乐啊. 遂在博客里面引入了网易云模块,这里要感谢网易云的分享机制,对开发者非 ...
- 《LoadRunner12七天速成宝典》来了
看到自己的新书又要发行了,算算从09年第一本书开始,不知不觉已经是第四本书了(帮朋友合写的书不算),每次写完之后都会说太累了,不想再写了,但是却又次次反悔,吞下食言的苦果.如果非要说第四本书的感受,那 ...
- 以向VS 程序打包集成自动写入注册表功能为例,介绍如何实现自由控制安装过程
最近由于项目部署时需要更灵活的控制程序安装的流程以及自定义安装行为,特意研究了一下VS程序打包,把解决办法和大家分享一下. 以VS2010为例: 这是一个已经设置好最基本的Visual Studio ...