业务实时监控的统一抽象

1,116 阅读8分钟

分层

<img src="https://pic4.zhimg.com/v2-354ebb118f6c376312e34b74463008bb_b.png" data-rawwidth="1129" data-rawheight="783" class="origin_image zh-lightbox-thumb" width="1129" data-original="https://pic4.zhimg.com/v2-354ebb118f6c376312e34b74463008bb_r.png">

监控可以分成这么三层。每层有完全不同的视角:

  • 业务层:任意的商业模式都可以建模为一个流程,流程都可以建模为一个单据流的形式。在营销的领域,我们称之为funnel analysis。
  • 服务层:业务逻辑都是由各种网络服务提供的。这些网络服务之间用rpc的方式,或者队列的方式进行连接。
  • 资源层:按照 brendan gregg 的教诲,所有的事务都可以建模成锁。资源是客观存在的,多个不同的流程都会去争抢这些锁。比如数据库表,比如文件系统,比如irq中断,比如cpu锁。我们可以把监控对象建模为一层层的锁,来分析资源的争抢情况。比如db的慢查询监控。

业务层

业务层监控的目标:业务流程是不是在正常运转?有没有可以优化转化的环节?

假设我们有这样的一个业务逻辑。客户先询问一下价格,然后客户再用给定的价格下单。我们保证成单用的价格就是用户之前看见的。

<img src="https://pic3.zhimg.com/v2-d99017b4b9eda3ded8a65b42f627bb9e_b.png" data-rawwidth="485" data-rawheight="221" class="origin_image zh-lightbox-thumb" width="485" data-original="https://pic3.zhimg.com/v2-d99017b4b9eda3ded8a65b42f627bb9e_r.png">

一种实现方式是我们把价格作为“询价”这个业务操作的返回值。由客户端保存,然后在客户决定进行“下单”这个业务操作的时候再带上来。考虑到客户端可能有可能被篡改,所以我们会用一些私钥加密的方式或者电子签名的方式来保证这份报价信息不是伪造的。

<img src="https://pic1.zhimg.com/v2-66f5ece001ea19be9e0176e98b60cbbc_b.png" data-rawwidth="590" data-rawheight="245" class="origin_image zh-lightbox-thumb" width="590" data-original="https://pic1.zhimg.com/v2-66f5ece001ea19be9e0176e98b60cbbc_r.png">

另外一种实现方式是,我们在询价的时候把报价单作为数据库记录保存。并把报价单id返回给客户端,然后再下单的时候提供报价单id。

从这个例子我们可以看到。无论是rpc的返回值,还是rpc中写数据库。其实都是一种保存交易双方合同的一种方式,我们称之为单据。所以无论是用客户端传递,还是走数据库。抽象上来说,前面的业务流程,都是这样的单据流

<img src="https://pic3.zhimg.com/v2-a24c74a5656eeb21c0ef790878e0df3e_b.png" data-rawwidth="513" data-rawheight="144" class="origin_image zh-lightbox-thumb" width="513" data-original="https://pic3.zhimg.com/v2-a24c74a5656eeb21c0ef790878e0df3e_r.png">一般来说,一个业务操作的处理,除了依赖 前序单据+本次操作的用户输入之外,经常还依赖其他的业务单据和状态。也就是单据和操作的关系是多对一的,复杂的操作需要很多的输入进行综合决策。

一般来说,一个业务操作的处理,除了依赖 前序单据+本次操作的用户输入之外,经常还依赖其他的业务单据和状态。也就是单据和操作的关系是多对一的,复杂的操作需要很多的输入进行综合决策。

<img src="https://pic3.zhimg.com/v2-eaa02872f6e88aebd7632b6545852d2e_b.png" data-rawwidth="750" data-rawheight="195" class="origin_image zh-lightbox-thumb" width="750" data-original="https://pic3.zhimg.com/v2-eaa02872f6e88aebd7632b6545852d2e_r.png">当我们有一个单据的时候,经常界面上会展示多个“call-to-action”。比如展示报价单的时候,可能提供一个按钮是下单,另外一个按钮是放弃。

当我们有一个单据的时候,经常界面上会展示多个“call-to-action”。比如展示报价单的时候,可能提供一个按钮是下单,另外一个按钮是放弃。

<img src="https://pic1.zhimg.com/v2-80c0dc8376fc7a1262544d2e3584bcfc_b.png" data-rawwidth="415" data-rawheight="748" class="content_image" width="415">比如我们在“已完成订单”的这个单据的展示页上可以,继续发出“再来一单”,“投诉”,“评价”这么三个业务操作。这些操作会把这个“已完成订单”作为参数,也会把我这次提供的评价信息作为参数,也会把我这个“已登录用户”,“已注册用户”这两个单据作为参数传递进去。

比如我们在“已完成订单”的这个单据的展示页上可以,继续发出“再来一单”,“投诉”,“评价”这么三个业务操作。这些操作会把这个“已完成订单”作为参数,也会把我这次提供的评价信息作为参数,也会把我这个“已登录用户”,“已注册用户”这两个单据作为参数传递进去。

<img src="https://pic4.zhimg.com/v2-9703d6bed40f4e53c5c89ae95f8d8dbb_b.png" data-rawwidth="462" data-rawheight="335" class="origin_image zh-lightbox-thumb" width="462" data-original="https://pic4.zhimg.com/v2-9703d6bed40f4e53c5c89ae95f8d8dbb_r.png">

有了这样一个模型。我们就可以统一地来分析各种业务:

  • 已完成订单,选择“再来一单”,“评价”,“投诉”。我们可以来度量不同业务操作被选择的概率。这种衡量 call-to-action 的转化率的做法,在营销入口是非常重要的
  • 业务操作的重试率:可以用于衡量用户是不是遇到问题了。如果一个司机在反复点结束订单,那么肯定是结束订单这个操作出问题了。
  • 业务操作的失败:比如设置密码失败,因为密码太简单了。通过分析这些失败的占比,可以用于发现我们的业务规则是不是太苛刻了。
  • 从已完成订单到被评价订单:我们可以衡量单据之间的转化率。比如呼叫应答率。这个也是营销领域里很重要的“漏斗”的概念,所谓funnel analysis。
  • 询价依赖于定价策略和产品:通过分析一个业务操作依赖哪些数据,可以方便我们快速知道一个问题单据是由哪些因素引起的。比如不合理的定价策略被通过了,第二天所有的询价单都异常了。

像顾客留存这样的非实时监控分析就不在这里讨论了。本文只讨论如何监控实时的健康状况。业务层的监控样式,最常见的是这样的。<img src="https://pic4.zhimg.com/v2-50371e86cc702cc5591bb574f3044373_b.png" data-rawwidth="550" data-rawheight="460" class="origin_image zh-lightbox-thumb" width="550" data-original="https://pic4.zhimg.com/v2-50371e86cc702cc5591bb574f3044373_r.png">

服务层

服务层监控的目标:出了一个问题,哪个模块提供的线上服务应该为这个负责?

这种rpc调用链的监控,应该是最常见的监控形式了。这些网络服务之间用rpc的方式,或者队列的方式进行连接。网络监控(丢包率之类的)也可以建模成rpc监控。

<img src="https://pic2.zhimg.com/v2-690c53a852565d54c02a8814bc913549_b.png" data-rawwidth="2558" data-rawheight="1598" class="origin_image zh-lightbox-thumb" width="2558" data-original="https://pic2.zhimg.com/v2-690c53a852565d54c02a8814bc913549_r.png">

如果是rpc直接调用,那么就是

  • 主调方
  • 被调方
  • 失败率
  • 错误码
  • 延迟
  • qps

这么几个方面。如果是中间走队列。差不多就是把rpc endpoint替换成topic。队列额外还需要监控一下端到端延迟,从入队列到出队列的per message的延迟。

资源层

资源层监控目标:性能有问题了,瓶颈在哪里?知道所有的瓶,方知哪里有颈。

这一层很好理解,大家多看看 brendan gregg 的文章就好了。任何业务都可以按照资源依赖来建模,总是最后会卡在某个资源的瓶颈上。通过对资源依赖关系进行建模和分析,我们可以很容易定位到任何性能问题。我们常见的cpu监控就是按资源视角的监控。

<img src="https://pic3.zhimg.com/v2-9ee6c1c5d88b0468af1a3280865a6b7a_b.png" data-rawwidth="3000" data-rawheight="2100" class="origin_image zh-lightbox-thumb" width="3000" data-original="https://pic3.zhimg.com/v2-9ee6c1c5d88b0468af1a3280865a6b7a_r.png">

一层层的依赖,一层层的瓶颈。所有有资源争抢的地方,都可能影响性能。这种依赖举两个例子:

  • 聚划算的业务,和天猫的业务,可能用了同一个订单系统。业务之间互相争抢资源。
  • 数据库的前台查询,以及机器上的监控脚本,可能并发地在做linux网络操作,两者之间导致了资源的争抢。

<img src="https://pic2.zhimg.com/v2-37bfce7d76cb3d8d394a102d88f29bb5_b.png" data-rawwidth="638" data-rawheight="491" class="origin_image zh-lightbox-thumb" width="638" data-original="https://pic2.zhimg.com/v2-37bfce7d76cb3d8d394a102d88f29bb5_r.png">

任何一个资源都建模成一个锁。对于这个锁的争抢,我们度量以下指标(USE Method):

  • utilization: the average time that the resource was busy servicing work
  • saturation: the degree to which the resource has extra work which it can't service, often queued
  • errors: the count of error events

举几个例子:

  • mutex locks: utilization may be defined as the time the lock was held; saturation by those threads queued waiting on the lock.
  • thread pools: utilization may be defined as the time threads were busy processing work; saturation by the number of requests waiting to be serviced by the thread pool.
  • process/thread capacity: the system may have a limited number of processes or threads, the current usage of which may be defined as utilization; waiting on allocation may be saturation; and errors are when the allocation failed (eg, "cannot fork").
  • file descriptor capacity: similar to the above, but for file descriptors.

总结

我相信世界是简单的。复杂的活动背后可以归纳为几种类型。我们也许不需要一个支持SQL和map reduce的日志处理系统。我们也许更需要的是一个提供几种通用监控模型的监控系统,按照需求,把数据按照模型的要求进行上报(比如 mixpanel 那样)就可以获得所有后续的服务,什么看图啦,什么监控啦,什么告警啦。