Skip to content

Commit

Permalink
auto-archiving for issue #254
Browse files Browse the repository at this point in the history
  • Loading branch information
kaola-blog-bot committed Apr 3, 2018
1 parent 999f869 commit 8b00cda
Showing 1 changed file with 89 additions and 0 deletions.
89 changes: 89 additions & 0 deletions source/_posts/Trace,让你的应用服务调用可追溯.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
## 背景
随着考拉服务化进程的推进,我们的应用架构逐步由集中式向分布式演进。这个时代的服务化调用往往带有以下特点:
1. 一个完整的调用过程可能横跨多个服务;
1. 服务提供方可能由不同团队、不同语言实现;
1. 服务提供方本身是集群的方式支撑;

![image](https://user-images.githubusercontent.com/10825163/38185682-ea81e494-3681-11e8-9e36-d345e962793f.png)

在这种场景下,对于我们服务调用的性能监控和异常监控显得尤为重要。

## Trace 的目标
出于对服务调用的性能监控和异常监控的根本性诉求,Trace 便应运而生了,细化以后的目标大概有这些:
1. 对应用运行时的服务调用,希望有一个直观的认识。
1. 当这些复杂的调用出现问题时,希望可以快速定位到是哪个服务出现了问题。
1. 当用户接口返回速度变慢时,希望可以快速得到出各个接口的耗时请情况。

## Trace 架构及流程
![image](https://user-images.githubusercontent.com/10825163/38196737-3a166888-36b7-11e8-8ae6-5afbd5bbd0b2.png)

我们对以上调用关系进行分析,可以得到以下结论:
* 他是一个有向图,且顶点之间的边是单向的;
* 顶点 0 是这个图的入口;
* 服务调用存在发送方和接收方,使用深度优先遍历可以将边编号;

我们可以有一个基础的 trace 设计思路了

### 流程
Trace 想要正常运转,需要以下步骤:

1. 上游服务接收到用户请求时,生成此次用户调用的唯一标示;
1. 所有调用的发起和处理全部生成调用信息;
1. 在当前端的所有调用都完成后,统一将生成的调用信息发送给消息队列;
1. 消息中心分析处理日志,将来自不同分布式应用的日志串联成 Trace 日志,入库;
1. 提供可视化界面,供用户查看数据;

### 架构
完成以上的流程,通常来说,需要三个角色

![image](https://user-images.githubusercontent.com/10825163/38228220-e5e2bdce-3734-11e8-9bef-aed0b4fba5f4.png)

1. trace 客户端,一般是 web server,也有可能是 service 服务提供方,负责生成调用信息,并往消息队列发送消息;
1. 消息队列,接收来自 trace 客户端生产的消息,并提供给 trace 服务端消费
1. trace 服务端,消息队列的消费者,会将来自所有 trace 客户端的消息生成成调用链,并入库

### 数据结构
在分布式系统中,用户的一次URL请求在各个系统中调用,这个调用关系会呈现成树形调用链。 Span记录了调用信息,Trace客户端会把Span记录在日志中。散落在各个系统的Span按唯一标识组装起来变形成了一颗树。

#### Trace
Trace 代表一个完整的调用链,对应到如图的 0 节点,往往是面向用户的顶层应用,具有唯一的 traceId,生成 Span 时需要用到。

#### Span
Span记录了调用信息,散落在各个系统的Span按唯一标识组装起来变形成了一颗树。Span中主要信息包含:

* traceId 整个调用链唯一的traceId,traceId在root节点生成。在分布式调用用通过技术手段传递到下一个节点。
* spanId 该节点的标识,在分布式调用用通过技术手段传递到下一个节点作为下一个节点的parentId。
* parentId 调用链中父节点的spanId。
* spanName 该节点的名字,如URL Pattern、Dubbo service name、sql
* 该节点的处理时间等


值得注意的是:
1. 需要 Server Span 和 Client Span 都生成的情况下,才能记录为一次合法的调用;
1. 同一次调用的 Client Span 和 Server Span 会拥有一致的 spanId 和 processId

## 使用 node 实现 trace 客户端
有了以上知识作为基础后,我们来思考如何用 nodejs 来实现这个 trace 的客户端。

![image](https://user-images.githubusercontent.com/10825163/38228205-cba3bb52-3734-11e8-824b-4bf9499daa00.png)


上图可以理解为一个 koa 的 middleware:

1. 当用户请求达到时,初始化一个 traceContext
1. 当前存在服务调用时,使用该 traceContext 去发起相关方法的调用操作,如: traceCurl、traceService 等等,当调用完成时手动执行 span.complete(),并将 error 信息传入
1. 当该用户请求完成时,调用 traceContext.complete() 终止此次 trace,并将该 traceContext 发送给消息队列

## 接入 Trace 后需要了解的
### 采样率
实际在生产环境,我们并不需要统计所有的服务调用,这会带来较高的统计成本,所以我们支持使用「采样率」来对一定比例的请求做跟踪。

比较优雅的方式是,你可能需要将这个「采样率」托管在 disconf 或者其他数据管理平台上

### 压测标识
Trace 期望监测的是线上的真实调用情况,所以对于压测场景,期望能做区分以排除此类数据,所以较为通用的方式是如果在 cookie 上传入某个字段,则表示为压测数据。

## 总结
以上阐述的 Trace 方案其实最终灵感都是来自于 Google Dapper。

而如何更为优雅的统计应用的服务调用,可能还需要不断地探索

0 comments on commit 8b00cda

Please sign in to comment.