【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;
}
@Override
public 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;
// 遍历所有session
for (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$TemperatureEventPullSource
source2=nl.justobjects.pushlet.test.TestEventPullSources$SystemStatusEventPullSource
source3=nl.justobjects.pushlet.test.TestEventPullSources$PushletStatusEventPullSource
source4=nl.justobjects.pushlet.test.TestEventPullSources$AEXStocksEventPullSource
source5=nl.justobjects.pushlet.test.TestEventPullSources$WebPresentationEventPullSource
source6=nl.justobjects.pushlet.test.TestEventPullSources$PingEventPullSource
source7=nl.justobjects.pushlet.test.TestEventPullSources$MyEventPullSource
source8=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 ...
随机推荐
- 51nod算法马拉松28-a
题解:水体一枚 按照贪心的思想求出是2的k次方,然后高精度计算 代码: #include<bits/stdc++.h> using namespace std; ; int ans,n,a ...
- L1-030 一帮一
“一帮一学习小组”是中小学中常见的学习组织方式,老师把学习成绩靠前的学生跟学习成绩靠后的学生排在一组.本题就请你编写程序帮助老师自动完成这个分配工作,即在得到全班学生的排名后,在当前尚未分组的学生中, ...
- [翻译]HTTP--一个应用级的协议
原文地址:HTTP — an Application-Level Protocol 简介 在不丹,当人们见面时,他们通常用“你身体还好吗?”互相打招呼.在日本,根据当时的情形,人们可能会互相鞠躬.在阿 ...
- ubuntu16上传文件到服务器
用windows时候,上传文件到服务器,一般都是用xshell和xftp配合使用,用ubuntu就不需要额外安装任何软件了.只用ctrl+alt+t,打开命令行用一句话就可以上传了. 将本地war包上 ...
- PDB调试模块
这里主要是一些对于调试常用的命令:1.直接通过命令端输入进行调试 以pdb调试模式运行(主要用这个) python3 -m pdb file.py 2.在代码中导入pdb模块 import pdb 功 ...
- CentOS下设置MySQL的root各种密码 总结
一.更改mysql密码常用的三种方法 大部分情况下,一般用户没有权限更改密码,只有申请了权限或root用户才可以更改密码: 1.方法1:用mysqladmin mysqladmin -u root p ...
- python海龟绘图
最近学了python,看了几本书之后,才明白python的强大,python是一种解释型的语言,即每写一行程序就执行一行. 而且在科学计算方面,处理的能力特别的方便. 比如python中的字典dict ...
- .NET 除了用 Task 之外,如何自己写一个可以 await 的对象?
.NET 中的 async / await 写异步代码用起来真的很爽,就像写同步一样.我们可以在各种各样的异步代码中看到 Task 返回值,这样大家便可以使用 await 等待这个方法.不过,有时需要 ...
- 结构体:HASH表模板
这种 HASHMAP 就是一个链式前向星的表: 其中: init 函数:hashmap 创建初始化: check 函数:寻找 hash 表中是否有需要查找的值,若有则返回 1 ,否则返回 0 :遍历方 ...
- 51Nod1526 分配笔名
分析 在trie树上贪心,将所有串加入trie树中,在深度较深的地方匹配会更优. 由于只需要知道最后的总质量,所以直接取每个点的子树中最大的匹配即可 复杂度\(O(\sum len)\) 加串的时候把 ...