`
strongant
  • 浏览: 66885 次
  • 性别: Icon_minigender_1
  • 来自: IT
社区版块
存档分类
最新评论

Ajax+Servlet3实现异步短连接消息推送

阅读更多

1. 实现原理

当主页加载完毕时候,一个ajax(jquery)请求到servlet端, servlet启动异步处理上下文AsyncContext,然后请求一直等待,直到上下文AsyncContext调用complete()方法,或者这个请求timeout,这个请求才会返回到UI

,这样一次连接就结束。请求返回到UI后,ajax马上又发送连接请求到Servlet,这样反反复复进行这个操作

 

2. 实现代码

Servlet端实现

 

//需要导入该类包名
import java.io.IOException;

import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

@WebServlet(urlPatterns = { "/servlet/asyn" }, asyncSupported = true)
public class AsynServlet extends HttpServlet{
	
	private static final long serialVersionUID = 1L;
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setCharacterEncoding("UTF-8");
		String timeoutStr = request.getParameter("timeout");
		long timeout;
		if (StringUtils.isNumeric(timeoutStr)) {
			timeout = Long.parseLong(timeoutStr);
		} else {
			// 设置10分钟
			timeout = 10 * 60 * 1000;
		}
		
		final HttpServletResponse finalResponse = response;
		final AsyncContext ac = request.startAsync(request, finalResponse);
		// 设置成长久链接
		ac.setTimeout(timeout);
		ac.addListener(new AsyncListener() {
			public void onComplete(AsyncEvent event) throws IOException {
				log.info("onComplete Event!");
				
				ServletService.getInstance().removeAsyncContext(ac);
			}

			public void onTimeout(AsyncEvent event) throws IOException {
				log.info("onTimeout Event!");
				
				ServletService.getInstance().removeAsyncContext(ac);
				ac.complete();
			}
			public void onError(AsyncEvent event) throws IOException {
				
				ServletService.getInstance().removeAsyncContext(ac);
				ac.complete();
			}

			public void onStartAsync(AsyncEvent event) throws IOException {
				log.info("onStartAsync Event!");
			}
		});

		ServletService.getInstance().addAsyncContext(ac);
	}
}
发消息业务方法
//需要导入该类包名

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;

import javax.servlet.AsyncContext;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class ServletService {

	
	//异步Servlet上下文队列.
	private final Map<Integer, AsyncContext> ASYNC_CONTEXT_MAP = new ConcurrentHashMap<Integer, AsyncContext>();

	//消息队列.
	private final BlockingQueue<TextMessage> TEXT_MESSAGE_QUEUE = new LinkedBlockingQueue<TextMessage>();

	//单一实例.
	private static ServletService instance = new ServletService();

	//构造函数,创建发送消息的异步线程.
	private ServletService() {
		new Thread(this.notifierRunnable).start();//线程发发消息给多个用户
	}

	//单一实例.
	public static ServletService getInstance() {
		return instance;
	}

	/**
	 * 
	 * 注册异步Servlet上下文.
	 * 
	 * @param asyncContext
	 *            异步Servlet上下文.
	 */
	public void addAsyncContext(final AsyncContext asyncContext) {
		HttpServletRequest req = (HttpServletRequest) asyncContext.getRequest();
		User user = (User) req.getSession().getAttribute("loginuser");
		if (null!=user) {
			ASYNC_CONTEXT_MAP.put(user.getId(), asyncContext);
		}
	}

	/**
	 * 
	 * 删除异步Servlet上下文.
	 * 
	 * @param asyncContext
	 *            异步Servlet上下文.
	 */
	public void removeAsyncContext(final AsyncContext asyncContext) {

		HttpServletRequest req = (HttpServletRequest) asyncContext.getRequest();
		User user = (User) req.getSession().getAttribute("loginuser");
		if (null!=user) {
			ASYNC_CONTEXT_MAP.remove(user.getId());
		}

	}

	/**
	 * 
	 * 发送消息到异步线程,最终输出到http response 流 .
	 * 
	 * @param text 发送给客户端的消息.
	 * 
	 */
	public void putMessage(final int userId, final String text) {

		try {
			TextMessage tm = new TextMessage(userId, text);
			TEXT_MESSAGE_QUEUE.add(tm);

		} catch (Exception ex) {
			throw new RuntimeException(ex);
		}

	}
	
	public void putMessage(final TextMessage tm) {
		try {
			TEXT_MESSAGE_QUEUE.add(tm);
		} 
		catch (Exception ex) {
			throw new RuntimeException(ex);
		}
	}
	
	public boolean pushMessage(final TextMessage tm) {
		boolean result = false;
		AsyncContext ac = ASYNC_CONTEXT_MAP.get(tm.getUserId());
		try {
			if (null != ac) {
				write(ac, tm.getText());
				result = true;
			}
		} catch (Exception e) {
			ASYNC_CONTEXT_MAP.remove(tm.getUserId());
			log.info(e);
		}
		
		return result;
	}

	/**
	 * 
	 * 异步线程,当消息队列中被放入数据,将释放take方法的阻塞,将数据发送到http response流上.
	 * 该方法暂时没用,用于并发测试
	 */
	private Runnable notifierRunnable = new Runnable() {

		public void run() {

			boolean done = false;
			while (!done) {
				try {
					final TextMessage tm = TEXT_MESSAGE_QUEUE.take();//当消息队列没有数据时候,线程执行到这里就会被阻塞
					if (tm.getUserId()==0) {//发送给所有人
						for (Entry<Integer, AsyncContext> entry : ASYNC_CONTEXT_MAP.entrySet()) {
							try {
								write(entry.getValue(), tm.getText());
							} catch (IOException ex) {
								log.info(ex);
							}
						}
					}
					else {
						pushMessage(tm);
					}
					
					Thread.sleep(100);//暂停100ms,停止的这段时间让用户有足够时间连接到服务器
					
				} catch (InterruptedException iex) {
					done = true;
					log.info(iex);
				}
				
			}
		}
	};
	
	private void write(AsyncContext ac, String text) throws IOException {
		PrintWriter acWriter = ac.getResponse().getWriter();

		acWriter.write(text);

		acWriter.flush();
		
		acWriter.close();
		
		ac.complete();
		
	}

}
 发消息实体对象
public final class TextMessage {

	private final int userId;
	private final String text;
	
	public TextMessage(final int userId, final String text) {
		super();
		this.userId = userId;
		this.text = text;
	}
	public int getUserId() {
		return userId;
	}
	public String getText() {
		return text;
	}
}
 

3. 前端js实现(重点)

主页要引入下面脚本代码,当主页加载完毕后,就会发送ajax请求到servet/asyn.do,然后就建立连接等待servlet返回消息到主页,返回到主页调用showMessage,这个方法调用ext组件弹出消息内容

initServlet();
function initServlet() {
	$.ajax({
		url:'servlet/asyn',
		type:'get',
		dataType:'text',
		contentType: "text/plain; charset=UTF-8", 
		timeout:600000,
		success: function(msg){
			if (msg!=null && msg!='') {//这个返回值要改成json对象
				showMessage(msg);
			}
			callSelf();//把这个方法里面的代码放在这里,IE8会一直死循环的调用(其它浏览器不会),不知道什么原因
		}
	});
}
function callSelf() {
	initServlet();
}
function showMessage(data) {
	Ext.create('widget.uxNotification', {
		title: '消息提醒',
		position: 'br',
		cls: 'ux-notification-light',
		iconCls: 'ux-notification-icon-information',
		html: data,
		width: 200,
		height: 90,
		autoCloseDelay: 4000,
		slideBackDuration: 500,
		slideInAnimation: 'bounceOut',
		slideBackAnimation: 'easeIn'
	}).show();
}
//js获取项目根路径,如: localhost:8080/test
function getRootPath(){  
    //获取当前网址,如: http://localhost:8080/test/test.jsp
    var curWwwPath=window.document.location.href;  
    //获取主机地址之后的目录,如: test/test.jsp
    var pathName=window.document.location.pathname;  
    var pos=curWwwPath.indexOf(pathName);  
    //获取主机地址,如: http://localhost:8080
    var localhostPaht=curWwwPath.substring(0,pos);
    var wsPath = localhostPaht.replace('http://','');
    //获取带"/"的项目名,如:/test
    var projectName=pathName.substring(0,pathName.substr(1).indexOf('/')+1);  
    return(wsPath+projectName);  
}  

 

前端要引用ext右下角弹出消息组件:

Notification extension for Ext JS 4.0.2+

Version: 2.1.3

从这里下载

https://github.com/EirikLorentsen/Ext.ux.window.Notification

 

 

分享到:
评论

相关推荐

    AJAX+Servlet开发案例-聊天室源代码

    WebSocket则建立持久连接,允许服务器主动推送新消息给客户端。 在提供的"ChatRoom"源代码中,你可以看到这些概念的具体实现,包括Servlet类、HTML页面和JavaScript脚本。通过分析和运行这个案例,你不仅可以了解...

    ajax+"服务器推送"简单web聊天

    3. 在服务器端,JSP接收到消息后进行处理,可以将消息保存到数据库或队列中,并触发服务器推送。 4. 实现服务器推送,可以通过Comet技术(如长轮询)或者WebSocket。在这个例子中,考虑到标签提及了“jsp聊天”,...

    servlet 3.0 异步 推送 聊天室

    Servlet 3.0的异步推送(也称为Comet技术)通过在服务器端维持一个打开的连接,可以在有新消息时立即推送到客户端,提高了用户体验。 在这个聊天室示例中,"application.js"文件可能是用于处理前端交互逻辑,如接收...

    Servlet+MySQL实现登录功能.zip

    这部分可能涉及到WebSocket或AJAX异步通信,或者使用其他服务器推送技术。 8. **部署与运行**:项目完成后,需要将所有依赖和Servlet部署到支持Java EE的Web服务器上,如Tomcat或Jetty。用户需要设置好服务器环境,...

    jsp+servlet实现的图书管理系统

    例如,当用户发送消息时,Ajax调用后台Servlet处理消息,并将回复推送给其他在线用户。 6. **文件上传下载**:系统可能包含文件上传功能,如用户上传书籍封面图片,或者管理员上传电子书。Servlet会处理文件上传...

    ajax-demo推送演示例子

    综上所述,"ajax-demo推送演示例子"涉及到Ajax技术与Servlet服务器端推送的结合,通过长轮询实现页面实时更新的效果。开发者可以通过理解Ajax的工作机制,以及如何在服务器端实现推送,来构建类似的交互式Web应用。

    servlet3.0推送聊天室

    本文将深入探讨如何利用Servlet 3.0的异步功能来构建一个推送信息至客户端的聊天室。 首先,我们需要了解Servlet 3.0的异步处理。在Servlet 2.x版本中,每次HTTP请求都会绑定到一个线程,直到请求处理完成。这种...

    dws 消息推送Demo

    这些消息可能存储在一个消息队列中,当新的消息到来时,Java后端会将消息推送到连接的客户端。 JavaScript在前端起着接收和展示消息的作用。DWR提供了一种机制,使得JavaScript可以注册监听器,一旦后端有新的消息...

    用AJAX+J2EE实现一个网上会议室系统

    当用户发送消息时,后台接收并处理,然后通过AJAX回调将新消息推送到用户的浏览器,而无需刷新整个页面。 2. **动态更新**:例如,当有人加入或离开会议室时,用户界面可以即时显示这些变化,无需用户手动刷新。 3...

    ajax+jsp聊天室

    2. **实时接收**:服务器端接收到消息后,通过Ajax技术将新消息推送到客户端,更新聊天窗口,保持聊天的实时性。 3. **异步处理**:在用户查看或发送消息时,后台可以继续处理其他任务,如处理其他用户的消息,提高...

    毕设课设-微博系统java博客系统jsp+servlet聊天软件.zip

    在Java中实现,可能需要设计用户接口、消息发布、关注/粉丝机制、消息推送等功能。这涉及到复杂的前后端交互和可能的实时更新技术,如WebSocket。 【聊天软件】 在Java Web环境中,聊天软件可能通过AJAX或者...

    Java Dwr实现消息精准推送以及js调用后台+demo

    在“Java Dwr实现消息精准推送以及js调用后台+demo”这个主题中,我们将深入探讨如何利用DWR来实现高效的消息推送和JavaScript与后台Java的交互。以下是一些关键知识点: 1. **DWR基础**:DWR的核心功能是提供一种...

    DWR消息推送简单实例

    DWR(Direct Web Remoting...综上所述,这个"DWR消息推送简单实例"涵盖了DWR框架的基础使用、Maven工程的构建以及消息推送的实现。通过学习这个实例,开发者可以更好地理解如何在实际项目中利用DWR实现高效的双向通信。

    聊天室源代码(JSP+SERVLET)

    或者,更高级的技术如WebSocket也可以被用到,提供双向通信,使服务器能主动推送新消息到客户端。 7. **安全性**:考虑到聊天室涉及用户交互,安全性至关重要。应防止SQL注入、XSS攻击等,对用户输入进行校验和过滤...

    dwr服务器推送,dwr.jar,推送,服务器ajax,dwr服务推送例子

    这个过程被称为“反向Ajax”或“服务器推送”,它能够实现在不依赖用户操作的情况下,服务器主动向客户端发送数据。 DWR的核心组件包括几个部分: 1. **Engine**:这是DWR的核心,负责处理JavaScript到Java的转换和...

    servlet 3.0 聊天室 推送

    为了实现消息推送,Servlet 3.0引入了异步处理能力。传统的Servlet模型是同步的,即每个请求都需要等待完成才能处理下一个请求,这对于实时应用来说效率低下。通过使用`AsyncContext`,我们可以启动一个后台线程来...

    一个完整的用ajax反转 server push(服务器主动向页面推送数据)技术实现的web聊天室源码

    本项目是一个利用Ajax反向推送(Comet技术)实现的Web聊天室源码,通过Java语言的Servlet作为控制器,提供了一个完整的可运行示例。 首先,我们要理解什么是Ajax反向推送(Comet技术)。传统的Ajax请求是客户端发起...

    dwr消息推送

    DWR(Direct Web Remoting...总结来说,DWR是一种强大的工具,它利用AJAX技术实现了浏览器与服务器之间的双向通信,从而实现了消息推送功能。通过理解和熟练掌握DWR,开发者可以创建出更加互动、响应式的Web应用程序。

    多种方式模拟服务器推送客户端

    本文将详细探讨如何通过JAVA实现多种服务器推送客户端的方式,包括Ajax轮询、长连接、长轮询以及Iframe刷新。 1. **Ajax轮询** Ajax轮询是最基础的实现方式,其原理是客户端定时发送Ajax请求到服务器,服务器接收...

    基于ajax技术的聊天室实现

    为了实现实时更新,前端需要定期(如每隔几秒)通过Ajax轮询Servlet获取新消息,或者使用更先进的技术如WebSocket,实现服务器向客户端的即时推送。 4. **Servlet技术**: Servlet是Java Web应用程序的一部分,...

Global site tag (gtag.js) - Google Analytics