分布式追踪系统的对比、实现与使用—NodeTracing

前言

在分布式领域,有一个十分使人头疼的问题,那就是分布式的追踪,日志与监控。由于服务部署在不一样的主机上,而实际的业务开发中,服务之间相互调用,尤为是伴随着服务微小化,单位化,微服务架构的趋势下服务数量激增 即使是有着良好的前期规划,可是中大型项目在实际开发中,依然会面临着服务关系错综复杂,问题追踪调试困难的等等问题javascript

现状

各种trace解决方案

那这个使人头疼的问题,是否已经有良好的解决方案了呢?答案是确定确又是否认的... 目前商用规模的分布式追踪方案主要有如下几类:html

  1. 非OpenTacing标准的分布式追踪系统
  2. 采用ServiceMesh架构的服务网格治理方案
  • envoy
  • istio ...
  1. 基于OpenTraing标准的嵌入式分布式追踪系统
  • zipkin
  • jaeger
  • skywalking ...

第1类是采用非标准化的追踪系统,通常为针对单一业务系统设计实现,不具有普遍通用性和持续维护性java

第2类是从架构层考虑使用服务网格治理的方案来进行分布式追踪,优势是功能强大,控制纬度广,追踪精度细,追踪覆盖面大。可是缺点也很明显,目前不管是第一代以envoy为表明的解决方案的ServiceMesh,仍是第二代以istio为表明的ServiceMesh,都极为重量,须要专门的架构和运维团队进行细致的前期安排和规划后方能实施,部署成本高昂node

第3类是采用OpenTracing标准的分布式追踪系统,OpenTracing是CNCF(大名鼎鼎的Cloud Native Computing Foundation)为了规范业界的分布式跟踪系统产品的统一范式,设计的trace标准,基于该标准,能够解决分布式追踪系统跨平台和兼容通用的问题。目前twitter、uber、apple等知名企业彻底遵循该标准设计trace系统。ios

各大厂商trace系统对比

分布式跟踪系统各种产品,根据设计目标和标准造成综合对比*(资料来源于互联网,非权威)*:git

产品名称 厂商 开源 OpenTracing标准 侵入性 应用策略 时效性 决策支持 可视化 低消耗 延展性
jaeger uber 开源 彻底支持 部分侵入 策略灵活 时效性高, UDP协议传输数据(在Uber任意给定的一个Jaeger安装能够很容易地天天处理几十亿spans) 决策支持较好,而且底层支持metrics指标 报表不丰富,UI比较简单 消耗低 jaeger比较复杂,使用框架较多,好比:rpc框架采用thrift协议,不支持pb协议之类。后端存储比较复杂。但通过uber大规模使用,延展性好
zipkin twitter 开源 部分支持 侵入性强 策略灵活 时效性好 决策通常(功能单一,监控维度和监控信息不够丰富。没有告警功能) 丰富的数据报表 系统开销小 延展性好
CAT 大众点评 吴其敏 开源 - 侵入性强 策略灵活 时效性较好,rpc框架采用tcp传输数据 决策好 报表丰富,知足各类需求 消耗较低 , 国内不少大厂都在使用 -
Appdash sourcegraph 开源 彻底支持 侵入性较弱 采样率支持(粒度:不能根据流量采样,只能依赖于请求数量);没有trace开关 时效性高 决策支持低 可视化太弱,无报表分析 消耗方面。不支持大规模部署, 由于appdash主要依赖于memory,虽然能够持久化到磁盘,以及内存存储支持hash存储、带有效期的map存储、以及不加限制的内存存储,前者存储量太小、后者单机内存存储没法知足 延展性差
MTrace 美团 不开源 - - - - - - - -
CallGraph 京东 不开源 - - - - - - - -
Watchman sina微博 不开源 - - - - - - - -
EagleEye 淘宝 不开源 - - - - - - - -
skywalking 华为 吴晟 开源 彻底支持 侵入性很低 策略灵活 时效性较好 因为调用链路的更细化, 可是做者在性能和追踪细粒度之间保持了比较好的平衡。决策好 丰富的数据报表 消耗较低 延展性很是好,水平理论上无限扩展

综合上分布式追踪系统对比github

1. jaeger对于go开发者来讲,可能比较合适一些,可是入手比较困难。它的rpc框架采用thrift协议,如今主流grpc并不支持。后端丰富存储,社区正在积极适配
2. appdash对于go开发者想搭建一个小型的trace比较合适,不适合大规模使用
3. zipkin项目,github很活跃,star数量不少,属于java系。不少大厂使用
4. CAT项目也属于java系, github不活跃,已不太更新了。不过不少大厂使用,平安、大众点评、携程...
5. skywalking项目, 也属于java系。目前已成为Apache下的项目了,github活跃,做者也很是活跃,当当、华为正在使用。
6. appdash项目,go语言开发,由于可视化过于简单、且彻底内存存储,不太适合大规模项目使用。
复制代码

以上除了闭源的分布式追踪系统,jaeger和zipkin还有skywalking是开源中不错的选择,可是,可能读者也发现了,以上的分布式追踪系统,对于Java平台的支持都很不错,可是对于其余平台的支持,就都比较有限了,尤为是低侵入代码的自动探针部分。并且,以上全部的分布式追踪系统,无一例外,生产部署(非简单体验)都十分复杂,有些甚至依赖于kubernetes。这让不少想要使用分布式追踪系统来解决实际问题的中小企业望而却步web

我很清楚的记得在去年,我疯狂的在寻找一个开箱可生产的分布式追踪系统,因而就有了本文以上部分,最终是以失败了结,很遗憾我没有寻找到任何一款开箱可生产的开源产品。可是,路是人走出来的嘛,既然已经有了OpenTracing的标准,那理论上任何人和任何组织均可以实现一套标准化的分布式追踪系统,由于产生了这个可怕的念头,因而就有了本文的下半部分——我决定造一个轮子,自行实现一套OpenTracing标准的分布式追踪系统,目标是开箱可生产docker

NodeTracing概览

我但愿,能够在上面的表格中插入一行:shell

产品名称 厂商 开源 OpenTracing标准 侵入性 应用策略 时效性 决策支持 可视化 低消耗 延展性
NodeTracing cheneyxu 开源 彻底支持 自动探针,几乎无侵入 策略灵活 时效性秒级 决策较好,且持续升级 包括服务拓扑图,跨度甘特图等在内的多种图表,且持续升级 消耗极低 无状态和关联架构,彻底容器化的追踪节点,可轻松简单集群化,采用内存与LevelDB配合的持久化存储,可应对十亿以上量级的span数据

最终,包括构思,架构,开发,测试等等两个多月的时间,终于完成了NodeTracing的初个开箱可生产版本!这是至今为止我我的最满意的做品,同时也指望NodeTracing在之后能够持续进化成为至少是NodeJS领域最好的分布式追踪系统,如下是架构图和演示demo

NodeTracing架构图
控制面板
服务拓扑图
跨度拓扑图
跨度列表
跨度甘特图

#NodeTracing使用 ##下载

git clone https://github.com/cheneyweb/nodetracing
cd nodetracing && npm i
复制代码

##快速开始&单例启动

cd server && npm run standalone
复制代码

##生产部署&集群启动

docker stack deploy --prune -c docker-compose.yml nodetracing
复制代码

**NodeTracing的部署就是这么简单!**根据须要选择单例启动/集群启动以后,打开浏览器访问: http://localhost:3636/nodetracing/web/index.html 即可以看到系统管理页,默认账号密码:admin/123456

##安装自动探针

npm i nodetracing
复制代码

探针初始化(在应用入口首行)

const nodetracing = require('nodetracing')
const tracer = new nodetracing.Tracer({
	serviceName: 'S1',      // 必须,服务名称
	rpcAddress: 'localhost',// 必须,后台追踪收集服务地址

	rpcPort: '36361',       // 可选,后台追踪收集服务端口,默认:36361
	auto: true,             // 可选,是否启用自动追踪,默认:false
	stackLog: false,        // 可选,是否记录详细堆栈信息(包括代码行号位置等,启用内存消耗较大),默认:false
	maxDuration: 30000      // 可选,最大函数执行时间(垃圾回收时间间隔),默认:30000
})
复制代码

由此便完成了nodetracing的加载工做,接下来您能够根据您的服务类型选择如下自动探针/手动探针...

async自动探针(支持async函数)

async function func1(){
	...
}
async function func2(){
	...
}
func1 = nodetracing.aop(func1)
func2 = nodetracing.aop(func2)
...
复制代码

http请求自动探针(axios)

axios.interceptors.request.use(nodetracing.axiosMiddleware())
复制代码

http响应自动探针(koa/express)

//koa
app.use(nodetracing.koaMiddleware())
//express
app.use(nodetracing.expressMiddleware())
复制代码

grpc-client自动探针(原生)

const grpc = require('grpc')
const Service = grpc.loadPackageDefinition(...)[packageName][serviceName]
new Service("ip:port", grpc.credentials.createInsecure(), { interceptors: [nodetracing.grpcClientMiddleware()] })
复制代码

grpc-server自动探针(原生)

const grpc = require('grpc')
const interceptors = require('@echo-health/grpc-interceptors')
let server = new grpc.Server()
server = interceptors.serverProxy(this.server)
server.use(nodetracing.grpcClientMiddleware())
复制代码

grpc-client自动探针(x-grpc框架

const RPCClient = require('x-grpc').RPCClient
const rpcClient = new RPCClient({
    port: 3333,
    protosDir: "/grpc/protos/",
    implsDir: "/grpc/impls/",
    serverAddress: "localhost"
})
rpcClient.use(nodetracing.grpcClientMiddleware())
rpcClient.connect()
let result = await rpcClient.invoke('demo.User.login', { username: 'cheney', password: '123456' } , optionMeta?)
复制代码

grpc-server自动探针(x-grpc框架

const RPCServer = require('x-grpc').RPCServer
const rpcServer = new RPCServer({
    port: 3333,
    protosDir: "/grpc/protos/",
    implsDir: "/grpc/impls/",
    serverAddress: "localhost"
})
rpcServer.use(nodetracing.grpcServerMiddleware())
rpcServer.listen()
复制代码

实现思路

简单的使用说明以后,下面重点讲解一下整个系统的实现方案 一个分布式追踪系统,由三大部分组成,分别是探针,追踪服务,可视化服务

  • 探针:嵌入与被监控追踪的服务之中,伴随服务启动而运行,须要追踪服务上下文,以及即时上报span给追踪服务
  • 追踪服务:独立部署,接收探针上报的span,计算分析处理后提供给可视化服务用做数据展现
  • 可视化服务:接收追踪服务的处理计算结果,可视化呈现数据报表,提供最终决策参考

实现难点

这三大部分,其实每个部分都是难啃的骨头,根据实践下来的经验,每一部分的难点以下:

  1. 手动探针对代码侵入至关大,几乎很难要求开发人员时刻谨记进行探针埋点,并且手动埋点也容易出错,容易形成结果呈现不正确
  2. 相对于手动探针,自动探针是更好的选择,可是自动探针实现难度高,且根据实现程度,支持覆盖面有限
  3. 追踪服务,如何集群化部署是在架构设计时就须要考虑的,尽量简单的部署是第一优先考虑
  4. 可视化服务,数据持久化方案如何选择?如何在简单部署和高性能可靠性之间平衡是难点
  5. 总体方案开发语言选择,高网络性能,高异步性能,低平台依赖,低部署难度是优先考虑,因此毋庸置疑,nodejs是最佳选择

设计&实现详解

NodeJS探针

OpenTracing API

首先针对探针的实现,OpenTracing的标准其实已经给出了接口,目前第一版优先考虑目前还没有有成熟分布式追踪系统的nodejs平台。因此第一步须要基于opentracing-javascript实现OpenTracing的API接口 在这一步中,很遗憾OpenTracing给出的官方文档实在有限,想要完整的实现全套API,必须耐下心来阅读OpenTracing的JS源码,没有别的办法

Async Hook

在实现OpenTracing的API以后,也仅仅是完成了手动探针的实现,由于OpenTracing其实只是指定了接口标准与追踪数据标准,并无提供自动探针的实现思路 因此,其实自动探针的实现实际上是依赖于各语言平台自由的特性。不过万变不离其中,任何想要实现自动探针的语言平台,就必定要实现AOP和上下文追踪,不然几乎不可能 由于首选支持nodejs平台,因此在nodejs上实现自动探针主要会依赖两个关键技术:

  • async hook
  • function merging

其中,async hook是node8.x以后版本推出的异步资源追踪方案,直至今日node11.x,已经初步成熟。利用async hook能够追踪全部异步资源关系,而这正是自动探针的必备条件

不过这里须要注意的是,nodejs目前没有同步资源的追踪方案(我没有找到,若是有读者知道有的,但愿能告知,不胜感激),因此,目前在nodejs中,目前仅能够对异步调用进行自动探针追踪。可是其实问题不大,由于nodejs中几乎全是异步资源,并且同步资源其实追踪意义不大

function merging主要是用于在nodejs平台中实现AOP,这里主要须要一些语法技巧来实现,AOP对于自动探针很是关键,有了AOP,才有跨服务追踪的可能

Java探针(规划中...)

追踪服务

追踪服务用于接收探针上传的span,很明显,若是追踪服务的架构设计很差,那这将会整个追踪系统的性能瓶颈。而一个APM监控系统,自己怎么性能瓶颈呢? 因此,追踪服务集群化是必然的。可是集群化必定会面临部署复杂难度高的问题,我不但愿每个使用NodeTracing的人都感概其难以部署,并且开箱可生产是制做NodeTracing的初心。 因此,在这一步的选择上,Docker Swarm是第一优先选择,Docker Swarm的极简优雅性实在使人印象深入,这一次,我依然决定使用容器集群来解决追踪服务的性能瓶颈问题

  • 首先,需求追踪服务可简单平行扩展,那就得要求每一个独立服务无状态,彼此之间无关联。在这里采用的是以前本身造好的轮子x-grpc框架。这是一个独立的grpc服务框架,很是适合于这种简单快速高性能的rpc数据吞吐
  • 而后,将追踪服务打包制做成镜像
  • 最后,咱们能够经过简单的一句docker stack命令,部署任意节点的追踪集群

可视化服务

到可视化服务这一步,已经最后的闯关了,这意味着span数据已经收集完毕,咱们须要考虑如何将其持久化存储和可视化展现提供决策 在这里最后的一道门槛是持久化存储,由于大规模数据的持久化存储方案通常都是很是复杂的,单点数据库性能有瓶颈,可是若是采用相似于分布式数据库之类的方案,会极大的提升部署难度。自己用户要安装自动探针,安装追踪服务已经要花些时间了,最后还须要部署一个分布式数据库? 很明显这里的平衡取舍是难点。可是所幸最终仍是找到了比较完美的解决方案,**那就是——LevelDB。这是一个谷歌实现的能支持十亿级别数据规模的kv数据库,特色是支持极高的写入吞吐。**它的做者是Jeaf Dean和 Sanjay Ghemawat,是谷歌的传奇工程师

LevelDB的发现让我很激动,由于它几乎就是为分布式追踪系统而生的,全部特效都极其符合追踪系统的持久化需求。**做为内嵌型数据库,能够跟随服务启动,无需额外部署,写入吞吐又极高。**在对LevelDB进行了基准测试后,我立马采用了它做为NodeTracing的持久化方案,由于它的性能表现实在太抢眼了

测试

在第一版完成后,针对NodeTracing的span吞吐作了一些基准测试:

  • 单个节点,双核i5+4G内存的追踪服务,可每秒处理一万个span(测试其实更可能是受限于网络速度)
  • 平行拓展到10个节点后,系统运行稳定(后续会进行更大规模节点部署测试)
  • 大约平均1GB容量能够持久化存储高达四百万个span(会根据实际运行的上下文大小的不一样而不一样,该值仅供参考)

参考资料

后记

感谢你的阅读,若是本文可以给你带来帮助,但愿你能在github上为NodeTracing点亮一颗Star:) 本文完成之际,NodeTracing刚刚也经过了OpenTracing的registry注册,能够前往搜索查看 本文篇幅有限,没有办法详细阐述NodeTracing全部配置和实现细节,有兴趣的读者可前往github阅读详情,也欢迎提出问题和看法

相关文章
相关标签/搜索