请注意这个实时打上了双引号,没有绝对的实时,只是时间的颗粒不一样罢了(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 开发 - 对图片进行虚化(毛玻璃效果,模糊)

    概述 IPAD,IPHONE上首页背景的模糊效果是不是很好看,那么在 Android中如何实现呢.我通过一种方式实现了这样的效果. 开源库名称:anroid-image-blur 一个android ...

  2. Windows下移动MariaDB数据目录 (转!)

    Windows下移动MariaDB数据目录: http://www.xue163.com/news/940/9403312.html [环境]OS:Windows Server 2008 R2 x64 ...

  3. python + hadoop (案例)

    python如何链接hadoop,并且使用hadoop的资源,这篇文章介绍了一个简单的案例! 一.python的map/reduce代码 首先认为大家已经对haoop已经有了很多的了解,那么需要建立m ...

  4. [转]安卓开发startservice 和bindservice详解

    原文 作者:aikongmeng 来源:安卓中文网 博主暗表:搜到此文,终于为我解惑,bindService并不会真正启动service,不会调用onStartCommand!还需要再bind之前st ...

  5. grails 私有库相关设置

    针对grails的私有库的设置,设置USER_HOME/settings.groovy如下: grails.dependency.cache.dir = "c:/m2_repositorie ...

  6. java之对象转型2

    public class TestCasting2{ public static void main(String args[]){ TestCasting2 test2= new TestCasti ...

  7. wireshark如何抓取本机包

    在进行通信开发的过程中,我们往往会把本机既作为客户端又作为服务器端来调试代码,使得本机自己和自己通信.但是wireshark此时是无法抓取到数据包的,需要通过简单的设置才可以. 具体方法如下: 方法一 ...

  8. C#调用Java类

    C#调用Java类 (2011-01-07 14:02:05) 转载▼   分类: Java学习  1. 在Eclipse中新建名称为hello的java project,此工程仅包含一个文件hell ...

  9. html5 audio/video 的那些坑

    当我最近项目用到audio的时候,我们用到了jPlayer作为三方库. 功能实现了,暂停播放,进度条什么的,都很顺利的搞定了.后来考虑到当网速过慢时需要给播放按钮一个载入动画,然后就一发不可收拾了. ...

  10. 受限玻尔兹曼机(RBM)学习笔记(六)对比散度算法

      去年 6 月份写的博文<Yusuke Sugomori 的 C 语言 Deep Learning 程序解读>是囫囵吞枣地读完一个关于 DBN 算法的开源代码后的笔记,当时对其中涉及的算 ...