[转载]关于大型asp.net应用系统的架构如何做到高性能高可伸缩性

mikel阅读(915)

[转载]关于大型asp.net应用系统的架构—如何做到高性能高可伸缩性 – Mike的技术博客 – 博客园.

简介

前面一篇<<关于大型ASP.NET应用系统的架构-架构的选择>>写完之后,有一些同仁热心回复,有的是提问题,同 时希望能举一些例子来说明;有的是提建议,希望下一篇写得更详细点;还有的同仁提出不同的观点。感谢大家的参与。会继续努力的。本文将针对层Layer和 排Tier的区别做个辨析。并详细介绍3 Tier / N Tier架构中各排Tier的开发。各Tier的分布式方式。以及为了达到高性能,低延迟,高可伸缩性,需要采取哪些方法和手段。

关于“大型ASP.NET应用系统 ”的概念

意指能支持同时在线用户数目很多的ASP.NET应用系统。同时在线用户数目要达到多少才算大型。其实也没有一个可以作为共识的定义,个人认为如果一个应用系统能做到7×24小时同时在线用户数不少于5000的,应该可以称为大型应用系统。例如:微软的官网www.microsoft.com,7×24 小时都有来自全球的人访问,有查阅MSDN的,有访问微软博客的,有看微软产品信息的,有逛微软论坛的,等等等等。同时访问微软官网的人太多了,远多于 5000。还有Myspace。 它有总数为几千万的用户,它的同时在线用户数也是相当惊人的。它之所以能服务众多的用户,是因其背后有一个庞大的系统来支撑。

层Layer和排Tier的辨析

这里针对上篇的评论,对层Layer和排Tier做个辨析。上篇提到了分层Layer的架构只能部署在同一台服务上,有同仁在评论里提出不同意 见,说分层的架构也可以部署到多台服务器上的。层Layer是指应用程序各功能在逻辑上的分组,而排Tier表示了应用程序各功能是物理分部在多台计算机 上。层Layer很好理解,就是相同功能的类被逻辑上分到了一组,如:数据存取的类都放到了一块,在同一个名称空间下,在同一个程序集里,商务逻辑的类也 是一样进行分组,各组之间有统一的调用形式。如商务逻辑的类引用数据存取的类,调用其方法,取得返回结果。同时UI层可调用商务逻辑层的类。商务逻辑层的 类既有服务UI层的功能,也有调用数据访问层的功能。是个承上启下的层。这些层都是按照功能来划分的。层Layer是一种逻辑上的划分。排Tier是特指 物理的划分,应用程序的各功能,分别被放在了不同的服务器上,如UI功能单独占用一些服务器,商务逻辑功能占用另外的一些服务器。这两种功能部件之间有服 务器的边界,那么就有专门负责分布式调用的功能部件。如果单从功能逻辑上看,排Tier中也是有层Layer的,只是比传统层Layer的划分多了一些用 于分布式调用的层Layer。排Tier是各层Layer物理分离后,再加入一些负责分布式调用的层Layer才形成的。排Tier和层Layer是有着 联系的。从这个意义上说,排Tier是层Layer物理分离时的特例。有层Layer物理分离的情况下,可以称之为分层的架构,但是实际上这并不准确,因 为排Tier是专门为这个场景定义的。有物理分离,就叫排Tier更准确些。层Layer只要一做物理分离,就转化成了排Tier。

从部署角度试图来区别分层Layer的架构和3 排Tier / N 排Tier的架构。因为物理分离的场景已经被定义成排Tier,那么剩下的就只能是物理不分离的场景了。所以分层Layered架构就特指部署在同一台服 务上的场景(即物理不分离),3 排Tier / N 排Tier架构就特指各层Layer物理分离的场景。分层的架构部署到多台服务器上,理论上是可以的,但是光靠原有的层是不够的,有了服务器的边界之后, 原来在同一个进程里面的方法调用就不再可行,必须新加一些层来做分布式的调用,才能让原来的各层运行起来。等做完这一切,发现这个架构再叫分层 Layered的架构就不合适了,必须得叫3 排Tier / N 排Tier架构才合适。

层Layer和排Tier之间有联系,分层Layered的架构和3排Tier / N 排Tier架构可以互相转化。

整体映象

从前面的描述中可以得知应用系统的每一排Tier都是由许多服务器来完成的。比如UI排Tier,可以是几十个服务器,几百个服务器,甚至是几 千个服务器。具体每一个排Tier所需服务器的数目根据实际的需要来配置。所谓实际的需要就是看这一排Tier服务器的硬件资源利用率。比如CPU, 内存,磁盘读写等情况,如果相当高,就必须加入新的服务器部署该排Tier同样的应用到新服务器上。让新的服务器也能分担些压力。其实这就是要让应用程序 能支持高可伸缩性。在每一个排Tier之间有硬件负载均衡,再其后就是下一个排Tier的服务接口了。在其服务接口之后才是该排Tier的服务。

除了高伸缩性之外,还有如何保证高性能。即应用程序必须是良好设计的。在每一个排Tier的内部,可以采取一些措施让应用程序的执行效率达到最 高。让硬件的资源得到充分的利用。这有一些策略,如缓存。减少访问数据库的次数,等等。以下是一个可伸缩的asp.net应用系统的整体映象图:

The big picture

一个在互联网上的用户的请求的处理过程是这样的:

1. 首先经硬件负载均衡处理,选定一个Web服务器来响应这个请求,然后将该请求交给该服务器。

2. 此Web服务器执行所请求的页面,该页面的后端代码先查询缓存服务器,即调用缓存服务接口查询是否已经有缓存,如果有,就直接返回缓存的结果。

3. 如果缓存里没有就调用商务逻辑服务接口,进而调用商务逻辑服务。商务逻辑服务执行时,如果需要访问数据库,会先检查缓存中是否有缓存的数据库内容,如果有,就会用缓存的数据库内容来进行商务逻辑的计算。如果没有缓存,就会调用数据访问接口以存取数据。

4. 类似地,数据访问服务也会查看缓存,然后根据所要求的数据内容去访问相应的数据库,如果是只读的请求,数据访问服务可以将数据库访问请求发给做日志复制的数据库服务器。如果是写的请求,可以发给主数据库服务器。

5. 数据库服务器执行应用的SQL请求,返回结果。再由数据服务返回给商务逻辑服务。

6. 商务逻辑服务再返回给Web服务器,由Web服务器生成页面内容返回给互联网上的用户。

以上过程与分层Layered的架构类似,只是比分层Layered的架构多经过了几个服务接口。如果没有这些服务接口,因为UI排Tier,商务 逻辑排Tier,数据访问排Tier是在不同的服务器上的,它们根本就不能直接对话。因为它们是在不同的.net VM中的。它们必须得借助与这些服务接口才能互相之间进行调用。这些服务接口具体的组成技术可以是WCF,也可以是.net remoting,等。应该说目前最好的选择是WCF。

UI排Tier

关于SessionState的技术方案

为了让应用程序具有可伸缩性,必须让每一排Tier都有负载均衡的特性,也就是要做到用户的请求由任何一个同一排Tier中的服务器来处理都不 会有任何问题。关于用户的Session的处理就必须有一个妥善的解决方案。有不少人不赞同采用SessionState,觉得SessionState 对ASP.NET应用的性能影响比较大。还有人写文章说同一个SessionID的AcquireRequestState会在页面代码前获得对 Session对象的锁,因此容易有较大的延迟,对性能影响不小。另外的人认为Session占用服务器的内存比较多,同时需要一些CPU资源来将 Session中的对象序列化和反序列化。所以一种比较普遍的观点是不采用ASP.NET本身提供的Session机制。其实采用 SessionState和不采用SessionState都各有特点。了解其特点后再做权衡取舍才比较合适。

完全不采用SesstionState

完全不采用SesstionState是在Web.config中写上<sessionState mode=”Off”/>     或者   <Pages enableSessionState=”Off”/>来禁止SessionState。那整个应用的所有页面都不会用SessionState。 其实这不全面,http请求处理周期里还有一个系统默认的httpmodule在处理SessionState。还须在Web.config加一句:

<httpModules>
<remove name=”Session” />
</httpModules>

应用程序里完全不采用ASP.NET本身提供的SessionState机制,但是应用的需求是要求应用程序有类似于Session的机制的。 比如购物车的概念。记住用户选择了哪些商品,在用户点了买单时才处理用户选择了的商品。如果不用ASP.NET本身提供的SessionState机制, 就必须自己实现一个Session机制。比如可以在数据库中有一张表来记录自定义的Session数据。如果用户浏览器支持cookie,可以用该 cookie存储自定义的Session ID值。这个Session ID值用于到数据库中去查询存储的Session数据。如果用户浏览器不支持cookie,那么就可以在页面中放置隐藏的字段(hidden field)。此隐藏字段用于存储自定义的Session ID。还可以用URL中参数放一个Session参数的办法。这样获得的Session机制是自己管理的Session机制。需要将Session的创 建,过时失效,查询Session数据,删除旧Session等都管理起来。

这样的自定义的Session机制将Session数据存储到了数据库。那么就可以不依赖与某一台具体的服务器。从而获得的可伸缩的特性。

采用SessionState

采用SessionState是ASP.NET默认的机制。ASP.NET的SessionState有几种模式。 InProc,StateServer,SQLServer模式和自定义模式。InProc不支持负载均衡的场景。只有StateServer和 SQLServer模式才支持。自定义模式是指我们自己实现Session数据的持久化,比如将Session数据放到Oracle数据库或者MySQL 数据库中,自定义模式也可以支持负载均衡。在StateServer和SQLServer模式时,放入Session中的数据都必须是能序列化的。建议采 用SqlServer模式的Session机制。配置是这样的:

<system.web>

<sessionState mode=” Off | InProc | StateServer | SQLServer ”

cookieless=” true | false ”

timeout=” number of minutes ”

stateConnectionString=” tcpip=server:port ”

sqlConnectionString=” sql connection string ”

stateNetworkTimeout=” number of seconds ” />

</system.web>

Session采用了SqlServer模式之后,所有数据都会经序列化,并存储到SqlServer数据库中。采用这种模式的Session 机制,其Session可以由任何一个UI排Tier的服务器来处理,因为Session数据是存储在专门的数据库中的。如果是采用这种模式的 Session机制,那么最好有专门的数据库服务器供存储Session数据。通过上述安排,ASP.NET应用就获得了负载均衡,可伸缩的能力。

采用了ASP.NET的SessionState的之后,同一个Session ID下的不同页面请求会有一定的制约。注意这里说的同一个Session ID下的不同页面。这就象数据库的锁机制一样。默认的ASP页面设置都是能对Session对象进行读和写。那么如果同一个Session ID的两个不同请求访问两个不同的页面,就会因为都去锁住Session对象,而造成有一个请求被阻塞较长时间,因为要等另一个请求处理完毕。有同仁可能 觉得奇怪,怎么会有同一个Session ID请求两个不同的页面。其实这与页面中的iframe,frameset和AJAX技术有关。包含iframe, frameset的页面已经要存取Session了,iframe或者frameset里面的页面也要存取Session,就有可能造成一先一后,都是同 一个Session ID,后面的页面被前面的页面锁住,直到前面的页面都处理完,释放对Session的锁,才能处理后面的页面。AJAX也类似。也存在这个问题。这个默认 的机制所带来的延迟在小型的ASP.NET应用中可以不用理睬。但是在大型的ASP.NET应用中是必须解决的问题。要解决这个问题,只能从应用的角度尽 力减少需要写Session的范围,即明确确定哪些页面需要读且写Session数据。还需要确定哪些页面是只需要读Session数据。另外还需要确定 哪些页面不需要参与读或者写Session数据,即与Session数据无关的页面。通过这样的工作,就确定了Session的范围。对于需要读且写 Session的页面,可以显示地在页面中写上< % @Page enableSessionState=”On”% >。对于只需要读Session的页面,可以写上< % @Page enableSessionState=”ReadOnly”% >。对于不需要Session的页面,可以写上< % @Page enableSessionState=”Off”%  >。在一个iframe相关的所有页面中,不要所有的页面都去读写Session,这样就可以避免Session争锁所带来的延迟。AJAX所涉及 的页面也是如此,尽可能地减少读写Session,发生这种Session争锁的延迟就会少一些。锁越少,整个UI排Tier的处理能力就会越大。

关于ViewState的技术方案

ViewState使服务器控件可以在往返行程中重新填充它们的属性值,而程序员不需要编写任何代码。这些属性值包括可见的属性,也包括不可见 的。可见的属性如Text属性,不可见的是某些控件的ControlState。ControlState是比较特殊的内容,它总是存储在 ViewState字段中。即使用EnableViewState=”false”禁止了ViewState,ViewState字段还是有一些内容,这 些内容就是ControlState。

曾经听到不少人抱怨说ViewState大,有时光ViewState就几百K。一个页面的HTML,很大的部分是ViewState占用了。 微软的文章也在说不需要ViewState的地方就禁止ViewState。所以合理决定应用程序哪些地方需要ViewState。毕竟ViewSate 也一定程度上带给程序员一些方便。禁止ViewState是可以在整个应用的级别,页面的级别,和控件的级别来禁止。整个应用的级别禁止 ViewState:  <pages enableViewState=”false” enableViewStateMac=”false” enableEventValidation=”false”></pages>,页面的级别如:< % @ Page EnableViewState=”false” % >,控件的级别如:<asp:datagrid EnableViewState=”false” datasource=”…” runat=”server”/>。禁止了ViewState之后,页面中的__ViewState字段已经大大减小了,但是还是存在。上面已经提 到了,__ViewState字段里剩下的内容就是ControlState的。如果想让__ViewState字段没有内容,可以改写Page类的此两 方法:

protected override void SavePageStateToPersistenceMedium(object viewState)
{
}

protected override object LoadPageStateFromPersistenceMedium()
{
return null;
}

这样__ViewState字段就完全没有内容了。当然我们可以在此两方法里面设计出自己的持久化ViewState内容的方案。比如将 ViewState持久化到缓存中去,或者持久化到SqlServer中去。那么ViewState的内容就不再需要发送的到用户浏览器中了。上面介绍了 一些在某些地方禁用ViewState的方法。下面就由开发者和用户来决定哪些页面或者控件需要ViewState,还是完全不要ViewState。 ViewState机制具有两面性,一方面方便了程序员,另一方面可能对性能造成影响。所以要小心对待。

减少与服务器的交互次数和不必要的服务器端处理

Page.IsPostBack

Page.IsPostBack可以判断是否有Form提交。第一次访问时的处理和有Form提交的处理是不一样的。这样可以避免不必要的服务器端处理。

AutoPostBack属性

许多服务器端控件都有AutoPostBack,能禁止的都禁止了。

多做客户端的数据验证

用户在浏览器里面的输入,尽量先用客户端JavaScript验证处理,等通过了在提交给服务器。这样减少向服务器提交请求的次数。

AJAX的请求量进行控制

AJAX带来了很炫的效果,但是能适当地减少调用AJAX调用次数,比如能否合并AJAX的调用。

用Server.Transfer不用Response.Redirect

Server.Transfer发生在服务器端,而Response.Redirect发生在用户浏览器中。会多一次HTTP请求。

去除不必要的默认httpModule

如不要SessionState,不要WindowsAuthentication,不要PassportAuthentication等等:

<httpModules>
<remove name=”Session” />
<remove name=”WindowsAuthentication” />
<remove name=”PassportAuthentication” />
<remove name=”AnonymousIdentification” />
<remove name=”UrlAuthorization” />
<remove name=”FileAuthorization” />
</httpModules>

设置processModel

手动设置processModel参数中的MaxWorkerThreads 和 MaxIOThreads 属性,通过观察效果带调整参数。如果机器资源允许,可以稍微多点。

设置Web garden

只要服务器资源允许,就可以建立Web garden,在同一个服务器上多开几个工作者进程。32位Windows上一个进程通常只能占用2G-3G内存(因为高地址的2G或者1G是 Windows本身用来装配系统文件用的)。64位Windows上一个进程能占用的内存相对32位大一点,但是服务器有比如100多G的内存,可以适当 多开几个工作者进程。这可以增加单台服务器的处理能力。要设置Webgarden可以先在IIS管理器里面找到对应的应用程序池,在查看该应用程序池的高 级属性,再找到最大工作者进程参数,见图。

Webgarden

缓存

ASP.NET中可用的缓存主要有:页面级的缓存,控件级,System.Web.Caching.Cache,以及分布式缓存如 Velocity和memcahced。页面级的缓存可以在ASPX页面用< % @ OutputCache Duration=”10″ VaryByParam=”none” % >,在用户控件中可以用< % @ OutputCache Duration=”10″ VaryByParam=”none” VaryByControl=””% >,与页面级的cache相比,多了VaryByControl参数。必须得指出这些页面级的和控件级的缓存是存储在特定的Web服务器上的。除非 在负载均衡的硬件上做特殊的设置,否则这些页面级和控件级的缓存都意义不大。因为这些页面级的和控件级的缓存是存储在特定的Web服务器上的,第一次用户 的请求是由此服务器处理的,然后有了页面缓存,如果负载均衡硬件将第一次以后的请求交由其他服务器处理,那么这个处理第一次请求所做的页面和控件级缓存都 失去了意义。只有进行了特殊设置后,负载均衡的硬件才能知道刚才这个请求是哪个服务器处理的,就继续向该服务器转发HTTP请求。那么保存的页面等缓存 Cache才会起到相应的作用。System.Web.Caching.Cache是个很好的缓存机制,可以给程序员利用来缓存一些内容。可惜它不是分布 式的。它的存储限定在特定的服务器上。所以它对负载均衡是不支持的。要支持负载均衡,需要使用分布式的缓存如Velocity或memcached,在 UI排Tier缓存的内容可以是数据库查询结果。如果是自己管理的Session机制,可以将分布式缓存作为Session的存储,所有Session中 的对象,可以存储在分布式缓存中。还有ViewState,如果希望客户浏览器不下载ViewState但是又要用ViewState,可以重载Page 类的SavePageStateToPersistenceMedium和LoadPageStateFromPersistenceMedium方法, 并在此方法中将ViewState存储到分布式缓存。

考虑预编译

将所有ASP.NET页面都预先编译。可以减少第一次访问时由于ASP.NET编译页面所带来的延迟。

在生产环境禁用调试模式

生产环境使用Release模式的编译,会使程序运行稍微快一点。

尽量避免异常

异常是非正常的程序控制流。发生异常多对性能的影响比较大。所以在程序中多对可能的情况进行检测,比如判断某对象是否为空。此同样适用于其他排Tier。

尽量避免锁住资源

在多线程的场景下,尽可能地去避免锁住资源。尽量各线程都用私有的资源。此同样适用于其他排Tier。

压缩页面和相关文件

比如可以打开IIS的gzip,还有用一个自制的HTTP module压缩页面的HTML, .js文件。去掉不显示的回车和空格。进行尽可能多的压缩。

商务逻辑排Tier

商务逻辑服务接口

前面已经提到,服务接口可以考虑用WCF, Remoting等技术。目前最好的是采用WCF。原因是WCF支持事务,支持多种通信方式。商务逻辑服务有时是必须在互联网上公开。所以WCF可以选用 基于Web service的通信方式,这样支持的外部系统比较多。如果商务逻辑服务只是在内部使用,可以选用TCP/IP socket的通信方式。这个商务逻辑服务接口其实就是后面的商务逻辑服务的包装。商务逻辑服务提供哪些方法,就用相应的接口进行对应。

商务逻辑

事务的控制

商务逻辑这里应该对事务进行控制。这与WCF接口支持事务想匹配的。

预取与缓存

比如翻页,可以在用户取第一页时,取出5页,缓存起来,用户往后翻几页时就可以不再查询数据库。减少对数据库的查询次数。有些查询特别多的数据,直接都在分布式缓存里面存着。只有缓存里没有的时候,才去查询数据库。

对数据库的访问也是可以分布式的调用

大家看到了上面的图,对数据库的访问也是需要通过分布式的调用才能完成。数据库查询的结果通过自定义的对象集合来传递。

采用自定义的对象作为商务逻辑的处理对象

这些自定义的对象其实就是一个数据库中数据的在内存中的反映。商务逻辑的处理对象最好用自定义的对象。不要用DataSet。

商务逻辑排Tier最好是无状态的

该排Tier最好是状态无关的。与商务有关的数据都存储到分布式缓存里面。服务器内存里面不长时间存储商务有关的数据。这样,一个对商务逻辑的请求就可以由任何一台商务逻辑排Tier的服务器来处理,这样就做到了负载均衡。

长时间计算型的任务最好交给其他系统来在后台处理

有些计算密集的任务,最好交给其他系统在后台运行。与计算密集的系统交互就只通过数据文件进行交互。

数据访问排Tier

数据访问服务接口

类似于商务逻辑服务接口,数据访问服务接口可以考虑用WCF, Remoting等技术。目前最好的是采用WCF。原因是WCF支持事务,支持多种通信方式。可以选用基于Web service的通信方式,也可以选用TCP/IP socket的通信方式。这个数据访问服务接口其实就是后面的数据访问服务的包装。

数据访问

对事务的支持

如前所述,商务逻辑控制着事务,数据访问排Tier只是作为商务逻辑控制的事务的一部分。数据访问排Tier中有许多数据库的操作,如,查询, 更新等。建议所有的数据库操作都用存储过程来实现。这些数据库操作都作为商务逻辑控制的事务的一部分。不要在存储过程中实现商务逻辑。这些数据库操作都只 是替商务逻辑服务完成数据库查询或者存储数据到数据库的任务。所以不要在存储过程或者数据访问排Tier实现任何商务逻辑的内容。

数据库读写分离的支持

如前图所示,数据库有只读模式的。可以将部分读的请求分流到只读模式的数据库服务器上。只有写的请求才流到主数据库服务器上。这就要求分别支持不同的连接。

连接池的管理

每台数据库服务器所允许的连接数是一定的。需要管理好个数据访问服务的数据库连接。管理好每台数据访问服务服务器连接池。

在读的时候用SqlDataReader

读取数据的时候,可用SqlDataReader来读取快速只进的数据流。

缓存

将数据库访问获得的内容缓存到分布式缓存服务器上。

数据库的设计和安排

读写分离

主数据库服务器是集群的数据库服务器。SqlServer 2008 R2 / Windows Server 2008 最多支持16台服务器的集群。可以架设一些只读模式数据库服务器,采用日志复制方式,将主数据库的所有日志复制到只读模式的数据库服务器上。那么只读模式 数据库服务器内容就可以保持和主数据库服务器一致。这些只读数据库服务器就可以用于分担读的压力。

库表的分离

从应用的角度将某一些数据分到多个数据库来存储。比如Myspace有7000多万用户,它把每一百万用户存放于一个数据库。这样每个数据库都 小了很多。查询起来相对快一些,但是程序就会设计得复杂一点。分开的数据库可以放在不同的服务器,也可在同意服务器。请根据实际情况来决定。

表的设计

3NF, BCNF是肯定要达到的。这不多说了。主要想说说聚集索引。表的聚集索引是很关键的一个索引。需要从应用角度考虑,最多的查询是什么样的,然后按照使用最 频繁的查询来设计聚集索引。一般来说聚集索引需要选用短的,基本数据类型的字段。比如整数, 固定长度的文本,日期之类的字段作为聚集索引的字段。而且具有单向递增的特性,比如日期,自增的字段。良好的聚集索引的设计,对最频繁的查询的性能改进是 很有帮助的,同时对插入,更新都有较大的帮助。插入时是在物理的表记录末尾加入新记录,引起的磁盘IO较小;更新时也可按照索引来很快查找到记录并更新。 同时也得考虑删除时的效率。如果可能的话尽量不要删除记录,只将需要删除的记录置成删除状态。

除了聚集索引,还有普通索引,合适的普通索引对查询的性能也是有帮助的。还是分析应用可能的查询,可以将次优先的那些查询分析一下,这些查询主要用到哪些字段作为搜索条件。然后可以适当地建立普通索引。这些聚集索引和普通索引对查询的性能是有帮助的。

创建表分区

将表的记录按一定规则来分到不同的数据文件上存储。可以分区的字段也是基本的类型。比如日期,文本等。创建分区的表的IO可以由多个线程同时来读写不同的数据文件。在IO上可以有所改进。

合理使用视图

创建一定数量的试图,可以对查询性能起到帮助。

分布式调用越少越好?

前面一篇文章<<关于大型asp.net应用系统的架构-架构的选择>>有同仁提出分布式调用越少越好的观点。这里可 以说一下。如果只有一台服务器的时候,单纯比较用分布式调用和非分布式调用,分布式调用肯定比非分布式调用慢,因为分布式调用要多一些中间接口的处理。但 是非分布式调用能同时支持那么多人同时访问吗?非分布式调用能将用户的请求交由任何一个服务器来处理而不出现问题吗?万一一台服务器出现了问题,那么这台 服务器上的用户就丢失他/她的会话和数据吗?大家看吧。

当然也有这种可能,就是整个系统中某些地方采用分布式调用,另外一些地方采用非分布式调用。例如:商务逻辑服务和数据访问服务之间就不用分布式调用了。那么整个系统的图就成了这样:

The big picture with partial non distributed calls

这样做不是不可以,就是有其优缺点,优点是商务逻辑调用数据访问可以比全部分布式的更快,缺点就有可能是,商务逻辑服务器多到一定程度,就会发现,数据库连接却不能再往上增加了,而要同一调度数据库连接也是很困难的。商务逻辑与数据访问的耦合度是否有点高?

结束语

对于大型的ASP.NET来说,首先要保证负载均衡和可伸缩性,再来做到每一台服务器的性能最大化。要使整个系统的服务能力最大化,需要使用软件硬件的所有手段。这里谈到的只是一些方面,不够全面。

[转载]重构:避免重构误区

mikel阅读(1213)

[转载]重构,避免重构误区 – 成长的足迹 – 博客园.

现在有太多是文章讨论各种重构的技术,咱们就不谈了,我们就谈谈要怎么重构。

一谈到重构,大家大多认为是运用设计模式,来使你的代码看上去非常优雅。其实重构包括很多,一个变量名的修改、代码格式的编排、分解一个大方法的等等都是属于重构的范畴。

我相信肯定有人和我一样,捧一本重构方面的书(《重构与模式》Joshua Kerievsky著,这本就不错),一劲的狂喊这样写代码太漂亮;可是丢开书本,面对自己的代码一直寻找可以运用设计模式、需要重构的代码,最后发现一 处可重构的都没。 写出优雅的代码,相信对于很多人来说一直是个瓶颈、很难去突破。不过突破后,你会发现你的世界变大了,犹如跳出井口的青蛙。

那我们到底才能写出那么优雅的代码呢。我们先说说,我们的重构误区吧。

误区:

1、从已有的代码寻找与书本中匹对一样或类似的场景。

2、在编写新代码时,脑子一直想着那几种模式,想去套用,一直想用上几个。

最终还是没什么好的消息,造成这样的原因是,你没真正的理解设计模式的运用场景、重构技术。

也许心里面会对自己说,这是项目比较小,没出现书本提到的那些场景。

其实重构你可以从以下几个方面入手。

1、寻找代码的坏味。 《你的代码需要重构吗?》

2、review自己的代码,如果觉得不好的地方就想想有没有更好的方法。可以拿出来和别人讨论,相信你一定有不一样的收获。

3、让别人review自己的代码。在团队开发中,难免其他队员开发的功能,会和你这边的模块有关联,当有队员来问你这个函数实现什么功能、这段代码的什么意思。

这有可能以下的原因:a、自己写的代码太烂! b、队员的技术太菜,看不懂。 c、队员较懒,不想去看。

在给他解释时,你就要注意你们的对话,对你重构是有帮助的。

队员:我不懂你这个变量是干什么的。

你就要想想,自己写变量命名不规范?

队员:你这段太长了,看得我头痛。

也是你的函数真的太长了,该修剪你的代码了,把这个大功能,分割为几个小功能。

4、阅读别人的代码。你可以阅读一些比较经典的开源代码,这样你可以和很多人一起讨论。在阅读时,要认认真真的看,读懂它的框架,渗透理解它的各个细节。

下面我举一个我在现实开发中碰到的一个问题:

第一次代码:

1 public class OrderSync 2 { 3 //订单状态,Created、Closee、Dealing、Paying、Posting 4   public string Status; 5 public void Sync() 6 { 7 OrderRequest req=new OrderRequest(); 8 req.Status=Status; 9 10 req.Search(); 11 ... 12 } 13 } 14 15 OrderSync orderSync=new OrderSync(); 16 orderSync.Status = "Created";

缺点:

1、不清楚订单有哪几种状态。

2、如果“Created”,改成为“Create”,需要修改所有的地方,那你就开始寻找吧。

3、orderSync.Status = “Created”;不小心写成orderSync.Status = “Created1111”;只能单独纳闷为什么无法查询到记录。

第二次代码:

1 public class OrderSync 2 { 3 //订单状态,Created、Closed、Dealing、Paying、Posting 4   public string Status; 5 public void Sync() 6 { 7 OrderRequest req = new OrderRequest(); 8 req.Status=Status; 9 10 req.Search(); 11 ... 12 } 13 } 14 15  public class OrderStatu 16 { 17 public static string Created = "Created"; 18 public static string Closed = "Closed"; 19 public static string Dealing = "Dealing"; 20 public static string Paying = "Paying"; 21 public static string Posting = "Posting"; 22 } 23 24 OrderSync orderSync = new OrderSync(); 25 orderSync.Status = OrderStatu.Created;

此次重构解决了第一次的3个缺点。但它还是有缺点的:

1、在OrderSync中Status是字符串的,没相应的格 式来限制。若你的团队来了一个新的成员,他对此系统不是很熟悉,他就有可能这么写: OrderSync orderSync = new OrderSync(); orderSync.Status = “Created”;

第三次代码:

1 public class OrderSync 2 { 3 public OrderStatu Status; 4 public void Sync() 5 { 6 OrderRequest req=new OrderRequest(); 7 req.Status = OrderStatuHelper.GetStatu(Status); 8 9 req.Search(); 10 ... 11 } 12 } 13 14  public enum OrderStatu 15 { 16 Created, 17 Closed, 18 Dealing, 19 Paying, 20 Posting 21 } 22 23  public class OrderStatuHelper 24 { 25 private static string Created = "Created"; 26 private static string Closed = "Closed"; 27 private static string Dealing = "Dealing"; 28 private static string Paying = "Paying"; 29 private static string Posting = "Posting"; 30 31 public static string GetStatu(OrderStatu orderStatu) 32 { 33 switch(orderStatu) 34 { 35 case OrderStatu.Closed: 36 return Closed; 37 case OrderStatu.Dealing: 38 return Dealing; 39 case OrderStatu.Payding: 40 return Pading; 41 case OrderStatu.Posting: 42 return Posting; 43 default: 44 return Created; 45 } 46 } 47 }

这样在给订单赋值时,只能从OrderStatu去选择状态,避免了第二次的缺点。

当然了它也是有缺点的:

1、性能相对于第一次不是很高。(在性能差的系统中,这往往不是影响系统性能的主要原因)

2、增加了系统的复杂度。

这样,如果以后我们再碰到“第一次的代码”,我们就能直接把它重构到“第三次代码”,不用经过“第二次代码”。

想想为什么我们无法写出像书本上那样的优雅的代码,那是因为我们没真正理解问题的所在、找到问题的关键点。必须先经历“第二次代码”,才能到达“第三次代码”。

[转载]C#特性Attribute的实际应用之为应用程序提供多个版本

mikel阅读(837)

[转载]C#特性Attribute的实际应用之:为应用程序提供多个版本 – luminji’s web – 博客园.

基于如下的几个理由,需要为应用程序提供多个版本:

1:应用程序有体验版和完整功能版;

2:应用程序测试过程中需要完成一些自动化的操作;

1:提供体验版

关于第一点,所谓体验版也就是完整版的一个缩小版。即出于某种理由,不对用户开放应用程序的全部功能。

假设我的应用程序共有两类功能。第一类功能属于单机版,而完整版还提供在线功能。那么,在功能上,我需要定制两个属性”ONLINE”和”OFFLINE”。在体验版中,我只开放”OFFLINE”功能。

要实现此目的,我不想提供两套应用程序,而是想通过最小设置,就能为一个应用程序输出两个发布版本。这个时候,就需要.NET中的特性Attribute了。

首先,我们模拟离线和在线两个功能:

代码

class MyService { [Conditional("OFFLINE")] [Conditional("ONLINE")] public static void Testing() { System.Windows.Forms.MessageBox.Show("offline game..."); } [Conditional("ONLINE")] public static void GetInfoFromNet() { System.Windows.Forms.MessageBox.Show("online game..."); } }

程序入口:

代码

private void button1_Click(object sender, EventArgs e) { MyService.Testing(); } private void button2_Click(object sender, EventArgs e) { MyService.GetInfoFromNet(); }

要实现两个不同的功能,则需要在程序入口这个文件中定义:

#define ONLINE //#define OFFLINE using System; using System.Collections.Generic; using System.ComponentModel;

1.1:全局宏定义

记住,这个定义一定要在文件的最开头处。同时,该定义只对本文件有效,如果想定义全局,必须在项目属性中定义,如下:

如果想定义多个全局宏定义,则用逗号隔开,如”LUMINJI,HUCHANGJUN”。

接下来的问题,就比较简单了,如果要发布全功能,就#define ONLINE,如果要发布离线版功能,就注释掉#define ONLINE然后定义OFFLINE就行了。

2:为测试过程提供自动化操作

在测试UI的时候,需要操作各类文本框、列表等控件。举个简单的例子来说,一个登录窗口,需要输入用户名和密码。如果每次我测试的时候,都要让我自己来输入,势必影响效率。这个时候,就可以让Attribute来发挥作用了。

在如下示例中,在登录窗口的Load事件中,我们为用户名和密码赋值,并模拟点击Click事件。

代码

#define TESTAUTO using System; …… public Form1() { InitializeComponent(); this.Load += delegate { TestAuto(); }; } [Conditional("TESTAUTO")] private void TestAuto() { Thread t = new Thread((ThreadStart)delegate { Thread.Sleep(1000); this.Invoke((MethodInvoker)delegate { this.textBox1.Text = "luminji"; this.textBox2.Text = "123"; button3_Click(this, null); }); Thread.Sleep(1000); }); t.IsBackground = true; t.Start(); }

记住,在提交代码的时候,可以注释掉TESTAUTO宏定义,这样,既不会影响到最终的版本,也能让自己在本机得到最大的自由度。从另一个角度来说,整个项目组可提前协调好,可以让这些测试方法共有。

3:关于测试多说一点:

提供一个思路:这个特性也可以让我们从程序员的角度来完成部分测试用例的编写及测试。

后记:

对于Attribute的基础介绍,本文不再阐述,除MSDN外,还可以参考下文(个人感觉这两篇已经写的非常好):

http://blog.csdn.net/fantasiax/archive/2007/05/28/1627694.aspx

http://blog.csdn.net/fantasiax/archive/2007/06/04/1636913.aspx

本文的测试代码下载:http://download.csdn.net/source/2868666

[转载]sql的between與查詢日期範圍

mikel阅读(1125)

[转载]sql的between與查詢日期範圍 – ie421.NET – 博客园.

資料 Customer

Id Name RegisterDate
1 澎澎 2007/1/5 00:00:00
2 丁丁 2007/1/6 00:00:00
3 亞亞 2007/1/7 00:00:00

aspx頁面查詢條件:

最小日期: [2007-01-06]      最大日期: [2007-01-06]

使用的 SQL:

SELECT ID, Name, RegisterDate
FROM Customer
WHERE (RegisterDate BETWEEN ‘2007-01-06’ AND ‘2007-01-06’)

結果是傳回1筆:丁丁, 符合我的預期

但是 如果丁丁的RegisterDate 的時間 不是00:00:00呢?

如果資料是這樣

Id Name RegisterDate
1 澎澎 2007/1/5 00:00:00
2 丁丁 2007/1/6 04:37:00
3 亞亞 2007/1/7 00:00:00

一樣的SQL

SELECT ID, Name, RegisterDate
FROM Customer
WHERE (RegisterDate BETWEEN ‘2007-01-06’ AND ‘2007-01-06’)

傳回來的是0筆

嗯,因為我沒有給予正確的時間範圍嘛,我的查詢條件應該是2007/1/6 號整天時間範圍。

所以我通常程式在查詢前,都會先幫日期加1天

RegisterMaxDate = RegisterMaxDate.AddDays(1);

但是,這樣就對了嗎??

這樣的程式產生的SQL

SELECT ID, Name, RegisterDate
FROM Customer
WHERE (RegisterDate BETWEEN ‘2007-01-06’ AND ‘2007-01-07’)

反而傳回2筆,把亞亞也傳回來了

因為 Sql 的 between min And  max  是 value >=min &&  value <= max 的意思

所以我應該更精確的

RegisterMaxDate = RegisterMaxDate.AddDays(1).AddSeconds(-1);

那樣產生的sql  才會是

SELECT ID, Name, RegisterDate
FROM Customer
WHERE (RegisterDate BETWEEN ‘2007-01-06’ AND ‘2007/1/6 23:59:59’)

傳回1筆,也就是丁丁,我要的資料。

[转载]MVC设计思想

mikel阅读(1039)

MVC设计思想

MVC英文即Model-View-Controller,即把一个应用的输入、处理、输出流程按照Model、View、Controller的方式进行分离,这样一个应用被分成三个层——模型层、视图层、控制层。

视图

视图(View)代表用户交互界面,对于Web应用来说,可以概括为HTML界面,但有可能为XHTML、XML和

MVC模式

Applet。 随着应用的复杂性和规模性,界面的处理也变得具有挑战性。一个应用可能有很多不同的视图,MVC设计模式对于视图的处理仅限于视图上数据的采集和处理,以 及用户的请求,而不包括在视图上的业务流程的处理。业务流程的处理交予模型(Model)处理。比如一个订单的视图只接受来自模型的数据并显示给用户,以 及将用户界面的输入数据和请求传递给控制和模型。

模型

模型(Model):就是业务流程/状态的处理以及业务规则的制定。业务流程的处理过程对其它层来说是黑箱操作,模型接受视图请求的数据,并返回最终的 处理结果。业务模型的设计可以说是MVC最主要的核心。目前流行的EJB模型就是一个典型的应用例子,它从应用技术实现的角度对模型做了进一步的划分,以 便充分利用现有的组件,但它不能作为应用设计模型的框架。它仅仅告诉你按这种模型设计就可以利用某些技术组件,从而减少了技术上的困难。对一个开发者来 说,就可以专注于业务模型的设计。MVC设计模式告诉我们,把应用的模型按一定的规则抽取出来,抽取的层次很重要,这也是判断开发人员是否优秀的设计依 据。抽象与具体不能隔得太远,也不能太近。MVC并没有提供模型的设计方法,而只告诉你应该组织管理这些模型,以便于模型的重构和提高重用性。我们可以用 对象编程来做比喻,MVC定义了一个顶级类,告诉它的子类你只能做这些,但没法限制你能做这些。这点对编程的开发人员非常重要。   业务模型还有一个很重要的模型那就是数据模型。数据模型主要指实体对象的数据 保存(持续化)。比如将一张订单保存到数据库,从数据库获取订单。我们可以将这个模型单独列出,所有有关数据库的操作只限制在该模型中。

控制

控制(Controller)可以理解为从用户接收请求, 将模型与视图匹配在一起,共同完成用户的请求。划分控制层的作用也很明显,它清楚地告诉你,它就是一个分发器,选择什么样的模型,选择什么样的视图,可以 完成什么样的用户请求。控制层并不做任何的数据处理。例如,用户点击一个连接,控制层接受请求后, 并不处理业务信息,它只把用户的信息传递给模型,告诉模型做什么,选择符合要求的视图返回给用户。因此,一个模型可能对应多个视图,一个视图可能对应多个 模型。   模型、视图与控制器的分离,使得一个模型可以具有多个显示视图。如果用户通过某个视图的控制器 改变了模型的数据,所有其它依赖于这些数据的视图都应反映到这些变化。因此,无论何时发生了何种数据变化,控制器都会将变化通知所有的视图,导致显示的更 新。这实际上是一种模型的变化-传播机制。模型、视图、控制器三者之间的关系和各自的主要功能,如图1所示。

MVC优点

大部分用过程语言比如ASP、PHP开发出来的Web应用,初始的开发模板就是混合层的数据编程。例如,直接向数据库发送请求并用HTML显示,开发 速度往往比较快,但由于数据页面的分离不是很直接,因而很难体现出业务模型的样子或者模型的重用性。产品设计弹性力度很小,很难满足用户的变化性需求。 MVC要求对应用分层,虽然要花费额外的工作,但产品的结构清晰,产品的应用通过模型可以得到更好地体现。   首先,最重要的是应该有多个视图对应一个模型的能力。在目前用户需求的快速变化下,可能有多种 方式访问应用的要求。例如,订单模型可能有本系统的订单,也有网上订单,或者其他系统的订单,但对于订单的处理都是一样,也就是说订单的处理是一致的。按 MVC设计模式,一个订单模型以及多个视图即可解决问题。这样减少了代码的复制,即减少了代码的维护量,一旦模型发生改变,也易于维护。 其次,由于模型返回的数据不带任何显示格式,因而这些模型也可直接应用于接口的使用。   再次,由于一个应用被分离为三层,因此有时改变其中的一层就能满足应用的改变。一个应用的业务流程或者业务规则的改变只需改动MVC的模型层。   控制层的概念也很有效,由于它把不同的模型和不同的视图组合在一起完成不同的请求,因此,控制层可以说是包含了用户请求权限的概念。   最后,它还有利于软件工程化管理。由于不同的层各司其职,每一层不同的应用具有某些相同的特征,有利于通过工程化、工具化产生管理程序代码。

MVC缺点

MVC的不足体现在以下几个方面:

(1)增加了系统结构和实现的复杂性。对于简单的界面,严格遵循MVC,使模型、视图与控制器分离,会增加结构的复杂性,并可能产生过多的更新操作,降低运行效率。

(2)视图与控制器间的过于紧密的连接。视图与控制器是相互分离,但确实联系紧密的部件,视图没有控制器的存在,其应用是很有限的,反之亦然,这样就妨碍了他们的独立重用。

(3)视图对模型数据的低效率访问。依据模型操作接口的不同,视图可能需要多次调用才能获得足够的显示数据。对未变化数据的不必要的频繁访问,也将损害操作性能。

(4) 目前,一般高级的界面工具或构造器不支持MVC架构。改造这些工具以适应MVC需要和建立分离的部件的代价是很高的,从而造成使用MVC的困难。

[转载]淘宝的可伸缩高性能互联网架构

mikel阅读(929)

[转载]淘宝的可伸缩高性能互联网架构 – ddatsh – 博客园.

一 应用无状态(淘宝session框架)

假如在session中保存了大量与客户端的状态信息,保存状态信息的server宕机时

通常通过集群解决,不仅有负载均衡,更重要的是要有失效恢复failover

tomcat用集群节点广播复制,jboss用配对复制等session状态复制策略,但严重影响系统的伸缩性,不能通过增加更多的机器达到良好的水平伸缩

因为集群节点间session通信随着节点的增多而开销增大,因此要想做到应用本身的伸缩性,要保证应用无状态,这样集群中的各个节点来说都是相同的,使系统更好的水平伸缩

淘宝的session框架用clientcookie实现,将状态保存到cookie里,使应用节点本身不要保存状态信息,这样在系统用户变多的时候,就可以通过增加更多的应用节点来达到水平扩展的目的

但有限制,比如每个cookie一般不能超过4K的大小,很多浏览器都限制一个站点最多保存20个cookie

淘宝cookie框架是“多值cookie”,一个组合键对应多个cookie的值,可以防止cookie数量超过20,还节省了cookie存储有效信息的空间

除了淘宝目前的session框架的实现方式以外,其实集中式session管理来完成,说具体点就是多个无状态的应用节点连接一个session 服务器,session服务器将session保 存到缓存中,session服务器后端再配有底层持久性数据源,比如数据库,文件系统等等。

二 有效使用缓存(Tair)

浏览器缓存,反向代理缓存,页面缓存,局部页面缓存,对象缓存等等

大部分情况都是读缓存,读写比不高,对数据安全性需求不高的数据,将其缓存,减少对数据库访问

店铺系统,如店铺介绍,店铺服务条款,宝贝详情,适合放到缓存中,减少DB的负载

三 应用拆分(HSF)

拆分(也算是一种解耦),将原来的系统根据一定的标准,比如业务相关性等分为不同的子系统,提高系统扩展性和可维护性,系统的水平伸缩性提升,可以有针对性的对压力大的子系统进行水平扩展而不影响到其它的子系统

子系统之间的耦合减低了,当某个子系统暂时不可用的时候,整体系统还是可用的,从而整体系统的可用性也大大增强了

拆分也给系统带来了问题,子系统之间如何通信

一般有同步通信和异步通信,这个时候高性能的远程调用框架就显得非常总要

四 数据库拆分(TDDL)

应用除了应用级别的拆分以外,另外一个很重要的层面就是存储如何拆分,通常就是所说的RDBMS进行拆分

数据库读取压力太大,就是到了读写分离的时候,配置一个server为master节点,然后配几个salve节点,通过读写分离,使得读取数据的压力分摊到了不同的salve节点上面,系统恢复正常

到了master负载太高时就要垂直分区了(就是所谓的分库)

比如将商品信息,用户信息,交易信息分别存储到不同数据库,同时还可以针对商品信息的库采用master,salve模式,

分库后,各个按照功能拆分的数据库写压力被分担到了不同的server上面,数据库的压力终又恢复到正常状态

用户量的不断增加,系统中某些表异常庞大,比如好友关系表,店铺参数配置表等,这个时候无论是写入还是读取这些表的数据,对数据库来说都是一个很耗费精力的事情,此时就要进行“水平分区”了(俗话说的分表,或者说sharding)

数据库是系统中最不容易scale out的一层

大型的应用必然会经过一个从单一DB server,到Master/salve,再到垂直分区(分 库),然后再到水平分区(分表,sharding)的过程,而在这个过程中,Master/salve 以 及垂直分区相对比较容易,对应用的影响也不是很大,但是分表会引起一些棘手的问题,比如不能跨越多个分区join查 询数据,如何平衡各个shards的 负载等等,这个时候就需要一个通用的DAL框架来屏蔽底层数据存储对应用逻辑的影响,使得底层数据的访问对应用透明化

从昂贵的高端存储(小型机+ORACLE)切换到MYSQL后,势必会遇到垂直分区(分库)以及水平分区(Sharding)的问题,淘宝根据其业务特点开发了自己的TDDL框架,解决分库分表对应用的透明化以及异构数据库之间的数据复制

五 异步通信(Notify)

消息中间件登场,采用异步通信关系到系统的伸缩性,以及最大化的对各个子系统进行解耦

异步一定是根据业务特点来的,一定是针对业务的异步,通常适合异步的场合是一些松耦合的通信场合,而对于本身业务上关联度比较大的业务系统之间,还是要采用同步通信比较靠谱

六 非结构化数据存储 ( TFS,NOSQL)

不是所有的数据都是结构化的

比如一些配置文件,用户对应的动态,一次交易的快照等,一般不适合保存到RDBMS,更符合一种Key-value的结构

另一类数据,数据量非常大,但实时性要求不高,此时这些数据也需要通过另外的一种存储方式进行存储

一些静态文件,比如各个商品的图片,商品描述等信息,这些信息因为比较大,放入RDBMS会引起读取性能问题,影响其它数据读取性能,也要和其它信息分开存储,一般的选择分布式文件系统,

随 着互联网的发展,业界从08年 下半年开始逐渐流行了一个概念就是NOSQL。我们都知道根据CAP理论,一致性,可用性和分区容错性3者 不能同时满足,最多只能同时满足两个

传统关系数据采用ACID事务策略,更加讲究高一致性而降低了可用性的需求,但是互联网应用往往对可用性的要求要略高于一致性的需求,这时候就要避免采用数据的ACID事务策略,转而采用BASE(基本可用性,事务软状态以及最终一致性)事务策略

通过最终一致性提升系统可用性,这也是目前很多NOSQL产品所采用的策略,包括facebook 的cassandra,apache hbase,google bigtable等,非常适合一些非结构化的数据,如key-value形式数据存储,并且这些产品有个很好的优点就是水平伸缩性

七 监控、预警系统

大型分布式系统涉及各种设备,比如网络交换机,普通PC机,各种型号的网卡,硬盘,内存等等,在数量非常多的时候,出现错误的概率也会变大,因此要时刻监控系统状态

监控有粒度的粗细之分

粒度粗一点,对整个应用系统进行监控,网络流量,内存利用率,IO,CPU负载,服务访问压力,服务的响应时间

细粒度一点,对应用中的某个功能,某个URL访问量,每个页面的PV,页面每天占用的带宽是多少,页面渲染时间,静态资源比如图片每天占用的带宽

有了监控系统以后,更重要的是要和预警系统结合起来,比如当某个页面访问量增多的时候,系统能自动预警,某台Server的CPU和内存占用率突然变大的 时候,系统也能自动预警,当并发请求丢失严重的时候,系统也能自动预警等等,通过监控系统和预警系统的结合可以使得能快速响应系统出现的问题,提高系统的 稳定性和可用性

[转载].net开发mvc架构分析

mikel阅读(978)

[转载].net开发mvc架构分析 – ChangeWorld – 博客园.

mvc 即 模型-视图-控制器 三层架构模式的开发框架,其目的是实现web程序系统职能的分工。

mvc的三层架构的显著优势是:

1. 高内聚,低耦合。mvc架构中 视图层 业务逻辑层 数据访问处理层 三层由controller(控制器)接收视图层传递来的用户输入信息 分配用户需要的数据到相应的视图(view)中           而model则担任数据访问及处理,业务逻辑和业务流程的工作。 层层分离 而各个模块内部的的数据代码依赖性又是高度聚合的。根据程序员多年的开发经 验,如果开发较大 复杂度较高的项目时采用这种开发模式能够对项目进行系统的管理和维护 也大大降低了测试的工作量。

2.高重用性。mvc的低耦合性决定了它的高度重用性。由于它模块之间是低耦合的,开发出的程序可以多次套用于类似的项目,只需修改业务逻辑层的少 数代码就能实先程序重用,各模块之间的依赖性较小,代码的修改不会影响其他模块的代码功能。再者mvc架构支持不同的视图访问服务器端的代码,如pc浏览 器以及日后会非常流行的手机端无线浏览器(wap),只需写出多种不同视图接收数据的代码就能够把项目的核心功能代码在不同的平台实现,这就避免了项目重 新开发造成的资源损失,也大大降低了代码重复性。

3.提高开发效率,加速了程序开发。使用mvc模式开发程序很大程度上缩减了开发时间,因为mvc开发模式中分工明细,程序员各司其职,如功能代码 程序员(ASP.NET程序员)专注于开发核心功能和业务逻辑及流程,显示界面代码程序员(Html JS 程序员)集中精力开发表现层。同时也有利于工作人员的工程化管理和维护以及程序的测试工作

当然mvc也有缺点,它没有明确的定义而且内部原理比较复杂,需要花费一定的时间思考 计划如何将mvc架构应用到自己的项目中,运用mvc架 构也会带来额外的工作量 所以 它适合开发较大 复杂度较高的大型及中型的项目,如果将mvc用于小项目的话反而会无谓的浪费一些时间 得不偿失。mvc要求各模块严格的分类,这就加大了调试人员的工作量,并且在使用每个部件的时候需要经过彻底的测试。

mvc框架与三层架构的区别:

很多人吧mvc框架与三层架构混为一谈,这是一个误区。虽然mvc架构同样也有三层,但此三层非彼也。传统三层架构:视图层 业务逻辑层 数据访问层 。mvc中同样也有视图层,但是mvc中将业务逻辑层和数据访问层都划到model中,又多出一个controller(控制器)层,做为一个桥梁(或者 理解成一个驱动器,驱动其余二层的数据交换)在视图层和model层之间传递数据。mvc继承了传统三层架构的优点:视图层和业务逻辑层及数据访问层分 离。并且mvc使各模块之间高度分离,使用controller进行桥接两层,而不像传统的三层结构三层自发的链接 传递数据,这样mvc耦合度更低,解决了传统三层架构的向下依赖的缺点,改善了级联修改的连锁修改体制。相比传统三层架构,mvc在一定程度上提高了系统 的效率,体现在mvc将数据访问层和业务逻辑层整合到一起,而不让业务逻辑层充当中间层,故而提高了系统效率。

随着mvc的发展,mvc显著的优点被越来越多的人所认可,近来微软也推出了自己的mvc设计模式。所以对.net方向(单方向的新手)的程 序员来说,mvc还是一门新技术,加之mvc内部原理复杂,一时还难以驾驭,不过通过学习加实践,大家会慢慢的适应这门技术。mvc在程序开发中的作用是 不可估量的,特别是在大中型项目中。

[转载]关于横向菜单导航。兼容IE6,7,8,Firefox,Chrome

mikel阅读(907)

[转载]关于横向菜单导航。兼容IE6,7,8,Firefox,Chrome。 – nyth – 博客园.

公司人少,所以有时候前台就也做一点。最近一个网站需要做一个横向菜单导航,以前也做过一些小例子想着没啥,网站做完后直接上传,第二天老板可说咋在我这显示不对呀,我过去一看。

突然想起来,IE6没试。我就回来开始调,也没调好,网上也有很多,但是一直也没找个合适的。最后还是google出来了一个外国的网页,一看这个不错。确实很好(它的主要特点是那个JS)。我在网上也搜了不少例子,可是一改就乱,就是这个原因换了好些也没见个合适的。记着,用时查看。。。。。

就这了。有空时,把css给注释下。方便改写。(在调css样式做自己样式的时候,用firbug改着调着很方便

对了,老外的博客地址:http://javascript-array.com/scripts/simple_drop_down_menu/

HTML:

01 <ul id="sddm">
02 <!--如果栏目下没有子菜单就不用onmouSEOver="mopen('m1')" onmouSEOut="mclosetime()"以及下面的那个DIV -->
03 <li><a href="#" onmouSEOver="mopen('m1')" onmouseout="mclosetime()">Home</a>
04 <div id="m1" onmouseover="mcancelclosetime()" onmouseout="mclosetime()">
05 <a href="#">HTML DropDown</a> <a href="#">DHTML DropDown menu</a> <a href="#">JavaScript
06 DropDown</a> <a href="#">DropDown Menu</a> <a href="#">CSS DropDown</a>
07 </div>
08 </li>
09 <li><a href="#" onmouseover="mopen('m2')" onmouseout="mclosetime()">Download</a>
10 <div id="m2" onmouseover="mcancelclosetime()" onmouseout="mclosetime()">
11 <a href="#">ASP Dropdown</a> <a href="#">Pulldown menu</a> <a href="#">AJAX dropdown</a>
12 <a href="#">DIV dropdown</a>
13 </div>
14 </li>
15 <li><a href="#" onmouseover="mopen('m3')" onmouseout="mclosetime()">Order</a>
16 <div id="m3" onmouseover="mcancelclosetime()" onmouseout="mclosetime()">
17 <a href="#">Visa Credit Card</a> <a href="#">Paypal</a>
18 </div>
19 </li>
20 <li><a href="#" onmouseover="mopen('m4')" onmouseout="mclosetime()">Help</a>
21 <div id="m4" onmouseover="mcancelclosetime()" onmouseout="mclosetime()">
22 <a href="#">Download Help File</a> <a href="#">Read online</a>
23 </div>
24 </li>
25 <li><a href="#" onmouseover="mopen('m5')" onmouseout="mclosetime()">Contact</a>
26 <div id="m5" onmouseover="mcancelclosetime()" onmouseout="mclosetime()">
27 <a href="#">E-mail</a> <a href="#">Submit Request Form</a> <a href="#">Call Center</a>
28 </div>
29 </li>
30 </ul>
31 <div style="clear: both">
32 </div>
33 <div style="clear: both">
34 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
35 </div>

JS:(主要就是这个把不变的东西用js来固定住,其余的都可以自定义)

01 <script type="text/JavaScript">
02 <!--
03 var timeout         = 500;
04 var closetimer      = 0;
05 var ddmenuitem      = 0;
06
07 // open hidden layer
08 function mopen(id)
09 {
10 // cancel close timer
11 mcancelclosetime();
12
13 // close old layer
14 if(ddmenuitem) ddmenuitem.style.visibility = 'hidden';
15
16 // get new layer and show it
17 ddmenuitem = document.getElementById(id);
18 ddmenuitem.style.visibility = 'visible';
19
20 }
21 // close showed layer
22 function mclose()
23 {
24 if(ddmenuitem) ddmenuitem.style.visibility = 'hidden';
25 }
26
27 // go close timer
28 function mclosetime()
29 {
30 closetimer = window.setTimeout(mclose, timeout);
31 }
32
33 // cancel close timer
34 function mcancelclosetime()
35 {
36 if(closetimer)
37 {
38 window.clearTimeout(closetimer);
39 closetimer = null;
40 }
41 }
42
43 // close layer when click-out
44 document.onclick = mclose;
45 // -->
46 </script>

CSS:

01 #sddm
02 { margin: 0;
03 padding: 0;
04 z-index: 30}
05
06 #sddm li
07 { margin: 0;
08 padding: 0;
09 list-style: none;
10 float: left;
11 font: bold 11px arial}
12
13 #sddm li a
14 { display: block;
15 margin: 0 1px 0 0;
16 padding: 4px 10px;
17 width: 60px;
18 background: #5970B2;
19 color: #FFF;
20 text-align: center;
21 text-decoration: none}
22
23 #sddm li a:hover
24 { background: #49A3FF}
25
26 #sddm div
27 { position: absolute;
28 visibility: hidden;
29 margin: 0;
30 padding: 0;
31 background: #EAEBD8;
32 border: 1px solid #5970B2}
33
34 #sddm div a
35 { position: relative;
36 display: block;
37 margin: 0;
38 padding: 5px 10px;
39 width: auto;
40 white-space: nowrap;
41 text-align: left;
42 text-decoration: none;
43 background: #EAEBD8;
44 color: #2875DE;
45 font: 11px arial}
46
47 #sddm div a:hover
48 { background: #49A3FF;
49 color: #FFF}

[转载]大小不定的图片垂直居中汇总

mikel阅读(1018)

[转载]大小不定的图片垂直居中汇总 – Dm.Yang’s Blog – 博客园.

最近一段时间一直学习js,CSS接触不多,前几天在蓝色理想经典论坛上看到一篇帖子(CSS图片垂直居中方法整理集合),按照上面的做法做了一些改进和扩展,分享如下:

针对一个box里面的n张图片,我总结了下面几种方法(持续更新)使其在box里面垂直居中(水平居中就不废话了,你懂的~)。

1、使line-height值和box的height值相同

结构如下(下面第6点的结构也一样,除了box的扩展类名不同):

1 <div class="box line-height"><br> <!--[if lte ie 7]><i></i><![endif]--><br>   <img src="http://bbs.blueidea.com/images/default/logo.gif" alt="blueidea" /><a href="">蓝色理想</a><br>   <img src="http://images.cnblogs.com/logo_small.gif" alt="博客园" /><a href="">博客园</a><br></div><br>

样式:

1 .line-height{height:100px;line-height:100px;overflow:hidden;zoom:1;}<br>.line-height i{writing-mode:tb-rl;_height:100%;vertical-align:middle;}/* 使用writing-mode:tb-rl;height:100%;兼容IE6 */

这种方式的优点是:同时支持块级和内联极元素;支持图片和图片以及图文混排。

缺点是:只能显示一行。

说明:这里使用writing-mode兼容ie6,参考了blueidea的那篇文章,以前算是孤陋寡闻吧,看来对ie使用“以毒攻毒”的办法还 是挺有效的嘛。同时在结构上也做了调整,传统的方式(即box里面嵌套一个span,然后把img放里面对span使用与i类似的样式)如果是把两张图放 在span里面,则IE6下两张图会上下排列(writing-mode的缘故),所以小弟在此做了一点点改进。

2、对box使用padding-bottom=padding-top方法

结构和第1点类似,,除了box的扩展类名不同,少了ie注释。

样式如下:

1 .padding{padding-top:3em;padding-bottom:3em;}/* 当然也可以用绝对值 */

优点:支持图片和图片以及图文混排;支持所有浏览器。

缺点:box的高度不能固定。

说明:如果box的高不固定,这算是最简单的一种方法了吧,简单到用起来的时候心里头都慌慌的(就像大中小学生对待1加1等于几那个问题一样^_^)。可是经过测试,在所有主流浏览器下都有效,读者可以试着换张灰常大的图试试(我没测试哈)。

3、使用定位方法

结构如下(下面4、5点的结构也一样,除了box的扩展类名不同):

1 <div class="box position"><br>  <span class="inner"><br>        <img src="http://bbs.blueidea.com/images/default/logo.gif" alt="blueidea"><a>蓝色理想</a><br>       <img src="http://images.cnblogs.com/logo_small.gif" alt="博客园"><a>博客园</a><br>    </span></div>

优点:所有浏览器都支持;支持图片和图片以及图文混排。

缺点:垂直方向的居中不完美,需要根据box的高度作调整;需额外嵌套一层标签。

说明:这个应该不算什么新方法了吧,以前做垂直居中都用这法子。定位确实是一种很不错的布局方式,如果不考虑IE6,float早可以回家休息了(貌似也不远了,请参考拜拜了,浮动布局-基于display:inline-block的列表布局,此文将让您有足够的理由放弃float)。

4、使用display:table-cell方法

结构同上,样式如下:

1 .table-cell{height:100px;display:table;}<br>.table-cell .inner{display:table-cell;vertical-align:middle;}

优点:完全按照table式布局。但要注意合法的table-cell元素应该作为table元素的子元素出现;支持图片和图片以及图文混排。

缺点:IE6/7不支持(要用hack,可以与定位法杂交);需额外嵌套一层标签。

说明:这种方法也不是什么新法子,模拟表格做布局,你懂的。

5、使用font-size方法

结构同上,样式如下:

1 .font-size{height:100px;}<br>.font-size .inner{font-size:50px;*font-size:65px;_font-size:80px;}<br>.font-size .inner a{font-size:14px;vertical-align:middle;}

优点:支持图片和图片以及图文混排。文本需重定义font-size。

缺点:font-size值强烈依赖box的height值;一个font-size值不能兼容所有浏览器,ie6、7下需做另外调整;目前Opera(10.6)不支持这种方法;需额外嵌套一层标签。

说明:这应该是几种方法中最蛋疼的一种了吧,兼容性也最差,在实际操作中尽量避免吧。

6、使用inline-block方法

结构同1,样式如下:

1 .inline-block{height:100px;}<br>.inline-block:after{content:".";visibility:hidden;display:inline-block;height:100%;vertical-align:middle;}<br>.inline-block i{display:inline-block;height:100%;vertical-align:middle;}/* ie6/7下可以使用此方法兼容 */<br>.inline-block img{vertical-align:middle;}

优点:如果不用:after伪元素而是添加一个额外的标签(如i),可兼容所有浏览器;支持图片和图片以及图文混排。

缺点:IE6/7不支持:after伪元素,这时需要用hack(或像本文一样额外添加了一个无语义的i)。

说明:inline-block!好东西!

叽叽喳喳说了半天,现在奉上测试源码,请单击查看Demo

注:本文测试所用到的浏览器为IE6、7、8,FF 3.6,Chrome 7.0,Opera 10.6,Safari 5.0。由于本人水品有限,目前想到的方法就这么多(后续会有更新),有不对的地方望各位指正!

本文出自chemdemo博客园,转载请注明来源,谢谢。

(全文完)