在WEB网站的规模从小到大不断扩展的过程中,数据库的访问压力也不断的增加,数据库的架构也需要动态扩展,在数据库的扩展过程基本上包含如下几步,每一个扩展都可以比上一步骤的部署方式的性能得到数量级的提升。
1、WEB应用和数据库部署在同一台服务器上 一般的小规模的网站采用这种方式,用户量、数据量、并发访问量都比较小,否则单台服务器无法承受,并且在遇到性能瓶颈的时候升级硬件所需要的费用非常高昂,在访问量增加的时候,应用程序和数据库都来抢占有限的系统资源,很快就又会遇到性能问题。 2、WEB应用和数据库部署在各自独立的服务器上 web应用和数据库分开部署,WEB应用服务器和数据库服务器各司其职,在系统访问量增加的时候可以分别升级应用服务器和数据库服务器,这种部署方式是一般小规模网站的典型部署方式。在将应用程序进行性能优化并且使用数据库对象缓存策略的情况下,可以承载较大的访问量,比如2000用户,200个并发,百万级别的数据量。 3、数据库服务器采用集群方式部署(比如Oracle的一个数据库多个实例的情况) 数据库集群方式能承担的负载是比较大的,数据库物理介质为一个磁盘阵列,多个数据库实例以虚拟IP方式向外部应用服务器提供数据库连接服务。这种部署方式基本上可以满足绝大多数的常见WEB应用,但是还是不能满足大用户量、高负载、数据库读写访问非常频繁的应用。 4、数据库采用主从部署方式 在面向大众用户的博客、论谈、交友、CMS等系统中,有上百万的用户,有上千万的数据量,存在众多的数据库查询操作,也有较多的数据库写操作,并且在多数情况下都是读操作远大于写操作的。在这个时候,假如能将数据库的读写操作分离的话,对于系统来讲是一个很大的提高啦。数据库的主从部署方式就走到我们面前啦。 主从复制: 几乎所有的主流数据库都支持复制,这是进行数据库简单扩展的基本手段。下面以Mysql为例来说明,它支持主从复制,配置也并不复杂,只需要开启主服务器上的二进制日志以及在主服务器和从服务器上分别进行简单的配置和授权。Mysql的主从复制是一句主服务器的二进制日志文件进行的,主服务器日志中记录的操作会在从服务器上重放,从而实现复制,所以主服务器必须开启二进制日志,自动记录所有对于主数据库的更新操作,从服务器再定时到主服务器取得二进制日志文件进行重放则完成了数据的复制。主从复制也用于自动备份。 读写分离: 为保证数据库数据的一致性,我们要求所有对于数据库的更新操作都是针对主数据库的,但是读操作是可以针对从数据库来进行。大多数站点的数据库读操作比写操作更加密集,而且查询条件相对复杂,数据库的大部分性能消耗在查询操作上了。 主从复制数据是异步完成的,这就导致主从数据库中的数据有一定的延迟,在读写分离的设计中必须要考虑这一点。以博客为例,用户登录后发表了一篇文章,他需要马上看到自己的文章,但是对于其它用户来讲可以允许延迟一段时间(1分钟/5分钟/30分钟),不会造成什么问题。这时对于当前用户就需要读主数据库,对于其他访问量更大的外部用户就可以读从数据库。 数据库反向代理: 在读写分离的方式使用主从部署方式的数据库的时候,会遇到一个问题,一个主数据库对应多台从服务器,对于写操作是针对主数据库的,数据库个数是唯一的,但是对于从服务器的读操作就需要使用适当的算法来分配请求啦,尤其对于多个从服务器的配置不一样的时候甚至需要读操作按照权重来分配。 对于上述问题可以使用数据库方向代理来实现。就像WEB方向代理服务器一样,MYsql Proxy同样可以在SQL语句转发到后端的Mysql服务器之前对它进行修改。 5、数据库垂直分割 主从部署数据库中,当写操作占了主数据库的CPU消耗的50%以上的时候,我们再增加从服务器的意义就不是很大了,因为所有的从服务器的写操作也将占到CPU消耗的50%以上,一台从服务器提供出来查询的资源非常有限。数据库就需要重新架构了,我们需要采用数据库垂直分区技术啦。 最简单的垂直分区方式是将原来的数据库中独立的业务进行分拆(被分拆出来的部分与其它部分不需要进行Join连接查询操作),比如WEB站点的BLOG和论坛,是相对独立的,与其它的数据的关联性不是很强,这时可以将原来的的数据库拆分为一个BLog库,一个论坛库,以及剩余的表所组成的库。这三个库再各自进行主从数据库方式部署,这样整个数据库的压力就分担啦。 另外查询扩展性也是采用数据库分区最主要的原因之一。将一个大的数据库分成多个小的数据库可以提高查询的性能,因为每个数据库分区拥有自己的一小部分数据。假设您想扫描1亿条记录,对一个单一分区的数据库来讲,该扫描操作需要数据库管理器独立扫描一亿条记录,如果您将数据库系统做成50个分区,并将这1亿条记录平均分配到这50个分区上,那么每个数据库分区的数据库管理器将只扫描200万记录。 6、数据库水平分割 在数据库的垂直分区之后,假如我们的BLOG库又再次无法承担写操作的时候,我们又该怎么办呢?数据库垂直分区这种扩展方式又无能为力了,我们需要的是水平分区。 水平分区意味着我们可以将同一个数据库表中的记录通过特定的算法进行分离,分别保存在不同的数据库表中,从而可以部署在不同的数据库服务器上。很多的大规模的站点基本上都是主从复制+垂直分区+水平分区这样的架构。水平分区并不依赖什么特定的技术,完全是逻辑村面的规划,需要的是经验和业务的细分。 如何分区呢?对于大型的WEB站点来说,必须分区,并且对于分区我们没有选择的余地,对于那些频繁访问导致站点接近崩溃的热点数据,我们必须分区。 在对数据分区的时候,我们必须要存在一个分区索引字段,比如USER_ID,它必须和所有的记录都存在关系,是分区数据库中的核心表的主键,在其它表中作为外键,并且在使用主键的时候,该主键不能是自增长的,必须是业务主键才可以。 余数分区: 我们可以将User_ID%10后的值为依据存入到不同的分区数据库中,该算法简单高效,但是在分区数据库个数有变动的时候,整个系统的数据需要重新分布。 范围分区: 我们可以将User_ID的范围进行分区,比如1-100000范围为一个分区数据库,100001-200000范围为一个分区数据库,该算法在分区数据库个数有变动的时候,系统非常有利于扩展,但是容易导致不同分区之间的压力不同,比如老用户所在的分区数据库的压力很大,但是新用户的分区数据库的压力偏小。 映射关系分区: 将对分区索引字段的每个可能的结果创建一个分区映射关系,这个映射关系非常庞大,需要将它们写入数据库中。比如当应用程序需要知道User_id为10的用户的BLOG内容在那个分区时,它必须查询数据库获取答案,当然,我们可以使用缓存来提高性能。 这种方式详细保存了每一个记录的分区对应关系,所以各个分区有非常强的可伸缩性,可以灵活的控制,并且将数据库从一个分区迁移到另一个分区也很简单,也可以使各个分区通过灵活的动态调节来保持压力的分布平衡。附:
有关架构的概念和其重要性此处就不再详细讨论了,在很多社区和书籍中都有介绍过。在这里推荐两本书,分别是和,其中,第二本适合.NET开发人员来看。
互联网项目(门户、社区、电商等)在初期架构阶段,首先,要分清楚项目所针对的人群有哪些,并根据需求分析和上线后的推广力度来估算有多大的访问量;然后, 负责架构的人员根据这些资料设计架构粒度。现在投资互联网项目的成本都很大,已经不像几年前买个虚拟主机就可以搞定了。所以,前期一般不会把架构搭的很 大,或者买很多服务器来支撑这个架构。但是我们可以根据需求设计一套可灵活扩展的架构,然后根据项目的市场发展状况来扩展架构,两位圈内名人曾讲过这样的话:
- :过早优化是万恶之源
- :好的架构和最初设计有关系,但最重要是发展中的演化。
本章分别从“物理架构”和“逻辑架构”两方面来描述目前项目的架构设计。
1.1 物理架构
我理解的物理架构也可以称之为宏观架构,主要包括硬件与网络的部署策略、整体架构硬件的可伸缩性、安全以及性能上分析。下图是目前项目的逻辑架构视图,此图虽没有把相关扩展点画出来,但是会给大家介绍哪些是可以在后期进行扩展。
1.1.1 客户端缓存
关于客户端缓存应该属于网站优化方面的知识,随着互联网技术的发展和客户端浏览器的支持,有效的利用客户端缓存可以为服务器减少很大的压力。客户端浏览器根据每个HTTP请求的Header信息来决定当前请求的资源是否需要缓存,目前项目的客户端缓存分为“动态页面”和“静态资源”两种:
- 动态页面:由ASP.NET MVC输出,根据需求为每个所输出的页面设计缓存策略,比如:网站总首页会在客户端“相对”缓存5分钟,而文章会缓存10分钟。如果服务器端对缓存页面内容更新后,可以通过改变“ETag”值来强制更新客户端缓存。
- 静态资源:包括“脚本文件、样式文件、网站图片、静态页面(404、专题等)”,目前这些文件都存储在“静态资源服务器”,可以通过Web服务器的批量设置调整资源文件的客户端缓存,不同的Web服务器需要不同的配置,大家可以根据需求去Google。
有关“静态资源”在客户端缓存的强制更新,可以通过版本号的形式来强制更新客户端缓存,比如:
1.1.2 防火墙
硬件防火墙的主要功能可以参考,这里介绍此项目正在使用的几个主要功能:
- 内网端口统一屏蔽:防火墙对外网服务器(IP)只开放80端口(目前需求),虽然操作系统本身也具备防火墙功能,但是配置比较麻烦,又不统一。
- 外网和内网IP的映射:在防火墙内配置了外网和内网(公网千兆交换机)的IP规则对应,浏览者通过外网访问会直接转换为内网IP进行访问。
- VPN:如果需要通过外网管理内容区网站或者远程管理服务器,就必须通过VPN进行连接,连接成功后会自动分配内网地址,然后才能对服务器进行管理。
注:在购买防火墙时,要根据需求查看防火墙所支持多大的“吞吐量”,因为它是测量防火墙性能的重要指标。
1.1.3 公网千兆交换机和内网存储交换机
公网千兆交换机
当外网请求过来时,防火墙会把其转换为内网请求,通过公网千兆交换机访问具体内网服务器,根据IDC提供的带宽,这里配置“千兆”已足够。
内网存储交换机
目前所有服务器都会有一块网卡接着内网存储交换机(万兆),服务器上的所有数据(除系统文件和程序文件)都是通过“”协议存储到“存储设备”上的。
1.1.4 Web服务器
一个网站最重要的就是Web服务器,因为它会把数据转换为页面(HTML)返回给浏览者,这种说法仅限于目前的环境。在SNS区,Web服务器后面是由多台应用程序服务器组成。为了减少成本,现在项目只有一台Web服务器,但是此台服务器上运行了多个站点和服务,后期可以根据访问量,把这些站点和服务扩展到 另外的服务器上。此台服务器的系统环境和所运行的服务如下:
- 操作系统:Windows Server 2008R2 x64
- Web服务器:IIS 7、.NET Framework 4、ASP.NET MVC 3+Razor
- 运行站点:内容区网站、内容区百科、内容区搜索服务
为了方便扩展,不同的站点都由不同的域名划分,同时也运行在不同的应用程序池当中。需要注意的是,Web服务器不会存储任何有关共享资源和用户的相关数据,所有资源都是通过绝对路径访问其它服务器上的内容,这样就方便以后为某个站点或服务增加负载均衡。有关负载均衡可以通过软件(Nginx)、硬件(F5) 或DNS轮循等几种方案来实现,因为硬件比较贵,所以我们在内部针对Nginx做过测试,在分离以上站点时,可以正常运行。很多大型站点是采用“混合型负载均衡”——以上几种方案都会使用。如果考虑到今后会使用负载均衡,在初期架构时,就要首先解决所有和状态有关的问题,比如用户登陆和验证码状态,在我们 SNS区架构时,就要考虑采用专门的状态服务器来存储这些内容。
301跳转:很多网站都会申请一些保护域名,并把这些域名转向到主域名上,我们的解决方案是在IIS上建立多个空站点,然后设置所有请求都转向到主域名站点上。
下面分享一些关于IIS7配置方面的资料:
- 错误页设置:如果采用“集成模式”,一定要把网站错误地址写成绝对地址,比如(),如果网站启动过程发生错误,这时就会导致错误死循环。
- 启用动态压缩(GZIP):
- 多个进程处理一个应用程序池设置:、
- IIS默认连接数设置:
- 权限设置:
- IIS7教程:
1.1.5 缓存服务器
当网站没有应用缓存时,每一个动态页面的访问请求,都需要通过连接数据库,执行相关数据查询,最后返回给客户端。这样无疑增加了服务器的压力,并且数据库连接是相对耗时的操作。在服务器端可以通地以下方式实现缓存:
- Web服务器:不同Web服务器的缓存设置方法不一样,Nginx和Squid都可以通过配置文件来设置资源的缓存,IIS也可以同时开启“静态资源”和“动态内容”的缓存(GZIP)。
- ASP.NET MVC:可以通过Action的OutputCache属性来设置缓存的类型和时间。
- 自定义缓存:根据网站自身的业务逻辑来使用缓存,把一些业务数据存储在缓存当中,可以使用ASP.NET中的“System.Web.Caching.Cache”来管理缓存,或者使用第三方缓存“Memcache、Redis”。
缓存模块设计
在设计缓存模块时,我们定义一系列的缓存操作接口,使用者(Web服务器)并不清楚缓存模块当前所采用的是哪种缓存方案,使用者只需要调用具体的方法来管理缓存。缓存模块在初始化时会根据配置文件来决定当前系统需要使用哪种缓存方案,目前系统支持ASP.NET Cache、Memcache、Redis三种缓存策略,三种缓存策略在不同的部署环境都有各自的优势。
- ASP.NET Cache:网站初步上线时,当没有单独的缓存服务器,并且访问量和缓存数据量都不是太大时,就可以采用ASP.NETCache缓存策略,因为它相对于 后两种是最快的。缺点是它和应用程序池在一个进程当中,网站重启或应用程序池崩溃都会导致缓存直接丢失。
- Memcache:弥补了第一种的缺点,支持分布式,很多大型网站都在使用。缺点是,如果在访问量高峰期出现了缓存雪崩,就会导致缓存大量失效,同时数据库服务器将承受巨大的压力,严重的话会导致整个网络集群的瘫痪。
- Redis:可以用来做缓存服务器,支持数据持久化,把缓存中的数据定期存储到磁盘上,目前,国内正在使用此技术,我们只是尝试使 用了它的缓存功能,并没有它的NoSQL特性。
缓存策略
缓存是一个好东西,但是如果我们错误的使用了它,就会带来意想不到的麻烦。在实践当中,我总结了以下常见的注意事项:
- 缓存键的设计:一般缓存键都是以常量的形式存储在类中,这样就会方便调用,并且缓存键常量是采用模板形式,灵活度比较高。如果要设计更为灵活的缓存键,可以参考的设计。
- 缓存对象粒度:缓存粒度的原则当然是越小越好,但是这样就会增加组装环节。根据业务逻辑灵活控制缓存对象的粒度,减少缓存中的数据冗余。
- 缓存管理:网站启动时,会把必要的初始化数据放入缓存中,其它的数据是在第一次访问时存储到缓存当中,在网站后台会有缓存的主动更新和强制更新功能。
1.1.6 数据库服务器
目前网站的数据存储在一台数据库服务器上,采用 “SQLServer 2008 Enterprise Edition (64-bit)”数据库。考虑到后期的扩展,前期规划时需要完成以下工作:
- 模块分库:根据内容区的产品模块,进行合理的分库,方便以后根据需求把这些数据库部署到不同的数据库服务器上。
- 读写分离:当数据库访问量很大时,可以把数据库部署到不同的数据库服务器上来解决压力,但是当某个模块数据库服务器压力过大时,可以通过读写分离来解决数 据库服务器的压力,SQL Server可以通过发布订阅来解决这个问题,但是网站程序的数据库访问层也需要提供支持,把不同的读写业务逻辑分发到不同的数据库服务器上。第一种方法是在读写业务逻辑中调用不同数据库连接字符串。另外一种方法,前期规范好存储过程和SQL语句的书写标准,在数据库访问组件中根据最终执行的命令,分发到 具体的数据库服务器,比如所有“select”都分发到“读”数据库服务器,而所有“Update、Delete”都分发到“写”数据库服务器上。随着业务的增加,如果存在多台“读”服务器时,就需要考虑数据同步延时的问题,可以根据具体情况来解决,比如:当用户刚添加一条评论时,在客户端增加一个标记, 在几分钟内他的所有读操作都分发到“写”数据库上。
- 负载均衡:如果存在多台“读”数据库服务器时,需要在数据库访问组件上增加负载均衡的功能,目前项目只是简单实现了“轮循算法”,也可以在算法中为每个数据库器连接字符串增加“权重”值。
安全设置
在开放的互联网环境中,互联网项目第一个要谈的就是安全问题,根据项目的不同,安全的侧重点也不一样。在保证操作系统本身的权限划分和漏洞升级后,在“第六章、内部测试”会讨论网站程序所需要的安全测试。在保证网站程序没有安全问题外,还要保证数据库服务器的访问安全性,我们分别从以下几个方面来确保数据库 服务器(SQL Server)的安全性。
- 隔离原则:在防火墙内隔离数据库服务器,禁止外部链接,并在数据库服务器操作系统防火墙中设置只允许网站程序服务器连接,修改默认连接端口。
- 权限分配:首先关闭没有用的数据库账户,对数据库文件所在的文件夹设置操作系统级别的权限分配。针对不同的网站程序,分配不同的数据库访问账号,并根据不同的业务逻辑为不同的账号分配不同的执行权限。比如:网站前台程序所拥有的数据库账号只有执行“特定存储过程”和“特定表”的查询操作,并不具备创建、删除表的操作,更没有查看数据库有多少表的操作。
- 数据加密:第一、对网站程序的配置文件进行加密,防止Web服务器被攻破后拿到明文的数据库连接账号。第二、对必要的“数据列”进行加密,不做明文存储, 比如会员表的密码字段,并且不能用简单的加密方式进行加密,防止碰撞破解,保存好加密干扰字符,并定期更新干扰字符。
- 最小化运行:只开启网站程序业务所需要的SQLServer服务。
备份机制
SQL Server已经具备了很强的备份功能,可以设置作业进行定时备份,根据需求选择具体备份策略,具体操作可以参考这篇文章“”。
数据镜像和同步
当网站“主数据库服务器”发生问题后,网站程序需要自动切换到另外一台正常工作的数据库服务器,这时可以采用主从数据库镜像的方案,网站程序的数据库访问组 件只需要配置主数据库的连接字符串,就可以做到主从镜像自动切换,具体配置方法可考或者“”。如果只是需要在主数据库服务器发生问题后,手动切换或者开启另一台工作的数据库服务器,可以采用数据库复制的方案,具体配置方法参考“”。
1.1.7 静态资源服务器
前端优化中最好做的就是动静分离,在前期项目规划时就应该在内部搭建动静分离的开发环境,在项目中采用绝对路径和前端进行协作开发。如果前期只有一台服务器,可以在服务器上建立多个站点存放静态资源。静态资源服务器可以采用的CentOS系统,使用高性能的Nginx作为Web服务器。
缓存和压缩
调整Nginx的配置,为静态资源增加客户缓存输出,并且可以把静态资源存入到Nginx内存当中,当然也可以使用Squid来做。关于静态资源的压缩,推 荐使用“、)”。如果有更高的区域性需求,可以使用第三方 来做。
1.1.8 图片浏览服务器
独立图片浏览服务器原因同上,图片浏览对于内容型网站来说是很重要的功能,在前期规划时需要注意以下事项:
- 图片上传:后台程序会把编辑上传的图片通过内网发送给图片上传服务,图片上传服务根据后台上传需求进行上传并保存图片,返回上传成功后的图片路径和文件名。图片上传服务具备如下功能(缩略图、水印、版权、保存Exif信息到数据库)。
- 目录规划:根据需求设计图片的存储机制(路径、文件名)以及原图片的备份,对后期的扩展很重要。
- 数据分析:图片上传成功后,会把图片的所有Exif信息存储到数据库中,方便后期进行数据分析。
- 图片访问:对图片的访问增加防盗链功能,采用“HttpHandler”来实现,目前为了良好的体验,考虑到图片都是编辑上传的,不会造成大量的恶意引用,此功能暂没有开启。
1.1.9 内容区后台管理服务器
基于安全和程序架构的原因,内容区前台和后台项目是分开部署的,并且采用不同的开发框架。内容区后台是单独部署在一台服务器上,管理员需要通过VPN才能登陆管理后台,并且后台采用HTTPS登陆。
1.1.10 数据存储设备
以上所有服务器的数据存储都分为两种:
- 原始数据:操作系统和应用程序所需要的文件是存储在本地硬盘上的,应用程序主要包括杀毒软件、运行环境和软件工具。
- 用户数据:网站程序所产生的数据和文件都是通过“iSCSI”协议存储在内部存储设备上的,后期根据发展会购买专门的存储设备,采用“FC HBA卡”进行数据传输。
单独的存储设备是防止服务器系统或者服务器硬件坏掉时,不需要单独从硬盘中把数据复制出来的麻烦,只需要启动一台新服务器连接到内部存储设备就可以正常运行了。
1.1.11 其它
远程管理卡
主要方便“运维”通过远程监控服务器(开机、关机)的重启过程,当然也可以远程管理操作系统。
管理工作站
一台远程管理服务器,安装 “VMware”系统,虚拟化了多个操作系统,方便安装相关监控和日志分析软件。
摄像头
监控机柜在IDC机房的动静。
1.2 逻辑架构
逻辑架构从某种程度来看是物理架构的实现,但不仅限于此。它需要考虑功能的重用性与可扩展性、服务接口的定义,并定义整个系统的架构风格。
在分解复杂的软件系统时,软件设计者用得最多的技术之一就是分层。当用分层的观点来考虑系统时,可以将各个子系统想像成按照“多层蛋糕”的形式来组织,每一 层都依托在其下层之上。在这种组织方式下,上层使用了下层定义的各种服务,而下层对上层一无所知。另外,每一层对自己的上层隐藏其下层的细节。——《企业应用架构模式》
1.2.1 基础设施和公共类库
公共类库
统一公共类库的目的是为了方便团队使用一致的“公用方法”,所有能提练出来并与业务逻辑无有关系的“公用方法”。公共类库与业务逻辑无关,公司的所有开发小组都可以使用,并长期对它进行维护。在开发公共类库时注意以下几点:
- 不求多而全,但要精而强,每个方法要求有详细注释和具体使用方法(测试用例)。内部要提供API清单,方便新人查看有哪些方法,免的重新制造轮子。
- 避免从网上直接下载使用,必须经过代码审查和测试。
- 规范类库和方法的命名规范,方便开发人员记忆和使用。
1.2.2 表现层
内容区前台(ASP.NET MVC)
ASP.NET WebForm的缺点在网上也都有讨论,目前很多采用ASP.NET的互联网项目都是自己来实现“HttpHandler”做项目,不再使用ASP.NET自带的页面模型。微软也已经意识到了这个问题,这才有了ASP.NET MVC框架,ASP.NET MVC主要取代项目中的表现层,下面分享我们用它都做了什么:
- Razor:ASP.NET MVC支持自定义视图引擎,自带了Razor,上手快并且有很好的智能感应输入提示支持。
- ASP.NET Routing:在之前的ASP.NET项目中,大家都采用第三方URL重写组件来实现更好的URL设计和SEO(盲目)优化,ASP.NET MVC中已经解决了这个问题,当然在ASP.NET 4.0 WebForms中也可以使用。具体讨论可参考“”
- XmlRoute:ASP.NET MVC默认路由规则是存储在“Global.asax.cs”中,也可以通过扩展把规则存储在XML中,具体实现参考“”。
- Ajax:在ASP.NET MVC中,Action可以返回多种丰富类型的结果,这样就不需要在传统ASP.NET中实现“HttpHandler”或创建“ashx”,也更方便我们来开发API。
- 重写输出:为了更好的控制页面缓存和客户端缓存,我们重写了ASP.NET MVC默认请求和输出,在请求时根据当请求的路由判断是否已经缓存并输出,输出时根据客户缓存策略输出对应的HTTP Hearder。
内容区后台(ASP.NET WebForm)
ASP.NETWebForm自有它的缺点,当然也有它存在的优点,合理的使用可以有效的提高开发效率和代码重用。
- Code-Behind:使用ASP.NET WebForm的页面模型和服务器端控件(自定义),可以快速开发应用系统,当然也会带来性能上的瓶颈,内容区后台只有内部人员使用,性能问题暂不考虑。
- 自定义服务器端控件:我们没有直接使用ASP.NETWebForm自带的控件,而是根据“效果图”和“前端交互”封装了所有控件,把常用的组件也封装为自定义控件,比如:图片上传控件、分类选择控件、超文本编辑器控件、数据显示控件(Repeater)等。
- ASP.NET Ajax:后台开发并没有专业的前端人员来编写Javascript实现Ajax效果,所以采用ASP.NET Ajax更适合我们在ASP.NET WebForm下开发。
1.2.3 服务层
处理领域逻辑的常见方法是将领域层再细分成两层。服务层独立出来,置于底层的领域模型或表模块之上。通常只有使用领域模型或表模块时才会这样细分,因为仅使用事务脚本的领域层并不复杂,没有必要再单独设服务层。表现逻辑与领域层的交互完全通过服务层,就好像应用程序的API一样。——《企业应用架构模式》
从逻辑架构视图上可以很清楚看到,内容区前台和后台的服务层架构也是不同的,前台的服务层主要有“ViewModels”和“Business logic”组成,而后台只有“Business logic”,后台很多页面逻辑已经在“Code-behind”中完成了。
在“第五章迭代开发”会分享服务层的一些最佳实践。
1.2.4 数据访问层
数据库访问层是完成服务层的具体实现,执行相关的存储过程或脚本命令,并以表模块或者对象(Data Mappings)的形式返回给服务层。如果项目有“读写分离”需求,就需要根据具体的业务逻辑在数据访问层把具体的操作路由到具体的数据库服务器,可以 通过调用数据库访问组件来实现,但前提是数据库访问组件必须支持多台数据库的操作。
- 前台:存储过程、对象映射
- 后台:脚本命令、表模块(DataTable)、对象映射
注意:所有数据库访问操作都必须采用参数化传递,防止SQL注入,采用using来使用DataReader对象。
1.2.5 数据库访问组件
在项目中,数据库访问组件也属于“基础设施层”,因为它是一个统用的组件。在项目中,并没有采用第三方ORM框架来实现数据库访问,主要是为了后期扩展考虑,使用ADO.NET可以灵活的实现读写分离、负载均衡,也方便日志记录和异常信息的捕获。
在开发数据库访问组件时,需要以下注意事项:
- 数据库访问组件是决定性能最重要的一层,每一行代码都要经过思考。根据需求进行设计,比如架构路线已经决定采用了SQL Server数据库,就不要盲目设计让数据库访问组件支持多种数据库。
- 增加错误日志记录功能,当数据库操作发生错误时,应该及时记录错误日志并通知管理员。
- 合理使用using,避免打开数据库连接后没有关闭,这样会导至数据库表加锁或占满默认连接池数目。()
- 支持事务操作、参数化传递。
- 性能测试,数据库访问组件开发完毕后,所有功能都必须经过严格的测试,并且根据需求进行压力测试,编写多种场景的测试功能并对这些功能进行压力测试。