有些场景中,APM 特指上面三个中的 Metrics,我们这里不去讨论这个概念这节我们先对这 3 个方面进行介绍,同时介绍一下这 3 个领域里面一些常用的工具。
题外话,Elastic 其实除了 Logs 以外,也提供了 Metrics 和 Traces 的解决方案,不过目前国内用户主要是使用它的 Logs 功能。2、我们再来看看 Traces 系统,它用于记录整个调用链路。
但是这真的是一个好事吗?值得大家去思考一下。两个方面,一个是对客户端的性能影响,另一个是服务端的压力。其次,Traces 系统因为有系统间调用的数据,所以很多 Traces 系统会使用这个数据做系统间的调用统计,比如下面这个图其实也蛮有用的:
科普:Prometheus 是一个使用内存进行存储和计算的服务,每个机器/应用通过 Prometheus 的接口上报数据,它的特点是快,但是机器宕机或重启会丢失所有数据。Metrics 和 Traces
Grafana 是一个好玩的东西,它通过各种插件来可视化各种系统数据,比如查询 Prometheus、ElasticSearch、ClickHouse、MySQL 等等,它的特点就是酷炫,用来做监控大屏再好不过了。
注意:下面的图都是我自己画的,不是真的页面截图,数值上可能不太准确下图示例 transaction 报表:
Message 中其他的字段:timestamp 表示事件发生的时间;success 如果是 false,那么该事件会在 problem 报表中进行统计;data 不具有统计意义,它只在链路追踪排查问题的时候有用;businessData 用来给业务系统上报业务数据 ,需要手动打点,之后用来做业务数据分析。Message 有两个子类 Event 和 Transaction ,区别在于 Transaction 带有 duration 属性,用来标识该 transaction 耗时多久,可以用来做 max time, min time, avg time, p90, p95 等,而 event 指的是发生了某件事,只能用来统计发生了多少次,并没有时间长短的概念。
下面简单介绍几个 tree id 相关的内容,假设一个请求从 A->B->C->D 经过 4 个应用,A 是入口应用,那么会有:介绍完了 tree 的内容,我们再简单讨论下应用集成方案。
1、总共会有 4 个 Tree 对象实例从 4 个应用投递到 Kafka,跨应用调用的时候需要传递 treeId, parentTreeId, rootTreeId 三个参数;
2、A 应用的 treeId 是所有节点的 rootTreeId;
3、B 应用的 parentTreeId 是 A 的 treeId,同理 C 的 parentTreeId 是 B 应用的 treeId;
4、在跨应用调用的时候,比如从 A 调用 B 的时候,为了知道 A 的下一个节点是什么,所以在 A 中提前为 B 生成 treeId,B 收到请求后,如果发现 A 已经为它生成了 treeId,直接使用该 treeId。
大家应该也很容易知道,通过这几个 tree id,我们是想要实现 trace 的功能。
通常来说,做 Traces 的系统选择使用 javaagent 方案比较省心,因为这类系统 agent 做完了所有需要的埋点,无需应用开发者感知。最后,我再简单介绍一下 Heartbeat 的内容,这部分内容其实最简单,但是能做出很多花花绿绿的图表出来,可以实现面向老板编程。
再介绍细一些。核心结构是一个 Map,key 类似于 “os.systemLoadAverage”, “thread.count” 等,前缀 os,thread,gc 等其实是用来在页面上的分类,后缀是显示的折线图的名称。关于客户端,这里就介绍这么多了,其实实际编码过程中,还有一些细节需要处理,比如如果一棵树太大了要怎么处理,比如没有 rootTransaction 的情况怎么处理(开发者只调用了 Dog.logEvent(...)),比如内层嵌套的 transaction 没有调用 finish 怎么处理等等。
科普时间:Disruptor 是一个高性能的队列,性能比 JDK 中的 BlockingQueue 要好这里我们使用了 2 个 Disruptor 实例,当然也可以考虑使用更多的实例,这样每个消费线程绑定的 processor 就更少。我们这里把 Processor 绑定到了 Disruptor 实例上,其实原因也很简单,为了性能考虑,我们想让每个 processor 只有单线程使用它,单线程操作可以减少线程切换带来的开销,可以充分利用到系统缓存,以及在设计 processor 的时候,不用考虑并发读写的问题。
你仔细想想就会发现,前面几个数据的计算都没毛病,但是 P90, P95 和 P99 的计算是不是有点欺骗人啊?其实这个问题是真的无解的,我们只能想一个合适的数据计算规则,然后我们再想想这种计算规则,可能算出来的值也是差不多可用的就好了。另外有一个细节问题,我们需要让内存中的数据提供最近 30 分钟的统计信息,30 分钟以上的才从 DB 读取。然后做上面介绍的 merge 操作。
讨论:我们是否可以丢弃一部分实时性,我们每分钟持久化一次,我们读取的数据都是从 DB 来的,这样可行吗?比如我们要统计最近一小时的情况,那么就会有 30 分钟的数据从各个机器中获得,有 30 分钟的数据从 DB 获得,然后做合并。
不行,因为我们的数据是从 kafka 消费来的,本身就有一定的滞后性,我们如果在开始一分钟的时候就持久化上一分钟的数据,可能之后还会收到前面时间的消息,这种情况处理不了。
之前我们在客户端介绍的时候说过,这几个 tree 通过 parent treeId 和 root treeId 来组织。要做这个视图,给我们提出的挑战就是,我们需要保存全量的数据。
大家可以想一想这个问题,为啥要保存全量数据,我们直接保存被 sample 到的数据不就好了吗?这里我们用到了 Cassandra 的能力,Cassandra 在这种 kv 的场景中,有非常不错的性能,而且它的运维成本很低。
欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) | Powered by Discuz! X3.4 |