写点什么

任何架构的调整只是拆了东墙补西墙,无法解决效率问题

  • 2016-09-04
  • 本文字数:7266 字

    阅读完需:约 24 分钟

第三章文章发表后,在服务版本问题上产生了很大的争议,这其实是在我预料之中的,因为没有版本是反直觉的,我作为一个开发人员如果第一次接触到这个说法也一定会质疑的。没错,我们的产品最初也是有服务版本的,从一个技术人员的直觉出发我在产品前几年的设计和实施中都是有服务版本的。

S++ 的概念不是一下子就从我脑子里冒出来的,而是在这近 10 年的 SOA 实践中逐步形成的,服务去版本化也是后期才有的。说到实践问题,这几天不停地有架构师来要案例,我咨询了公司,答案是案例都是公开的,只要不涉及合同和实施的细节就行,所以下面我先把案例贴出来:

从我个人的观点来说,案例的多少并不能证明一个理论的正确与否,现实中有大把的不合理系统在线上运行着。所以,我的文章中从来不用案例来证明什么观点,虽然我的案例都是在高度复杂的行业中实施的成功案例,但我觉得还是把原理讲明白才有意义。

本章继 S++ 的基础特性和应用之后,重点分析 S++ 理论对业务流程的影响,尤其是微服务化后产生的作用。

后台业务逻辑的分层

我们知道,传统的业务系统内部是通过面向对象的方法来建立数据模型的,通过这种建模方式,我们通常将业务系统按对象分为不同的业务功能模块。比如传统的销售系统中我们基本上会划分产品、订单、账户、支付、客户等等业务模块,每个模块内部围绕核心的数据对象,完成对象从最基本的增删改查操作到复杂的计算操作;业务逻辑最终由多个对象之间的复杂交互来完成。

那么事实上,围绕对象完成的所有业务逻辑基本上可以分为 3 个层次

1、对象基本操作增删改查

对象的基本操作不用过多解释了,增删改查的动作大多数和数据库相关,一般来说对象的变更会被持久化到数据库中以便后续的使用。

2、对象的计算操作

对象的某些业务属性在业务过程中都是变化的,那么这些变化的规律就是对这些属性的计算方法,这些计算方法通常不仅仅需要所需要操作的对象本身的属性,更多的还需要来自对象外部的参数。对象通过它的计算操作来完成它自身在整个业务过程中的变化。

3、对象间的流程操作

整个业务过程,其实就是一群对象在时间上完成自身空间变化的过程(对象属性的变化可以看作是空间上的变化,而空间的变化是在一个时间轴上完成的),这个时间轴上每一个可观测到的空间变化就是流程上的一个个节点,这就是我们经常提到的业务流程。

业务流程是整个业务中最为活跃的部分,对象和对象计算大多数都属于固定的约定,现实中业务对象大体是不会有什么不同的,买蚕豆的客户和买保险的客户在各自的业务系统中没有多大差异;对象的计算方法在同业中也大体相同,竞争的差异主要来源于不同的产品和业务流程,而且往往不同的产品与不同的业务流程之间有着密切的关系。

举个例子来说,对于同样的修车业务,可能有些客户对车的内部很感兴趣,那么一个可参观的甚至可动手的修车流程(在流程中改变或增加处理节点)可能就会提供更好的服务体验从而形成新的产品和竞争力。

微服务架构下的业务流程

传统的 SOA 大服务模式中,业务系统完成业务对象的所有 3 层操作,即所有的业务逻辑被封装在服务内部,也就是说服务是高内聚的,这有利于服务的性能。而在微应用架构下,业务对象被不断的独立出来形成一个个独立的业务系统,那么传统大服务模式下原本封装在业务系统内部对象间流程就必然形成跨系统的远程调用。

传统的微服务架构无法明确的说明这些对象间的流程到底应该属于谁?比如当业务需要对一个订单进行支付并将支付的结果体现在账务中时,这个业务过程到底应该由哪一个业务实体来完成?换句话来说,这段代码到底应该放到哪个业务系统中去?一个符合逻辑的直观判断可以很自然的得出来,这些对象间的业务流程应该独立于所有业务对象之外,形成专业的业务流程系统。

但是,微应用架构是不敢这么说的,显然微应用架构出现了统一的流程调度中心了,说好的去中心架构呢?其实这个统一的调度中心就是传统 SOA 大服务中的 ESB,但是传统的 ESB 其实很难实现这样的组合,即便是我们在现实中成功的 ESB 实施,基本上也是不含组合流程的,原因就是这个调度中心无论从业务上还是从性能上都不稳定。

这个不稳定的问题,即便在去中心的微服务架构下也存在相类似的问题,只要把应用按对象拆开那么就一定面临着系统间流程调度的问题。所以,其实微服务架构并没有解决传统 SOA 架构下存在的问题,这个重要的问题直接被忽视了。

然而这个问题任何人都无法回避,不解决它就会一直存在,从而阻碍技术和业务的发展。

完全解耦:微服务可以更纯粹

多年的 SOA 实践中,我们发现大部分的系统既是服务提供者,也是服务消费者,这意味着服务具有系统间的耦合性。原因在于,大服务系统将服务的 3 层业务逻辑合并在一个系统中实现了,所以跨业务对象的业务流程必然造成系统间的调用,这个问题在微服务架构下尤为显著。

我一直有个愿望,就是彻底的消除系统间耦合度。但是在传统 SOA 架构下,这个愿望是难以实现的,因为为保证服务的高内聚以提高服务的执行效率,往往将跨对象的业务流程集成在业务系统内部,这样基本通过 API 调用就可以完成业务流程,只有较少的对象调用需要跨系统,所以在大服务模式下消除服务对外的耦合需要重新打散原有的业务流程,这样改造是成本高昂的。

不过微服务架构的出现,使得这个愿望更趋近于可实现,如果我们把服务的第三层跨对象业务流程从服务系统中剥离出去,在服务系统中只包含业务的原子部分即基本操作和计算部分(为方便起见我将这种剥离了对象间流程的服务命名为本征服务),那么服务系统之间将完全解耦,而且效率不会有任何降低。那么,微服务架构将变成如下模式:

按照上面的方式构建出来的微应用更加纯粹,后应用之间消除了相互调用,服务就是纯粹的对象的计算和修改。在 UI 层和后应用之间插入了组合服务层,所有复杂业务逻辑都通过组合服务层进行调度。唯一让我不满意的就是那些细细的蓝线,这些蓝线是应用的前端直接访问后台的本征服务的,这些直接的访问显得不够优雅,后面一个章节我们再来探讨这个问题。

微服务完全解耦后带来两个好处:

1、实现非侵入式的异步调度,可以实现微服务异构化

传统的微服务实现异步调用,必须将异步框架嵌入到微服务的应用中去(第一章我们就提到了,异步流程是很复杂的,必须要有框架支撑),所以传统微服务必须是同构的系统,否则没有办法实现异步调用。这个问题大大的限制了业务开发平台的可选择性,如果微服务可以异构化,那么企业实施微服务的成本将大大降低。

2、后系统的任何变动都不会影响其他后系统

这是很明显的,并且复杂逻辑的执行效率并不会因此而受到任何影响,更进一步的可以通过专业的 S++ 组合容器完成对开发人员更加友好透明的异步服务调度框架。

用 S++ 实现稳定可靠的服务组合

上一章节我们重点分析了业务和技术分离这个特性对组合流程起到的稳定作用,其实我们知道对稳定业务流程起关键作用的大杀器是多态性,在面向对象的方法论中我们就认识到多态性的强大之处,这也是C++ 能够建立超大规模业务系统的一个要素。下面,我们会分析面向对象的多态性在微服务架构下的问题,以及如何用服务多态来实现流程稳定性。

传统的面向对象解决不了跨系统多态的问题

面向对象的方法是通过运行时刻产生不同的对象实体来完成多态性的,对于一个应用系统内部很容易实现。但对于解决系统间的多态问题就会遇到很多麻烦:

1、面向对象的多态性是由对象的调用者来决定的

我们知道,面向对象的方法中,对象的多态其实是由最初初始化对象的模块来决定的,当我们构造一个需要进行处理的对象时,就已经决定了未来在业务流程中使用哪个对象,从而决定了流程中的业务分支。

所以,用面向对象的方法实现多态,必然会造成消费者和提供者之间的耦合。

2、系统间编程语言不统一

我们知道微应用和 SOA 的一大特点就是可以利用不同的技术来实现不同的应用,即用合适的技术实现合适的业务,所以系统间编程技术不统一是必然的,而且随着时间的发展新技术出现也是必然,那么异构系统是必然的大量的存在。那么,显然要么实现跨技术的远程对象,要么就无法在系统间实现对象的调用,对象的多态也就无法实现。

3、系统间对象版本不统一

即便我们将系统间的技术进行统一,或者我们实现了跨技术的远程对象(比如 WebServices),由于系统各自都是独立发展的,事实上很快各应用之间的对象版本就会出现差异,这种差异最终会导致系统频繁修改。传统的 SOA 技术中 WebServices 为了消除这一问题,拒绝了远程对象的派生和继承的特性,也就是通过消除对象间的耦合性,解决了远程对象变更对外部业务系统的影响。

所以,为了 SOA 的另一个重要特征“松耦合”,传统的 SOA 放弃了对象的多态性这个重要的属性。当然,微服务架构干脆就不考虑多态问题了。

4、单一系统必须包含所有派生的类定义,系统耦合度高

假如我们让跨技术的远程对象具备多态能力,需要付出什么样的代价呢?那就是系统间的强耦合,也就是说为了实现多态任何一个独立的业务系统必须包含他引用的外部对象的所有定义,无论是抽象的还是具体的。这意味着,任何一个外部系统对象的修改甚至新增,都必须将对象的定义发布到当前这个使用了外部对象的系统中来,这会导致系统的高度不稳定性。

所以,WebServices 放弃对象多态性是非常必要而有道理的;同理,微服务完全不提组合流程也是有原因的。

S++ 如何实现跨异构系统的多态

S++ 由于不再使用远程对象进行系统间的沟通,那么为实现对象多态而必须的、天然的强耦合性就不存在了。

  1. 抽象服务的调用并不需要一个对象实例作为载体,所以不需要在调用点之前就来解决对象的多态问题,而是将多态问题交由外部的系统来解决,所以服务调用者与服务的实现者耦合度基本为零。
  2. 任何一个系统,只要包含了被调用服务的所有继承派生关系定义,就可以独立的解决多态问题,而不需要服务的真正实现者来参与,所以多态的完成者与服务的实现者耦合度也是零。

由于 S++ 不再依赖于对象,所以通过 S++ 实现的多态不存在系统间的强耦合性。与面向对象的方式不同的是,当一个消费者调用一个抽象服务的时候,消费者不需要知道具体要调用哪一个派生服务,从而实现跨系统的多态调用;同时,针对异构系统,由于不需要实现远程对象的调用,所以消费者不需要加载任何与派生类相关的信息,从而保证了异构系统间多态调用的实现。

通过 S++ 实现业务组合的过程中,可以建立独立的组合服务 应用,并且在组合业务应用中实现服务的多态性,不需要业务系统配合。举例说明,假设组合应用中某流程需要调用服务 C,服务 C 具有 C1、C2…Cn 这 n 个派生服务,那么在组合应用中引入所有这些服务的定义就可以实现业务流程的多态性。将多态性的实现从业务系统中剥离出来的好处是,当 C1、C2…Cn 分属于不同的业务系统时,仍然可以很好的实现,同时不会影响业务系统的稳定性。

S++ 如何实现基于冲正的串行流程并行化

架构的代价

阻碍 SOA 发展的另一个拦路虎就是组合流程的效率问题,一个串行的流程中,每一次业务服务的调用都会消耗一定的时间,一旦流程中组合的服务多了,那么这个组合流程的响应时间就会非常的慢。

谈到效率问题,我们第一章说过通过架构解决效率问题远没有算法直接。是的,微服务化通过架构调整提高了系统的开发效率、降低了运维成本、提高了系统可扩展性获得了诸多好处,但是不能没有代价,架构只能平衡资源,不能创造资源。微服务架构就是通过损失效率来获得上述好处的,微服务将原本是一个完整的数据库事务拆分为多个零碎的事务,每次数据库事务都受限于数据库事务需要的基础开销,我们都知道数据库事务基础开销是很大的,而且高并发对基础开销的影响更大。

根据经验,非并发 insert 操作,每条记录单独事务要比整体使用一个事务进行插入慢 4-10 倍左右,如果是 update 操作,更是要慢几十倍。考虑到业务操作,每次更新的数据很少,所以数据库事务的基础损耗基本远大于数据更新本身。所以,基本上你把一个大服务拆成几个小服务,性能就会降低几倍,这还没考虑并发。并发情况下,单一数据库根本无法承受如此细颗粒度的事务操作,所以微服务要求每个微服务独占一个数据库,当然数据解耦也是一方面的原因,不过我相信性能是主要的考量。

另一方面,拆细后原本应用内部的 API 调用变成了应用间的 RPC 调用,所以为了降低 ESB 通讯带来的损失,微应用采用了去中心架构。但是,去中心化通过损失管控能力而节省的那几毫秒通讯时间,对于数据库事务带来的损耗来说根本就是杯水车薪。所以,牢牢记住:“任何架构的调整,必然是拆东墙补西墙,调整后的架构要么需要增加资源,要么需要改进算法”。

必须采用算法优化来弥补架构缺陷

因而,微服务真正可行的提高效率的方法是将串行业务流程并行化,如果能并行化,组合流程的响应时间就变成了甚至比没拆分之前的应用还可能短。但是,并行化不是那么简单的,我们看看 S++ 的特性如何使得并行化成为可能:

1、可并行化流程中不能含有业务分支

如果流程中出现业务分支,那么串行就无法改成并行,因为预先判断需要执行哪个分支是一件非常困难的事情。S++ 的多态性可以帮助程序员消除一部分的业务分支,这样就可以提高流程的可并行化率。

2、可并行流程中前后节点不能是因果依赖的

假如 B 服务节点的调用需要前置节点 A 服务返回的数据作为输入参数,那么是无法并行化的。这种情况下,我们需要系统分析员首先将因果依赖去相关性。举例说明如何去相关性,某支付流程中需要先扣积分(当前有多少可用积分流程是不知道的),然后再扣款,扣款的多少就和扣除积分的多少形成因果依赖。去相关性就要求,扣积分的服务必须明确的输入扣多少积分,通过预先查询积分余额来决定扣除多少,这样扣款金额就不需要等待积分扣除后才能计算出来,这样就消除了因果依赖。

理论上,只有数学上完全随机的程序才无法预先消除因果依赖,幸运的是,现实的业务场景中几乎不存在完全随机的场景,因此几乎所有的业务都是可以去相关的。

3、流程必须是业务与技术分离的

前面讨论的因果依赖,在传统的流程引擎中是无处不在的,任何 A、B 两个节点之间的赋值节点都会造成因果依赖,流程引擎在执行赋值技术节点之前是无法调用后续节点的。即便系统分析员已经通过业务分析人为的解除了服务之间的因果依赖,流程引擎依然无法判断哪些赋值操作是必须在调用之前完成的。更可怕的是,某些流程工具为了适应复杂的映射操作,提供了规则引擎甚至编程接口,使得并发引擎更加无法判断。

S++ 通过基于元数据的自动映射,可以自动的判断哪些赋值操作是因果依赖的,从而可以自动的判断哪些服务是可以并行化的。

4、容器必须提供完全技术化的全局事务一致性服务

可并行化的一个前提假设是,我们认为 90% 以上的交易是客户理性的行为,也就是说 90% 以上的交易是可以成功执行的。举个例子,客户网购一个 100 块钱的商品,但是他的银行账户上只有 70 块钱,90% 的客户不会去点支付按钮的,因为这笔交易客户自己知道注定是完成不了的,一个理性的消费者不会试图去尝试系统能否出现漏洞从而完成一笔不可能的交易。

在这个前提假设下,所有的去相关的串行服务并行化后,其结果与串行结果毫无差异。但是,仍然会有一定的交易失败的概率,比如账户余额因为一个定期的还款交易恰巧被执行,从而导致余额不足以支付。那么,这种失败就要求容器提供自动化的手段,对流程开发人员透明的来实现交易的回滚。

总体来说,只要容器提供了透明的冲正服务,由于客户的理性行为居多,系统整体的回滚压力是不大的。当然,系统还可以通过监控手段控制恶意的、非理性的消费行为,从而抑制并行化后引起的回滚压力。

5、并行化要求单一系统具有极高的可靠性

从采用事后冲正补偿这一策略看,为了保证系统尽可能低的发生补偿操作,降低补偿的压力。并行算法要求参与流程的微应用系统必须具备极高的可靠性,否则大量的补偿会导致整个系统不可用。
计算可得,当一个大服务被拆分成 N 个小服务的时,为要求保证整体的可靠性,每个小服务必需提供原本大服务 N 倍的可靠性。这样,原本拆分带来的好处到此为止基本荡然无存了,当然办法还是有的。原本拆分会带来单体的简化,简化就会提高可靠性。同时如果将复杂业务流程剥离出去,形成的本征服务就会具备更高的可靠性,本征服务的可靠性提升可能会高于 N 倍。最后一个绝招就是增加单体微服务的集群数量,用资源冗余来换取可靠性。

6、并行化要求业务系统尽量将校验和控制前移

当然,这本身与架构的基本原则相符,因为对于无法执行的请求越早拒绝对系统的开销越小。对于并行化来说,这一点更是至关重要,因为一旦请求被并发执行了,那么就只有启动回滚流程了。
基于 S++ 的独特性质,我可以乐观的判断,未来串行流程并行化一定会普及开来,并极大的影响 SOA 和微服务的普及率。

小结

本章着重分析了 S++ 在业务流程方面的应用,尤其是针对微服务化应用提出几点改进:

  1. 完全解耦微服务,将本征服务与业务流程分离,本征服务可以实现异构化。
  2. 实现稳定的、跨异构微服务系统的异步组合交易
  3. 消除组合交易并行化的障碍,通过并行化提高业务处理性能。

作者介绍

李东,14 岁开始学习计算机语言,作为课外兴趣自学了 BASIC 和汇编,利用放假期间编写了贪吃蛇、打飞碟等游戏。高中、大学期间继续自学软件编程,曾将 C 和汇编结合使得从高级语言中能够调用绘图功能,并模仿 Borland C++ 开发了一套适合学校机器的图形化开发环境的原型。

93 年大学毕业后在西门子合资公司作为交换机软件安装人员工作两年,然后来到 JInfonet 公司先后参与 4GL 的研发和 JReport 的研发。作为 JReport 的第一代主要研发人员,编写了从原型一直到 3.0 版本的核心引擎部分。2000 年与合伙人一起创建了 Bi-Soft 公司,主营业务是商业智能软件 Bi-Pilot,负责整个产品的研发及管理工作,从最基本的查询一直到多维分析模型和引擎都是产品的涵盖范围。

2007 年 Bi-Pilot 被神州信息收购合并,李东开始在神州信息研发 SmartESB 产品,用 SOA 的方法论为客户提供底层产品服务。


感谢郭蕾对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们。

2016-09-04 17:302190

评论

发布
暂无评论
发现更多内容

一致性哈希算法 Java 实现

escray

极客大学 极客大学架构师训练营 课程作业

【第五周】技术选型(一)

云龙

架构师训练营 1 期第 4 周:系统架构 - 作业

灵霄

极客大学架构师训练营

第一周作业

阿呆

【建议收藏】18个适合程序员的在线学习网站,每个我都帮您试过了

田维常

学习 程序员 成长 网站

食堂就餐卡系统设计

Sandman

极客大学架构师训练营 作业

架构师训练营第 1 期 -Week5 - 课后练习

鲁小鲁

极客大学架构师训练营

第五周作业

极客大学架构师训练营

第01周学习总结[架构师训练营第 2 期]

Airship

极客大学架构师训练营

架构训练营第五周作业

Geek_ce484f

极客大学架构师训练营

【原创】90%的人都不会做的一道笔试题

田维常

程序员 面试

极客时间架构师培训 1 期 - 第 5 周作业

Kaven

第5周 技术选型(一)总结

bearlu

朋友被“卖”了两次:程序员,真的别去外包公司!

田维常

程序员 外包 外包公司

程序员是不是青春饭?年纪大了何去何从

田维常

程序员 青春饭

面试官角度,聊聊写简历这事

田维常

程序员 面试

技术是否要追新?基于4点判断谈谈4点认识

田维常

程序员 技术 最新 技术追新

数字人民币将如何改变金融生态?

CECBC

数字人民币

架构师第一周作业

丁乐洪

[架构师训练营第 1 期] 第五周命题作业

猫切切切切切

极客大学架构师训练营

架构师训练营第 1 期 - 第五周作业提交

Todd-Lee

极客大学架构师训练营

第一周学习总结

Alvin

学习 极客大学架构师训练营 2组

week5

张兵

极客大学架构师训练营

架构师训练营 1 期第 4 周:系统架构 - 总结

灵霄

极客大学架构师训练营

手把手教你理解决策树:从概念到应用

计算机与AI

Python 学习 决策树

架构师训练营第 1 期第 5 周学习总结

du tiezheng

极客大学架构师训练营

Spring Boot 过滤器、监听器、拦截器的使用

田维常

程序员 过滤器 拦截器

第四周作业

橘子皮嚼着不脆

年薪50万开发者相亲失败:程序员,别输在不会说话上

田维常

程序员 好好说话 不会说话

牛逼的程序员,都长什么样?

田维常

程序员 牛逼

架构师训练营 Week5 - 课后作业

算法 分布式缓存 一致性哈希

任何架构的调整只是拆了东墙补西墙,无法解决效率问题_架构_李东_InfoQ精选文章