[OpenSource] ScriptLoader V5:不一样的体验,客户端分布式缓存平台

简介:
前言

   ScriptLoader断断续续也开发了半年多了, 很多朋友都觉得这样的项目花俏多于实用, 其实有一个很重要的功能一直都没有开发,那就是我在[OpenSource]ScriptLoaderV2:彻底改变你的编程习惯提到过的ClientCacheManager.

   在开发中,每个人都或多或少的使用了Cache,为了减少数据库的负担,或者为了增加处理请求的速度.但是使用Cache也是一件很麻烦的事情,必须维护真实数据与缓存的同步,必须根据查询命中率判断Cache优先级有的放矢的删除低效缓存项,必须维护所有缓存对象的可序列化,必须维护服务器群集间的缓存同步......

   只不过,这些缓存的处理大部分都是在Server端.在RIA横行的今天,我们是否忽视了成千上万的客户端?当Ajax+Json支撑起很多系统的时候,我们是否应该可以更好的利用Browser的缓存机制来减少请求的次数,降低资源的消耗,获得更快的速度?

   客户端缓存的真正意义,在于减少网络请求,降低数据库查询,充分利用Server的内存,增加Application的吞吐量和请求处理效率,同时也可以提高客户端的响应速度,因为数据本来就存在客户端嘛^_^. 同时,缓存在客户端的东西,即使服务器重启亦可以持久保留.在现在这个大硬盘高内存为标配的PC时代,已经没啥人去手工清理IE缓存了,所以搞不好1年下来,它都一直用着同一个缓存呢:)

   基于ScriptLoader的缓存控制原理,ScriptLoaderV5,将带入走入这样一个被忽视的世界,带给你一个简单但用处颇多的客户端分布式缓存平台.

 

插件接口

  ScriptLoader在V5之前,只能算是一个Library,但现在它已经成为一个小的Framework,它同样可以管理外部插入的ScriptObject对象,并管理其版本的更新.

IPlugin的定义很简单.

复制代码
namespace LangZi.Scripts
{
     public  interface IPlugin
    {
         // Get the json data
         string GetContent();
         // Get the last modified datetime of the live data
        DateTime GetLastModified(); 
    }
}
复制代码

  

公共方法:

  为了对Plugin进行管理,ScriptLoader同样新增了几个方法

  1.LoadPlugin
    
看得出来,跟以前版本中的LoadStylesheet一样,只是封装了下Load的方法而已

     服务端:

public  void LoadPlugin( string fileName)
{
    Load(fileName +  " .Plugin ");
}

    客户端:

function LoadPlugin(name){
   Load(name+".plugin");
}

 

  2.RefreshPlugin:

     手工更新某个指定的Plugin的数据和版本信息. 当然ScriptLoader还有一个自动的维护Plugin版本信息的轮询机制.

  public  void RefreshPlugin( string pluginName)


  3.配置项:

  Plugin版本更新的轮询间隔时间:如果不配置,默认是180000;//30 minutes

< appSettings >
     < add  key ="VersionUpdateTime"  value ="50000" />
</ appSettings >

  Plugin部署配置

复制代码
     < configSections >
         < sectionGroup  name ="LangZi" >
             < section  name ="ScriptLoaders"  type ="System.Configuration.NameValueSectionHandler" />
         </ sectionGroup >
     </ configSections >
     < LangZi >
         < ScriptLoaders >
             < add  key ="Locations"  value ="ScriptLoader.Plugins.Location,ScriptLoader.Plugins" />
         </ ScriptLoaders >

    </LangZi>

 

复制代码

 

< httpHandlers > 
    < add  verb ="GET"  path ="*.plugin"  type ="LangZi.Scripts.ScriptLoaderHandler,LangZi.ScriptLoader" />
</ httpHandlers >

 

 应用场景演示:

 我相信很多人都做过省市联动控件,举这样一个例子,每个人都会比较熟悉.所以我开发了一个演示性的Plugin:

 

 Location.cs

复制代码
using System;
using System.Collections.Generic;
using System.Text;
using LangZi.Scripts;
using System.Xml;
using System.Web;
using System.IO;

namespace ScriptLoader.Plugins
{
     public  class Location:IPlugin
    {
         #region IPlugin Members

         public  string GetContent()
        {
            StringBuilder data =  new StringBuilder();
             /*
var locations = [{Name:'FuJian',City:[{Name:'XiaMen'},{Name:'FuZhou'}] } ,{Name:'BeiJing',City:[{Name:'ChongWen'},{Name:'ChaoYang'}]}];  
             
*/
            data.Append( " var locations = [ ");

            XmlDocument doc =  new XmlDocument();
            var filename = System.AppDomain.CurrentDomain.BaseDirectory+  " location.xml ";
            doc.Load(filename);
            XmlNode node = doc.SelectSingleNode( " /China ");
            
             for( int i= 0;i<node.ChildNodes.Count;i++)
            {
                XmlNode item = node.ChildNodes[i];
                 if(i== 0)
                {
                    data.AppendFormat( " {{Name:'{0}',City:[ ", item.Name);
                     for ( int n =  0; n < item.ChildNodes.Count; n++)
                    {
                        XmlNode city = item.ChildNodes[n];
                         if (n ==  0)
                        {
                            data.AppendFormat( " {{Name:'{0}'}} ", city.Name);
                        }
                         else
                        {
                            data.AppendFormat( " ,{{Name:'{0}'}} ", city.Name);
                        }
                    }
                    data.Append( " ]} ");
                }
                 else
                {
                    data.AppendFormat( " ,{{Name:'{0}',City:[ ", item.Name);
                     for ( int n =  0; n < item.ChildNodes.Count; n++)
                    {
                        XmlNode city = item.ChildNodes[n];
                         if (n ==  0)
                        {
                            data.AppendFormat( " {{Name:'{0}'}} ", city.Name);
                        }
                         else
                        {
                            data.AppendFormat( " ,{{Name:'{0}'}} ", city.Name);
                        }
                    }
                    data.Append( " ]} ");
                }
            }
            data.Append( " ]; ");
             return data.ToString();
        }
         public DateTime GetLastModified()
        {
            var filename = System.AppDomain.CurrentDomain.BaseDirectory +  " location.xml "
            FileInfo fi =  new FileInfo(filename);
             return fi.LastWriteTime;
        }
         #endregion
    }
}
复制代码

 

Location.xml

复制代码
<? xml version="1.0" encoding="utf-8"  ?>
< China >
     < FuJian >
         < FuZhou ></ FuZhou >
         < XiaMen >
             < SiMing ></ SiMing >
             < HuLi ></ HuLi >
             < TongAn ></ TongAn >
             < JiMei ></ JiMei >
             < HaiCang ></ HaiCang >
         </ XiaMen >
         < QuanZhou >
             < AnXi ></ AnXi >
             < NanAn ></ NanAn >
             < YongChun ></ YongChun >
             < JinJiang ></ JinJiang >
             < ShiShi ></ ShiShi >
         </ QuanZhou >
         < ZhangZhou ></ ZhangZhou >
     </ FuJian >
     < Shanghai >
         < HuangPuQu ></ HuangPuQu >
         < PuTuoQu ></ PuTuoQu >
     </ Shanghai >
     < BeiJing >
         < ChongWen ></ ChongWen >
         < ChaoYang ></ ChaoYang >
     </ BeiJing >
</ China >
复制代码

 

消费Plugin:

protected  void Page_Load( object sender, EventArgs e)
{
    LangZi.Scripts.ScriptLoader loader = LangZi.Scripts.ScriptLoader.RegisterInstance( this);
    loader.LoadPlugin( " Locations ");
}

 

复制代码
< html  xmlns ="http://www.w3.org/1999/xhtml" >
< head  runat ="server" >
     < title >Untitled Page </ title >
     < script  type ="text/javascript" >
    
function  Get(id){
    
return  document.getElementById(id);
    } 
    
function  SelectedProvinceOnChange(value){
      
var  currentProvince;
      
for (i = 0 ;i < locations.length;i ++ ){
          
if (locations[i].Name  ==  value){
            currentProvince 
=  locations[i];
            
break ;
          }
      }
      
var  cities  =  Get( " cities " );
      
for (i  =  cities.options.length - 1 ;i >- 1 ;i -- ){
            cities.options.remove(i)
        }
      
if (currentProvince  !=   null ){     
        
for (i = 0 ;i < currentProvince.City.length;i ++ ){
            
var  option  = new  Option(currentProvince.City[i].Name);
            cities.options.add(option);
        }
      }
    }
   
    
function  OnInit(){
     
var  Provinces  =  Get( " provinces " );
     
for (i = 0 ;i < locations.length;i ++ ){
      
var  option  = new  Option(locations[i].Name);
      Provinces.options.add(option);
     }    
     SelectedProvinceOnChange(locations[
0 ].Name);
    }
    
</ script >
</ head >
< body  onload ="OnInit()" >
     < form  id ="form1"  runat ="server" >
     < div >
        Province:
         < select  id ="provinces"  onchange ="SelectedProvinceOnChange(this.options[this.selectedIndex].text)" >
         </ select >
        City:
         < select  id ="cities" >
         </ select >
         < asp:Button  ID ="Button1"  runat ="server"  onclick ="Button1_Click"  Text ="Button"   />
     </ div >
     </ form >
</ body >
</ html >
复制代码

 

远期计划

    ScriptLoader开发到这个版本,已经接近我的思维极限了,后续的开发暂时找不出更大的突破,或许只是小修小补,比如对缓存的控制进一步精细,把目前的缓存控制周期进行分割,新增其他的周期,例如基于Page的周期(比Context长,但是比Session短,离开页面即失效).或者将ScriptObject的范畴扩展出去,包括任何Broswer可缓存的对象(image......).

   当然,基于组件的应用,那可想像的空间还是很大的,但这不能算是ScriptLoader的原生功能.

   所以说,如果到如今,你还找不到ScriptLoader在你的项目中的应用场景的话,那请忽略它,因为暂时我无能力再突破它,增加更新鲜的功能.

   09年,或许是我开源的一年,如果你对ScriptLoader不感兴趣,可以关注我的另一个开源项目LangZi.QuickCMS,当然目前这个项目只完成了一个组件LangZi.Web.UrlEngine,如果对UrlRewriter比较在乎的朋友,小瞄一下或许你会有不一样的收获^_^.


资源下载

   1.svn: https://scriptloader.svn.sourceforge.net/svnroot/scriptloader

   2.packages: http://code.google.com/p/langzi/downloads/list

 


本文转自浪子博客园博客,原文链接:http://www.cnblogs.com/walkingboy/archive/2009/01/13/ScriptLoaderV5.html,如需转载请自行联系原作者
目录
相关文章
|
消息中间件 运维 Kafka
直播预告|Kafka+Flink双引擎实战:手把手带你搭建分布式实时分析平台!
在数字化转型中,企业亟需从海量数据中快速提取价值并转化为业务增长动力。5月15日19:00-21:00,阿里云三位技术专家将讲解Kafka与Flink的强强联合方案,帮助企业零门槛构建分布式实时分析平台。此组合广泛应用于实时风控、用户行为追踪等场景,具备高吞吐、弹性扩缩容及亚秒级响应优势。直播适合初学者、开发者和数据工程师,参与还有机会领取定制好礼!扫描海报二维码或点击链接预约直播:[https://developer.aliyun.com/live/255088](https://developer.aliyun.com/live/255088)
755 35
直播预告|Kafka+Flink双引擎实战:手把手带你搭建分布式实时分析平台!
|
消息中间件 运维 Kafka
直播预告|Kafka+Flink 双引擎实战:手把手带你搭建分布式实时分析平台!
直播预告|Kafka+Flink 双引擎实战:手把手带你搭建分布式实时分析平台!
369 11
|
10月前
|
消息中间件 监控 Java
Apache Kafka 分布式流处理平台技术详解与实践指南
本文档全面介绍 Apache Kafka 分布式流处理平台的核心概念、架构设计和实践应用。作为高吞吐量、低延迟的分布式消息系统,Kafka 已成为现代数据管道和流处理应用的事实标准。本文将深入探讨其生产者-消费者模型、主题分区机制、副本复制、流处理API等核心机制,帮助开发者构建可靠、可扩展的实时数据流处理系统。
849 4
|
Java 关系型数据库 MySQL
新一代 Cron-Job分布式任务调度平台 部署指南
简单易用、超低延迟,支持用户权限管理、多语言客户端和多租户接入的分布式任务调度平台。 支持任何Cron表达式的任务调度,支持常用的分片和随机策略;支持失败丢弃、失败重试的失败策略;支持动态任务参数。
558 110
|
Java 调度 Maven
新一代 Cron-Job 分布式任务调度平台 正式发布!
简单易用、超低延迟,支持用户权限管理、多语言客户端和多租户接入的分布式任务调度平台。 支持任何Cron表达式的任务调度,支持常用的分片和随机策略;支持失败丢弃、失败重试的失败策略;支持动态任务参数。
631 130
|
缓存 NoSQL Redis
【Azure Redis 缓存】Redission客户端连接Azure:客户端出现 Unable to send PING command over channel
【Azure Redis 缓存】Redission客户端连接Azure:客户端出现 Unable to send PING command over channel
1412 3
|
存储 监控 固态存储
【vSAN分布式存储服务器数据恢复】VMware vSphere vSAN 分布式存储虚拟化平台VMDK文件1KB问题数据恢复案例
在一例vSAN分布式存储故障中,因替换故障闪存盘后磁盘组失效,一台采用RAID0策略且未使用置备的虚拟机VMDK文件受损,仅余1KB大小。经分析发现,该VMDK文件与内部虚拟对象关联失效导致。恢复方案包括定位虚拟对象及组件的具体物理位置,解析分配空间,并手动重组RAID0结构以恢复数据。此案例强调了深入理解vSAN分布式存储机制的重要性,以及定制化数据恢复方案的有效性。
538 5
|
机器学习/深度学习 人工智能 Shell
人工智能平台PAI操作报错合集之在分布式训练过程中遇到报错,是什么原因
阿里云人工智能平台PAI是一个功能强大、易于使用的AI开发平台,旨在降低AI开发门槛,加速创新,助力企业和开发者高效构建、部署和管理人工智能应用。其中包含了一系列相互协同的产品与服务,共同构成一个完整的人工智能开发与应用生态系统。以下是对PAI产品使用合集的概述,涵盖数据处理、模型开发、训练加速、模型部署及管理等多个环节。
|
安全
【📕分布式锁通关指南 07】源码剖析redisson利用看门狗机制异步维持客户端锁
Redisson 的看门狗机制是解决分布式锁续期问题的核心功能。当通过 `lock()` 方法加锁且未指定租约时间时,默认启用 30 秒的看门狗超时时间。其原理是在获取锁后创建一个定时任务,每隔 1/3 超时时间(默认 10 秒)通过 Lua 脚本检查锁状态并延长过期时间。续期操作异步执行,确保业务线程不被阻塞,同时仅当前持有锁的线程可成功续期。锁释放时自动清理看门狗任务,避免资源浪费。学习源码后需注意:避免使用带超时参数的加锁方法、控制业务执行时间、及时释放锁以优化性能。相比手动循环续期,Redisson 的定时任务方式更高效且安全。
1170 24
【📕分布式锁通关指南 07】源码剖析redisson利用看门狗机制异步维持客户端锁
|
SQL 监控 Go
新一代 Cron-Job分布式调度平台,v1.0.8版本发布,支持Go执行器SDK!
现代化的Cron-Job分布式任务调度平台,支持Go语言执行器SDK,多项核心优势优于其他调度平台。
342 8