【pushlet学习】具体实战
业务需求:
1. 前端界面需要实时显示空调、照明等设备的状态, 如:空调电压、空调电流、光照强度等,这些量每一个称作一个测点;
采用传统的“请求/响应”方式,很难达到前台界面实时显示最新数据,为达到实时显示最新数据,我们采用一种“服务器推”的技术comet,而pushlet是“服务器推”技术的一种实现,这里我们采用pushlet技术来实现上述业务需求。
1. 前台界面打开时,会将测点名称集合以及主题名传递到后台,格式形如:{[测点1,测点2,测点3,....],subject};并在前台开启对此主题的监听;(注:主题名是动态随机生成的,每个界面的主题名都保证不相同)


主要功能:
package com.guoguo;import java.io.BufferedInputStream;import java.io.IOException;import java.io.InputStream;import java.util.ArrayList;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.stringtree.json.JSONReader;import org.stringtree.json.JSONValidatingReader;public class TestServlet extends HttpServlet {private static final long serialVersionUID = 1L;public TestServlet() {super();}/*** get/post方法的处理函数*/protected void service(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {// 读取请求报文数据request.setCharacterEncoding("UTF-8");// 获取请求的数据StringBuffer reqData = new StringBuffer();InputStream in = request.getInputStream();BufferedInputStream buf = new BufferedInputStream(in);byte[] buffer = new byte[1024];int iRead;while ((iRead = buf.read(buffer)) != -1) {reqData.append(new String(buffer, 0, iRead, "UTF-8"));}// 获取请求的测点名称数组JSONReader r = new JSONValidatingReader();@SuppressWarnings("unchecked")ArrayList<Object> keyList = (ArrayList<Object>) r.read(reqData.toString());// 获取订阅主题名称String aSubject = request.getParameter("subject");System.out.println("请求的测点:" + reqData.toString() + " , 主题名:" + aSubject);// 启动一个线程,实现创建Pushlet事件、做业务、向前台推送数据等功能PushThread pushThread = new PushThread(aSubject, keyList);pushThread.start();}}
- 获取各测点的值;
- 将各测点的值组装成字符串;
- 将该字符串设置为事件源的属性。
package com.guoguo;import java.text.SimpleDateFormat;import java.util.ArrayList;import java.util.Date;import java.util.HashMap;import java.util.Random;import org.stringtree.json.JSONValidatingWriter;import nl.justobjects.pushlet.core.Dispatcher;import nl.justobjects.pushlet.core.Event;import nl.justobjects.pushlet.core.Session;import nl.justobjects.pushlet.core.SessionManager;public class PushThread extends Thread {// 主题public String aSubject; // 客户端传递过来// 关键字列表public ArrayList<Object> keyList; // 客户端传递过来/*** 构造函数** @param aSubject* @param keyList*/public PushThread(String aSubject, ArrayList<Object> keyList) {this.aSubject = aSubject;this.keyList = keyList;}@Overridepublic void run() {Event event = Event.createDataEvent(aSubject);int i = 0;while (true) {try {Thread.sleep(5000);} catch (InterruptedException e) {// 线程阻塞,结束线程System.out.println("=========>sleep异常 --->" + "线程"+ Thread.currentThread().getId() + "关闭");break;}System.out.println("\n-----Thread ID: "+ Thread.currentThread().getId());// 判断当前连接的会话个数,没有会话,则线程退出Session[] sessions = SessionManager.getInstance().getSessions();// 当前无会话,结束线程if (0 == sessions.length) {System.out.println("=========>无sessions --->" + "线程"+ Thread.currentThread().getId() + "关闭");break;}// 判断当前会话中是否存在订阅该主题的订阅者,不存在则结束线程boolean if_exist_subscriber = true;// 遍历所有sessionfor (int j = 0; j < sessions.length; j++) {System.out.println(sessions[j].getSubscriber().match(event) == null ? "Session"+ j + ": 未订阅该事件 ": "Session" + j + ":订阅了该事件 ");if (null != sessions[j].getSubscriber().match(event)) {if_exist_subscriber = false;}}if (if_exist_subscriber) {System.out.println("=========>无"+aSubject+"订阅者 --->" + "线程" + Thread.currentThread().getId() + "关闭");break;}// 模拟业务处理:获取各测点的值HashMap<Object, Object> ret_value = new HashMap<Object, Object>();for (Object keyStr : keyList) {SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");// 设置日期格式String currTm = df.format(new Date());ret_value.put(keyStr, currTm);// ret_value.put(keyStr, (10*(new Random().nextFloat())));}// 将返回值封装为json数据形式String ret_string = "[";ret_string += new JSONValidatingWriter().write(ret_value);ret_string += "]";event.setField("key1", ret_string);// 推送消息Dispatcher.getInstance().multicast(event); // 向所有和event名称匹配的事件推送}}}
## Properties file for EventSource objects to be instantiated.## Place this file in the CLASSPATH (e.g. WEB-INF/classes) or directly under WEB-INF.## $Id: sources.properties,v 1.2 2007/11/10 14:12:16 justb Exp $## Each EventSource is defined as <key>=<classname># 1. <key> should be unique within this file but may be any name# 2. <classname> is the full class name### Define Pull Sources here. These classes must be derived from# nl.justobjects.pushlet.core.EventPullSource# Inner classes are separated with a $ sign from the outer class.source1=nl.justobjects.pushlet.test.TestEventPullSources$TemperatureEventPullSourcesource2=nl.justobjects.pushlet.test.TestEventPullSources$SystemStatusEventPullSourcesource3=nl.justobjects.pushlet.test.TestEventPullSources$PushletStatusEventPullSourcesource4=nl.justobjects.pushlet.test.TestEventPullSources$AEXStocksEventPullSourcesource5=nl.justobjects.pushlet.test.TestEventPullSources$WebPresentationEventPullSourcesource6=nl.justobjects.pushlet.test.TestEventPullSources$PingEventPullSourcesource7=nl.justobjects.pushlet.test.TestEventPullSources$MyEventPullSourcesource8=nl.justobjects.pushlet.test.TestEventPullSources$SpEventPullSource# TO BE DONE IN NEXT VERSION# define Push Sources here. These must implement the interface# nl.justobjects.pushlet.core.EventSource
这里主要是配置servlet:TestServlet,其他servlet用不到
<?xml version="1.0" encoding="UTF-8"?><web-app><!-- Define the pushlet servlet --><servlet><servlet-name>pushlet</servlet-name><servlet-class>nl.justobjects.pushlet.servlet.Pushlet</servlet-class><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>pushlet</servlet-name><url-pattern>/pushlet.srv</url-pattern></servlet-mapping><servlet><display-name>ChatServlet</display-name><servlet-name>ChatServlet</servlet-name><servlet-class>com.guoguo.ChatServlet</servlet-class></servlet><servlet-mapping><servlet-name>ChatServlet</servlet-name><url-pattern>/ChatServlet</url-pattern></servlet-mapping><servlet><display-name>TestServlet</display-name><servlet-name>TestServlet</servlet-name><servlet-class>com.guoguo.TestServlet</servlet-class></servlet><servlet-mapping><servlet-name>TestServlet</servlet-name><url-pattern>/TestServlet</url-pattern></servlet-mapping></web-app>
前台界面,主要功能如下:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><%String path = request.getContextPath();String basePath = request.getScheme() + "://"+ request.getServerName() + ":" + request.getServerPort()+ path + "/";%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><base href="<%=basePath%>"><meta http-equiv="pragma" content="no-cache"><meta http-equiv="cache-control" content="no-cache"><meta http-equiv="expires" content="0"><meta http-equiv="keywords" content="keyword1,keyword2,keyword3"><meta http-equiv="description" content="This is my page"><script type="text/javascript"src="<%=basePath%>js/pushlet_js/ajax-pushlet-client.js"></script><script type="text/javascript">var subscriptionId = null;window.onload = onInit;window.onbeforeunload = onUnsubscribe;// 监听后台返回的数据信息,更新页面function onData(event) {// 保存订阅编号,用于页面关闭时进行退订subscriptionId = event.get('p_sid');// 更新页面document.dataEventDisplay.event.value = event.get("key1");// ------实际案例处理(test)---------/* var respData = decodeURIComponent(event.get("key1"));var respObj = eval(respData);//var respActionNum = respObj.length;var obj = respObj[0];var str = "";for(var p in obj){if(typeof(obj[p]) != "function"){str += p + "=" + obj[p] + ", " ;}}alert(str); */}// 页面关闭时,取消订阅function onUnsubscribe() {if (subscriptionId != null) {PL.unsubscribe(subscriptionId);}}// 页面加载完,初始化请求、监听function onInit() {var aSubject = _getRandomString(6); //主题名var httpRequest = getXMLHttpRequest();if (httpRequest) {var reqData = getData();httpRequest.onreadystatechange = function() {if (httpRequest.readyState == 4) {if (httpRequest.status == 200) {// 请求成功,起pushlet监听PL._init();PL.joinListen(aSubject);} else {alert("实时请求失败!\n" + httpRequest.statusText);}}}url = '<%=request.getContextPath()%>' + '/TestServlet'+ '?subject=' + aSubject;httpRequest.open("POST", url, true);httpRequest.send(reqData);}}// 请求关键字function getData() {var reqData = "[30200000001010, 30200000001012, 30800000003009, 30800000006009]";return reqData;}// 获取http请求function getXMLHttpRequest() {req = false;//本地XMLHttpRequest对象if (window.XMLHttpRequest) {try {req = new XMLHttpRequest();} catch (e) {req = false;}//IE/Windows ActiveX版本} else if (window.ActiveXObject) {try {req = new ActiveXObject("Msxml2.XMLHTTP");} catch (e) {try {req = new ActiveXObject("Microsoft.XMLHTTP");} catch (e) {req = false;}}}return req;}// 获取长度为len的随机字符串function _getRandomString(len) {var len = len || 32;var chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz';var maxPos = chars.length;var pwd = '';for (i = 0; i < len; i++) {pwd += chars.charAt(Math.floor(Math.random() * maxPos));}return pwd;}</script></head><body><form name="dataEventDisplay"><table border="2" bordercolor="white" cellpadding="0" cellspacing="0"><tr><td><textarea cols="60" rows="10" name="event">没有消息 </textarea></td></tr></table></form><button onclick="onUnsubscribe()">取消订阅</button></body></html>

















附件列表
【pushlet学习】具体实战的更多相关文章
- NGUI 学习笔记实战之二——商城数据绑定(Ndata)
上次笔记实现了游戏商城的UI界面,没有实现动态数据绑定,所以是远远不够的.今天采用NData来做一个商城. 如果你之前没看过,可以参考上一篇博客 NGUI 学习笔记实战——制作商城UI界面 ht ...
- 深度学习入门实战(二)-用TensorFlow训练线性回归
欢迎大家关注腾讯云技术社区-博客园官方主页,我们将持续在博客园为大家推荐技术精品文章哦~ 作者 :董超 上一篇文章我们介绍了 MxNet 的安装,但 MxNet 有个缺点,那就是文档不太全,用起来可能 ...
- Android JNI学习(二)——实战JNI之“hello world”
本系列文章如下: Android JNI(一)——NDK与JNI基础 Android JNI学习(二)——实战JNI之“hello world” Android JNI学习(三)——Java与Nati ...
- Spring的学习与实战(续)
@ 目录 背景 JavaMailSender Spring集成邮件发送功能 1. 添加maven依赖 2. 添加Spring邮件配置 3. 创建邮件管理Bean并注入Spring应用上下文 4. 修改 ...
- Github点赞超多的Spring Boot学习教程+实战项目推荐!
Github点赞接近 100k 的Spring Boot学习教程+实战项目推荐! 很明显的一个现象,除了一些老项目,现在 Java 后端项目基本都是基于 Spring Boot 进行开发,毕竟它这 ...
- 深度学习入门实战(一):像Prisma一样算法生成梵高风格画像
本文由云+社区发表 作者:董超 导语:现在人工智能是个大热点,而人工智能离不开机器学习,机器学习中深度学习又是比较热门的方向,本系列文章就从实战出发,介绍下如何使用MXnet进行深度学习~ 既然是实战 ...
- Flask框架的学习与实战(一):开发环境搭建
Flask是一个使用 Python 编写的轻量级 Web 应用框架.其 WSGI 工具箱采用 Werkzeug ,模板引擎则使用 Jinja2.很多功能的实现都参考了django框架.由于项目需要,在 ...
- 1 如何使用pb文件保存和恢复模型进行迁移学习(学习Tensorflow 实战google深度学习框架)
学习过程是Tensorflow 实战google深度学习框架一书的第六章的迁移学习环节. 具体见我提出的问题:https://www.tensorflowers.cn/t/5314 参考https:/ ...
- Shiro入门学习与实战(一)
一.概述 1.Shiro是什么? Apache Shiro是java 的一个安全框架,主要提供:认证.授权.加密.会话管理.与Web集成.缓存等功能,其不依赖于Spring即可使用: Spring S ...
随机推荐
- MinGW安装教程——著名C/C++编译器GCC的Windows版本
前言本文主要讲述如何安装 C语言 编译器——MinGW,特点是文章附有完整详细的实际安装过程截图,文字反而起说明提示作用. 编写本文的原因始于我的一个观点:图片可以比文字传达更多的信息,也能让其他人更 ...
- [翻译]HTTP--一个应用级的协议
原文地址:HTTP — an Application-Level Protocol 简介 在不丹,当人们见面时,他们通常用“你身体还好吗?”互相打招呼.在日本,根据当时的情形,人们可能会互相鞠躬.在阿 ...
- iframe 问题集合
1. 历史记录问题 Firefox: 如果iframe是静态存在在HTML中时,iframe的任何src或者location改变都会被记录到浏览器history中. 如果iframe是在页面加载完成后 ...
- php 易忽略问题
- python Gevent – 高性能的Python并发框架
话说gevent也没个logo啥的,于是就摆了这张图= =|||,首先这是一种叫做greenlet的鸟,而在python里,按照官方解释greenlet是轻量级的并行编程,而gevent呢,就是利用g ...
- 批量解帧视频文件cpp
前言 将多个视频文件进行解帧. 实现过程 1.批量获取文件路径: 2.对某个视频文件进行解帧: 代码 /************************************************ ...
- Html页面Dom对象之Element
HTML DOM Element 对象 HTML DOM 节点 在 HTML DOM (文档对象模型)中,每个部分都是节点: 文档本身是文档节点 所有 HTML 元素是元素节点 所有 HTML 属性是 ...
- BZOJ1835,LG2605 [ZJOI2010]基站选址
题意 有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为\(D_i\) 需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为\(C_i\) 如果在距离第i个村 ...
- 剑指offer-顺时针打印矩阵-二维数组
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ...
- 一张图解释java跨平台