openfire批量推送

今天分享一个openfire批量推送的插件,上次我们做的插件只是适合小范围推送,这次我们做一个给全部“在线”的用户推送消息的插件,只有在线用户才会收到推送消息,离线的用户不会收到消息(不会收到离线消息),如果同一账号使用多个终端登录那么多个终端都会收到推送消息。当然也可以把这个插件稍作修改就变成一个批量推送的插件,不一定要给全服的用户推送消息。


由于这次考虑到操作的用户量比较大,所以采用比较传统的方式

1. 推送的信息首先保存到数据库

2. checkThread以轮训的方式检查数据库是否有需要推送的消息

3. 有消息后创建Runnable对象并且扔到threadpool内执行




插件实现起来并不能,甚至是有一点简单,主要是如何获取数据库内的用户和如何检查用户是否在线,这两点了解的剩下的就简单了。


1. 检查是否有需要推送的消息


CheckThread.java

个人觉得这部分是写的最失败的地方,采用的是原始的数据库轮训的机制,如果是生产环境是可以使用像redis、activeMQ或者其他的方式来实现

package com.xxdd.openfire.batchpush.plugin.thread;

import java.util.List;

import org.xmpp.component.ComponentManager;
import org.xmpp.component.ComponentManagerFactory;

import com.xxdd.openfire.batchpush.plugin.model.OFBatchPush;
import com.xxdd.openfire.batchpush.plugin.model.OFUser;
import com.xxdd.openfire.batchpush.plugin.provider.OFBatchPushProvider;
import com.xxdd.openfire.batchpush.plugin.provider.OFUserProvider;

public class CheckThread extends Thread {

	ComponentManager componentManager = ComponentManagerFactory.getComponentManager();
	
	public void run() {
		while (ThreadPool.RUNNING) {
			OFBatchPush batPush = OFBatchPushProvider.getInstance().getPushContent();
			if (batPush == null) {//没有需要推送的消息
				try {
					Thread.sleep(3000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			} else {//有需要推送的消息
				//检查数据库内用户的数量
				int count = OFUserProvider.getInstance().getUserCount();
				int len = 20;
				int maxPage = (int) Math.ceil((float) count / (float) len);
				for (int i=0; i<maxPage; i++) {
					//分页查询用户
					List<OFUser> users = OFUserProvider.getInstance().findUsers(i*len, len);
					if (users != null && users.size() > 0) {
						for (OFUser user : users) {
							//把要发送的消息内容和消息的接收人组装成Runnable对象
							PushRunnable runnable = new PushRunnable(
									user.getUsername(),
									componentManager,
									batPush.getBody()
									);
							//把runnable对象放到线程池内执行
							ThreadPool.execute(runnable);
						}
					}
				}
				//最后发送完成,在数据库内删除消息内容
				OFBatchPushProvider.getInstance().delPush(batPush.getId());
			}
		}
	}
}


OFBatchPushProvider.java

package com.xxdd.openfire.batchpush.plugin.provider;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.jivesoftware.database.DbConnectionManager;

import com.xxdd.openfire.batchpush.plugin.model.OFBatchPush;

public class OFBatchPushProvider {

	private static OFBatchPushProvider self;
	
	private OFBatchPushProvider() {
		
	}
	
	public static OFBatchPushProvider getInstance() {
		if (self == null)
			self = new OFBatchPushProvider();
		return self;
	}
	
	/**
	 * 获得需要推送的内容
	 * @return
	 */
	public OFBatchPush getPushContent() {
		String loadPushSQL = "SELECT id, body, create_time FROM ofBatchpush ORDER BY create_time LIMIT 1";
		Connection con = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        
        
        try {
			con = DbConnectionManager.getConnection();
			pstmt = con.prepareStatement(loadPushSQL);
			
			rs = pstmt.executeQuery();
			if (rs.next()) {
				OFBatchPush batPush = new OFBatchPush();
				batPush.setId(rs.getInt(1));
				batPush.setBody(rs.getString(2));
				batPush.setCreateTime(rs.getDate(3));
				return batPush;
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			DbConnectionManager.closeConnection(rs, pstmt, con);
		}
        return null;
	}
	
	/**
	 *  删除推送消息内容
	 * @param id
	 */
	public void delPush(int id) {
		String delPushSQL = "DELETE FROM ofBatchpush WHERE id = ?";
		Connection con = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        
        try {
			con = DbConnectionManager.getConnection();
			pstmt = con.prepareStatement(delPushSQL);
			pstmt.setInt(1,id);
			
			pstmt.executeUpdate();
			
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			DbConnectionManager.closeConnection(rs, pstmt, con);
		}
	}
	
}

OFBatchPush.java

package com.xxdd.openfire.batchpush.plugin.model;

import java.util.Date;

public class OFBatchPush {

	private int id;
	private String body;
	private Date createTime;
	
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getBody() {
		return body;
	}
	public void setBody(String body) {
		this.body = body;
	}
	public Date getCreateTime() {
		return createTime;
	}
	public void setCreateTime(Date createTime) {
		this.createTime = createTime;
	}
}


OFUserProvider.java

package com.xxdd.openfire.batchpush.plugin.provider;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import org.jivesoftware.database.DbConnectionManager;

import com.xxdd.openfire.batchpush.plugin.model.OFUser;

/**
 * 
 * @author xxdd
 */
public class OFUserProvider {

	private static OFUserProvider self = null;
	
	private OFUserProvider() {
		
	}
	
	public static OFUserProvider getInstance() {
		if (self == null)
			self = new OFUserProvider();
		return self;
	}
	
	/**
	 * 分页查询用户
	 * @param offset
	 * @param len
	 * @return
	 */
	public List<OFUser> findUsers(int offset, int len) {
		
		String loadUsersSQL = "SELECT username FROM ofUser LIMIT ?,?";
		Connection con = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        
        List<OFUser> users = new ArrayList<OFUser>(len);
        try {
			con = DbConnectionManager.getConnection();
			pstmt = con.prepareStatement(loadUsersSQL);
			pstmt.setInt(1, offset);
			pstmt.setInt(2, len);
			
			rs = pstmt.executeQuery();
			while (rs.next()) {
				OFUser user = new OFUser(rs.getString(1));
				users.add(user);
			}

		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			DbConnectionManager.closeConnection(rs, pstmt, con);
		}
        return users;
	}
	
	/**
	 * 获得用户总量
	 * @return
	 */
	public int getUserCount() {
		
		String loadUserCountSQL = "SELECT COUNT(1) FROM ofUser";
		Connection con = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        
        int count = 0;
        try {
			con = DbConnectionManager.getConnection();
			pstmt = con.prepareStatement(loadUserCountSQL);
			
			rs = pstmt.executeQuery();
			rs.next();
			count = rs.getInt(1);

		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			DbConnectionManager.closeConnection(rs, pstmt, con);
		}
        return count;
	}
	
}

OFUser.java

package com.xxdd.openfire.batchpush.plugin.model;

public class OFUser {

	private String username;
	
	public OFUser() {
		
	}
	
	public OFUser(String username) {
		this.username = username;
	}
	
	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}
}



2. 线程池

使用jdk自带的executor框架,创建一个简单的线程池,网上已经有很多的例子了,这里就不啰嗦了

package com.xxdd.openfire.batchpush.plugin.thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPool {


	private static ExecutorService pool = null;
	
	private static int minThread = 5;
	private static int maxThread = 20;
	
	private static LinkedBlockingDeque<Runnable> queue = new LinkedBlockingDeque<Runnable>(100);
	
	public static boolean RUNNING = true;
	
	static {
		pool = new ThreadPoolExecutor(
				minThread, 
				maxThread,
                60L, 
                TimeUnit.SECONDS,
                queue,
                new ThreadPoolExecutor.CallerRunsPolicy()
			);
		RUNNING = true;
	}
	
	
	public static void execute(Runnable runnable) {
		if (RUNNING) {
			pool.execute(runnable);
		}
	}
	
	public static void shutDown() {
		RUNNING = false;
		pool.shutdownNow();
	}
}

3. 发送线程

这部分代码不叫关键,是整个推送的核心部分

package com.xxdd.openfire.batchpush.plugin.thread;

import java.util.Collection;

import org.jivesoftware.openfire.SessionManager;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.session.ClientSession;
import org.xmpp.component.ComponentException;
import org.xmpp.component.ComponentManager;
import org.xmpp.packet.JID;
import org.xmpp.packet.Message;
import org.xmpp.packet.Message.Type;

public class PushRunnable implements Runnable {

	SessionManager sessionManager = SessionManager.getInstance();
	
	String toUsername;
	ComponentManager componentManager;
	String body;
	
	private static final String from = "system";
	private static final String domain = XMPPServer.getInstance().getServerInfo().getXMPPDomain();
	
	public PushRunnable(
			String toUsername,
			ComponentManager componentManager,
			String body) {
		this.toUsername = toUsername;
		this.componentManager = componentManager;
		this.body = body;
	}
	
	@Override
	public void run() {
		//获得在线用户session
		Collection<ClientSession> sessions = sessionManager.getSessions(toUsername);
		if (sessions != null && sessions.size() > 0) {//存在在线用户的session
			Message message = new Message();
			message.setBody(body);
			message.setFrom(from + "@" + domain);
			message.setTo(toUsername + "@" + domain);
			message.setType(Type.normal);
			
			//给单个用户的所有登录的客户端发送消息
			for (ClientSession session : sessions) {
				JID jid = session.getAddress();
				Message cpMessage = message.createCopy();
				cpMessage.setTo(jid);
				try {
					//给在线用户推送消息
					componentManager.sendPacket(null, message);
				} catch (ComponentException e) {
					e.printStackTrace();
				}
			}
		}
	}

}





完整代码下载地址:http://download.csdn.net/detail/kaixinwoo5/7579365


有问题欢迎邮件讨论7325356@qq.com




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值