采用DWR推送技术+消息队列,前端通过jquery 追加消息。具体实现细节如下:
部分源码:
package com.qunyiinfo.chat.common.task;
import java.util.Collection;
import java.util.LinkedList;
import java.util.concurrent.locks.ReentrantLock;
import org.directwebremoting.ScriptBuffer;
import org.directwebremoting.ScriptSession;
import org.directwebremoting.WebContext;
import org.directwebremoting.WebContextFactory;
import org.directwebremoting.proxy.dwr.Util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import com.qunyiinfo.chat.service.IChatService;
import com.qunyiinfo.chat.web.form.MsgFrm;
/**
* 类PushMessageTask.java的实现描述:TODO 类实现描述
*伦理电影www.akdy.cn
* @author shengshang.tang 2014年6月24日 上午10:10:29
*/
@Component(value = "pushMessageTask")
public class PushMessageTask {
private static ReentrantLock lock = new ReentrantLock();
private volatile Boolean readyState = false;
private WebContext contex = null;
@Autowired
private IChatService chatService;
public void ready() {
contex = WebContextFactory.get();
readyState = true;
}
/**
* 推送消息
* <p>
* 每5分钟扫描一次
*/
@Scheduled(cron = "0/10 * * * * *")
public void pushMessage() {
lock.lock();
try {
if (!readyState) { // 还么有准备好
return;
}
Collection<ScriptSession> sessions = contex.getScriptSessionsByPage("/chat/enterChat.htm");
Util util = new Util(sessions);
ScriptBuffer sb = new ScriptBuffer();
// 获得聊天记录
LinkedList<MsgFrm> mll = chatService.getMsgList();
// 获得在线用户
Collection<String> personList = chatService.getPersonList();
sb.appendScript("showMsgList(");
sb.appendData(mll);
sb.appendScript(");");
sb.appendScript("showPersonList(");
sb.appendData(personList);
// 推送
util.addScript(sb);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
package com.qunyiinfo.chat.service.impl;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.LinkedBlockingDeque;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Service;
import com.apache.platform.common.ServeltContextManager;
import com.apache.platform.common.ServeltContextManager.ServeltContext;
import com.apache.platform.service.AbstractBaseService;
消息队列实现
* 类ChatServiceImpl.java的实现描述:TODO 类实现描述
*
* @author shengshang.tang 2014年6月23日 下午2:50:07
@Service
public class ChatServiceImpl extends AbstractBaseService implements IChatService {
private static LinkedBlockingDeque<MsgFrm> stList = new LinkedBlockingDeque<MsgFrm>(100);
private static LinkedBlockingDeque<MsgFrm> stHisList = new LinkedBlockingDeque<MsgFrm>(100);
private static ConcurrentMap<String,String> personMap = new ConcurrentHashMap<String,String>();
@Override
public Boolean join(HttpSession session,String username) {
String sid = session.getId();
if (personMap.containsValue(username)) {
return false;
} else {
personMap.put(sid,username);
session.setAttribute(sid,sans-serif; font-size:14px; line-height:25.1875px"> return true;
public Collection<String> getPersonList() {
return personMap.values();
public void removeUserBySid(String sid) {
if (personMap.containsKey(sid)) {
personMap.remove(sid);
public void sendMsg(String username,String content,String styleClass) {
MsgFrm msgFrm = new MsgFrm(username,content,styleClass);
stList.add(msgFrm);
public LinkedList<MsgFrm> getMsgList() {
int chatMsgSize = 50;
// 申明一个输出队列
LinkedList<MsgFrm> ll = new LinkedList<MsgFrm>();
int curCount = 0;
for (int i = 0; i < chatMsgSize && !stList.isEmpty(); i++) {
MsgFrm msgFrm = stList.removeLast();
stHisList.addFirst(msgFrm);
ll.addFirst(msgFrm);
curCount++;
// 新的队列中记录还有剩余,则放到老队列记录中
while (!stList.isEmpty()) {
if (ll.size() < chatMsgSize) {
MsgFrm[] data = stHisList.toArray(new MsgFrm[] {});
int stepLen = chatMsgSize - ll.size();
for (int i = 0; i < stepLen && (i + curCount) < data.length; i++) {
MsgFrm content = data[i + curCount];
ll.addFirst(content);
return ll;
public void removeExpiresUser() {
ServeltContext sc = ServeltContextManager.get();
if (sc == null) {
return;
HttpSession session = sc.getRequest().getSession();
Set<Entry<String,String>> set = personMap.entrySet();
for (Entry<String,String> entry : set) {
String sid = entry.getKey();
if (session.getAttribute(sid) == null) { // 已经过期
personMap.remove(sid); // 移除
前端JS+HTML
chat.js
$(document).ready(function() {
$('#myTab a').click(function(e) {
e.preventDefault();
$(this).tab('show');
})
//进入推送消息
pushMsgTask.pushMessage();
// 发送
$("#send").click(function() {
// 加入
if (!$("#editForm").validate(vSettings)) {
return;
$.post("sendMsg.htm",{
msg : $("#msg").val()
},function(data,textStatus) {
if (data == "1") {
// 清空当前发送区域消息
$("#msg").val("");
// 手动调用消息刷新
});
$(document).keypress(function(e) {
if (e.ctrlKey && e.which == 13 || e.which == 10) { // ctrl +回车 发送
$("#send").trigger("click");
// 设置dwr推送技术
dwr.engine.setActiveReverseAjax(true);
// 开始推送准备
pushMsgTask.ready();
// 校验
vSettings = {
rules : {
msg : {
required : true,
maxLength : 50
messages : {
required : "不能发送空消息",sans-serif; font-size:14px; line-height:25.1875px"> maxLength : "消息不能超过50个字符"
};
function showMsgList(data) {
var array = new Array();
for (var i = 0; i < data.length; i++) {
var msg = data[i];
array.push('<div class="chat_content_group">');
array.push('<p class="chat_nick">'+msg.username+'</p>');
array.push('<p class="chat_content">');
array.push(msg.content);
array.push('</p>');
array.push("</div>");
$("#content").html(array.join(""));
function showPersonList(data) {
array.push("<p>");
array.push(msg);
array.push("</p>");
$("#pl").html(array.join(""));
$("#content").scrollTop($("#content")[0].scrollHeight);
msg.vm
<div class="main_container">
<div id="content" class="main_chat">
</div>
<div class="person_list" >
<div><h3>聊天成员</h3></div>
<div id="pl">
</div>
<div id="buttom">
<div class="txt_content">
<textarea id="msg" name="msg" style="width:100%;height:60px"></textarea>
<div class="btn_send">
<input type="button" value="发送" class="btn btn_save" id="send" />
</div>
原文链接:https://www.f2er.com/ajax/164739.html