1.简介随着分布式系统和微服务的不断发展,系统开发和运维对可观察性的需求越来越多更紧迫。术语可观察性[1]的起源最初是从控制理论中借用的。当我们谈论可观察性时,通常会提到以下三个方面:LinkTracingIndicatorMetricsLogLogging这三个并不是完全独立的概念,而是相辅相成的。说到这三个方面,我们总是不得不提到PeterBourgon的文章[2],以及其中最经典的维恩图:2.CollectMoney监控系统的历史发展。CollectMoney,2017年开始逐步搭建应用监控系统,系统建设的主要方向是提供链路追踪(Tracing)和性能监控(Metrics)的能力。在监控系统的选择上,我们尽量使用开源系统:Tracing,我们选择Zipkin[3],它是Twitter开源的,为我们提供链接跟踪的后端系统,使用Elasticsearch作为后端存储用于追踪。Metrics在Tracing数据的基础上,我们通过消费Kafka的Zipkin格式的数据聚合获得分钟级的指标。时序数据简单的使用MySQL作为后端存储。在接入层,我们用最原始的方式提供了各种检测工具包,供各种Java模块和组件嵌入。业务研发同学以pom依赖的形式引用自己的业务服务,如:通过MySQLDriver提供的拦截器机制[4]采样MySQL数据库请求;通过封装新的JSON-RPC包实现RPC层的埋点;通过Spring的HandlerInterceptor[5]接口拦截实现Rest风格;通过SpringAOP访问Redis的链接集合;...该系统支持我们度过了业务发展最快的时期,为大量的故障排除和故障诊断提供了一些线索。但是,业务开发逐渐开始对这个系统不满意,主要有以下几个方面,1)由于我们前期使用MySQL作为底层时序数据的存储,这在当时似乎是一个主流方案时间[6],但我们遇到了一个很大的性能问题。毕竟,MySQL等数据库提供的存储引擎并没有针对这种场景进行优化[7]。同时,MySQL也没有为时间序列提供丰富的查询操作。PgSQL9.6.2数据插入吞吐量作为表大小的函数[8]。在链路跟踪或应用程序监控的场景中,我们需要的是高吞吐量和线性性能[9]。同时,我们还需要增加数据生命周期管理的功能:因为随着新数据的写入,历史数据会随着时间的推移而贬值。2)由于我们需要将Tracing数据中的数据倒过来得到Metrics数据,所以我们“神奇地改变”了Zipkin传输部分的逻辑,在客户端聚合了所有未采样数据(Unsampled)并批量上报,这就导致我们的Zipkin升级出现了很大的困难。特别是在https://github.com/openzipkin/zipkin/pull/1968,以后将不再允许用户自定义开发服务器。3)业务方的升级依赖需要采集器组件的升级支持,导致额外的工作量。同时,也有大量的组件难以通过这种侵入的方式支撑,或者需要大量的人力成本进行研发和适配。3.新一代应用监控系统——Hera基于以上原因,我们决定开发一个新的系统,同时满足几个条件:存储成本低:可以低成本存储长期数据,并且可以存储至少四个星期的指标。对于一周的链接存储,这让我们排除了ElasticSearch的选择;实时查询性能高,灵活性高:不再使用MySQL等关系型数据库作为时序存储,使用Prometheus或Prometheus兼容的存储系统;优化研发效率:利用字节码编织技术埋点无侵入,与DevOps流程结合更紧密。1.链接跟踪分布式链接跟踪的概念和心智模型主要受谷歌2010年发表的Dapper论文[10]的影响。在Dapper论文中,作者明确指出了Trace的树形结构:我们倾向于将Dappertrace视为嵌套RPC的树。并提出了所谓Span的概念:在Dappertracetree中,树节点是基本的工作单元,我们称之为span。边缘表示跨度与其父跨度之间的偶然关系。在Dapper链接树中,每个跨度之间存在因果关系和时间关系。在链接跟踪系统选择方面,我们对比了当时活跃的几个开源项目:ZipkinApache/Skywalkingv6.6.0Jaegerv1.16Jaeger是Uber[11]2016年开源的链接跟踪平台,捐赠致CNCF云原生基金会。Jaeger的主要组件及控制流、数据流图,其中使用Kafka作为缓冲管道。Jaeger得到了开源社区的广泛支持,例如:Istio[12]原生支持使用Jaeger来增强ServiceMesh服务网格的可观察性;服务网格Envoy[13]的数据平面实现支持使用Jaeger作为链接跟踪服务提供商;...1)链接跟踪后端系统和存储的选择我们关注它们对存储系统的支持和扩展能力。①各种开源链接追踪实现的存储能力Jaeger社区具有优秀的存储扩展性,提供基于gRPC的插件机制[14]方便自定义扩展。+--------------------------------++--------------------------+|||||+------------+|unix套接字|+------------+||||||||||jaeger组件|grpc-client+--------------------->grpc-server|插件实现||||||||||+------------+||+------------+||||+------------------------------------++---------------------------+parentprocesschildsub-process关于存储的具体选择,我们注意到阿里云SLS可以支持linktracking的后端,并且官方提供了一个实现https://github.com/aliyun/aliyun-log-jaeger,我们内部基于这个思路实现了gRPC插件版SLS后端实现目前在生产环境稳定运行存储周期:SLS可提供长达30天的存储周期。存储容量:一天存储的Spans数量超过4亿,使用存储空间约6TB。性能:SLSQuery接口条件查询,3-5s返回结果。成本:日成本约70元,年约2万元(约两台8U32GECS年付价)。Jaegeroperator在https://github.com/jaegertracing/jaeger-operator/pull/1517引入了对gRPC插件的原生支持,gRPC插件可以作为InitContainer[15]将插件二进制文件复制到共享的EmptyDir存储卷。同时我们也积极回馈社区,为社区提供gRPC插件的自观察功能(SelfObservability):aeger-grpc插件支持opentracing上下文传递:https://github.com/jaegertracing/jaeger/pull/2870go-plugin插件支持参数配置:https://github.com/hashicorp/go-plugin/pull/1682)业务端接入优化SkyWalking的美妙之处不仅在于不仅在于其强大的功能,还在于其出色的代码实现[16]。过去,我们使用侵入式方法来提供应用程序监控访问。监控服务商需要为各业务方提供插件和模块,版本兼容需要付出很大的努力。这种方式缺乏统一的方面和工作机制,需要将各个组成部分一个一个“打散”。Skywalking是华为吴胜等人于2015年开源的一款APM产品,现已成为Apache的顶级项目。Skywalking-Java利用字节码增强技术,提供非侵入式链接埋点,大大降低了成本。在Java中,常用的字节码工具有以下几种。ASM和BCEL属于LowLevel,而CGLib、Javassist和ByteBuddy比较好用。关于字节码技术的具体分析可以参考StackOverflow上的回答[17]。其中,ByteBuddy的易用性和性能均达到一流水平:ByteBuddy官方提供的性能测试结果。为了充分利用Skywalking-Java提供的插件,我们在OpenTracing接口上实现了一整套Skywalking链路追踪模型。具体来说,Skywalking的链接跟踪语义包括三层:①Skywalking中的Trace类似于OpenTracing语义中的Trace②Skywalking中的Span类似于OpenTracing语义中的SpanEntrySpan:相当于OpenTracingSpan中的Kind=Consumer或Server;ExitSpan:相当于OpenTracing中Kind=Producer或Client的Span;LocalSpan:不属于以上两种的其他类型。③Skywalking增加了一层Segment的概念。一个Segment被约束在一个线程上,它包含的所有AbstractTracingSpan都在这个线程上创建和销毁。这里SegmentID对应OT中的SpanID,Skywalking中的Spans按照创建顺序从0开始编号。当然,模型也有区别:跨线程OpenTracing的标准要求实现者将Span设计成线程安全的,因为Span是允许跨线程传递的。在Skywalking中,跨线程是通过对当前段[18]进行快照来实现的,而Span在大多数场景下不需要保证线程安全。AsynchronousSpan主要用于记录异步操作真正开始和结束的时刻。以SpringReactive为例[19]:用户编写的Controller返回一个可执行的任务(通常是Mono类型)而不是最终结果,Dispatcher会通过线程池执行任务,所以我们需要记录的内容实际上,这个请求就是任务创建到被“计算”完成的整个循环。这部分的实现在OpenTracing标准中没有提及。这种机制在Skywalking的多个插件中都有使用,比如Redis客户端Lettuce、SpringWebflux、ApacheAsyncHttpClient等。我们通过在OpenTracing接口上实现与Skywalking相同的语义,实现几乎零成本的移植和使用其所有插件.我们在使用Skywalking-Java的过程中也发现了很多问题,并积极向社区反馈,做出了一些贡献,主要包括:JSON日志格式的实现:https://github.com/apache/skywalking/pull/5357SpringKafka1.x插件:https://github.com/apache/skywalking/pull/5879SpringDevTools支持和多类加载器优化:https://github.com/apache/skywalking/pull/6973JedisTransaction支持:https://github.com/apache/skywalking-java/pull/573)服务依赖分析服务依赖分析一直是公司内部业务发展急需的功能。对服务能力规划、问题诊断、服务强度等具有重要作用,在性依赖判断中具有实用价值。在Jeager社区的实现中,推荐在生产中使用Spark批处理[20]来实现全局依赖分析。还有基于Flink的实时处理[21],但已经不在维护状态了。为了实现这个功能,我们使用ApacheFlink,通过消费Kafka中的链接数据,实时计算服务之间的依赖关系,转换成Tuple格式
