[转载]ASP.NET应用下基于SessionState的“状态编程框架”解决方案

mikel阅读(912)

[转载]ASP.NET应用下基于SessionState的“状态编程框架”解决方案 – Artech – 博客园.

在一个基于ASP.NET的Web应用程序中,我们通常使用SessionState保存基于某个客户端的状态信息。但是这种单纯使用SessionState的编程方式具有很多局限,比如Session Item的Key值冲突,比如没有一个有效的SessionState清除机制会为Web Server带来内存压力。 为了实现对客户端状态的有效管理,并提高应用开发效率,在很多年前我们的开发框架体系中就具有相应的一个叫做State的编程框架。最近我开始对其进行升 级和重新设计,将实现原理和概要设计方面的东西写出来与大家共享,希望对各位有些启发。同时希望借此得到你们一些好的建议和意见,以便能够充实我们的框 架。于此同时,我写了一个简单的模拟程序实现了该设计思想,有兴趣的话可以通过这里下载该模拟程序。

一、单纯基于SessionState编程的局限性

SessionState对于ASP.NET的开发者在熟悉不过了,我们可以通过它来存储一些基于客户端的状态信息。从编程角度来说,SesssionState是依附和当前HttpContext的一个用于类似于字典的数据容器,我们通过键值对的方式进行Session Item的设置和获取。但是这种单纯地基于字典索引的编程方式,具有诸多局限:

  • 首先,这种弱类型的编程方式不便于快速开发需求。放入SessionState的值是一个System.Object类型的对象,在获取的使用我们需要进行手工转型;而Session Item的Key是手工指定的字符串,如果没有对Key值进行有效的分配,在进行设置的时候很容易造成一个Key值得冲突,从而导致整个状态的混乱;在获取某个Session Item的时候,你指定的Key值可能和预先指定的不符。
  • 其次,统一的SessionState的清除机制的缺乏导致服务端内存压力。 在默认的情况下(采用InProc会话模式),SessionState存储于服务端内存,如果过多、过大的Session Item常驻内存,势必会为服务端带来内存压力。实际上,基于客户端的所有的Session Item并不是在整个Session存续期间都是必须的,很多Session Item仅仅是在某几个少数的Web页面中使用。但是我们不能通过程序手工地将其从SessionState中删除,因为我们不能确定该Session Item在那一刻不再需要,因为这往往取决于UI交互的行为。如果太多的低频率使用的Session Item存在,并且它们还不小,服务端内存过多地被占用必要导致性能的下降。
  • 最后,如果你采用State Server或者SQL Server会话管理模式,还会造成更多的性能问题。 这样的性能损失包括:Session Item的序列化和反序列化、序列化后的Session Item在Web Server和State Server或者SQL Server的网络传输、针对State Server或者SQL Server的数据存取(保存和提取)等。

实际上,我们的State框架还是建立在SessionState基础之上,但是它能够很好的解决上述的三大难题:

  • 通过配置为所有使用到的状态项(状态属性名称、数据类型等)提供结构化的定义,并通过基于该结构化配置提供的代码生成使强类型编程成为可能。这比较类似于ASP.NETProfile的配置和强类型编程的方式;
  • 提供状态的后备存储(Backing Storing)机制将低频率使用的大对象从SessionState中移到相应的后备存储(比如文件、数据库)中,从而缓解服务端内存压力;
  • 提供灵活的后备策略定义方 式以实现基于具体运行环境的最优配置。后备策略主要包括两方面的内容,其一是怎样的状态项需要被后备存储,其二采用怎样的方式进行后备存储。确定后备存储 状态项的因素包括:自最近一次被访问以来的超时时限(通过使用频率判断状态项再次被使用的可能性);需要被后备存储对象必须具有的最小字节数(后备存储小 对象毫无意义) ;以及状态项的作用域(很多状态项的作用范围仅仅限于某一个相关的Web页面,或者基于某个基地址)等。而具体采用的后备存储方式决定于配置的“后备存储 器”,比如在我提供的例子中采用的是基于文件的存储方式,你可以编写基于数据库的后备存储器。

二、通过状态后备存储机制解决Web Server内存的压力

状态的后备机制是整个状态编程框架的核心。通过对所有状态项的扫描,标记出所有需要进行后备存储的状态项。然后将它们进行序列化,并借助于指定的后备存储器将它们存储到相应的物理存储介质。最后,相应的状态会从SessionState中删除,从而缓解了Web Server的内存压力。除了将序列化的状态对象进行后备存储之前,后备存储器还负责从相应的存储介质中提取状态数据。

image简单起见,我们并没有在后台运行一个实施后备检测操作的引擎,而是直接通过事件注册的方式让每一个请求自动去触发基于本会话的后备存储,我们注册的事件是HttpApplication的PostRequestHandlerExecute。出于性能的考虑,当事件PostRequestHandlerExecute被触发的时候,并不是总是立即执行后备状态项的检查。而是设置一个相邻两次后备检查的间隔,只有超出这个间隔的情况下,才会进行真正地区检查那些状态向需要进行后备存储了。状态项的后备存储紧接着在后备对象的检查之后进行。

我们通过一个具体的例子来进一步说明后备存储的过程。如左图(点击看大图)所示,在Web Server的IIS进程中的SessionState中维持着三个状态项:Foo、Bar、Baz。当Web Server接收并执行来自浏览器的HTTP请求后,PostRequestHandlerExecute事件的处罚激活了我们的后备检查管理器,它发现状态项Baz最近一次被访问的时间到当前时间的间隔已经超出了设置的超时时限,并且计算出该对象的总字节数超过了设定的下限,就会将该对象标记为后备存储对象。在这种情况下,状态项Baz的值,同它的Key一并进行序列化并进行后备存储。最后将该Baz从SessionState中移除。

如果该Web应用使用Web Farm部署方式,并采用了Sate Server或者SQL Server的会话模式,在同步到Sate Server或者SQL Server的时候,由于SessionState中缺少了Baz这个大对象,也会因为少了对它序列化、网络传输和数据存取使性能得到相应的提升。

三、后备存储状态项的“复苏”

Drawing1被后备存储的状态项已经不再存储于SessionState中,但是并不意味着它已经是所谓的垃圾对象,它们依然可以被再次访问。在这种情况下,我们会通过我们指定的后备存储器将相应的状态值以字节数组的形式从存储介质中提取出来,进行反序列化后再次放到SessionState中,我个人将这种机制成为“后备对象的复”。

在对后备对象的复苏机制进行进一步讲解之前,我们需要了解一个前提:框架始终维护着每一个状态项运行时信息,这些信息包括:状态项最后一次被访问的时间状态项的使用范围状态项当前的存储位置(SessionState或者BackingStore)、以及相关的后备策略信息等。这个列表放在SessionState中。

右 面所示的序列图(点击看大图)反映了当我们的程序获取某个状态项时,状态后备机制采用的处理流程:当接收到一个来自对某个状态项的请求时,根据Key值获 取该状态项当前的运行时信息。如果运行时信息反映它还存在于SessionState中(Location=Session),则直接从 SessionState中返回,并更新它的运行时信息(最后一次被访问时间)。

如果该状态项已经进行了背后存储 (Location=BackingStore),则借助相应的后备存储器从存储介质中对应的值以字节数组的形式提取出来。在完成反系列化后再次保存到 SessionState中,并更新相应运行时信息(最后一次访问时间和当前位置:BackingStore-〉Session)。最后返回反序列化后的 具体状态对象。

四、状态项后备策略的定义

判断一个存在于SessionState中的状态项是否应该被后备存储取决于以下三个方面,当同时满足条件1和2,或者2和3的状态项会被后备存储。

  • 针对该状态项的最近一次访问的事件到当前时间的间隔超过了设定的超时时限;
  • 状态项的总的字节数超过了设定的需要进行后备存储的下限;
  • 当前的请求的URL是否超出了设定的状态作用的范围。

但是我们的状态后备策略并没有直接应用于单个的状态项,而是应用于一个较大的粒度:状态组——若干相关状态项的组合。状态组的结构和应用在它上面的后备策略通过配置进行定义,下面的XML体现的配置大体上的结构。

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <states>
   3:   <properties>
   4:     <property name="UserName" type="System.String"/>
   5:     <property name="Position" type="System.String"/>
   6:   </properties>
   7:   <group name="Profile" inactiveTimeout="00:10:00" minimunTotalBytes="1024" >
   8:     <property name="Age" type="System.Int32"/>
   9:     <property name="Address" type="System.String"/>
  10:   </group>
  11:   <group name="Product" inactiveTimeout="00:10:00" minimunTotalBytes="1024" scope="Page1, Page2,Page3" >
  12:     <property name="ProductId" type="System.String"/>
  13:     <property name="UnitPrice" type="System.Decimal"/>
  14:   </group>
  15: </states>

在上面的XML片段中,我们定义两个全局的状态项(UserName和Position)和两个状态组(Profile和Product)。两个状态组中又包含各自的状态项,以及对应的后备策略。inactiveTimeoutminimumTotlaBytesscope分别表示超时时限、序列化后的最下值和使用的范围。

五、通过代码生成机制帮助你以强类型的方式操作状态

既然所有的状态和数据类型(即可以是系统预定义类型,也可以是自定义类型)都能通过XML的形式表示出来,那么我们就能通过代码生成机制将它们通过代码的形式反映出来。你可以采用CodeDOM+Cutom Tool的方式[可以参考我的文章《从数据到代码》(上篇下篇)],或者是直接使用T4模板[可以参考我的文章《创建代码生成器可以很简单:如何通过T4模板生成代码?》(上篇下篇)]。比如说,你可以生成一个继承自Page的类型,比如PageBase,添加如下一个State的属性。(下面的代码仅仅代码大体的结构,并省略的具体的实现)

   1: public class PageBase : Page
   2: {
   3:     public ExtendedRootStateNode State { get; }
   4: }
   5: public class ExtendedRootStateNode : RootStateNode
   6: {
   7:     public string UserName { get; set; }
   8:     public string Position { get; set; }
   9:     public ProfileGroupStateNode Profile { get; private set; }
  10:     public ProductGroupStateNode Product { get; private set; }
  11: }
  12: public class ProfileGroupStateNode : GroupStateNode
  13: {
  14:     public int Age { get; set; }
  15:     public Gender Gender { get; set; }
  16:     public string Address { get; set; }
  17: }
  18: public class ProductGroupStateNode : GroupStateNode
  19: {
  20:     public string ProductId { get; set; }
  21:     public string ProductName { get; set; }
  22: }

如果让你的所有Web页面都继承自这个PageBase,你可以通过强类型的方式获取或者设置每个状态项了。

image

作者:Artech
出处:http://artech.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

[转载]JavaScript闭包深入详解

mikel阅读(781)

[转载]js闭包深入详解 – systemxgl – 博客园.

一、什么是闭包?

“官方”的解释是:闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
相信很少有人能直接看懂这句话,因为他描述的太学术。其实这句话通俗的来说就是:JavaScript中所有的function都是一个闭包。不过一般来说,嵌套的function所产生的闭包更为强大,也是大部分时候我们所谓的“闭包”。看下面这段代码:

function a() {    var i = 0;    function b() {        alert(++i);    }    return b;}
var c = a();
c();

这段代码有两个特点:

  1. 函数b嵌套在函数a内部;
  2. 函数a返回函数b。

引用关系如图:

jsclosure

这样在执行完var c=a()后,变量c实际上是指向了函数b,b中用到了变量i,再执行c()后就会弹出一个窗口显示i的值(第一次为1)。这段代码其实就创建了一个闭包,为什么?因为函数a外的变量c引用了函数a内的函数b,就是说:

当函数a的内部函数b被函数a外的一个变量引用的时候,就创建了一个我们通常所谓的“闭包”。

让我们说的更透彻一些。所谓“闭包”,就是在构造函数体内定义另外的函数作为目标对象的方法函数,而这个对象的方法函数反过来引用外层外层函数体中 的临时变量。这使得只要目标 对象在生存期内始终能保持其方法,就能间接保持原构造函数体当时用到的临时变量值。尽管最开始的构造函数调用已经结束,临时变量的名称也都消失了,但在目 标对象的方法内却始终能引用到该变量的值,而且该值只能通这种方法来访问。即使再次调用相同的构造函数,但只会生成新对象和方法,新的临时变量只是对应新 的值,和上次那次调用的是各自独立的。

为了更深刻的理解闭包,下面让我们继续探索闭包的作用和效果。

二、闭包有什么作用和效果?

简而言之,闭包的作用就是在a执行完并返回后,闭包使得JavaScript的垃圾回收机制GC不会收回a所占用的资源,因为a的内部函数b的执行需要依赖a中的变量。这是对闭包作用的非常直白的描述,不专业也不严谨,但大概意思就是这样,理解闭包需要循序渐进的过程。
在上面的例子中,由于闭包的存在使得函数a返回后,a中的i始终存在,这样每次执行c(),i都是自加1后alert出i的值。

那么我们来想象另一种情况,如果a返回的不是函数b,情况就完全不同了。因为a执行完后,b没有被返回给a的外界,只是被a所引用,而此时a也只会 被b引用,因此函数a和b互相引用但又不被外界打扰(被外界引用),函数a和b就会被GC回收。(关于Javascript的垃圾回收机制将在后面详细介 绍)

三、闭包的微观世界

如果要更加深入的了解闭包以及函数a和嵌套函数b的关系,我们需要引入另外几个概念:函数的执行环境(excution context)、活动对象(call object)、作用域(scope)、作用域链(scope chain)。以函数a从定义到执行的过程为例阐述这几个概念。

  1. 定义函数a的时候,js解释器会将函数a的作用域链(scope chain)设置为定义a时a所在的“环境”,如果a是一个全局函数,则scope chain中只有window对象。
  2. 执行函数a的时候,a会进入相应的执行环境(excution context)
  3. 在创建执行环境的过程中,首先会为a添加一个scope属性,即a的作用域,其值就为第1步中的scope chain。即a.scope=a的作用域链。
  4. 然后执行环境会创建一个活动对象(call object)。活动对象也是一个拥有属性的对象,但它不具有原型而且不能通过JavaScript代码直接访问。创建完活动对象后,把活动对象添加到a的作用域链的最顶端。此时a的作用域链包含了两个对象:a的活动对象和window对象。
  5. 下一步是在活动对象上添加一个arguments属性,它保存着调用函数a时所传递的参数。
  6. 最后把所有函数a的形参和内部的函数b的引用也添加到a的活动对象上。在这一步中,完成了函数b的的定义,因此如同第3步,函数b的作用域链被设置为b所被定义的环境,即a的作用域。

到此,整个函数a从定义到执行的步骤就完成了。此时a返回函数b的引用给c,又函数b的作用域链包含了对函数a的活动对象的引用,也就是说b可以访问到a中定义的所有变量和函数。函数b被c引用,函数b又依赖函数a,因此函数a在返回后不会被GC回收。

当函数b执行的时候亦会像以上步骤一样。因此,执行时b的作用域链包含了3个对象:b的活动对象、a的活动对象和window对象,如下图所示:

http://www.felixwoo.com/wp-content/uploads/attachments/200712/11_110522_scopechain.jpg

如图所示,当在函数b中访问一个变量的时候,搜索顺序是:

  1. 先搜索自身的活动对象,如果存在则返回,如果不存在将继续搜索函数a的活动对象,依次查找,直到找到为止。
  2. 如果函数b存在prototype原型对象,则在查找完自身的活动对象后先查找自身的原型对象,再继续查找。这就是Javascript中的变量查找机制。
  3. 如果整个作用域链上都无法找到,则返回undefined。

小结,本段中提到了两个重要的词语:函数的定义执行。文中提到函数的作用域是在定义函数时候就已经确定,而不是在执行的时候确定(参看步骤1和3)。用一段代码来说明这个问题:

function f(x) {
 var g = function () { return x; }
 return g;
}
var h = f(1);
alert(h());

这段代码中变量h指向了f中的那个匿名函数(由g返回)。

  • 假设函数h的作用域是在执行alert(h())确定的,那么此时h的作用域链是:h的活动对象->alert的活动对象->window对象。
  • 假设函数h的作用域实在定义时确定的,就是说h指向的那个匿名函数在定义的时候就已经确定了作用域。那么在执行的时候,h的作用域链为:h的活动对象->f的活动对象->window对象。

如果第一种假设成立,那输出值就是undefined;如果第二种假设成立,输出值则为1。

运行结果证明了第2个假设是正确的,说明函数的作用域确实是在定义这个函数的时候就已经确定了。

四、闭包的应用场景

  1. 保护函数内的变量安全。以最开始的例子为例,函数a中i只有函数b才能访问,而无法通过其他途径访问到,因此保护了i的安全性。
  2. 在内存中维持一个变量。依然如前例,由于闭包,函数a中i的一直存在于内存中,因此每次执行c(),都会给i自加1。
  3. 通过保护变量的安全实现JS私有属性和私有方法(不能被外部访问)
  4. 私有属性和方法在Constructor外是无法被访问的
    function Constructor(...) {    var that = this;    var membername = value;    function membername(...) {...}}

以上3点是闭包最基本的应用场景,很多经典案例都源于此。

五、Javascript的垃圾回收机制

在Javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收。如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。因为函数a被b引用,b又被a外的c引用,这就是为什么函数a执行后不会被回收的原因。

六、结语

理解JavaScript的闭包是迈向高级JS程序员的必经之路,理解了其解释和运行机制才能写出更为安全和优雅的代码。

本文采编于租赁宝网内部技术人员 参考网址:http://www.zulinbao.com

原文链接:http://hi.baidu.com/bluedream_119/blog/item/acff57d6dfd9d52506088b42.html

[转载]由web程序出现乱码开始挖掘(Bom头、字符集与乱码)

mikel阅读(847)

[转载]由web程序出现乱码开始挖掘(Bom头、字符集与乱码) – 沉于思考,默默学习! – 博客园.

从第一次开始写web程序,自己还有身边同事开发出现乱码情况基本都没有消停过。估计以后还会一样继续。 这么些年,不断修修改改,也总结也归纳。程序从asp,ASP.NET,jsp,php,服务器从windows到linux,数据库也从 SQLServer,mySQL到oracle;它还是偶尔会出现。 好了,我总结下我与它较量的一些收获吧。乱码都与字符集有关系,一切都从它开始说。

  • 什么是字符集,什么是字符编码,它做什么用?

字符(Charcter)是文字与符号的总称,包括文字、图形符号、数学符号等。而字符集是一组抽象的字符组合的集合。如:英文字符集,中文字符集,日文字符集等

什么是字符编码?

计算机只能存储0,1之类2进制数字,怎么样让它表示那么多各种各样的字符呢?就需要对各种字符指定一个数值的编码代号它就是字符编码。如:a这个 字符,在ascii字符集编码表中对应的编号是97,而“中”在gb2312字符集中对应的编号是:16进制是D6D0 10进制是54992 。通过编号就可以找到计算机对应字符。不用将那么复杂的字符保存在计算机中,只需要保存它代号就好。字符集只是指定了一个集合中有哪些字符,而字符编码,是为这个集合中所有字符定义个编号,这就是字符集与编码区别所在。

如果我告诉别人,我这个字符是:gb2312字符集中编号是:54992或者是D6D0 ,无论那个程序都知道是”中”,如果有人听错了,把它弄成日文JIS字符集,然后他也去找编号是:54992对应的字符,却找到的是:”面“。

打了这个比方,相信大家找到原因了,同样如果把54992拿到ascii 码表找,就会得到对应:�� 两个不能打印字符了。

从上面看,当你拿到本来是gb2312编号,在不是它的字符集里面找就出现这样问题了。 其它,程序出现乱码也都是这个原因,找错了字符集表了.

  • 字符在计算机是怎么样存储的呢?

看了上面介绍,我想大家一定会说,如果所有文本的字符,都用它的符号存储在计算机里面,不就什么问题都么有吗? 以前也这么想,后来一想啊,如果都存在计算机中,各种各样,怎么样表示呢?计算机处理01之类数字该多方便呢。

我们可以通过winhex实际来检查下,下面在简体中文下,将”中按照gb2312字符集编码保存。

image

以gb2312编码保存中文“中”,实际存储在计算机中是:D6D0,是“中”在字符集gb2312中的编号啦!

计算机中只保存字符在某字符集中对应的字符编号值,计算机只需要维持一份字符集清单,当读到这种编号值(编码),就在对应字符清单中找出该字符显示出来即可。字符大小颜色都是程序按照字符样式绘制而成的。

看个图:

image

计算机中只保存该字符在某字符集中对应的字符编号(也叫字符编码)

  • 怎么样读取文件并正确显示文件内容?

从上面例子知道字符实际以该字符在某字符集中字符编码存储与计算机磁盘中。

下面以“中国”为例(中文简体windows):

“中国” 保存编码 存储内容 记事本打开
ANSI D6D0 B9FA 正常
gb2312 D6D0 B9FA 正常
utf-8+BOM EFBBBF E4B8AD E59BBD 正常
utf-8 E4B8AD E59BBD 正常
unicode+BOM FFFE 2D4E FD56 正常
unicode 2D4E FD56 乱码(变成ANSI)
EUC-JP C3E6 B9F1 乱码(变成ANSI)
iso-8859-1 3F 3F 乱码(变成ANSI)

以下以:“abc”为例

“abc” 保存编码 存储内容 记事本打开
ANSI 61 62 63 正常
gb2312 61 62 63 正常
utf-8+BOM EFBBBF 61 62 63 正常
utf-8 61 62 63 正常
unicode+BOM FFFE 61 62 63 正常
unicode 61 62 63 正常(变成ANSI)
EUC-JP 61 62 63 正常(变成ANSI)
iso-8859-1 61 62 63 正常(变成ANSI)

从上面可以得到几点:

1、以某字符集存储字符时,它会在该字符集中搜索这个字符的位置(编号或编码),以这个编号(编码)保存在文件,如果所搜找不到该字符,一般会以:3F 3F保存(一定会出错)

2、当需要显示字符,从取文件中字符编码,如果没有指定字符集,会通过文件头(主要unicode字符集有特殊头标记)判断字符集,如果不是 unicode字符集,默认都以ANSI字符集读取,用字符编码在该字符集中寻找对应字符,如果搜索到就正常显示,搜索不到就会显示乱码.

  • 常见问题字符疑问收集

什么是ANSI字符集?

这个不是固定字符集,如果在中文简体windows中,它代码字符集是gb2312,在繁体值代表是big5等等。

为什么英文字符不会出现乱码?

常见ascii码字符集是:128字符,对应编码值是:1-128 ,二进制表示是:00000001-01111111。它表示了所有常见英文数字,标点符号。其它字符集都是由ascii码字符集扩展而来,扩展了最高位 由10000000开始,用多字节表示新的字符,基本都保留了:0xxxxxxx 开头128个基本字符,而且对应编码与ascii码相同。

这样,常见英文字符不论在那种字符集中,对应字符编码一致,存储编码也一样。读取时候无论用什么字符集读取,它所对应字符也一直。所有基本不会出现乱码情况。

读取软件能够识别存储文件的字符集吗?

由于目前各种字符集加起来有上百种,目前除了unicode字符集,定义的存储文件头,基本其它字符集只是给出了对应的字符编号值。因此,相同编号 会出现在不同的字符集中,光从文件存储的编码值,是不能确定它的字符集的。如:gb2312字符集中,D6d0对应是“中”,而同样是:D6D0在 ecu-jp 字符集中,对应是“面”

什么是bom头?

bom全称是:byte order mark,汉语意思是标记字节顺序码。只是出现在:unicode字符集中,只有unicode字符集,存储时候,要求指定编码,如果不指定,windows还会用默认的:ANSI读取。常见的bom头是:

  UTF-8 ║ EF BB BF 
  UTF-16LE ║ FF FE (小尾)
  UTF-16BE ║ FE FF (大尾)
  UTF-32LE ║ FF FE 00 00 
  UTF-32BE ║ 00 00 FE FF


unicode与utf-8 、utf-16 utf-32是什么关系?

unicode(统一码、万国码、单一码)是一种字符集,Unicode是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。 Unicode用数字0-0x10FFFF来映射这些字符,最多可以容纳1114112个字符,或者说有1114112个码位。UTF-8、UTF- 16、UTF-32都是将数字转换到程序数据的编码方案。在Unicode中:汉字“字”对应的数字是23383。我们可以用:UTF-8、UTF- 16、UTF-32表示这个数字,将数字23383存储在计算机中。UTF-8对应是:0xE6, 0xB1, 0x89(3个字节),UTF-16对应是:0x6c49(2个字节),UTF-32对应是:0x6c49(4个字节)。utf-8,utf- 16,utf-32是unicode码一种实现形式,都是属于unicode编码。

unicode编码特点是什么?

unicode编码特点是,它定义了编码方式和存储实现方式。编码方式就是上面说的可以用,utf-8…utf-32表示,而存储实现方式,无论那种编码都知道了文件头(bom)。因此,可以通过这个特殊头来判断存储的文本文件使用那种字符集编码。
 
为什么utf-8编码不指定bom头(可以理解为文件头),软件任然可以正常判断出它字符集编码?
这个问题估计很多朋友都会产生疑问,为什么utf-16不指定就读乱码,而utf-8可以。我们可以从下面的例子看下: utf-8是怎么样从unicode转换而来了。

  Unicode编码(16进制) ║ UTF-8 字节流(二进制) 
  000000 - 00007F ║ 0xxxxxxx 
  000080 - 0007FF ║ 110xxxxx 10xxxxxx 
  000800 - 00FFFF ║ 1110xxxx 10xxxxxx 10xxxxxx 
  010000 - 10FFFF ║ 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
从上面看,发现规律没有?第一个自己开头有几个”1”,后面就对应有几个10开头字节了。 这样我们都可以通过正则进行检测了.

[\x09\x0A\x0D\x20-\x7E]             # ASCII
|[\xC2-\xDF][\x80-\xBF]              # non-overlong 2-byte
|\xE0[\xA0-\xBF][\x80-\xBF]         # excluding overlongs
|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}   # straight 3-byte
|\xED[\x80-\x9F][\x80-\xBF]         # excluding surrogates
|\xF0[\x90-\xBF][\x80-\xBF]{2}      # planes 1-3
|[\xF1-\xF3][\x80-\xBF]{3}           # planes 4-15
|\xF4[\x80-\x8F][\x80-\xBF]{2}      # plane 16

由于它独特的编码存储特点,因此目前常见文本处理软件就能够自动分析出来。(windows记事本,editplus,notepad++等)

为什么bom头会产生乱码?

有bom头的存储或者字节流,它一定是unicode字符集编码。到底属于那一种(utf-8还是utf-16或是utf-32),通过头可以判断 出来。由于已经说过utf-16,utf-32不指定bom头,解析程序默认就认为是ansi编码,出现乱码。 而utf-8指定或者不指定程序都可判断知道对于的字符集编码。问题就出在这里,可能有的应用程序(ie6浏览器),它就认为如果utf-8编码,就不需 要指定bom头,它可以自己判断,相反指定了bom头,它还会出现问题(因为它把头当utf-8解析出现乱码了)。这里不截图了,cnblogs里面谈这 个比较多,目前ie6会出现问题。其它ie7+,firefox,chrome不会出现,会忽略掉bom头。 统一解决办法是:存为utf-8编码是,不需要加入bom头,其它utf-16,utf-32加入。

通过程序运算gb2312编码能够自动转换为utf-8编码吗?

utf-8实际是unicode字符集表现方式。如果看了这2种字符集编码表就清楚了。 它是2个独立字符集,相同汉字在2个字符集中所对应编号没有关系,而且汉字顺序也不同,gb2312先按照拼音后按照笔画排序,而unicode没有做相 应规定。我们清楚知道,如果没有对应字符集映射关系表在手。通过直接程序进行运算是实现不了的。如果你手里有这2个字符集映射表。如:”字”utf-8 是:0xE6, 0xB1, 0x89 ,对应unicode编码是:23383,然后拿23383,在unicode字符集寻找,发现是字符“字”,接着将“字”这个字符,拿到gb2312表 中查询:0xCE,0xC4 因此转换结果是:0xE6,0xB1,0x89 —> 0xCE,0xC4。

GB2312、GBK、gb18030、Big5是什么关系?

GB2312:1980年的GB2312一共收录了7445个字符,包括6763个汉字和682个其它符号。汉字区的内码范围高字节从B0-F7,低字节从A1-FE,占用的码位是72*94=6768。其中有5个空位是D7FA-D7FE。

GBK: 汉字国标扩展码,基本上采用了原来GB2312-80所有的汉字及码位,并涵盖了原Unicode中所有的汉字20902,总共收录了883个符号, 21003个汉字及提供了1894个造字码位。包括港、台两种汉字字库.

GB18030-2000产生,在GBK汉字标准字符集继续扩展,GB18030是GBK的超集,也就是包含的字符要比GBK多,又增加了6351 个字符,其中一部分为4字节字(four-byte encoding range)。增加了六种少数民族语言和一些四字节字。

Big5是中国台湾的,是繁体中文代表

GB18030兼容GBK兼容GB2312 ,相同常用汉字在GB2312编码表中字符编号(编码)与GBK,GB18030相同。如:”字“gb2312字符编码是:0xCE,0xC4 ,它在其它2个里面也是这个。因为GB2312只有7000多常用汉字,当出现繁体,古文时候就会出现问题,因此采用大集合的GB18030是个不错选 择。

Big5与GB2312不能通过程序相互转换,需要有字符集映射关系表才能完成。

字符集是怎么样一个演变过程呢?

这个如果讲故事可以讲很久了。当计算机有美国人发明后,当时设计到字符输入,由于是英文字符,通过收集整理。它们形成了标准的ascii码 (128) 字符集。8位,首位为0。 由于不断普及,欧洲西方国家相应使用,发现有些特殊字符它们不能表示,如:λφ等。如是出来想法,想利用ascii码后128位,增加它们的字符。这样就 出现了EASCII码。这些还是不能表示所有国家,想法语,俄语等有自己特殊字符。因此制定标准将后128位进行分片制定。制定出iso-8859系列字 符集。

ISO/IEC 8859-1 (Latin-1) – 西欧语言
ISO/IEC 8859-2 (Latin-2) – 中欧语言
ISO/IEC 8859-3 (Latin-3) – 南欧语言。世界语也可用此字符集显示。
ISO/IEC 8859-4 (Latin-4) – 北欧语言
ISO/IEC 8859-5 (Cyrillic) – 斯拉夫语言
ISO/IEC 8859-6 (Arabic) – 阿拉伯语
ISO/IEC 8859-7 (Greek) – 希腊语
ISO/IEC 8859-8 (Hebrew) – 希伯来语(视觉顺序)
ISO 8859-8-I – 希伯来语(逻辑顺序)
ISO/IEC 8859-9(Latin-5 或 Turkish)- 它把Latin-1的冰岛语字母换走,加入土耳其语字母。
ISO/IEC 8859-10(Latin-6 或 Nordic)- 北日耳曼语支,用来代替Latin-4。
ISO/IEC 8859-11 (Thai) – 泰语,从泰国的 TIS620 标准字集演化而来。
ISO/IEC 8859-13(Latin-7 或 Baltic Rim)- 波罗的语族
ISO/IEC 8859-14(Latin-8 或 Celtic)- 凯尔特语族
ISO/IEC 8859-15 (Latin-9) – 西欧语言,加入Latin-1欠缺的芬兰语字母和大写法语重音字母,以及欧元(€)符号。
ISO/IEC 8859-16 (Latin-10) – 东南欧语言。主要供罗马尼亚语使用,并加入欧元符号。

这些在一段时间,可以解决西方国家常见字符。当后来电脑在中日韩等国家普及时候,象中国常见汉字有7000多个,扩展128个空位,完全不够。因 此,需要用多个字节表示。后来就定,第一个字节,第一位如果是1,后面还有一个字节与之一起表示一个字符。如果是0,就对应ascii码。 这样就形成了国内的gb2312,后来还是不够表示繁体中文,加入了:gbk,最后是gb18030,但是,这样全世界各个国家还是用它们自己字符集进行 表示。没有一个统一的大字符集,能够表示全球所有字符。直到unicode出现,它的设计最多可以表示100多万个字符。全球所有字符都可以收纳在其中。 写出的程序,不用经常进行各种编码转换。就可以让世界上所有国家可以阅读对应字符文字。

什么是代码页,它与字符集有什么关系?

大家在指定网页程序语言生活,还记得cp936表示中文代码页(code page)。那么它与我们说的gbk字符集有什么关系呢?代码页是字符集编码的别名,也有人称”内码表”。早期,代码页是IBM称呼电脑BIOS本身支持的字符集编码的名称。

常见字符集与代码页直接映射是:

cp      charset

932 — 日文
936 — 简体中文(GBK)
949 — 韩文
950 — 繁体中文(大五码)
1200 — UCS-2LE Unicode 小端序
1201 — UCS-2BE Unicode 大端序
65001 — UTF-8 Unicode

936就是我们的gbk字符编码集。

CJK字符集是什么?

cjk代号意思是:汉语(Chinese)、日语(Japanese)、韩语(Korean)。也就是包含这3国语言的字符集。包含这3个国家常见的汉字,一共有2万多个。

所有软件都会默认是:ANSI字符集吗?

不同程序默认读取字符集不同,上面举例是记事本默认是这样的。在php里面会以:iso-8859-1读取。 jsp程序,java默认字符集也是:iso-8859-1。

为什么很多软件程序编译过程使用是:iso-8859-1字符集?

由于我们通过应用程序书写的软件是用各种字符集编码保存在磁盘中。如果中文默认是用gbk。保存于磁盘中,默认以字节码保存,是否有无中文,每个字 码值在:1-256。这些刚好可以用iso-8859-1扩展单字节字符集。因为,它是存储最小单元。在程序语言里面,不会用中文作变量,函数等名称。那 些对应都是常见ascii码。而中文用字注释,或者一些常量中。在编译时候不会影响到语法问题。它会以原来字节码保持原样。在读取时候,只需要用对应的字 符集编码读出,就能得到对应中文字符。

[转载]Windows Phone 7程序设计”完全版电子书可以免费下载了

mikel阅读(917)

[转载]Windows Phone 7程序设计”完全版电子书可以免费下载了 – 永恒的记忆 – 博客园.

现在学习Windows Phone 7开发资料十分有限,除了MSDN的官方开发文档外和一些博客外,几无其他的学习渠道。幸运地是美国的资深程序员兼作家Charles Petzold为大家免费放出了他的最新大作—《Programming windows phone 7 series》。他以往写的书籍本本是经典,包括我们耳熟能详的《Windows 程序设计》一书。

目前该书一共24章,,对Windows Phone 7平台的特征、XAML在程序界面和布局方面,以及XNA游戏开发都做了深入的解读。

这里奉上该书的免费下载地址和源代码,让我们开始Windows Phone 7的开发之旅吧!
PDF格式: Programming Windows Phone 7 Series

源代码

下面是该书的目录:

Part I   The Basics

1   Hello, Windows Phone 7

Targeting Windows Phone 7

The Hardware Chassis

Sensors and Services

File | New | Project

A First Silverlight Phone Program

The Standard Silverlight Files

Color Themes

Points and Pixels

The XAP is a ZIP

An XNA Program for the Phone

2   Getting Oriented

Silverlight and Dynamic Layout

Orientation Events

XNA Orientation

Simple Clocks (Very Simple Clocks)

3   An Introduction to Touch

Low-Level Touch Handling in XNA

The XNA Gesture Interface

Low-Level Touch Events in Silverlight

The Manipulation Events

Routed Events

Some Odd Behavior?

4   Bitmaps, Also Known as Textures

XNA Texture Drawing

The Silverlight Image Element

Images Via the Web

Image and ImageSource

Loading Local Bitmaps from Code

Capturing from the Camera

The Phone’s Photo Library

5   Sensors and Services

Accelerometer

A Simple Bubble Level

Geographic Location

Using a Map Service

6   Issues in Application Architecture

Basic Navigation

Passing Data to Pages

Sharing Data Among Pages

Retaining Data across Instances

The Multitasking Ideal

Task Switching on the Phone

Page State

Isolated Storage

XNA Tombstoning and Settings

Testing and Experimentation

Part II Silverlight

7   XAML Power and Limitations

A TextBlock in Code

Property Inheritance

Property-Element Syntax

Colors and Brushes

Content and Content Properties

The Resources Collection

Sharing Brushes

x:Key and x:Name

An Introduction to Styles

Style Inheritance

Themes

Gradient Accents

8   Elements and Properties

Basic Shapes

Transforms

Animating at the Speed of Video

Handling Manipulation Events

The Border Element

TextBlock Properties and Inlines

More on Images

Playing Movies

Modes of Opacity

Non-Tiled Tile Brushes

9   The Intricacies of Layout

The Single-Cell Grid

The StackPanel Stack

Text Concatenation with StackPanel

Nested Panels

Visibility and Layout

Two ScrollViewer Applications

The Mechanism of Layout

Inside the Panel

A Single-Cell Grid Clone

A Custom Vertical StackPanel

The Retro Canvas

Canvas and ZIndex

The Canvas and Touch

The Mighty Grid

10   The App Bar and Controls

ApplicationBar Icons

Jot and Application Settings

Jot and Touch

Jot and the ApplicationBar

Elements and Controls

RangeBase and Slider

The Basic Button

The Concept of Content

Theme Styles and Precedence

The Button Hierarchy

Toggling a Stopwatch

Buttons and Styles

TextBox and Keyboard Input

11   Dependency Properties

The Problem Illustrated

The Dependency Property Difference

Deriving from UserControl

A New Type of Toggle

Panels with Properties

Attached Properties

12   Data Bindings

Source and Target

Target and Mode

Binding Converters

Relative Source

The “this” Source

Notification Mechanisms

A Simple Binding Server

Setting the DataContext

Simple Decision Making

Converters with Properties

Give and Take

TextBox Binding Updates

13   Vector Graphics

The Shapes Library

Canvas and Grid

Overlapping and ZIndex

Polylines and Custom Curves

Caps, Joins, and Dashes

Polygon and Fill

The Stretch Property

Dynamic Polygons

The Path Element

Geometries and Transforms

Grouping Geometries

The Versatile PathGeometry

The ArcSegment

Bézier Curves

The Path Markup Syntax

How This Chapter Was Created

14   Raster Graphics

The Bitmap Class Hierarchy

WriteableBitmap and UIElement

The Pixel Bits

Vector Graphics on a Bitmap

Images and Tombstoning

Saving to the Picture Library

Becoming a Photo Extras Application

15   Animations

Frame-Based vs. Time-Based

Animation Targets

Click and Spin

Some Variations

XAML-Based Animations

A Cautionary Tale

Key Frame Animations

Trigger on Loaded

Animating Attached Properties (or Not)

Splines and Key Frames

The Bouncing Ball Problem

The Easing Functions

Animating Perspective Transforms

Animations and Property Precedence

16   The Two Templates

ContentControl and DataTemplate

Examining the Visual Tree

ControlTemplate Basics

The Visual State Manager

Sharing and Reusing Styles and Templates

Custom Controls in a Library

Variations on the Slider

The Ever-Handy Thumb

Custom Controls

17   Items Controls

Items Controls and Visual Trees

Customizing Item Displays

ListBox Selection

Binding to ItemsSource

Databases and Business Objects

Fun with DataTemplates

Sorting

Changing the Panel

The DataTemplate Bar Chart

A Card File Metaphor

18   Pivot and Panorama

Compare and Contrast

Music by Composer

The XNA Connection

The XNA Music Classes: MediaLibrary

Displaying the Albums

The XNA Music Classes: MediaPlayer

Part III   XNA

19   Principles of Movement

The Naïve Approach

A Brief Review of Vectors

Moving Sprites with Vectors

Working with Parametric Equations

Fiddling with the Transfer Function

Scaling the Text

Two Text Rotation Programs

20   Textures and Sprites

The Draw Variants

Another Hello Program?

Driving Around the Block

Movement Along a Polyline

The Elliptical Course

A Generalized Curve Solution

21   Dynamic Textures

The Render Target

Preserving Render Target Contents

Drawing Lines

Manipulating the Pixel Bits

The Geometry of Line Drawing

Modifying Existing Images

22   From Gestures to Transforms

Gestures and Properties

Scale and Rotate

Matrix Transforms

The Pinch Gesture

Flick and Inertia

The Mandelbrot Set

Pan and Zoom

Game Components

Affine and Non-Affine Transforms

23   Touch and Play

More Game Components

The PhingerPaint Canvas

A Little Tour Through SpinPaint

The SpinPaint Code

The Actual Drawing

PhreeCell and a Deck of Cards

The Playing Field

Play and Replay

24   Tilt and Play

3D Vectors

A Better Bubble Visualization

The Graphical Rendition

Follow the Rolling Ball

Navigating a Maze

[转载]SqlDataReader在通用数据库封装类中的关闭问题(转)

mikel阅读(1233)

[转载]SqlDataReader在通用数据库封装类中的关闭问题(转) – Asp.net 学习资料 – 博客园.

本文解释使用SQLDataReader关闭数据库连接的问题:

例如把数据库的操作都封装到了一个类中,但SQLDataReader只有在读取完毕时才能关闭数据库,这样类中就不能关闭书库库连接。在函数中关闭,如果在函数中就关闭了会提示‘阅读器关闭时Read的尝试无效’ .

这点微软当然想到了。用着个方法dr   =   Cmd.ExecuteReader(CommandBehavior.CloseConnection);,MSDN中对 CommandBehavior.CloseConnection的解释是‘在执行该命令时,如果关闭关联的   DataReader   对象,则关联的   Connection   对象也将关闭。’

但。。。。。,请看下面的问题:

在函数中执行操作然后返回DataReader,然后在外面读出数据,读取完毕后在外部关闭DataReader,这样函数中的Connetion会自动关闭吗?

实例展示:

我把所有对数据库的操作都封装到了一个类中,写程序时就直接调用这个类的某个函数来返回结果!(我们学习网-weareleran.net)

     

  下面是一个用来返回DataReader对象的通用类,
public    SqlDataReader    GetReader(string    SQL)      

  {    

  SqlConnection    Conn;    

  Conn    =    new    SqlConnection(strConn);    

  Conn.Open();    

  SqlCommand    Cmd    ;    

  Cmd    =    CreateCmd(SQL,    Conn);    

  SqlDataReader    dr;    

  try    

  {    

  dr    =    Cmd.ExecuteReader(CommandBehavior.CloseConnection);    

  }    

  catch    

  {    

  throw    new    Exception(SQL);    

  }    

  Cmd.Dispose();    

  return    dr;    

  }

在程序在需要的时候我就直接构造好SQL语句,调用这个函数即可,如下:

  string    sql="select    *    form    student";    

       SqlDataReader    dr    =    mydate.GetReader(sql);//调用数据库操作通用类中的GetReader函数,返回SqlDataReader    

           if(dr.Read())    

             {    

             .........读出数据,赋值给页面控件.....    

             }    

  dr.Close();//操作完毕,关闭DataReader

我按照这样的写基本已经没问题了,可以正常读取出数据,但是我现在担心的一个问题是Connection对象的关闭问题,因为是在 函数中建立的 Connection对象,没办法在外部来关闭它,又不能在函数中关闭,如果在函数中就关闭了会提示‘阅读器关闭时Read的尝试无效’ 但 我在函数中就用了这一行:dr   =   Cmd.ExecuteReader(CommandBehavior.CloseConnection);,MSDN中对 CommandBehavior.CloseConnection的解释是‘在执行该命令时,如果关闭关联的   DataReader   对象,则关联的   Connection   对象也将关闭。’ 也就是说我在关闭了DataReader之后,关联的Connection会自动关闭,我也测试过,测试代码如下:

  string    strConn    =    ConfigurationSettings.AppSettings["SqlDatabase"];    

  SqlConnection    cn=new    SqlConnection(strConn);    

  cn.Open();    

                     string    sql="select    *    form    student";    

  SqlCommand    cm=new    SqlCommand(sql,cn);    

  SqlDataReader    dr=cm.ExecuteReader(CommandBehavior.CloseConnection);    

  SqlDataReader    dr    =    mydate.RunProcGetReader(sql);    

                             if(dr.Read())    

  {    

                                     .......读取数据    

                                       }    

     dr.Close();//关闭DataReader    

                     Response.Write(cn.State);//输出Connetion的状态

结果运行这一段,输出:Closed   ,可见Connetion确实自动关闭了!但是如我在上面所说,我的Connection是在函数在建立的,在函数中执行操作然后返回 DataReader,然后在外面读出数据,读取完毕后在外部关闭DataReader,这样函数中的Connetion会自动关闭吗 我把所有对数据库的操作都封装到了一个类中,写程序时就直接调用这个类的某个函数来返回结果! 自己做了个测试:

 public     SqlConnection conn;

     protected void Page_Load(object sender, EventArgs e)

     {

         conn = new SqlConnection(DAL.SQLHelper.conn_String);

         show();

     }

  //简单的封装方法
     protected static SqlDataReader reDatareader(SqlConnection conn)

     {

         SqlCommand cmd = new SqlCommand("select top 10 * from article ", conn );

         conn.Open();

         SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);

         return dr;

     }
   //应用封装方法
     protected SqlDataReader redr()

     {

         SqlDataReader dr = reDatareader(conn);

         return dr;

     }
 //调用类,模拟3层构架。
     protected void show()

     {

         SqlDataReader dr = redr();

         if (dr.HasRows)

         {

             while (dr.Read())

             {

             }

             Response.Write(conn.State.ToString());

             dr.Dispose();

             Response.Write(conn.State.ToString());

         }

     }

结果输出 OpenClosed,呵呵,结果能关闭

记住reader查询类中的数据库链接对象不要用using,这样当查询结束后,conn会关闭,reader也就会提示错误:

using (SqlConnection conn = new SqlConnection(connectionString))
{
 SqlCommand    Cmd    ;    

  Cmd    =    CreateCmd(SQL,    Conn);    

  SqlDataReader    dr;    

  try    

  {    

  dr    =    Cmd.ExecuteReader(CommandBehavior.CloseConnection);    

  }    

  catch    

  {    

  throw    new    Exception(SQL);    

  }    

  Cmd.Dispose();    
}

[转载]数据库大型应用解决方案总结

mikel阅读(854)

[转载]数据库大型应用解决方案总结 – 成长的足迹.NET – 博客园.

随着互联网应用的广泛普及,海量数据的存储和访问成为了系统设计的瓶颈问题。对于一个大型的互联网应用,每天百万级甚至上亿的PV无疑对数据库造成了相当高的负载。对于系统的稳定性和扩展性造成了极大的问题。

一、负载均衡技术
负载均衡集群是由一组相互独立的计算机系统构成,通过常规网络或专用网络进行连接,由路由器衔接在一起,各节点相互协作、共同负载、均衡压力,对客户端来说,整个群集可以视为一台具有超高性能的独立服务器。

1、实现原理
实现数据库的负载均衡技术,首先要有一个可以控制连接数据库的控制端。在这里,它截断了数据库和程序的直接连接,由所有的程序 来访问这个中间层,然后再由中间层来访问数据库。这样,我们就可以具体控制访问某个数据库了,然后还可以根据数据库的当前负载采取有效的均衡策略,来调整 每次连接到哪个数据库。
2、实现多据库数据同步
对于负载均衡,最重要的就是所有服务器的数据都是实时同步的。这是一个集群所必需的,因 为,如果数不据实时、不同步,那么用户从一台服务器读出的数据,就有别于从另一台服务器读出的数据,这是不能允许的。所以必须实现数据库的数据同步。这 样,在查询的时候就可以有多个资源,实现均衡。比较常用的方法是Moebius for SQL Server集群,Moebius for SQL Server集群采用将核心程序驻留在每个机器的数据库中的办法,这个核心程序称为Moebius for SQL Server 中间件,主要作用是监测数据库内数据的变化并将变化的数据同步到其他数据库中。数据同步完成后客户端才会得到响应,同步过程是并发完成的,所以同步到多个 数据库和同步到一个数据库的时间基本相等;另外同步的过程是在事务的环境下完成的,保证了多份数据在任何时刻数据的一致性。正因为Moebius 中间件宿主在数据库中的创新,让中间件不但能知道数据的变化,而且知道引起数据变化的SQL语句,根据SQL语句的类型智能的采取不同的数据同步的策略以 保证数据同步成本的最小化。

数据条数很少,数据内容也不大,则直接同步数据
数据条数很少,但是里面包含大数据类型,比如文本,二进制数据等,则先对数据进行压缩然后再同步,从而减少网络带宽的占用和传输所用的时间。
数据条数很多,此时中间件会拿到造成数据变化的SQL语句, 然后对SQL语句进行解析,分析其执行计划和执行成本,并选择是同步数据还是同步SQL语句到其他的数据库中。此种情况应用在对表结构进行调整或者批量更改数据的时候非常有用。
3、优缺点
(1) 扩展性强:当系统要更高数据库处理速度时,只要简单地增加数据库服务器就 可以得到扩展。
(2) 可维护性:当某节点发生故障时,系统会自动检测故障并转移故障节点的应用,保证数据库的持续工作。
(3) 安全性:因为数据会同步的多台服务器上,可以实现数据集的冗余,通过多份数据来保证安全性。另外它成功地将数据库放到了内网之中,更好地保护了数据库的安全性。
(4) 易用性:对应用来说完全透明,集群暴露出来的就是一个IP

(1) 不能够按照Web服务器的处理能力分配负载。
(2) 负载均衡器(控制端)故障,会导致整个数据库系统瘫痪。

二、数据库的读写分离
1,实现原理:读写分离简单的说是把对数据库读和写的操作分开对应不同的数据库服 务器,这样能有效地减轻数据库压力,也能减轻io压力。主数据库提供写操作,从数据库提供读操作,其实在很多系统中,主要是读的操作。当主数据库进行写操 作时,数据要同步到从的数据库,这样才能有效保证数据库完整性。

(ebay的读写比率是260:1,ebay的读写分离)

(微软数据库分发)

2,实现方法:在MS Sql server中可以使用发布定义的方式实现数据库复制,实现读写分离,复制是将一组数据从一个数据源拷贝到多个数据源的技术,是将一份数据发布到多个存储 站点上的有效方式。使用复制技术,用户可以将一份数据发布到多台服务器上。复制技术可以确保分布在不同地点的数据自动同步更新,从而保证数据的一致性。 SQL SERVER复制技术类型有三种,分别是:快照复制、事务复制、合并复制。SQL SERVER 主要采用出版物、订阅的方式来处理复制。源数据所在的服务器是出版服务器,负责发表数据。出版服务器把要发表的数据的所有改变情况的拷贝复制到分发服务 器,分发服务器包含有一个分发数据库,可接收数据的所有改变,并保存这些改变,再把这些改变分发给订阅服务器。

3,优缺点
(1)数据的实时性差:数据不是实时同步到自读服务器上的,当数据写入主服务器后,要在下次同步后才能查询到。

(2)数据量大时同步效率差:单表数据量过大时插入和更新因索引,磁盘IO等问题,性能会变的很差。

(3)同时连接多个(至少两个)数据库:至少要连接到两个数据数据库,实际的读写操作是在程序代码中完成的,容易引起混乱

(4)读具有高性能高可靠性和可伸缩:只读服务器,因为没有写操作,会大大减轻磁盘IO等性能问题,大大提高效率;只读服务器可以采用负载均衡,主数据库发布到多个只读服务器上实现读操作的可伸缩性。

三、数据库拆分(分布式)

通过某种特定的条件,将存放在同一个数据库中的数据分散存放到多个数据库上,实现分布存储,通过路由规则路由访问特定的数据库,这样一来每次访问面对的就不是单台服务器了,而是N台服务器,这样就可以降低单台机器的负载压力。

垂直(纵向)拆分:是指按功能模块拆分,比如分为订单库、商品库、用户库…这种方式多个数据库之间的表结构不同。

水平(横向)拆分:将同一个表的数据进行分块保存到不同的数据库中,这些数据库中的表结构完全相同。

(纵向拆分)

(横向拆分)

1,实现原理:使用垂直拆分,主要要看应用类型是否合适这种拆分方式,如系统可以分为,订单系统,商品管理系统,用户管理系统业务系统比较明的,垂 直拆分能很好的起到分散数据库压力的作用。业务模块不明晰,耦合(表关联)度比较高的系统不适合使用这种拆分方式。但是垂直拆分方式并不能彻底解决所有压 力问题,例如 有一个5000w的订单表,操作起来订单库的压力仍然很大,如我们需要在这个表中增加(insert)一条新的数据,insert完毕后,数据库会针对这 张表重新建立索引,5000w行数据建立索引的系统开销还是不容忽视的,反过来,假如我们将这个表分成100个table呢,从table_001一直到 table_100,5000w行数据平均下来,每个子表里边就只有50万行数据,这时候我们向一张只有50w行数据的table中insert数据后建 立索引的时间就会呈数量级的下降,极大了提高了DB的运行时效率,提高了DB的并发量,这种拆分就是横向拆分

2,实现方法:垂直拆分,拆分方式实现起来比较简单,根据表名访问不同的数据库就可以了。横向拆分的规则很多,这里总结前人的几点,

(1)顺序拆分:如可以按订单的日前按年份才分,2003年的放在db1中,2004年的db2,以此类推。当然也可以按主键标准拆分。

优点:可部分迁移

缺点:数据分布不均,可能2003年的订单有100W,2008年的有500W。

(2)hash取模分: 对user_id进行hash(或者如果user_id是数值型的话直接使用user_id的值也可),然后用一个特定的数字,比如应用中需要将一个数据 库切分成4个数据库的话,我们就用4这个数字对user_id的hash值进行取模运算,也就是user_id%4,这样的话每次运算就有四种可能:结果 为1的时候对应DB1;结果为2的时候对应DB2;结果为3的时候对应DB3;结果为0的时候对应DB4,这样一来就非常均匀的将数据分配到4个DB中。
优点:数据分布均匀
缺点:数据迁移的时候麻烦;不能按照机器性能分摊数据 。
(3)在认证库中保存数据库配置
就是建立一个DB,这个DB单独保存user_id到DB的映射关系,每次访问数据库的时候都要先查询一次这个数据库,以得到具体的DB信息,然后才能进行我们需要的查询操作。
优点:灵活性强,一对一关系
缺点:每次查询之前都要多一次查询,会造成一定的性能损失。

[转载]我的python工具-代码生成器

mikel阅读(1009)

[转载]我的python工具-代码生成器 – 编码人生 – 博客园.

做企业MIS系统,最烦人的就是建立实体文件,如果使用ORM多数还要建立映射文件,字母稍有不慎拼写错误组件就加载异常,搞的心浮气躁, 于是自然而然想起写一个代码生成工具,虽然业界CodeSmith等成熟工具早已如雷贯耳,但自己动手量体裁衣,用着也舒服,心里也踏实,早些年也曾使用 C#和Groovy分别写过类似的代码,用于不同的项目,今天又想用python实现一个,目的只是想给刚入门或尚未入门的小朋友(新手)一些建议,代码 也是练出来的,多写代码,多总结,多练习,尤其学习一门新的语言时,根据需要做一些实用的工具,按照自己的思路在设计和实现过程中,会遇到很多问题,当你 解决这些问题后,你也很快掌握了该门语言,何乐而不为呢?
闲话不多讲,这就来说说用python实现一个简单的代码生成工具的思路
步骤1.连接数据库
步骤2.查询数据库系统表,获取数据库表及字段元数据信息,如字段名,类型,长度,是否主键,是否可空等信息
步骤3.加载模板信息,根据命名规则,输出实体类、业务类、映射文件等字符串信息
步骤4.输出结果,保存到文件中

1.使用pymsSQL连接数据库:

代码

1 import pymsSQL
2
3 host=.
4 user=sa
5 password=sa
6 database=Test
7
8 conn = pymssql.connect(host=host, user=user, password=password, database=database)
9 cur = conn.cursor()
10 sql = select * from test
11 cur.execute(sql)
12 cur.fetchall()
13 conn.close()

步骤2.查询数据库系统表,获取数据库表及字段元数据信息,如字段名,类型,长度,是否主键,是否可空等信息

代码

1 sql = “””SELECT sysobjects.name AS Tb_name, syscolumns.name AS Col_name,
2 systypes.name AS Col_type, syscolumns.length AS Col_len,
3 ISNULL(sys.extended_properties.[value], syscolumns.name) AS Col_memo,
4 CASE WHEN Syscolumns.Name IN
5 (SELECT A.Name
6 FROM Syscolumns A INNER JOIN
7 Sysobjects B ON A.Id = B.Id AND B.Xtype = U AND
8 B.Name <> Dtproperties
9 WHERE EXISTS
10 (SELECT 1
11 FROM Sysobjects
12 WHERE Xtype = Pk AND Name IN
13 (SELECT Name
14 FROM Sysindexes
15 WHERE Indid IN
16 (SELECT Indid
17 FROM Sysindexkeys
18 WHERE Id = A.Id AND Colid = A.Colid))) AND
19 B.Name = Sysobjects.Name) THEN 1 ELSE 0 END AS Is_key,
20 Syscolumns.isnullable as IsNullable
21 FROM sys.extended_properties RIGHT OUTER JOIN
22 sysobjects INNER JOIN
23 syscolumns ON sysobjects.id = syscolumns.id INNER JOIN
24 systypes ON syscolumns.xtype = systypes.xtype ON
25 sys.extended_properties.major_id = syscolumns.id AND
26 sys.extended_properties.minor_id = syscolumns.colid
27 WHERE (sysobjects.xtype = U OR
28 sysobjects.xtype = V) AND (systypes.name <> Sysname) AND
29 (sysobjects.name LIKE %)
30 ORDER BY sysobjects.name, syscolumns.colid“””

执行1中的cur.execute(sql); result = cur.fetchall();获取执行上边的sql获取数据库表定义的元数据信息,有心的同学,请自行copy上述sql,在SQL Server 查询窗体里边执行即可看到结果,无需多说.

步骤3.加载模板信息,根据命名规则,输出实体类、业务类、映射文件等字符串信息
通过导入python模块string中的Template定义模板信息如:

代码

from string import Template
EntityTemplate = Template(“””
/***************************************************
#  ${ClassName}.cs
#  Comment:
#
#  Current Version: V1.0
#  Author:
#
#  History List:
#  V1.0    Created by CodeBuilder@${Datetime}
#*****************************************************************************
*/
using System;
using System.Text;
using Higo.Business

namespace ${NameSpace}.Entities
{
[Serializable]
public class ${ClassName}:BaseEntity
{
public ${ClassName}()
{
${Init}
}

${Property}
}
}
“””)

根据从数据库中获取到的表结构信息,生成模板中的变量${NameSpace}, ${ClassName}等上下文信息,调用Template.safe_substitute(context),进行变量的替换,并输出替换后的字符 串,此处数据库字段名称到应用程序代码中的类名称需要事先约定好命名规则,如:数据库表明为T_SYS_CODE_DATA,对应实体类名称为 SysCodeData,另外数据表T_SYS_CODE_DATA中的某一列,如F_CODE_NAME,类型为nvarchar(200),对应应用 程序中属性的定义为:public string CodeName {get;set;}, 除命名规则外,还需要一张数据库类型对应应用程序类型映射表,可以定义一个Python全局字典变量,如:

代码

1 dbmap = {
2 int:int,
3 text:string,
4 bigint:Int64,
5 binary:byte[],
6 char:string,
7 datetime:DateTime,
8 decimal:decimal,
9 float:double,
10 image:byte[],
11 money:decimal,
12 nchar:string,
13 ntext:string,
14 numeric:float,
15 nvarchar:string,
16 real:Single,
17 smalldatetime:DateTime,
18 smallint:Int16,
19 smallmoney:decimal,
20 timestamp:DateTime,
21 tinyint:byte,
22 uniqueidentifier:Guid,
23 varbinary:byte[],
24 varchar:string,
25 variant:object,
26 bit:bool}

通过dbmap[“nvarchar”]获取到的对应类型为string

步骤4.输出结果,保存到文件中

代码

1 def saveFile(filePath, buf):
2 if not os.path.exists(filePath):
3 temp = os.path.dirname(filePath)
4 if not os.path.exists(temp):
5 os.makedirs(temp)
6 else:
7 os.remove(filePath)
8 f = open(filePath,w)
9 f.write(buf)
10 f.close()

ok,大功告成,思路很简单,但在用python代码实现的过程中,熟悉了一下python的数据库操作以及字符串模板替换、列表表达式过滤等常规用法。

源码在这里:/Files/wdong/CodeBuilder.rar,同时也希望得到您的回复和指教,谢谢

[转载]跨域SSO的实现之一:架构设计

mikel阅读(1313)

[转载]跨域SSO的实现之一:架构设计 – 好记性不如烂键盘 – 博客园.

翻译自CodeProject网站ASP.NET9月份最佳文章:Single Sign On (SSO) for cross-domain ASP.NET applications

翻译不妥之处还望大家多多指导、相互交流。

文章分为两部分:架构设计和程序实现,此为第一篇即:架构设计或者叫设计蓝图(Part-I – The design blue print)。:)

简介

周一的早晨,当你正在纳闷周末咋就一眨眼过去了并对接下来漫长的一周感到无比蛋疼之时,你收到了一份Email。

操蛋的是它既不是微软的offer也不是Google的offer,而是客户发来的一个新需求。

他说你们现在帮我们公司做了很多的ASP.NET网站和忽悠我们上线的各种系统,现在我想要我的客户只要在我们拥有的任何一个网站上登录一次,那么在我所有的网站上该用户就都已经登录了,同样,随便他从哪个网站上注销掉,那么他也就从我们所有的网站上注销了……

你受不了客户这么罗嗦了,心想不就是要一个SSO功能吗?使用ASP.NET的form authentication不就可以实现了?因为这样可以在同域的不用网站下共享cookie,只需要在machineKey设置一样的配置节就可以 了。放狗一搜,果然有xxxx条结果。放狗找东西可是我们程序员的特长。

开工前,你又扫了一眼邮件,等等,你看到了邮件中的一行话,微微一蛋疼:我们部署了那些网站,但不是都在同一个域名下。

你的客户狠狠地给你来了个下马威,好像他早就放狗搜过,因为cookie不能跨域共享,也就不能用来实现跨域验证了。

这到底是神马一回事情!(和老外一样扯玩淡,下面正经些)

ASP.NET中的验证原理

这个问题可能是老生常谈了,但在解决难题之前,还是先回归基础来看一看事物的本质到底是如何的。因此,我们重温一下ASP.NET表单验证的原理也并不坏。
下面是ASP.NET表单验证的流程图

验证流程

1:你访问一个需要用户验证的ASP.NET页面

2:在此请求中ASP.NET运行时开始查找cookie(由于表单验证的cookie),如果没有查找到,那么将跳转到登录页面(登录页地址配置在了web.config文件中)

3:在登录页面中,你提供了相关的验证凭证并点击了登录按钮,系统和已存储的数据对比验证成功后,将 Thread.CurrentPrincipal.Identity.Name的属性值设置成了你提供的用户名,并在Response中写入了 cookie(同时还写入了用户信息和一些如cookie名,失效日期等),并重定向到登录前的页面。

4:当你再点击其他的页面(或者点击导航到其他的页面),浏览器发送验证的cookie(也可能包含在该网站下写入的一些其他cookie),这一次已经包含了在上一次response中上次验证获取到的cookie。

5:和以前一样,ASP.NET运行时在请求中查找验证的cookie,这一次找到了,接下来做一些检查(如失效日期、路径等等),如果还没有失 效,那么读取出它的值,恢复出用户的信息,将Thread.CurrentPrincipal.Identity.Name的属性值设置成恢复出的用户 名,检查该用户是否有权限去访问当前请求的页面,如果有,那么页面执行的结果返回到用户的浏览器。

过程很简单,对吗?

ASP.NET中多站点同域下的验证原理

如前所述,ASP.NET表单验证完全依赖于cookie。那么只要使得不同的站点共享同样的验证cookie,那么就可以实现在一个站点登录实现所有站点的登录。

HTTP协议指出,如果两个站点是同域(或者是子域)的,那么可以共享cookie。本地的处理是浏览器根据网站的URL存储cookie在本地 (磁盘或者内存中)。当你请求接下来的任意页面时,浏览器读取和当前请求的URL匹配的域或子域的cookies,并将此cookies包含在当前的请求 中。

现在我们假设有下面两个网站:

www.mydomain.com/site1

www.mydomain.com/site2

这两个站点共享同样的主机地址(同样的域mydomain.com和子域www),且两个站点都被配置成了对用户验证和授权都使用表单验证。假设你已经登录过了站点www.mydomain.com/site1,如前所述,你的浏览器现在对于站点www.mydomain.com/site1已经有了表单验证的cookie。

现在你随意访问以www.mydomain.com/site1开头的URL,表单验证的cookie都将被包含在请求被发送。为什么?是因为此cookie本来就属于该站点吗?对的,但不是完全正确。事实上,是因为请求的URL:www.mydomain.com/site1http://www.mydomain.com/拥有同样的域名和子域名。

那么在你登录了www.mydomain.com/site1后,如果你点击www.mydomain.com/site2下的URL,表单验证的cookie也将被包含在请求中发送,这同样是因为www.mydomain.com/site2与站点http://www.mydomain.com/拥有同样的域名和子域名,尽管它是不一样的应用站点(site2)。显然,在拥有一样主机地址不一样的应用站点名之间是可以共享表单验证cookie的,这样就实现了一处登录处处都已经登录的功能(也就是单点登录)。

然而,ASP.NET没有允许你仅仅通过将同主机地址下的站点部署上表单验证后就自动完成了单点登录。为什么这样呢?因为每一个不同的 ASP.NET web应用程序使用它自己的密钥去加密和加密cookie(还有诸如ViewState之类的)从而确保了安全。除非你给每一个站点指定了同样的加密密 钥,那么cookies将被发送,但是另一个应用站点不能够读取验证cookies的值。

指定同样的验证密钥可以解决这个问题。为每一个ASP.NET应用站点使用同样的<machinekey>配置节即可,如下:

<machineKey
validationKey=”21F090935F6E49C2C797F69BBAAD8402ABD2EE0B667A8B44EA7DD4374267A75D”
decryptionKey
=”ABAA84D7EC4BB56D75D217CECFFB9628809BDB8BF91CFCD64568A145BE59719F”
validation
=”SHA1″
decryption
=”AES”/>

如果同样的machinekey(包括validationKey和decryptionKey)被用在同域下的所有应用站点时,就可以实现了跨站点读取cookie。

如果是同样的域不同的子域呢?

假定你有下面两个站点:
site1.mydomain.com
site2.mydomain.com
这两个站点共享同样的域(同样的二级域名mydomain.com),但拥有不一样的三级域名(不一样的子域site1和site2)。
默 认情况下浏览器仅仅发送主机地址一样(相同的域和子域)的站点的cookie。因此站点site1.mydomain.com不能获取到站点 site2.mydomain.com下的cookie(因为他们没有相同的主机地址,它们的子域不同),尽管你为这两个站点配置了相同的 machineKey,一个站点还是不能获取另一个站点下的cookie。
除了你为所有的站点配置了一样的machineKey,你还需要为验证cookie定义相同的域以使得浏览器在同样的域名下能够发送任何请求。

你需要像下面这样配置表单验证cookie:

<forms name=”name” loginUrl=”URL” defaultUrl=”URL” domain=”mydomain.com”/>

那么,在不用的域下如何去共享验证cookie呢?

显然这是不可能的,因为HTTP协议基于安全的原因阻止了你在不同的域之间共享cookie。
同样,假设有下面这两个域名:
http://www.domain1.com/
http://www.domain2.com/
如果你使用表单验证登录进了http://www.domain1.com/,当你点击http://www.domain2.com/下的URL时,浏览器将不能发送domain1.com的cookie到domain2.com。在ASP.NET中没有内置的方法去完成在两个不同的站点间实现单点登录。
要在两个站点间通过访问同样的cookie来实现单点登录,还真没有什么高级的技巧或即有的架构模型去解决它。

跨域单点登录设计雏形

假设有下面三个站点:
http://www.domain1.com/

http://www.domain2.com/
http://www.domain3.com/
为了实现在这些站之间实现SSO,当用户在任意一个站登录时,我们需要为所有的站点设置验证cookie。
如果用户1登录进http://www.domain1.com/,那么在给站点1response前会在response中加入验证的cookie,但当我们需要同时能够登录进http://www.domain2.com/http://www.domain3.com/时,我们需要同时在同样的客户端浏览器上为站点2和站点3设置验证cookie。因此,在response返回到浏览器前,站点1不得不定向到站点2和站点3去设置验证cookie。

下面的流程图详细描述了思路:

操作流程:

请求http://www.domain1.com/中一个需要验证的页面

状态:浏览器没有验证cookie

浏览器发送一个请求到http://www.domain1.com/,但请求中没有验证cookie(因为还没有属于http://www.domain1.com/的cookie)。

状态:浏览器没有验证cookie

因为请求中没有验证cookie,所以请求http://www.domain1.com/

的登录页面

状态:浏览器没有验证cookie

用户提供登录凭证点击登录按钮,浏览器发送一个POST请求到http://www.domain1.com/
http://www.domain1.com/验证用户提供的登录凭证,验证通过后,标记用户的状态为已登录,添加验证的cookie和其他的用户信息一起添加在response中

状态:浏览器没有验证cookie

response并没有返回给浏览器,而是将请求重定向到http://www.domain2.com/的一个页面,并将ReturnUrl设置成重定向前http://www.domain1.com/的URL值。在验证cookie被包含在了response中了后,cookie被发送给浏览器。

状态:浏览器没有验证cookie

浏览器接收到了包含验证cookie的response和重定向到http://www.domain2.com/的命令。浏览器存储了http://www.domain2.com/的验证cookie并向http://www.domain2.com/发送请求。

状态:浏览器包含了http://www.domain2.com/的验证cookie

http://www.domain2.com/立即再重定向到存储在ReturnUrl中的URL地址,在此请求中读取cookie值并为http://www.domain1.com/设置验证cookie。最终,在重定向的命令中也包含了这些验证cookie。

状态:浏览器包含了http://www.domain2.com/的验证cookie

浏览器接收到包含了验证cookie的重定向命令跳转到http://www.domain1.com/。现在浏览器存储了站点1的验证cookie并开始请求站点1,当然在请求中包含了验证cookie。

状态:浏览器包含了http://www.domain1.com/和http://www.domain2.com/的验证cookie

站点1检查了请求中包含了验证cookie,就不需要再去跳转到验证页面去验证,而是返回用户请求的页面

状态:浏览器包含了http://www.domain1.com/和http://www.domain2.com/的验证cookie

如果此时用户请求站点2, 因为浏览器已经存储了站点2的验证cookie,cookie将被包含在请求中,站点2从cookie中获取到用户信息,并为此用户返回请求的页面。
当浏览器验证了站点2和站点3后,那么用户就已经登录了所有的站点,这样就完成了一次单点登录。

如何单点注销?

作为单点登录的一部分,我们还需要去关注下单点注销,就是说当用户在一个站点注销后,那么就认为他从所有的站点都注销了。
清除所有站点的cookie和上面登录一样,也是请求-重定向-返回的过程。只是和设置验证cookie不一样的是,这次从response中移除验证cookie。

此单点登录模型的缺点

这个模型在两个站点上还是能运行的很好的。从一个站点登录或注销,此SSO模型下的站点都将遵从请求-重定向-返回的流程。当用户登录任一页面时,因为已经存储了所有站点的验证cookie,那么就不需要再执行上面的那个循环的流程了。
但 是当站点超过两个时,问题就变得复杂了,当登录站点1时,程序将重定向到站点2和站点3进行验证cookie的设置,最后站点3在跳转到站点1,服务器返 回用户请求的页面。这使得每个站点的登录和注销的过程变得复杂并花费较高的代价。如何超过3个站点呢?如果这样去设计20+站点的单点登录呢?这个模型将 完全不能胜任了。
并且此模型需要每个站点都具备用户验证逻辑,因为需要来请求此站点并设置其验证cookie。
因此此模型丢失了一般意义上的单点登录的概念,我们需要一个更好一点的模型去实现单点登录的功能。

更好的跨域单点登录架构

前面提到的架构中,设置移除cookie都需要跳转到N-1个站点去完成。每个站点还需要知道N-1个站点复杂的登录注销逻辑,
如果我们为所有的站点只去维护一份验证cookie呢?使用一个独立的站点去完成验证用户并设置验证cookie的工作呢?这个想法好像不错。
要使用单点登录,那么就需要用户的数据是统一的,这样的话就可以通过一个站点提供web或者WCF服务来完成验证和授权的功能。这样就省去了冗余的用户验证逻辑,现在最重要的是这个独立的站点如何在SSO架构中起作用。
在这个架构模型中,浏览器不存储任何其他站点的验证cookie,只存那个独立站点的验证cookie,我们就给它起名叫http://www.sso.com/
在此架构中,对每一个站点的请求都将被直接跳转到http://www.sso.com/,由于检查验证cookie是否存在。如果cookie存在,如果存在,返回请求的页面,如果不存在,那么就跳转到对应的登录页面。
大致流程图如下:

便于理解,我们假定有下面两个网站:
http://www.domain1.com/
http://www.domain2.com/
还有一个用于管理验证cookie的站点:http://www.sso.com/
验证流程如下:

用户请求http://www.domain1.com/中一个需要验证的页面
重定向到http://www.sso.com/,ReturnUrl参数设置成请求站点1时的URL。
http://www.sso.com/检查是否有验证cookie存在,如果在请求中没有任何用户令牌存在,那么请求中带着用户需要登录的指令就跳转到站点1。在query string中仍然保留着之前ReturnUrl参数的值。
站点1从参数中得知是从http://www.sso.com/跳转而来,且得知没有用户验证cookie,最后跳转到站点1的登录页面进行登录,而不跳转到http://www.sso.com/
用户提供验证信息点击登录按钮,请求没有回置到站点http://www.sso.com/,这时,站点1通过http://www.sso.com/提供的web/WCF接口进行用户的验证,如果验证成功,那么为用户颁发一个令牌(可以是一个GUID)。
站点1标志用户已经登录成功(在session中存储用户对象),一个包含了令牌的URL跳转到http://www.sso.com/设置验证cookie,ReturnUrl参数还是设置成前面请求的URL。
http://www.sso.com/站点检查过来的URL,发现有用户令牌,但还没有用户验证cookie,说明已经通过了站点1的认证,现在需要设置站点http://www.sso.com/下的验证cookie。照例设置好了cookie后,将cookie添加在response中,还添加上用户令牌按照ReturnUrl参数中的URL一并返回。
浏览器得知要跳转到站点1,并且有了站点http://www.sso.com/的验证cookie,在本地存储下sso站点的验证cookie并对站点1发起请求。
站点1检查了用户令牌,因为是通过站点SSO的web/WCF服务验证并通过的,所以站点1返回用户请求的页面。

现在用户请求站点2
浏览器跳转到sso站点,依然设置好ReturnUrl的值。
浏览器因为要跳转到sso站点,发现本地有了sso站点的验证cookie,所以将cookie添加在请求中一并发出。
sso站点检查cookie,发现cookie还没有过期,那么在query string中添加上用户令牌按照ReturnUrl返回。
站点2发现有用户令牌,证明已经走过验证流程,那么就返回用户请求的页面。

总结

刚开始,浏览器没有任何http://www.sso.com/站点下的验证cookie。请求站点1和站点2任何需要验证的页面(需要内部的跳转到sso站点检查验证cookie是否存在)。用户登录后,sso站点的验证cookie存储在本地(重要的是用户令牌仅仅用户用户登录会话时)。
现在请求站点1或者站点2都跳转到sso站点,浏览器发送sso站点的验证cookie并检查用户令牌,验证后再跳转到原始请求的URL,原始站点检查用户令牌正确后返回用户请求的页面。

传输代价

场景1:访问公共页面
从浏览器到站点+站点到浏览器
1请求+1返回
场景2:访问一个需要验证的页
从浏览器到站点+重定向到sso站点(检查cookie)+重定向到原站点(没有cookie)+原站点返回登录页面到浏览器
1请求+2跳转+1返回
场景3:登录
浏览器POST到站点+调用验证服务进行用户验证+浏览器跳转到SSO站点(带有令牌)+重定向到原站点(带有验证cookie)+通用服务验证令牌+返回用户请求的需要验证的页面
1请求+2验证服务调用+2跳转+1返回
场景4:登录后请求一个需要验证的页面
请求站点+向SSO站点跳转验证cookie(带有验证cookie)+跳转到原站点(检查验证cookie)+调用服务验证令牌+返回请求页面
1请求+2跳转+1服务请求+1返回
场景5:注销
请求站点进行注销+请求SSO站点进行注销+请求原站点移除验证cookie+返回
1请求+2跳转+1返回

孰是孰非

比较这两种架构,第一中架构更适合两个站点,最多三个站点,虽然需要部署复杂冗余的验证逻辑,但是随后的页面请求中就是普通的页面请求了(1请求+1返回)。
第一种架构不易于扩展,且会冗余出很多的用户验证逻辑。
而第二种架构,不管有多少个需要进行单点登录的网站,也不需要其他网站参与此过程,验证的cookie只有sso站点管理,这样的架构逻辑清晰、易扩且部署方便。
然而有一些性能的问题,不同于第一种架构,这种架构当用户请求一个需要验证的页面时需要请求三次(请求sso站点和原站点,两次请求是内部的跳转),且多的两次请求花费的时间很少(空跳转请求,用于设置和检查cookie),在如今这样的网络环境下是可以接受的。

第二种架构的程序实现

等有空了来翻译完第二部分:程序实现
等不及的朋友可以先看原文:http://www.codeproject.com/KB/aspnet/CrossDomainSSOExample.aspx

程序实现源码下载

[转载]android 条码识别软件开发全解析(续1详解)

mikel阅读(828)

[转载]android 条码识别软件开发全解析(续1详解) – tankaixiong – 博客园.

前天我发了一篇关于android开发条码识别软件应用程序的博客,没想到竟很受关注。

我也挺受鼓舞的,好吧,我接上次的博客继续往下写。

前篇有人讲我写的没有实质内容,我接受建议,这里我就带大家体验一下最简单最直接的完美体验。

编写你的第一个Android条码识别程序。zxing的简单使用!

第一步:下载zxing组件:我上篇也简单介绍了一下这个组件,这里也不啰嗦了。

下载两个东东

源码和文档说明:

地址下如

http://code.google.com/p/zxing/downloads/detail?name=ZXing-1.6.zip&can=2&q=

BarcodeScanner3.5.apk这个是编译好的一个可安装的apk程序!这个后面将用到。

地址如下

http://code.google.com/p/zxing/downloads/detail?name=BarcodeScanner3.51b1.apk&can=2&q=

第二步:
模拟器已完全打开后
安装BarcodeScanner3.5.apk

cmd中cd 到sdk目录

利用adb命令安装BarcodeScanner3.5.apk

adb install BarcodeScanner3.5.apk 所在目录,请确定安装成功了。

第三步:ok 终于到编码了!

代码

import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.TextView; public class MyTest extends Activity { /** Called when the activity is first created. */ private TextView tv; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.mytest); tv=(TextView) findViewById(R.id.mytxt); findViewById(R.id.mybtn).setOnClickListener(listener); } public Button.OnClickListener listener=new Button.OnClickListener(){ public void onClick(View v){ Intent intent = new Intent("com.google.zxing.client.android.SCAN");//调用扫描的actity,这里其实只是转到刚才安装的BarcodeScanner3程序的中一个actity intent.putExtra("SCAN_MODE", "QR_CODE_MODE");//输入参数,(扫描类型,..二维码) startActivityForResult(intent, 0);//启动intent } }; //扫描成功后回调函数,传回code public void onActivityResult(int requestCode, int resultCode, Intent intent) { if (requestCode == 0) { if (resultCode == RESULT_OK) { String contents = intent.getStringExtra("SCAN_RESULT"); String format = intent.getStringExtra("SCAN_RESULT_FORMAT"); // Handle successful scan tv.setText(" 条形码为:"+contents+" 条码类型为: "+format);//利用页面的textveiw显示扫描后的结果 } else if (resultCode == RESULT_CANCELED) { // Handle cancel tv.setText(" 扫描失败!"); } } } }

对应的xml如下

01 <?xml version="1.0" encoding="utf-8"?>
03 Android:orientation="vertical" android:layout_width="fill_parent"
04 android:layout_height="fill_parent">
05 <TextView android:layout_width="fill_parent"
06 android:layout_height="wrap_content" android:text="@string/hello"
07 android:id="@+id/mytxt" />
08
09 <Button android:text="点我开始扫描" android:id="@+id/mybtn"
10 android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
11 </LinearLayout>

第三步:OK我们赶快看看效果吧, 运行。
你会看到如下:


模拟器当然没有摄像头设备是扫描不到结果的。
总结:
这里只是做了一个简单的调用,调用简单但有一个很大的缺陷,这里我们要安装两个程序!

在安装BarcodeScanner3.5.apk为前题,这明显不能接受!所以接下来我将在下篇博客继续深入。敬请关注!

[转载]Web前端设计模式--制作漂亮的弹出层

mikel阅读(1037)

[转载]Web前端设计模式–制作漂亮的弹出层… – 翁智华 – 博客园.

设计场景:

Ben最近在负责一个购书网站,在网站的首页上,有一个叫做“最新上架”的板块,板块的内容比较简单,只有书籍名称,作者姓名和上架时间(如图),当初设计的时候并i没有过于丰富的构思…

现在问题来了,这个版块不大,更新频率却很高,每天都有十数条最新的信息上去,浏览网站的会员对于最新图书的了解和需求越来越大,因此需要对这个板块进行 改良,以满足会员的需求,会员的主要要求有以下几个方面:显示该最新上架的图书的封面缩略图,该图书的名称和作者名称,以及该书部分内容的介绍和作者的简 介…

这下把Ben给愁坏掉了,首页上根本就没有多余的空间,怎么来呈现封面缩略图甚至是内容简介,如果去掉别的板块空间来实现这一板块的扩张,无异于在一家公司以牺牲一个部门来壮大另外一个部门,这是万万不可取的…

于是Ben想到了以弹出层的方式来显示每条信息的详细内容…

设计目标:

在不改变页面结构的情况下,以弹出层(用Dom重构的方式来实现元素的追加append和移除remove)的方式提高页面信息量…

解决方案:

首先,我们设计一个Div,样式如下:

代码

.TipDiv { width:500px; height:120px; padding:8px; border-top:solid 5px #a6c9e2; border-bottom:solid 5px #a6c9e2; border-left:solid 1px #a6c9e2; border-right:solid 1px #a6c9e2; background:#ffffff; z-index:10;/*z-index很重要,它决定了Div框在页面上的叠加顺序*/ position:absolute;/*绝对定位,它决定了该元素可以根据top 和 left 叠在其他元素上*/ } .TipDiv img { width:110px; height:110px; margin-right:36px; margin-left:10px; float:left; } .TipDiv span { /*×*/ width:340px; height:110px; float:left; word-break:break-all; border-top:dashed 1px #3a7ac8; margin-top:8px; }

下面是脚本,当鼠标经过的时候才响应弹出框事件:

代码

$(document).ready(function(){ //标题鼠标经过 $("ul li a").mousemove(function(e){ $(".TipDiv").remove();//若页面上有该元素,则移除该元素...0 var x=e.clientX + 10;//获取鼠标的x轴坐标 var y=e.clientY + 10;//获取鼠标的y轴坐标 var num=$(this).attr("id"); var imgs; var word; var name; switch(num) { case "1":{ imgs="images/mimi.bmp"; name="秘密 朗达·拜恩 (Rhonda Byrne)..." ; word="这是一个神圣的秘密花园,住着爱丽丝..." ; break; } case "2":{ imgs="images/mama.bmp"; name="一位母亲的记忆 爱心团..." ; word="这是一个关于母亲的故事,感染了每个中国人,她是一位暴走族母亲,也是一位为儿子捐献肝的母亲,她更是一位伟大的,典型的中国母亲..." ; break; } case "3":{ imgs="images/nikesong.bmp"; name="尼克爷爷讲故事 (巴特沃斯, 漪然)..." ; word="★当今世界最出色的儿童绘本作家、插画家!<br>★获得1992年度英国图书奖(British Book Awards)<br>★全球每15分钟就有一本由他创作的绘本被买走<br>★他的绘本让阅读变得赏心悦目!" ; break; } case "4":{ imgs="images/lqz.bmp"; name="李清照:人生不过一场绚烂花(蔚起)..." ; word="《李清照:人生不过一场绚烂花事》精选易安词作50首,从《武陵春(风住尘香花已尽)》始,至《好事近(风定落花深)》结束。通篇以闲话家常、婉约诚挚的笔法评析、阐释,娓娓道来,不生涩,没有说教。" ; break; } } popDiv(imgs,name,word,x,y); }) //标题鼠标离开 $("ul li a").mouseout(function(){ $(".TipDiv").remove(); }) }) //随鼠标移动的信息框 function popDiv(face,name,info,xx,yy) { var str=""; str+="<div class='TipDiv'>"; str+="<img alt='face' src='"+face+"'/>"; str+="<strong><em>"+name+"</em></strong><br />"; str+="<span>"+info+"</span>"; str+="<div>"; $('body').append(str);//在页面上追加该元素,样式如上已经写好 $(".TipDiv").css({"top":yy+"px","left":xx+"px"});//设置该元素出现的位置(这里是出现在鼠标的右边和下边的偏离10px位置) }

结果如下(当鼠标指向第三条数据时,弹出该框, 并随鼠标移动):

做到这边,会员有了一个新的要求,就是不要弹出框随着鼠标的移动而移动,那样鼠标一旦离开焦点,就会移除该弹出框,操作起来不是很方便。他们要求弹出框固定,假设就在相应的数据行的右侧吧,而且打开和关闭由会员自己控制,于是Ben就进行改良了…

同样的,先设计一个id为tips的Div元素,样式如下:

代码

#tips { background-color: white; border-left: 1px solid #a6c9e2; border-right: 1px solid #a6c9e2; border-top:5px solid #a6c9e2; border-bottom:5px solid #a6c9e2; width:268px; height:60px; z-index:9; position:absolute; -moz-border-radius: 5px; -webkit-border-radius: 5px; padding:8px 18px; } /* 弹出层的指向图标,left:-10 使它出现在整个Div的左侧 */ #tips #tipsArrow { position:absolute; top:26px; left: -10px } #tips #light { width:36px; height:36px; margin:6px 16px 16px 16px; float:left; } #tips span { margin-top:18px; } #tips #close { width:20px; height:16px; border:none; z-index:1; left:280px; top:6px; position:absolute; cursor:pointer; }

脚本如下:

代码

$(document).ready(function(){ //时间鼠标经过 $("ul li span").mouseover(function(){ $("#tips").remove(); var elem= $(this).parent(); var mTop=elem.offset().top;//获取该元素的top坐标 var mLeft=elem.offset().left;//获取该元素的left坐标 var addLeft=elem.width();//获取该元素的宽度 var finalTop=mTop-30;//获取最终元素出现的Top位置,此时-30个元素是提高这个Div的高度,让箭头指向对应行 var finalleft=mLeft+addLeft+20; // 获取最终元素出现Left的,对应行的左边加上行宽加上20个空元素 var num=$("li").index(elem)+1; popDiv1(finalTop,finalleft,"提示框提醒你,这是第"+num+"行数据!"); }) }) //固定的信息框 function popDiv1(tops,lefts,messages) { var str=""; str="<div id='tips'><img id='tipsArrow' src='images/arrow.png' alt=''/><img id='close' src='images/close.jpg' alt='' onclick='closeUp()'/><img src='images/light.gif' alt='' id='light'/><p>"+messages+"</p></div>"; $('body').append(str); $("#tips").css({"top":tops+"px","left":lefts+"px"}); } function closeUp() { $("#tips").remove(); }

最终显示效果如下:

鼠标移动到相应的数据行上面,显示相应的提示框,右边的打叉小图标用以关闭整个弹出层…

设计小结:

这 个设计过程的关键是position:absolute(绝对定位,作用是让层在页面上叠加),z-index(用以显示层的叠加次序),top、 left(显示弹出页面坐标),(offset().left,offset().top)在页面上找到某个元素的坐标,位置找到了,就可以随意在它的周 边定位弹出层了,其他的样式可以根据自己的美工需求随意调节…

源码下载(http://files.cnblogs.com/wzh2010/popDiv.rar