zoukankan      html  css  js  c++  java
  • 饿了么:业务井喷时订单系统架构的演进

    摘要:饿了么是一家创业公司,业务发展非常快,可能准备不是很充分,比如说监控、日志、告警、框架、消息、数据库,很多基础设施还在建设之中。在这个过程中出现一些问题是在所难免的,对系统的要求不是不能挂、不能出问题,而是出了问题要第一时间能恢复。

    演进工作的核心就是一个字“拆”,跟“拆”对等的就是分治的思想。怎么拆分呢?面向服务有很多拆分原则。我将拆分过程中最具帮助和指导性的点罗列了以下几条:

    1 明确的定义  之前也确实犯了一些错误,为了拆而拆。其实我们需要更明确,什么才算是一个服务?服务一定具有非常独立的技术能力或者业务能力,而且一定意义上能够很抽象

    2松耦合  最基本的松耦合就是Customer的消费不依赖于Provider的某一个特定实现,这样服务器的内部变更不会影响外部消费,消费者可以切换到其他服务能力的提供方,这是最基本的松耦合。还有时间上的松耦合或者位置上的松耦合,我们希望的松耦合是消费方和服务方是可以分离的

    3基于领域认知 这对于整个产品起到非常大的作用。因为当时整个饿了么所有系统是在一起的,基于领域的认知,在面向用户的维度和面向商户的维度做了切分,还有基于交易链路做了切分

    4单一职责与关注分离   简单说,我们希望一个服务或者一个模块拥有单一的能力,而不是承担过多的职责,否则责任不清晰,导致能力也不清晰。

    5可被验证的结果 在订单拆分的过程中我们犯了一些错误,当时认为这样拆分是没有问题的,但是过一、两个月,并没有带来效率和能力的提升,反而是跨团队的要求越来越多,能力要求也越来越多。这时候可能是拆错了。如果是一个好的拆分一定有利于发展,拆分之后的发展是更迅速的。

    基于这几条原则,他们对饿了么的整体服务做拆分之后,如上图所示,架构就有了一些变化,看起来跟刚才架构区别不大。把Order Service做了分离。当时拆分虽然比较垂直,但是用户、商户、金融、订单等还是有一些横向交互。

    一个接口有一个非常明确的Owner,一个表、一个库也能保证仅有单一的操作方,让我感受比较直接的是,为服务的治理奠定了基础,以后可以针对某项特定业务做一些降级、熔断,以及单独的监控。拆分实际上是让各自模块的掌控力变得更强了,对业务起到更好的支撑作用。

    这时每个部门或者每个团队都负责自己独立的领域,代码和数据都拆分完毕是不是就可以了?但是后来发现好像还不对。因为虽然大的领域上确实已经干净了,但是在小的领域上依然问题很多,订单并不仅仅只有一张表,一个单一的模块,其实还有很多复杂的内容。

    在一些技术工作上,这些问题曝露得并不是那么明显,那时候大家对于一些领域认知或者业务边界的认识还是模糊的,没有人界定这些。但是当更进一步地去发展一个领域的时候,还是会有职责不清晰或者能力模糊的地方。我们思考,还要基于业务进行更细腻的规划。

    于是我们把订单本身做了一些业务层次的拆分,拆分之前首先要确认订单到底在整个系统中,尤其是交易系统、O2O系统中承担什么角色,担负什么职责。

    在这个思考过程中,我们的认知大概是以下四点

    第一,订单是整个交易链路的核心,围绕了一些相关服务,比如金额计算服务、催单服务、售中异常服务等,我们希望这些服务之间有明确的区别。

    第二,订单实时处理是整个链路的中心,我们将这个过程定义得尽量简洁。一笔交易中,订单被推进得越复杂,说明系统设计得越复杂,出问题的概率也会越高。所以我们希望订单核心流程非常简单、轻薄,把复杂的东西剥离出来,把简单和复杂明确成两个部分。

    第三,考虑到交易的时效性和异常场景越来越复杂,将交易分成正向交易流程和逆向交易流程两个部分。正向交易流程,99%的订单会根据这个流程走完生命周期;逆向交易流程,比如说退单要求时效性比较低,处理会牵扯多方业务可能很复杂,所以通过一个逆向的交易流程来解决。

    第四,能够在功能和业务上独立的部分,尽可能抽象为单独的模块或服务。简单来说,比如催单的服务,它其实对交易链路无法起到推进作用,它只是一个动作或者附带服务,我们把它单独抽象出来,为后面的发展做出铺垫。

    基于这些之后,我们对订单进行完整的认知,对订单的服务架构和业务架构做成图中的样子,大概是三层。下面一层是基本数据;中间层是正向逆向的流程、最核心的状态和最关联的交易链上耦合的服务;上层是用户服务、商户服务,包括跟交易链相关的,比如饿了么最近推出的“准时达”的服务。

    监控和告警的峰值非常明显,午间和晚间两个高峰,其他时间流量相对平缓。下面主要讲三个部分。

    第一,对于订单而言,吞吐量是最需要重点关注的指标。一开始对业务指标的感知并不是特别清晰,就在某一个接口耗费了很多时间。后来发现一些很小BD的问题不太容易从小接口感知到,但是从业务方面感知就比较明显,所以就更多关注业务指标的控制。

    第二,通常我们重视系统指标,而容易忽视业务指标,其实业务指标更能反映出隐晦的问题。

    第三,目前我们致力于基于监控和数据学习的过载保护和业务自动降级。虽然现在还没有完全做好,但是已经能感觉到一些效果。如果商户长时间不接单,用户会自动取消订单,自动取消功能的开关目前是人工控制的,我们更希望是系统来控制。

    比如说有大量订单取消了,有可能是接单功能出了问题,就需要临时关闭这个功能,如果还是依靠人来做,往往已经来不及,这时候就应该基于数据的学习,让系统自动降级这个功能。

    Kennel有四个主要的作用。

    首先,帮助我们发现链路中隐蔽的缺陷,将小概率事件放大。比如说缓存不一致的问题,之前极少出现,一旦出现之后,处理手段比较缺乏,那就可以通过Kennel来模拟。网络的抖动是很随机的,那么Kennel可以在某个时间段专门进行模拟,把小概率事件放大。如果怀疑某个地方出了问题,可以通过它来测试是不是真的能查出问题。

    第二,重大功能可以在发布之前通过其进行测试,迫使你更深入地设计和编码。通过模拟流量或者线上流量回放,来检验系统运行是否如你设计那样工作,比如监控的曲线或者告警以及相关服务之间的依赖等。

    第三,我们做了很多失败的准备和设计,要看到底会不会起作用、起多大作用。可以通过Kennel进行校验,在某个时间通过随机手段攻击相关服务,服务方不知道具体的攻击内容,这时原本设定的监控告警,降级熔断等措施有没有及时起作用就是一个很好的校验。同时还可以检验之前准备的容错或者补偿措施是否能按照预期工作。

    第四,需要验证FailOver的设计,只有验证通过才可以依靠。所有的设计都是经历了一次一次的失败,一些设计原以为有用,但是真实问题发生时并没有起到作用。真正有意义的FailOver设计一定是经过验证的。

  • 相关阅读:
    celery的使用和原理
    内核通知链
    数据流中的中位数
    二叉搜索树的后序遍历序列
    Javascript设计模式系统讲解与应用,JS设计模式详解
    微服务系列之ZooKeeper注册中心和Nacos注册中心
    微信小程序开发详解:小程序入门与实战-纯正商业级应用技术
    Java零基础该怎么去学习Java?学好Java应该如何去做?
    Flutter从入门到进阶实战携程网App项目详解
    Python升级3.6强力Django+杀手级Xadmin打造在线教育平台
  • 原文地址:https://www.cnblogs.com/z12568/p/10986754.html
Copyright © 2011-2022 走看看