1.2 荒野大集成
想象一个场景,Ash是一名后端开发者,他负责构建一个小型的后端系统,用于通过信用卡进行收款。这看起来并不复杂,对吧?Ash行动迅速,很快便设计出一个美妙的架构。项目中最容易推进的是让订单履约服务提供REST API,在和订单履约服务的开发人员沟通后,他们很快便同意了。之后,Ash继续编码。
开发到一半时,一位同事走进来,看着Ash的白板,那里勾勒着那个美妙的架构。同事漫不经心地说:“啊,你用的这个是外部的信用卡服务吧。我之前也用过它。当时我遇到了很多连接泄露和故障恢复的问题,那个服务现在有所改进吗?”
这个问题让Ash有一点惊讶。这种昂贵的SaaS服务居然如此脆弱?这让Ash那优雅、简洁的代码显得过于天真了!不过问题不大,Ash添加了一些代码,用于在服务不可用时重试调用。又多聊了一会,这位同事透露,那个外部服务的故障状态有时会持续几个小时。于是,Ash需要想出一种支持等待时间更长的重试方法。但这过于复杂,涉及状态处理还要使用调度器(scheduler)!因此,Ash决定先不去解决这个问题,而是把它放到待办列表中,并且寄希望于订单履约团队能够解决这个问题。目前,当信用卡服务不可用时,Ash的代码只是抛出一个异常(exception),然后祈祷一切都会正常工作。
服务发布到生产环境两周后,订单履约部门的另一位同事与CEO一起走过来。Ash的服务抛出了许多“信用卡服务不可用”的错误,CEO面对大量未完成的订单表示很不开心——这个问题直接影响了营收。Ash立刻尝试改进,并要求订单履约团队重试支付操作,但他们有更紧迫的问题亟待解决,并不愿意处理Ash的问题(你会在第7章中读到一些内容,可以说明他们的拒绝是完全合理的)。
Ash保证会尽快解决这些问题并上线新版本。然后大家回到了各自的工位上,Ash创建了一个数据库表,名为payment,其中有一列名为status。每个支付请求都会追加到表里,status为open。此外,Ash还添加了一个简单的调度逻辑,每隔几秒钟会检查一次,对未支付的记录进行处理。现在,服务可以在更长的时间跨度中进行有状态的重试。看起来问题解决了。因为支付现在改为了异步处理,所以Ash打电话给履约团队的同事,和他们讨论API中需要进行的修改。最初被调用的REST API将返回HTTP 202(Accepted)响应,之后有两种选择,一是Ash的服务通过回调的方式向履约服务发送消息,二是履约服务定期轮询支付状态。履约团队同意暂时使用轮询进行快速修复,所以Ash只需要提供另一个REST接口来做支付状态查询。
这个修改发布到生产环境后,解决了CEO所关注的问题,Ash很开心。但平静的时光并未持续太久。一群人来到了Ash的办公室,其中还有运营总监。他们告诉Ash,现在一点货都没发出,因为没有一笔成功支付的订单。怎么会有这种事情?Ash在心里记下,之后要添加一些监控,以免将来再发生类似的情况而不知,然后去查看了数据库,发现未支付订单堆积如山。在日志中,Ash发现调度器被一个异常情况打断后崩溃了。Ash有一点慌。
Ash单独处理了那个中断整个流程的异常支付,然后重启了调度器,眼看着支付流程回归正常。Ash松了一口气,他发誓要密切关注这个服务,并编写了一个小脚本来定期查询数据表,在发生异常情况时会发送邮件告警。Ash还决定为该脚本添加一些特殊情况的缓解策略。完美!
在经历了这几周的跌宕起伏后,Ash计划去度假。但显然,老板并不希望Ash就此休息,因为除了Ash外,没有人真正理解他们刚刚搭起的工具栈。更糟的是,老板还拿出了一张表,上面列出了对支付服务的新需求。原因是一些业务同事听说了那个脆弱的信用卡服务,他们希望获得关于这个服务可用性和响应时间的深入报告。他们还想知道商定好的服务级别协议(SLA)是否真的被满足了,而且希望能有实时的监控。现在,Ash不得不给数据库添加一个生成报表的功能,而这个数据库在最初设计中似乎并没有存在的必要。图1-1展示了美妙架构中衍生出的混乱。
图1-1:荒野大集成——这是一种常见的混乱,你会在大多数企业中看到它的身影
很遗憾,Ash刚刚使用的正是一种非常常见的流程自动化方法,我称之为荒野大集成。这是一种临时方案,创建的系统没有任何管理方案。这样创建的系统很可能对整个业务都有不好的影响。
下面是荒野大集成的一些特点:
通过数据库集成
服务直接访问其他服务的数据库来进行通信,其他服务通常并不会被通知。
简单的点对点集成
两个组件之间会直接通信,通常是基于REST、SOAP或消息协议,但没有充分描述远程通信的所有内容。
数据库触发器
每当你向数据库写入内容时,数据库都会再调用其他逻辑。
脆弱的工具链
例如,通过FTP传输逗号分隔的(CSV)文本文件。
Ash需要自己编写的大量代码事实上是工作流引擎的内置功能:维持当前状态、调度重试、报告当前状态和操作长期运行的流程。与其自己编写代码,不如利用现有工具。自己迭代解决方案实在没什么优势。即使你认为项目还不需要引入工作流引擎的复杂特性,也应该再想想。
不用工作流引擎来编写流程通常会产生复杂的代码,状态处理最终与组件本身耦合。这会使代码中业务逻辑和业务流程的实现更难以理解。
Ash的故事也很容易发展为一个自研的工作流引擎。这种专门针对公司做的解决方案带来了更多的研发和维护工作,甚至还缺乏现有工具所能提供的优势。