热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Web网站数据”实时”更新设计

请注意这个实时打上了双引号,没有绝对的实时,只是时间的颗粒不一样罢了(1ms,1s,1m)。服务器数据有更新可以快速通知客户端。Web基于取得模式,而服务器建立大量的和客户端连接来

 

>  请注意这个实时打上了双引号,没有绝对的实时,只是时间的颗粒不一样罢了(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 ) {
html += "

  • 【" + data[i].user + "】说:" + data[i].msg + "
  • "
    }
    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 ) {
    html += "
  • 【" + data[i].user + "】购买了:" + data[i].msg + "
  • "
    }
    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/
    ///


    /// 根据缓存Key获取当前缓存最后更新的标示
    ///

    /// 缓存Key
    /// 最后更新的时间
    public string getlast(string cacheKey) {
    List
    ls = HttpRuntime.Cache.Get(cacheKey) as List;
    if (ls != null)
    return ls.First().time;
    return "0";
    }
    ///
    /// 服务器定时方法,这个方法是检测,数据有没有进行更新
    ///

    ///
    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 > 1) {
    builder.Remove(builder.Length
    - 1, 1);
    }
    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 ls = HttpRuntime.Cache.Get(cacheKey) as List;
    //判断是否为空
    if (ls == null) {
    ls
    = new List();
    HttpRuntime.Cache.Insert(cacheKey, ls);
    }
    //添加到集合
    ls.Insert(0, d);
    //移除大于这个数
    if (ls.Count > 10)
    ls.RemoveRange(
    10, ls.Count - 10);
    }
    public JsonResult GetList(string key) {
    if (string.IsNullOrEmpty(key))
    return Json(null, JsonRequestBehavior.AllowGet);
    return Json(HttpRuntime.Cache.Get(key) as List, 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  点击下载


    推荐阅读
    • 基于layUI的图片上传前预览功能的2种实现方式
      本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
    • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
      本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
    • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
    • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
    • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
    • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
      本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
    • 本文讲述了作者通过点火测试男友的性格和承受能力,以考验婚姻问题。作者故意不安慰男友并再次点火,观察他的反应。这个行为是善意的玩人,旨在了解男友的性格和避免婚姻问题。 ... [详细]
    • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
    • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
    • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
    • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
    • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
    • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
    • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
    • Voicewo在线语音识别转换jQuery插件的特点和示例
      本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
    author-avatar
    N__Z少爷_763
    这个家伙很懒,什么也没留下!
    PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
    Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有