谈一谈我对系统架构的理解,欢迎大家一起讨论

在软件开发中,架构是一个很抽象的词汇,在我看来,架构是指系统在明确了前期需求理解
后,架构工作人员对于系统决策的一种规定.
架构可以分成两类,一类是业务上的架构,也就是我们通常说的设计,它面向的是系统的业务,
这项工作一般由系统设计师(SE)来完成.
考虑的问题首先是业务实体的设计,需要决定是以数据库为中心进行设计还是以面向对象为中心.
因为随着面向对象思想的不断普及,关系型数据库已经成为仅仅是为了存放数据的地方,人们
往往更喜欢以对象的观念来考虑客观事物,此时需要根据需求抽象出若干实体对象,并且决定
对象之间的关系诸如关联,依赖,聚合等,再利用ORM工具将对象实体转化为关系数据库中的表.
但由于经验上的不成熟,真正在设计中以对象为中心的项目还是比较少,大多项目还是传统的以
关系数据库为中心,然后如果需要持久化,再将表转化为对象. 此时需要抽象的不是对象,而是
表和表之间的关系如一对多,多对多等. 业务实体设计的是否合理往往对系统的性能起决定作用
在设计好业务实体后,接下来要考虑的问题是将需求模块化,此项工作往往在实体设计中已经基本明确.
接着要考虑每个模块需要实现哪些功能,为这些功能设计对外的接口,并且合理的定义接口的粒度.
此外还要为一些通用的部分设计一个合理的类继承结构,将通用的功能抽象到最高层次,提高系统
的可维护性和可扩展性. 业务上的架构直接指导开发人员的编码.
除了业务的架构之外,对系统的性能及伸缩性更加起决定作用的是技术上的架构,也就是我们通常
说的架构.技术的架构需要在业务架构之前进行,此工作通常由系统架构师来完成.
技术上的架构往往以层次的观念来考虑问题,企业开发通常被分成表示层,业务层,数据访问层和
后台数据库层.
在表示层我们需要考虑的问题首先是是否需要一个合适的表示层开源框架,由于目前开源的不断繁荣,
它们对于模式的运用已经达到了如火纯青的境界,不使用任何的开源而仅仅利用JSP/Servlet来进行
开发往往是不明智的选择,因为那意味着我们需要按照模式自己实现一个通用的Front Controller,
自己封装请求数据,并且自己来将请求转发给具体的处理器.开发人员的水平掺差不齐,这样的项目往往很
难维护.当然你也可以不使用任何模式,完全的跟着感觉走,那意味着你的能力也是跟着感觉走...
其次我们需要考虑选择哪一种框架更适合自己的系统. Struts? Webwork? JSF? or others,此时需要
架构师对各种开源框架的特点比较熟悉,以便决定哪个才是自己最需要的.
在业务层,我们首先要考虑的问题如何组织你的业务对象之间的关系,以便做到功能的接口与具体的实现分离,
这是最重要也是最基本的,如果连这点都做不到,你的程序也就是死的,丝毫不具备可扩展性.传统的解决
方式是利用工厂模式来间接创建对象,但技术发展到今天工厂模式已经显得过了时,无论JNDI式的组件查询
或者spring式的组件注入都能很好的解决此问题.具体使用什么由我们的架构师来决定. 其次应该业务组件
应该以怎样的方式来为表示层提供服务,此处是指单实例,多实例还是需要缓冲池. 处于性能上的考虑,此时应
该优先使用单实例,如果业务对象确实存在线程不安全的因素,我们可以适当的使用同步,此时注意同步的
范围应尽可能的小. 如果不安全的因素太大,以至于我们没有把握同步的很恰当,此时为每个线程单独提供
一个对象也是不错的选择,因为JVM发展到现在对象的创建和回收已经不会过分影响性能. 至于实例池,就像
工厂模式一样,也显得有些过了时. 接下来,在业务层我们要考虑系统究竟需要哪些企业级服务,事务? 安全?
分布式? 线程池... 企业服务往往由系统框架负责解决,使用EJB式的重量级解决方案或者spring式的轻量级,
同样由我们的架构师来决定.
在数据访问层,要考虑的问题首先是是否适合使用ORM, 在实际项目中我们往往看到项目经理未经大脑般的强迫
开发者必须使用hibernate,JDO等工具,而实际我们是否需要它们却从未认真思考过. ORM除了解决对象和关系
的不匹配,最大的作用就是在业务层和数据库之间加了一层缓存,数据从内存从获取显然比从数据库查找要快
得多,这就说明了在读取数据较多的时候使用ORM是不错的注意,而在写入数据较多的时候,往往就没那么必要
使用它. 而在一个系统中有很多模块,往往有的模块读多于写而有的写多于读,此时我个人认为甚至可以在某
些读的模块使用ORM,而写的模块不使用. 当然,在不使用的地方我们需要按照常用的设计模式,将数据访问的
代码单独的抽离出来.
在后台数据库,我们要思考整个系统是否要应对不断增长的负载请求,是否需要具有集群能力,如果是,我们有
必要选择类似Oracle9i这种具有集群能力的数据存储设备,否则将会在数据库造成性能瓶颈. 我们还应该对存储
过程的使用进行思考,网上也有很多关于存储过程的讨论帖子,这里我比较赞同一篇好象叫什么'存储过程是天使
还是魔鬼'的贴子的观点:存储过程可以用来处理数据,但不适合处理业务.
除了以上层次的思考,对于一些常见的企业问题,也应该加以考虑:
1.客户状态的保存
究竟是保存在客户端?
表示层? 业务层? 或者后台数据库.如果保存在客户端,对于C/S应用来说这不是问题,因为胖客户端的存储能力
十分强大. 而对于目前大多数的浏缆器客户端,就成了问题,无论是页面隐藏字段还是Cookies都不能存储很大
的数据量,而且Cookies更是面临安全问题. 其次客户端的数据往往需要网络传递到服务器,这也增加了网络
负担. 状态保存在表示层或是业务层从本质上来说都是保存在内存中(排除某些高手保存在其他设备或文件里),
因为在内存中,所以在访问性能上会更有优势,例如常用的HttpSession或有状态会话Bean. 有状态会话Bean
我没用过,但口碑不是很好. 而如果在集群环境中内存中的状态往往需要在节点间复制,这会带来开销,所以
如果是集群环境还是不介意把大量数据放在内存中. 此时应把数据尽量放在数据库中,虽然这会增加编程量
并且降低性能(因为每次取状态到要查数据库),但伸缩性会得到显著的提高.
2.关于并发
我个人喜欢把并发分成三类:
一是数据库级别的并发,这发生在多个系统事务在同一时间试图操作同样的数据的
时候,此时往往通过事务的隔离级别来解决.隔离级别过小会使数据不安全,而隔离级别过大又会降低数据库性能.
因此大部分数据库将隔离级别设置成Read Committed,意思就是说一个事务在执行过程中可以得到其他事务
已经提交的更新(注意是更新而不是插入). 而此时为了在一个事务内的多次查询中获得同样的数据,我们往往
还需要将这多个查询放在一个只读事务里.
二是代码级别的并发,这发生在某个类中具有不安全因素(例如具有多个线程都可以修改的类变量),而这个类又
处在多线程访问的模式下. 此时我们往往通过具体语言提供的同步机制来解决,在安全的同时也降低了性能,
因为一个线程在访问时其他线程必须等待.
三是应用程序级别的并发, 这发生在多个业务事务同一时间操作同样的数据的时候,这会带来丢失更新和不一致读,
在对数据的实时性要求不是很高的系统中,不一致读往往不是问题,用户也不会觉得不合理. 而在用户操作数据
时发生丢失更新往往不能忍耐. 此时我们的解决方案是应用程序级别的锁定. 一种是乐观锁,通过给记录加版本号,
在操作提交时如果发现在操作期间其他人修改过该条记录,则提示用户更新失败. 这适合在并发机率很低的情况
使用,因为用户很可能填了一堆东西你却告诉他失败需要重填,他会发疯. 另一种是悲观锁,通过严格限制来避免
冲突. 悲观锁分为写锁和读锁,写锁很简单,一个人在操作一条记录的时候其他人只能读该记录,而不能操作. 而
读锁很严格,它又分为共享读锁和独占读锁,共享读锁指一个人只要在读该记录的时候,其他人就也只能读不能操作.
而独占读锁是所有锁中最严格的,他实现只要一个在正在读该记录,其他人就不能读,这是为了保证用户获得的一定
是最新记录,它假定其他人只要在读一条记录,就可能会修改它. 因为悲观锁需要在操作期间不断的加锁解锁,
会严重影响性能,并且实现起来很麻烦,所以尽量少使用.
3.关于分布式
在技术发展到今天关于分布式已经没有太多讨论价值,一句话,能不用就别用. 只有以下几种情况才需要用:
一是C/S应用,没办法的事. 二是表示层和业务层被分配到不同机器上(彪子经常这么干). 三是业务组件被分配
到不同机器上(以前大伙都这么干,说有伸缩性)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值