请注意这个实时打上了双引号,没有绝对的实时,只是时间的颗粒不一样罢了(1ms,1s,1m)。

服务器数据有更新可以快速通知客户端。Web 基于取得模式,而服务器建立大量的和客户端连接来提供数据实时更新反而拉低服务器的使用效能。

请下载DEMO  点击下载


一、现有方案归纳有两类。

  1. 服务器真实推送 - 基于浏览器外部控件数据实时更新。

    IE  ActiveX(flash)控件,还有其他浏览器比如Firefox插件。这种基于浏览器外部插件的,由于移植性差。主要要在一些浏览器安全上得到应用。比如在线支付(支付宝),自动登陆(QQ)。和一些内网控制(电网内部控制管理)。 Flash 其实也是浏览器的一种插件后台通过建立Socket 来与客户端实时数据更新。这点比较有优势的是Flash 插件几乎每台机器上都有安装,移植性没有问题。但同样对防火墙穿透能力差,而且需要消耗服务器大量的。

  1. 基于XMLHttpRequest 定时取的解决方案.

    Ajax 通过定时去询问服务器是否有数据更新,似乎是一个通用的解决方案,如:1元xx 类的网站,因为抢购模式需要实现更新商品的剩余份数。比如要获取服务器当前参与人数,获取最新购买人数,发送的私信,好友消息,每一种类型数据都设定一个时间如:1秒到数据库取一次数据。而大多数请求的链接是无效的,而且过多的请求会导致浏览器无响应。


二、为什么我们不能两者结合,选择折中的方案呢?

客户端脚本需要一个可以告知的程序,告诉我们服务器中有数据更新了,然后执行自己注册好的程序到服务器取对应的数据。

我们只需要服务器通知提供这样的数据结构:

{
//用户消息更新时间
msg: '20141192003261234',
//网站购买记录更新时间
buy: '20141192003534567'
}

获取客户端获取当前的数据后 和自己当前浏览器中存储的用户消息更新时间进行对比。如果msg更新时间与服务器给的时间完全一致,我们就没有必要到服务器中去用户个人消息了,反之服务器中消息更新,开始执行我们预定的程序来获取服务器,用户收到的消息。显示给用户,并且设置一下当前消息的更新时间。

我们的需要的就这么简单——需要一个通知程序通知我们


三、我们需要做什么,会遇到那些问题。

让服务器通知客户端程序数据更新显然不是很划算的事情我们上面讨论过了,所以我们需要在客户端设置一个循环往复的定时程序到服务器里面去取注册好的类型(用户消息,网站成交数量,购买人次,商品剩余数量)的更新的时间,通过对于注册对于数据类型的时间来判断要不要执行我们注册好的方法。

  问题1:定时程序必须有序执行Ajax方法(前一个ajax完毕后才能发送第二次Ajax),服务器获取数据是一个耗时操作。

Ajax异步递归。

  问题2:并不是每一个页面都需要知道数据的变动情况

不同的页面注册不同的监听信息。

  问题3:如何动态开始和阻止定时程序,并且保持程序只能运行一个定时实例。

设置互斥量.

  问题4:服务器如何存储这些数据的更新状态(公共的:成交量,个体的:用户消息)。

需要一个存储介质,存储对应类型信息的更新时间

  问题5:如何在异常中恢复。确保定时程序能正常运行。

Try ... Catch...  $.ajax error.

四、代码实现

客户端定时程序:

 //客户端监听对象
var listener = {
tid: 0,
keys: "",
//任务存储对象
taskType: {},
//注册一个任务
appendTaskType: function (key, type) {
if (typeof (type) == "function" && typeof (key) == "string" && /^[a-z0-9]+$/.test(key)) {
//添加一个任务
this.taskType[key] = {
//任务执行函数
fun: type,
//变化量
ts: ''
};
var a = [];
for (var k in this.taskType) { a.push(k); }
this.keys = a.join('.');
}
},
//开始运行监听
start: function () {
//如果定时器正在运行则返回
if (this.tid != 0)
return;
fn();
//私有定时执行方法
function fn() {
$.getJSON("/api/listener", { keys: listener.keys }, function (d) {
//获取定时器所有的注册类型
for (var key in listener.taskType) {
//获取注册类型对象
var O = listener.taskType[key];
//判断当前对象是否存在和当前的值是否和之前的变化值一样,是否真正执行
if (d[key] && d[key] != O.ts) {
//更改现有状态
O.ts = d[key];
//执行注册函数
O.fun(O);
}
}
//设置ID
listener.tid = setTimeout(fn, 1000);
})
}
},
//关闭监听
stop: function () {
//清除定时器
clearTimeout(this.tid);
//归零
this.tid = 0;
}
}
//注册对页面的监听
listener.appendTaskType("msg", function () {
$.getJSON("/api/getlist", { key: 'msg' }, function (data) {
var b = $("#msgList");
b.hide();
b.empty();
var html = '';
for (var i = 0; i < data.length; i++) {
html += "<li>【" + data[i].user + "】说:" + data[i].msg + "</li>"
}
b.html(html);
b.slideDown(300);
})
});
//注册对购买记录的监听
listener.appendTaskType("buy", function () {
$.getJSON("/api/getlist", { key: 'buy' }, function (data) {
var b = $("#buyList");
b.hide();
b.empty();
var html = '';
for (var i = 0; i < data.length; i++) {
html += "<li>【" + data[i].user + "】购买了:" + data[i].msg + "</li>"
}
b.html(html);
b.slideDown(300);
})
})
//开始执行监听任务
listener.start();

服务器代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Web;
using System.Web.Mvc; namespace MvcApplication1.Controllers {
public class ApiController : Controller {
//
// GET: /Api/
/// <summary>
/// 根据缓存Key获取当前缓存最后更新的标示
/// </summary>
/// <param name="cacheKey">缓存Key</param>
/// <returns>最后更新的时间</returns>
public string getlast(string cacheKey) {
List<data> ls = HttpRuntime.Cache.Get(cacheKey) as List<data>;
if (ls != null)
return ls.First().time;
return "";
}
/// <summary>
/// 服务器定时方法,这个方法是检测,数据有没有进行更新
/// </summary>
/// <returns></returns>
public ContentResult Listener(string keys) {
if (string.IsNullOrWhiteSpace(keys))
return Content("{}");
System.Text.StringBuilder builder = new System.Text.StringBuilder("{");
//客户端需要知道用户信息是否变动
if (keys.Contains("msg")) {
builder.AppendFormat("\"msg\":\"{0}\",", getlast("msg"));
}
//客户端需要知道购买列表是否变动
if (keys.Contains("buy")) {
builder.AppendFormat("\"buy\":\"{0}\",", getlast("buy"));
}
//类推各种监听....... //移除最后“,”
if (builder.Length > ) {
builder.Remove(builder.Length - , );
}
builder.Append("}");
return Content(builder.ToString());
}
public ContentResult msg(data ms) {
InsertCache(ms, "msg");
return Content("ok");
}
public ContentResult buy(data buy) {
InsertCache(buy, "buy");
return Content("ok");
}
private void InsertCache(data d, string cacheKey) {
//使用时间设置最后更新量
d.time = DateTime.Now.ToString("yyyyMMddhhmmssffff");
//获取存储的值
List<data> ls = HttpRuntime.Cache.Get(cacheKey) as List<data>;
//判断是否为空
if (ls == null) {
ls = new List<data>();
HttpRuntime.Cache.Insert(cacheKey, ls);
}
//添加到集合
ls.Insert(, d);
//移除大于这个数
if (ls.Count > )
ls.RemoveRange(, ls.Count - );
}
public JsonResult GetList(string key) {
if (string.IsNullOrEmpty(key))
return Json(null, JsonRequestBehavior.AllowGet);
return Json(HttpRuntime.Cache.Get(key) as List<data>, JsonRequestBehavior.AllowGet);
}
public JsonResult GetCache() { return Json(new {
p1 = HttpRuntime.Cache.EffectivePercentagePhysicalMemoryLimit,
p2 = HttpRuntime.Cache.EffectivePrivateBytesLimit
},
JsonRequestBehavior.AllowGet);
}
}
//定义一个数据存储介质
public class data {
public string user { get; set; }
public string msg { get; set; }
public string time { get; set; }
}
}

请下载DEMO  点击下载

Web网站数据”实时”更新设计的更多相关文章

  1. 使用php+swoole对client数据实时更新(下)

    上一篇提到了swoole的基本使用,现在通过几行基本的语句来实现比较复杂的逻辑操作: 先说一下业务场景.我们目前的大多数应用都是以服务端+接口+客户端的方式去协调工作的,这样的好处在于不论是处在何种终 ...

  2. 使用php+swoole对client数据实时更新(二) (转)

    上一篇提到了swoole的基本使用,现在通过几行基本的语句来实现比较复杂的逻辑操作: 先说一下业务场景.我们目前的大多数应用都是以服务端+接口+客户端的方式去协调工作的,这样的好处在于不论是处在何种终 ...

  3. MySQL 实现将一个库表里面的数据实时更新到另一个库表里面

    MySQL 实现将一个库表里面的数据实时更新到另一个库表里面 需求描述:MySQL 里面有很多的数据库,这些数据库里面都有同一种表结构的表 (tb_warn_log),这张表的数据是实时更新的,现在需 ...

  4. 基于server broker 的数据实时更新

    Service Broker介绍:SQL Server Service Broker 为消息和队列应用程序提供 SQL Server 数据库引擎本机支持.这使开发人员可以轻松地创建使用数据库引擎组件在 ...

  5. 使用php+swoole对client数据实时更新(上)

    如果想对一个列表做实时的更新,传统的做法是采用轮询的方式.以web为例,通过Ajax定时请求服务端然后获取数据显示在页面.这种方式实现简单,缺点就是浪费资源. HTTP1.1新增加了对websocke ...

  6. vue 跳转并传参,实现数据实时更新

    原文链接:点我 比如我现在在页面A跳转到页面B,A中的router-link :to={path:’B’,params:{id:’5’}} 求助:在页面B中的mounted生命周期函数中使用this. ...

  7. MFC编辑框数据实时更新问题

    在VC里,很多情况下需要更新控件,也就是调用UpdateData(FALSE);但是如果是在循环中调用该函数,会导致没有时间来刷新界面,消息得不到相应,从外部看来,似乎整个循环只执行了一次Update ...

  8. IDEA 修改JSP和后端数据后,页面刷新可以实时更新

    情况:刚开始使用IDEA进行开发时,发现修改JSP页面或者后端数据后,再刷新浏览器页面,发现没有变化,页面无更新. 这样就导致不得不频繁重启tomcat服务器.非常麻烦 解决方法: 步骤1. 先设置t ...

  9. Tornado实现监控数据实时展示

    前言: It has been a while since I last updated my blogs. 使用Tornado开发一个实时监控信息系统,其中包括 CUP.内存.网卡.磁盘使用率. 涉 ...

随机推荐

  1. Android中AutoCompleteTextView的使用

    1.http://blog.csdn.net/FX_SKY/article/details/9326129 此博客讲解了auto组件如何使用baseAdapter来扩展功能,推荐参照此博客写demo ...

  2. JCEF3——谷歌浏览器内核Java版实现(一):使用jawt获取窗体句柄

    前言 最近一段时间研究谷歌浏览器内核.谷歌浏览器内核一直开源,并维护更新,它的开源项目中内核更新速度和Chrome浏览器版本更新进度一样!而且它不同于WebKit(值得一题的是谷歌浏览器已不使用Web ...

  3. shell 复习

    grep -v zip$  -v 逻辑否  $以zip结尾 (^开头) -n str不空,-z str 空

  4. square开源vim,tmux配置在linux上使用

    首先安装需要的软件 apt-get install vim ack-grep git tmux gnome-terminal ctags xclip silversearcher-ag 这里tmux需 ...

  5. BATCH(BAT批处理命令语法)

    bat语法备忘扩展名是bat(在nt/2000/xp/2003下也可以是cmd)的文件就是批处理文件[@more@] bat语法备忘扩展名是bat(在nt/2000/xp/2003下也可以是cmd)的 ...

  6. 【资源下载】Ext4.1.0_Doc中文版_V1.0.0_Beta正式提供下载!

    *************************************************重要提示: 在2014年1月1日前一天,历时两年左右的时间,翻译小组终于完成了该API的翻译.可喜可贺 ...

  7. android手势创建及识别

    使用一些浏览器或者输入法应用时会有一些手势操作,还可以自定义手势.这些神奇的操作是怎么做的呢?这一篇重点记录手势的识别和创建.这篇的内容使用到的是android.gesture包,具体的例子参考的是S ...

  8. android sqlite导入数据

    @Override public void onUpgrade(SQLiteDatabase db, int arg1, int arg2) { // TODO Auto-generated meth ...

  9. bootstrap插件学习-bootstrap.scrollspy.js

    先看bootstrap.dropdown.js的结构 function ScrollSpy(){} //构造函数 ScrollSpy.prototype = {} //构造器的原型 $.fn.scro ...

  10. [SLAM] 01 "Simultaneous Localization and Mapping" basic knowledge

    发信人: leecty (Terry), 信区: ParttimeJobPost标 题: 创业公司招SLAM 算法工程师发信站: 水木社区 (Thu Jun 16 19:18:24 2016), 站内 ...