技术文档链接地址以下:javascript
http://www.ibm.com/developerworks/cn/web/wa-cometjava/html
Comet的别称反向 Ajax 或服务器端推技术(server-side push),我理解的定义是:使用http长链接从后台主动推送数据到前台的技术。java
如今ajax很是流行,他解决了网页所有刷新才能更新内容的方式,但它在一些使用场景中并不适用,好比,股票走势的图实时更新,我的通知的实时更新。这些须要实时更新的模块使用ajax的定时刷新方式,体验不好,当时我就想有没有一种方式,若是后台数据发生变化主动将变化信息传给前台相应的模块中去呢,我google之,发现了Comet。它的使用特色是,一般的ajax请求当数据从后台返回数据后与后台的链接就会关闭,而Comet则一直创建着链接,后台程序则保存了全部请求者的HttpServletResponse,若是后台数据发生变化就会经过这个链接传到前台,这就会出现一个问题,若是100个用户须要推送数据,后台代码必然须要保存着100个用户的HttpServletResponse,若是是1000个用户甚至更多呢,这是一个问题,还有一个问题就是虽然前台刷新的次数少了,但后台须要处理的就相应增多,这样是否符合最优原则,第三个问题是他依赖于运行程序的容器。tomcat,glassfish等的处理方式有些区别,至于Comet是否值得使用还得经过实验得出结论。jquery
我用百度的echarts作了一个简单的Comet长链接例子,在后台Java程序中我设置了一个定时器,后台会定时向前台推送数据,实现了基本功能。长链接不关闭对浏览器来讲也是很大的开销。下面是例子的代码。web
(1) 接受后台请求的servletajax
package tbktest; import java.io.IOException; import java.util.Timer; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.catalina.CometEvent; import org.apache.catalina.CometProcessor; public class CometServlet extends HttpServlet implements CometProcessor { private static final long serialVersionUID = 1L; private static final Integer TIMEOUT = 60 * 1000; Timer timer = new Timer(); public CometServlet() { super(); } public void event(CometEvent event) throws IOException, ServletException { HttpServletRequest request = event.getHttpServletRequest(); HttpServletResponse response = event.getHttpServletResponse(); try { if (event.getEventType() == CometEvent.EventType.BEGIN) { request.setAttribute("org.apache.tomcat.comet.timeout", TIMEOUT); log("Begin for session: " + request.getSession(true).getId()); MessageSender pMessageSender = new MessageSender(); pMessageSender.setConnection(response); timer.schedule(pMessageSender, 5000,50000); } else if (event.getEventType() == CometEvent.EventType.ERROR) { log("Error for session: " + request.getSession(true).getId()); event.close(); } else if (event.getEventType() == CometEvent.EventType.END) { log("End for session: " + request.getSession(true).getId()); event.close(); } else if (event.getEventType() == CometEvent.EventType.READ) { throw new UnsupportedOperationException( "This servlet does not accept data"); } } catch (Exception e) { e.printStackTrace(); } } }
(2) servlet所用到的类apache
package tbktest; import java.io.OutputStream; import java.util.ArrayList; import java.util.TimerTask; import javax.servlet.ServletResponse; public class MessageSender extends TimerTask { private static int mOne = 1; private static int mTwo = 1; private static int mThree = 1; private static int mFour = 1; private static int mFive = 1; private static int mSix = 1; private static int mSeven = 1; private static int mEigt = 1; private static int mNigh = 1; private static int mTen = 1; private static int mTel = 1; private static int mEs = 1; protected boolean running = true; protected final ArrayList messages = new ArrayList(); protected ArrayList<ServletResponse> connection = new ArrayList<ServletResponse>(); public synchronized void setConnection(ServletResponse connection) { this.connection.add(connection); notify(); } // 发送信息 public void send() { // 同步队列,加入发送信息 synchronized (messages) { mOne = mOne+1; mTwo = mTwo+1; mThree = mThree+1; mFour = mFour+1; mFive = mFive+1; mSix = mSix+1; mSeven = mSeven+1; mEigt = mEigt+1; mNigh = mNigh+1; mTen = mTen+1; mTel = mTel+1; mEs = mEs+1; messages.add(mOne); messages.add(mTwo); messages.add(mThree); messages.add(mFour); messages.add(mFive); messages.add(mSix); messages.add(mSeven); messages.add(mEigt); messages.add(mNigh); messages.add(mTen); messages.add(mTel); messages.add(mEs); // 唤醒 messages.notify(); } } public void run() { send(); // 线程启动 log("start"); if (messages.size() == 0) { try { synchronized (messages) { log("MessageSender wait[空闲状态,线程等待]"); // 释放锁 messages.wait(); } } catch (InterruptedException e) { e.printStackTrace(); // Ignore } } String pendingMessages = null; synchronized (messages) { // 导出发送的信息至数组 pendingMessages = messages.toString(); // 清空信息队列 messages.clear(); } try { if (connection == null) { try { synchronized (this) { // 等待注入HTTP RESPONSE wait(); } } catch (InterruptedException e) { // Ignore e.printStackTrace(); } } for(int i=0;i<connection.size();i++){ OutputStream out = connection.get(i).getOutputStream(); final String forecast = pendingMessages; out.write(forecast.getBytes()); out.flush(); connection.get(i).flushBuffer(); System.out.println(pendingMessages); } // 输出流操做 } catch (Exception e) { log("IOExeption sending message", e); } } // 中止 public void stop() { running = false; } // 日志 private void log(Object obj) { System.out.println(obj); } // 日志 private void log(Object obj, Throwable e) { System.out.println(obj); e.printStackTrace(); } }
(3) html页面须要引入百度的echarts相关js文件数组
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>ECharts</title> <!--Step:1 Import a module loader, such as esl.js or require.js--> <!--Step:1 引入一个模块加载器,如esl.js或者require.js--> <script src="js/esl.js"></script> <script src="js/jquery-1.9.0.min.js"></script> </head> <body> <!--Step:2 Prepare a dom for ECharts which (must) has size (width & hight)--> <!--Step:2 为ECharts准备一个具有大小(宽高)的Dom--> <div id="main" style="height:500px;border:1px solid #ccc;padding:10px;"></div> <script type="text/javascript"> $(document).ready(function(){ try { var request = new XMLHttpRequest(); } catch (e) { alert("Browser doesn't support window.XMLHttpRequest"); } request.open("GET", "CometServlet", true); request.send(null); var pos = 0; request.onreadystatechange = function () { if (request.readyState === 3) { var mdata = new Array(); mdata = request.responseText; require.config({ paths:{ echarts:'./js/echarts', 'echarts/chart/bar' : './js/echarts', 'echarts/chart/line': './js/echarts' } }); // Step:4 require echarts and use it in the callback. // Step:4 动态加载echarts而后在回调函数中开始使用,注意保持按需加载结构定义图表路径 require( [ 'echarts', 'echarts/chart/bar', 'echarts/chart/line' ], function(ec) { var myChart = ec.init(document.getElementById('main')); var option = { tooltip : { trigger: 'axis' }, legend: { data:['蒸发量','降水量'] }, toolbox: { show : true, feature : { mark : true, dataView : {readOnly: false}, magicType:['line', 'bar'], restore : true, saveAsImage : true } }, calculable : true, xAxis : [ { type : 'category', data : ['1月','2月','3月','4月','5月','6月','7月','8月','9月','10月','11月','12月'] } ], yAxis : [ { type : 'value', splitArea : {show : true} } ], series : [ { name:'蒸发量', type:'bar', data:mdata }, { name:'降水量', type:'bar', data:[] } ] }; myChart.setOption(option); } ); } }; //$.ajax({ // type : "get", // url: "CometServlet", // dataType:"text", // success: function(data) { // = data; // }); }); // Step:3 conifg ECharts's path, link to echarts.js from current page. // Step:3 为模块加载器配置echarts的路径,从当前页面连接到echarts.js,定义所需图表路径 </script> </body> </html>