Web服务器推送技术介绍及Cometd使用例子

本文介绍了如何使用Cometd技术实现服务器推送,包括Ajax轮询、长轮询、套接字和HTML5 WebSocket的对比,以及如何在项目中使用Cometd进行实时数据推送,特别强调了Cometd的简单性和强大的功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传统模式的 Web 系统以客户端发出请求、服务器端响应的方式工作。不能满足很多现实应用的需求,譬如:

监控系统:后台硬件温度、电压发生变化;
即时通信系统:其它用户登录、发送信息;
即时报价系统:后台数据库内容发生变化;
即时信息系统:微博、说说实时推送

    目前主流的是采取如下几种方式来实现以上需求:

    Ajax轮询:异步响应机制,即通过不间断的客户端 Ajax 请求,去发现服务端的变化。这种方式由于是客户端主动连接的,所以会有一定程度的延时,并且服务器的压力也不小。

    长轮询:原理是客户端发出一个http长连接请求,然后等待服务器的响应,服务器接到请求之后,并不立即发送出数据,而是hold住这个Connecton。这个处理是非阻塞的,所以服务器可以继续处理其他请求。在某个时刻,比如服务器有新数据了,服务器再主动把这个消息推送出去,即通过之前建立好的连接将数据推送给客户端。客户端收到返回。这个时候就可以处理数据,然后再次发起新的长连接。服务器压力一般,实时性很高。Servlet 3.0开始已经支持该技术。sina微博就是使用的原生Servlet 3实现的消息推送。

    套接字:可以利用 Flash 的 XMLSocket 类或者 Java 的 Applet 来建立 Socket 连接,实现全双工的服务器推送,然后通过 Flash 或者Applet 与 JavaScript 通信的接口来实现最终的数据推送。但是这种方式需要 Flash 或者 JVM 的支持,同样不太合适于终端用户。

    HTML5的WebSocket:这种方式其实与套接字一样,但是这里需要单独强调一下:它是不需要用户而外安装任何插件的。HTML5 提供了一个 WebSocket 的 JavaScript 接口,可以直接与服务端建立Socket 连接,实现全双工通信,这种方式的服务器推送就是完全意义上的服务器推送了,没有半点模拟的成分,只是现阶段支持 HTML5 的浏览器并不多,而且一般老版本的各种浏览器基本都不支持。不过 HTML5 是一套非常好的标准,在将来,当HTML5 流行起来以后将是我们实现服务器推送技术的不二选择。

    Ajax轮训压力大,套接字不适用,HTML5目前支持不大多,这样看来长轮询是我们的必然之选。 可是使用 servlet 3 实现自己要做的事很多。没有有什么可供选择的框架呢?当然是有了,如下:

 1、Cometd

http://cometd.org/

    基于Bayeux协议实现,支持长轮询、WebSocket等..

 2、Pushlet

http://www.pushlets.com/

    基于HTTP流的JSP/Semlet技术实现

 3、Atmosphere

http://atmosphere.java.net/

 4、DWR

http://directwebremoting.org/dwr/index.html

        据我的了解,使用最多的应该是 DWR 和 Cometd了,其中Cometd功能强大使用又简单。我参与的一个项目中使用Cometd 做前台地图上人员轨迹的实时推送,几百个人员的实时轨迹推送都是没问题的。用。Cometd前台有2个实现版本 Jquery 和 Dojo,也可以用JS但是会复杂点。(PS Cometd 官方文档相当使用,遇到问题就读一遍,肯定能找到你想要的。 http://docs.cometd.org/reference/)

  如果你安装Maven了,以下2个简单的命令就能让你体验一把推送效果

 1、mvn archetype:generate -DarchetypeCatalog=http://cometd.org (选择任意一个)
 2、进入刚才创建的项目目录
 3、mvn install jetty:run
 4、前台访问 http://localhost:8080/刚才创建的项目名

    下面下载包我在项目中使用的实例。实例下载,请猛击这里 传送门

        你可能想向前台传送的数据是一个类而不仅仅是字符串,那么你可以使用Jackson让cometd帮你做自动的转换,而你的Java代码中是直接向客户端输出对象。甚至是List都可以。你需要如下操作:1、向前端输出的对象必须实现 Serializable , Cloneable 和 JSON.Convertible 接口

import java.util.Date;
import java.util.Map;
import org.eclipse.jetty.util.ajax.JSON;
import org.eclipse.jetty.util.ajax.JSON.Output;
import com.yixun.util.TimeUtil;
public class Alarmrecord implements java.io.Serializable  , Cloneable, JSON.Convertible{
	// Fields
	private Long recordid;
	private Readerinfo readerinfo;
	private Cardinfo cardinfo;
	private Position position;
	private Alarminfo alarminfo;
	private String alarmdetail;
	private Date recordtime;
	private String alarmType;
	private String alarmState;
	/**
	 * @see org.eclipse.jetty.util.ajax.JSON$Convertible#fromJSON(java.util.Map)
	 * @author simon
	 * create on Apr 14, 2012  11:58:41 AM
	 */
	public void fromJSON(Map map) {
		this.recordid = (Long) map.get("recordid");
		this.readerinfo = (Readerinfo) map.get("readerinfo");
		this.cardinfo = (Cardinfo) map.get("cardinfo");
		this.position = (Position) map.get("position");
		this.alarminfo = (Alarminfo) map.get("alarminfo");
		this.alarmdetail = (String) map.get("alarmdetail");
		this.alarmType = (String) map.get("alarmType");
		this.alarmState = (String) map.get("alarmState");
		this.recordtime = (Date) map.get("recordtime");
	}

	/**
	 * @see org.eclipse.jetty.util.ajax.JSON$Convertible#toJSON(org.eclipse.jetty.util.ajax.JSON.Output)
	 * @author simon
	 * create on Apr 14, 2012  11:58:46 AM
	 */
	public void toJSON(Output out) {
		out.add("recordid", recordid);
		out.add("readerinfo", readerinfo);
		out.add("cardinfo", cardinfo);
		out.add("position", position);
		out.add("alarminfo", alarminfo);
		out.add("alarmdetail", alarmdetail);
		out.add("recordtime", TimeUtil.format(recordtime,"yyyy-MM-dd HH:mm:ss"));
		out.add("alarmType", alarmType);
		out.add("alarmState", alarmState);
	}
}



2、Web.xml 中,org.cometd.server.CometdServlet 下添加 使用Jetty json 的参数。如下图:

         <!-- 报警推送 -->
	<servlet>
		<servlet-name>cometd</servlet-name>
		<servlet-class>org.cometd.server.CometdServlet</servlet-class>
		<init-param>
			<!-- 使用jetty json 模块 -->
			<param-name>jsonContext</param-name>
			<param-value>
				org.cometd.server.JettyJSONContextServer
			</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>cometd</servlet-name>
		<url-pattern>/cometd/*</url-pattern>
	</servlet-mapping>

	<servlet>
		<servlet-name>initializer</servlet-name>
		<servlet-class>
			com.yixun.epolice.action.comet.BayeuxInitializer
		</servlet-class>
		<load-on-startup>2</load-on-startup>
	</servlet>
	<filter>
		<filter-name>cross-origin</filter-name>
		<filter-class>
			org.eclipse.jetty.servlets.CrossOriginFilter
		</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>cross-origin</filter-name>
		<url-pattern>/cometd/*</url-pattern>
	</filter-mapping>
	<filter>
		<filter-name>continuation</filter-name>
		<filter-class>
			org.eclipse.jetty.continuation.ContinuationFilter
		</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>continuation</filter-name>
		<url-pattern>/cometd/*</url-pattern>
	</filter-mapping>



3、OK,这样你后台就可以直接这样写来向前台写数据了,cometd会帮你做对象到Json的转换。

public  void update(Object o) {
		// 对 "/alarm" 频道广播消息
		if( null!= getBayeux().getChannel("/alarm") && o instanceof Alarmrecord ){
				getBayeux().getChannel("/alarm").publish(getServerSession(), (Alarmrecord)o,null);
		}
	}



4、前台这样获取数据:

if (handshake.successful === true)
            {
                cometd.batch(function()
                {
                    cometd.subscribe('/alarm', function(message)
                    {
                    	//对接收到的消息进行显示处理
                    	var alarm = message.data;
						if(len<1){ //如果初始时表中没有行
							$("#tableClass tbody").append("<tr bgcolor='#FFFFFF'>" +
							 "<td align='center'> <input type='checkbox' value='"+alarm.recordid+"'name='selecteditems'/></td>"+
	   						 "<td nowrap='nowrap' align='center'>"+alarm.cardinfo.cardid+"</td>"+
	   						 "<td nowrap='nowrap' align='center'>"+alarm.alarminfo.information+"</td>"+
	   						 "<td nowrap='nowrap' align='center'>"+alarm.alarmdetail+"</td>"+
	   						 "<td nowrap='nowrap' align='center'>"+alarm.position.positionName+"</td>"+
	   						 "<td nowrap='nowrap' align='center'>"+alarm.recordtime+"</td>"+
	   						 "<td nowrap='nowrap' align='center'>"+alarmState+"</td><tr>");
						}
                   cometd.publish('/service/alarm', { });
                });
            }

5、另外有一点要说的是 如果你向前台写的对象 AlarmRecord中包含其他对象如Positon ,那么Position对象也要实现 Serializable , Cloneable 和 JSON.Convertible 接口

    再次强调,官方文档很全很有用。另如果还有什么问题可以在文章下留言,大家互相探讨,互相学习。


文章出处:http://www.neversaydie.cc/server-push-cometd/

Jetty 欢迎访问Jetty文档 Wiki. Jetty是一个开源项目,提供了http服务器、http客户端和java servlet容器。 这个wiki提供jetty的入门教程、基础配置、功能特性、优化、安全、JavaEE、监控、常见问题、故障排除帮助等等。它包含教程、使用手册、视频、特征描述、参考资料以及常见问题。 Jetty文档 ---------------- 入门: 下载Download, 安装, 配置, 运行 Jetty入门(视频) 下载和安装Jetty 如何安装一个Jetty包 如何配置Jetty – 主要文档 如何运行Jetty 用JConsole监控Jetty 如何使用Jetty开发 Jetty HelloWorld教程 Jetty和Maven HelloWorld教程 Jetty(6)入门 (www.itjungle.com) Jetty Start.jar 配置Jetty 如何设置上下文(Context Path) 如何知道使用了那些jar包 如何配置SSL 如何使用非root用户监听80端口 如何配置连接器(Connectors) 如何配置虚拟主机(Virtual Hosts) 如何配置会话ID(Session IDs) 如何序列化会话(Session) 如何重定向或移动应用(Context) 如何让一个应用响应一个特定端口 使用JNDI 使用JNDI 在JNDI中配置数据源(DataSource) 内嵌Jetty服务器 内嵌Jetty教程 内嵌Jetty的HelloWorld教程 内嵌Jetty视频 优化Jetty 如何配置垃圾收集 如何配置以支持高负载 在Jetty中部署应用 部署管理器 部署绑定 热部署 Context提供者 如何部署web应用 webApp提供者 如何部署第三方产品 部署展开形式的web应用 使用Jetty进行开发 如何使用Jetty进行开发 如何编写Jetty中的Handlers 使用构建工具 如何在Maven中使用Jetty 如何在Ant中使用Jetty Maven和Ant的更多支持 Jetty Maven插件(Plugin) Jetty Jspc Maven插件(Plugin) Maven web应用工程原型 Ant Jetty插件(Plugin) 使用集成开发环境(IDEs) 在Eclipse中使用Jetty 在IntelliJ中使用Jetty 在Eclipse中工作 在Eclipse中开发Jetty Jetty WTP插件(Plugin) JettyOSGi SDK for Eclipse-PDE EclipseRT Jetty StarterKit SDK OSGi Jetty on OSGi, RFC66 基于Jetty OSGi的产品 OSGi贴士 Equinox中使用Jetty实现HTTP Service Felix中使用Jetty实现HTTP Service PAX中使用Jetty实现HTTP Srevice ProSyst mBedded Server Equinox Edition Spring Dynamic Modules里的Jetty JOnAS5里的Jetty 配置Ajax、Comet和异步Servlets 持续和异步Servlets 100 Continue和102 Processing WebSocket Servlet 异步的REST Stress Testing CometD 使用Servlets和Filters Jetty中绑定的Servlets Quality of Service Filter Cross Origin Filter 配置安全策略(Security Policies) 安全领域(Security Realms) 安全域配置教程 Java Authentication and Authorization Service (JAAS) JAAS配置教程 JASPI 安全模型(Secure Mode) 存储在文件中的安全密码以及编程教程 如何开启或禁止Jetty中的SSL功能 如何在Jetty中安全存储密码 如何安全终止Jetty 如何配置Spnego Application Server Integrations(集成) Apache Geronimo JEE 配置Apache httpd和Jetty教程 配置Apache mod_proxy和Jetty 配置Jetty中的AJP13 在JBoss中配置Jetty Remote Glassfish EJBs from Jetty Jetty and Spring EJB3 (Pitchfork) JBoss EJB3 ObjectWeb EasyBeans
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值