阅读目录:
最近一段时间都在做系统分析和设计工作,面对的业务是典型的重量级企业应用方向。突然发现很多以往觉得很简单的问题变得没有想象的那么容易,最大的问题就是职责如何分配。论系统架构设计的最大的问题,其实也就是职责的分配,分配的合理,实现起来就会很柔性,反之就会使架构很混乱。
软件的生命周期大概可以归纳为四个基本的过程,分析、设计、实现、测试,当然这仅仅是一个最为粗略的表示而已。不同的方法论有着不同的使用这几个过程的方式。RUP使用快速迭代的过程,在这个几个子过程中适当的输出一些过程制品,每次迭代都是进行相同的分析、设计、实现、测试。而在Scrum中,不提倡输出任何文档形式的过程制品,也同样有着上述几个过程,强调以人为中心,通过沟通来解决大部分的问题。
不能用好与不好来判断哪一种方法论,只能根据目前的实际情况综合权衡。RUP的每次迭代中有几个关键的制品对系统分析、设计很重要,可以说是非常重要,如:词汇表、业务规则文档、用例、领域草图。这几个制品对分析、设计很重要,需要从这几个制品中提炼出设计模型最终才能落地。这主要用在业务复杂的应用系统中。而Scrum更加的轻量级,可以用在互联网项目中,业务不是太复杂的情况下。
其实我为什么要强调软件工程及开发方法论,是因为我最近发现,做设计其实是建立在分析的基础上的,但是这里面又有很多问题。大型企业级应用,并不能通过一次性分析就可以得出准确和全部的需求,初期阶段建立的需求70%都是不准确的。所以做架构需要有分析的能力才行且是建立在适当的开发方论上的分析,什么时候该用RUP,什么时候该用Scrum,什么时候该用XP都很有讲究。分析与设计都需要有一个执行上下文,不同的上下文对分析、设计的执行有着不同的要求。
有句话我觉得对架构者来说很有启发:分析就是做正确的事,设计就是正确的做事。架构跟语言跟平台关系不大,毕竟架构是设计过程中的子过程,我想如果你的设计不合理,你用任何语言任何平台都解决不了问题。(这里的架构上下文指:企业应用架构不是基础设施的系统架构)
进行SOA类型的架构设计就需要搞清楚SOA架构模型才行。并不能想当然的对系统进行简单的拆分就行,需要搞清楚SOA的架构模型是怎样的,每一块是干什么用的,这样设计由分析阶段输出的需求时才能正确的划分职责。
如果把SOA的架构简单的理解为是多个子系统之间的整合其实有点太过于简单,也没有真正搞清楚SOA的架构模型。按照SOA的正确方法论及目标模型,其实SOA在实现架构落地上,需要考虑到对服务的组合,不断的重用现有的服务,让企业应用可以逐步集成,快速实现业务的迭代。其实这就是本节要讲的服务的分层,通过分层将服务按照使用类型进行分配,上层服务对下层服务的包装,下层服务负责原子性的操作,上层服务对下层服务进行业务性的组合。
我们来看具体的每一层的作用及主要职责。
应用服务就是诸如:订单服务、仓库服务、销售服务、客户管理服务,这些服务直接对应不同的应用系统,直接服务这些应用系统的原子操作。订单服务直接原子性的插入订单,没有任何跨其他服务的分支逻辑。仓库服务只管自己的仓库逻辑。同样其他的应用服务只管好自己的职责,杜绝对其他服务的调用。
图1:
应用服务位于UI与后台之间,后台我们可以认为它是一异构的系统或者是数据库之类的。应用服务的位置位于前端与后端之间,起到类似一个服务API的作用,但是SOA中的服务还远远不止这一个应用服务,如果我们的SOA架构中只有一种类型的服务,那么这会增加我们系统的耦合程度,因为你没有对系统的服务进行层次的划分,你的业务功能会直接的落到某一个应用线上的服务,继续往下看。
组合服务是对应用服务的一个组合,根据实际项目的规模大小,不一定非要进行物理的隔离,在代码层面的服务化也是可以的,在将来的某一天有必要的情况下再进行物理的拆分,毕竟物理的拆分有着严重的成本和代价,对系统的稳定性带来很多挑战。所以经验告诉我们必要的时候在进行拆分。”分布式系统设计的第一个原则就是尽量不要分布式“,这是马丁.福勒大师说的,现在理解确实感同身受。
图2:
组合服务对下层的应用服务进行了组合,完成了一个基本的业务动作,应用服务中是最基本的基础性的原子性的操作。但是在复杂的业务需求下大部分业务功能都需要跨越多个应用线来完成一个最外层的企业动作。提交订单可能需要穿过很多应用线,订单管理、仓库、财务等等环节。所以这里我们还需要一个能在最外层对组合服务进行编排的业务服务。这个编排服务可以完全是自动化的,通过工作流引擎进行组合自动化来完成,这对企业应用的自动化流程很有意义。
业务服务是最外层的服务,向下编排了组合服务。业务服务位于最上层,当需要有跨越多个应用线来完成的业务,这个业务就放入业务服务中。比如提交订单,先检查库存、扣减库存(冻结库存),然后下单,再往后通知财务,再往后通知物流等等都是一个复杂的企业服务线。这种最外层的业务逻辑如果你不进行SOA分层然后将其放入最外层的业务服务中,你把它放入任何一个应用线都会使系统调用混乱不堪。所以问题就是需要进行纵向的划分层次。如果进行了SOA的层次划分后就不会出现互相乱用的情况。其实这里可以参考阿里的服务设计方法。(李智慧写的一本大型互联网架构与实践里面也讲到了服务要划分层次)
图3:
当在业务服务中执行的业务逻辑时,需要跨越多个应用线来完成。这部分的逻辑也说是职责,如果不放入这个位置,放在哪个应用线都不合适,放入哪个应用线都会使系统调用出现混乱。其实这里的问题就是我们不能用一个维度来进行SOA系统的设计,本来服务就具有组合特性,所以适当的提升服务的层次是有好处的,但是应用服务和组合服务可以在代码层面上进行构建,而业务服务也叫编排服务是需要进行物理隔离的,毕竟考虑到系统复杂度和稳定性问题这是值得的。在排查问题,系统性能、稳定性等等方面,物理的隔离有一定的作用,毕竟业务服务本来就是来组合多个应用线的,这样做会使整个系统架构很清晰。
进行SOA化的实施,大部分情况下都是对现有系统进行重构后考虑的,初期企业发展阶段以快速出原形为首要目标,只有当系统出现瓶颈了才会考虑运用SOA来解决。但是在这个时候有很多历史包袱无法解决,进行SOA化的重构其实成本是很高的,而且很危险,对有些复杂的逻辑说的现实点,是无能为力的。如果都可以通过重构这个技术来解决,那我们就太天真了。《重构—马丁.福勒》一书讲的是代码层面的重构,跟做系统级重构两个概念。对系统级别的重构还没有太多成熟的方法论支撑。尤其现在新技术层出不穷的,各有优点,能很好的运用这些技术、方法论、过程来重构大型企业级系统,难度非常大,这需要整个公司投入很多人力、资源成本。回过头来想想,其实在前期适当考虑一下还是有必要的,这样可以减少后期很多技术债务。
这里我只总结我在分析、设计公司某一块业务系统的时候对其进行SOA化的重构思路。重构本来就是一个不断迭代的过程,不可以跨大步。通过很多脚手架支撑,让系统慢慢的过度到新的SOA架构下,既然要实施SOA架构,那么很重要的一点就是对迁移的业务逻辑适当的归类,什么业务逻辑该放入“业务服务”中,什么逻辑该在代码层面上放入“组合服务”中,对基本的操作有如何放入代码层面的“应用服务”中。
在进行系统拆分的时候,对当前后端的调用都进行适当的规划,将其分为两类,一类是应用服务,一类是组合服务,这两个服务是可以在代码层面上进行抽象。重点是那些调用其他系统的地方,需要将其放入业务服务中,这块逻辑比较复杂,难以抽取,需要适当的结合”数据落地“的思路来综合考虑。有时候把一部分数据落入本地可以提升系统的整体简洁度和稳定性,但是要考虑数据的一个生命周期性质。
在迁移的过程中可能还会有一些新的功能并行开发的,既有新的逻辑需要放入新的SOA服务中,也会有迁移过来的逻辑,这两个过程同时进行其实很痛苦,尽量避免这样同时进行,但是现实是根本不可能,正常都是一起并行推进。如果这两个过程是同一组团队负责其实还好,毕竟对这块的代码、业务都比较了解。
这一节目的是想强调对现在系统进行迁移的时候要考虑服务的层次,不要只进行一个简单的搬移代码,这个时候是一个对代码进行重构的好机会,该划分层次的要划分层次,该读写分离的要读写分离,要重点考虑那些“业务服务”,需要跨越多个应用线的逻辑。
顺便说一下,还有一个重点就是迁移的时候还要考虑数据存储方面的迁移,光代码层面的迁移只是第一步,第二步还需要进行数据层面的迁移。当然这两个大的步骤都是要通过很多次迭代完成,并且还是一个对业务、代码进行很好梳理和整理的好机会。
在将系统进行服务化的时候要考虑服务层,如果当前没有业务服务的逻辑那么就保留服务空间,至少要清楚在服务层中有这么一个空间是要预留的,当有其他的应用线需要与你交互的时候可以顺利的进入到你的服务区,而不是直接到达你的应用。
做系统设计时最怕的就是职责搞错了,这会使系统的架构突然就复杂了,而且系统架构都是很难改变的或者压根就无法改变的决定。所以我对这块引起了重视,有时候你对业务在了解在熟悉依然会搞错职责,对于这块光凭主观的判断是不长远的,无发复制、无法传播的,也无法落字成文的。
对DDD我这里就不多做介绍了,这里要强调是GRASP。运用DDD可以很好的帮助我们来战略性的观察企业所坐立的领域,我还是很提倡DDD在公司实施的,不说DDD中的“战术设计”方法论,就光说它的“战略设计”方法论还是有很大作用的,让我们可以在脑海中建立一个战略性的模型。具体要不要进行代码层面的落地这就看实际情况了。而且DDD中的很多不错的思想都可以借鉴过来,包括领域通用语言,有了领域通用语言团队之间的沟通和交流会节省很多成本。对于新人来说,可以很快的了解公司的一些大概的业务,这和“词汇表”其实还是有区别的。
上面说了,在划分职责的时候很多都是通过经验来主观的判断,没有其他的客观证据了。那么有没有一个不错的方法论或模式来指导我们进行这类问题的解决呢,其实还是有的,因为在国外人家这方面已经很成熟。
GRASP就是这样的一套模式,它可以帮助我们进行客观的设计职责。到底该把这块数据放入哪个应用中,到底该把这个逻辑放入哪个服务中,都有指导,包括对对象层面的设计依然可以。我们可能对“信息专家模式”都有了解,但是以往我们可能都只把它用在对象设计上,而没有提升一个系统层面中考虑。那是因为我们以往可能没有碰见很复杂的职责分配场景,只有当出现问题时我们才能真正领会某个东西的好坏。
DDD只有结合GRASP才能客观准确的方配某个领域的职责,不管是战略设计层面还是战术设计层面,都是一个很好的平衡标准,不会由于技术人员主观的兴趣倾向导致一个错误的职责分配决定,而这个错误的决定最终是要开发人员来买单。
传统分布式系统与当代的面向SOA的分布式系统有一定区别,论概念上来讲SOA是以服务为中心,既然以服务为中心就会有很多面向服务的设计原则。而传统的分布式系统没有服务的概念,也没有所谓的一切皆是服务的原则。而当代SOA则首要原则就要以服务为中心,针对服务的设计又有了很多服务设计原则。
SOA对服务还进行了类型的划分,按照服务的应用层次来分类: 业务服务 、 组合服务 、 应用服务 , 包装服务 等。再按照管理与运维的层面来分类: 控制服务 、 调度服务 、 监控服务 等等。传统的分布式系统是没有这些的,我们谈论的是当代SOA的分布式系统,所以我们强调的是以服务为中心,以服务设计原则为架构设计的指导要求,当代SOA是对传统分布式系统的一个迭代进化,不是一个时代的产物,SOA更加强调了以服务为首要原则,已经提升到了另外一个更加高级的层面。
本节我们交流一下在当代SOA分布式系统中的数据一致性问题,在SOA中这主要涉及两个层面来考虑,一个是服务层面、一个数据持久化层面。再按照一致性的基本要求,可以分为:读一致性、写一致性、会话一致性、最终一致性、实时一致性等几个维度,当然还有其他几个维度的一致性要求。
我们这里重点讨论在企业应用中实施SOA时遇到的一些比较棘手的数据一致性问题和解决方案,对于刚才提到的几个维度的一致性要求均具有重要的参考价值。
以往包括目前很多项目还是倾向于使用DTC来处理分布式事务,这个方案多数适用于一般的企业应用,业务、访问量、数据量要求都不是很高的情况下。用DTC很方便,事务的自动传播、事务的自动感知、事务的自动回滚和提交,这都是中央DTC帮我们管理好了。
由于有中央DTC的统一协调,看似好像帮我们解决了很多我们需要考虑的问题,但是它也是整个平台的致命的瓶颈,一旦DTC由于某个问题出现错误,而且这种错误都是系统层面的错误,很多问题我们是无能为力的。如果出现问题,整个应用平台都无法完成任何一个跨服务的业务流程,这其实很危险,你不无法控制系统的稳定性。
这里总结,DTC用于一般的小型企业应用,不建议用在中等规模的企业应用中,不是说这个东西不好,而是无法控制它。
世界级SOA专家所编写的书籍里都提到了使用“补偿”操作来完成数据的不一致性,当我们编写了一个服务方法A,就需要一个服务方法A1的补偿接口来完成A服务的补偿操作。但是真实的业务情况下很难实施这种看起来好像很优美很柔性的设计。没有实践就没有发言权,我们公司的技术团队就实施过这种方案,但是很不理想,这跟技术本身及技术团队没关系,只是我们的平台业务太复杂,很难去“补偿”一个已经做过的操作。
这当然也要看你所面对的项目情况,量变引起质变,如果你的各种量都上去了,这个“补偿”方案不实际,而且很难在数据层面进行“补偿“。总之,这不是一个中长期的方案。
EDA简称”事件驱动架构“。多个系统之间通过传播”事件“来驱动整个业务的运转。系统之间没有紧耦合的同步调用的操作,都是通过发出异步的“事件”来通知下一个业务环节。
可能你会有一个疑问,异步操作,是不是系统之间延迟会很长,其实不是,现在有很多成熟的消息中间件在内网内几乎是毫秒级别的延迟,至于跨机房就看物理上的距离了。
异步操作有很多好处,这里我就不浪费大家时间重复那些好处。使用EDA实现系统之间的一个松散的事务关系,要把控好项目的质量,对系统的非功能需求、BUG数等等可能会影响业务操作中断的地方都要建立起适当的机制,让这些问题尽早的在线下解决。比如可以实施UnitTest、持续集成等一些敏捷的方法论。
同样一个工具在于什么人用,真正的工匠都是使用很朴实的工具来雕刻无法超越的艺术品,这就是工匠情怀。最近对工匠情怀感受越来越深,一直以为自己是一个比较喜欢专的人,这是不是偏离了一个大的方向冲进了一个小胡同,直到最近我才领悟,这其实是”软件工匠“的精神。但是这不代表不考虑全局,这只是一种情怀,一种态度,对于架构也是,对于代码也是,不要认为那些看似无关紧要的问题就忽视它,带着工匠精神雕刻它。
参考书籍:《SOA实践指南》、《SOA概念、技术与设计》、《精益软件》、《UML与模式应用》、《软件预构的艺术》