基于elastic APM的链路跟踪

徘徊边缘 提交于 2019-12-28 14:01:03

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

前言

今年上海团队开展了一个叫funlab的项目,小伙伴根据个人兴趣自愿报名参加,以项目主题的形式进行组队,利用业余时间来学习和研究一些热门的主题(Profiling, APM, Data Science(Statistics & Machine learning)),最后能够将这些研究的成果应用到我们实际的工作项目中。

我加入到APM(Application Performance Management)学习小组,组长和大部分成员是我们性能组的小伙伴,初期大家对现有APM技术,工具的学习和调研,最后选定开源的elastic APM作为我们第一阶段深入学习和应用的标的。团队的小伙伴,有研究前端js agent的,有研究后端 python agent的,有研究数据传输协议的,有专注在Splunk端对接的,我主要负责性能组调度平台和测试服务化平台的开发工作,因此在学习小组,主要负责elastic APM在实际项目中的集成和应用。

在公司Hackathon的评比中,我们的项目在cloud first类别中获得第一名,在受到大家认可的同时,大家继续学习其他一些主流APM相关技术。在这个学习团队,大家以调研,分享的方式相互学习,共同进步。

这篇文章,主要从APM相关基础知识的角度进行整理,给想了解和学习APM的小伙伴一个入门篇。

OpenTracing

在软件架构的演化中,我们可以看到,一种趋势是在由单体系统向微服务架构转化。

(图片来自网络)

在微服务架构体系下,调用方式由进程内的方法调用变成跨进程服务间的RPC调用。运维和问题排查相对于单体系统也变得更加复杂和困难,APM因此而生。为了更加高效定位和排查在这种分布式服务架构体系下的问题追踪和分析,诞生了一批相应的产品和系统,包括集中式日志系统(Logging),集中式度量系统(Metrics)和分布式追踪系统(Tracing)

Logging,Metrics 和 Tracing

(图片来自网络)

  • Logging: 用于记录离散的事件。代表的公司(产品)有Splunk, Elastic(ELK), etc
  • Metrics: 用于记录可聚合的数据。代表的公司(产品)有SoundCloud(Prometheus), etc
  • Tracing: 用于记录请求范围内的信息。代表公司(产品)有Datadog,Zipkin, etc

在数据分析这块大蛋糕中,这些公司的产品覆盖也在相互渗透,比如Splunk今年有收购SignalFx来提供APM解决方案,Elastic提供开源APM, etc

为了解决不同的分布式追踪系统 API 不兼容的问题,诞生了 OpenTracing 规范。现在主流的大部分APM产品(Zipkin,Jaeger,SkyWalking, etc)都支持OpenTracing。Elastic APM就是其中之一。

(图片来自网络)

OpenTracing只是其中的一种规范,还有另外的两个主流竞品(OpenCensus, OpenTelemetry)。OpenTelemetry的终态就是实现Metrics、Tracing、Logging的融合:基于Metrics的告警发现异常,通过Tracing定位问题(可疑)模块,根据模块具体的日志详情定位到错误根源,最后再基于这次问题调查经验调整Metrics(增加或者调整报警阈值等)以便下次可以更早发现/预防此类问题。下面简单介绍下OpenTracing规范。

OpenTracing规范

(概念模型,图片来自网络)

  • Trace: 调用链,一条Trace(调用链)可以被认为是一个由多个Span组成,例如客户端的一个请求
  • Span: 跨度,可以被理解为一次方法调用,一个程序块的调用,或者一次RPC/数据库访问。只要是一个具有完整时间周期的程序访问,都可以被认为是一个span
  • Span Context: 分布式调用跟踪的上下文信息,包括Trace id,Span id以及其它需要传递到下游服务的内容。通过span context,把不同进程里的span关联到相应的trace上。span context可以基于不同的方式进行传递,比如Http header。

以Elastic APM为例,我们可以看到trace id和span id在http header中以elastic-apm-traceparent进行传递(中间先后是trace id和span id):

Elastic APM介绍

架构

(图片来自网络)

Elastic APM包括:

  • APM Agent:收集运行时的性能数据和错误数据。APM Agent提供多种语言支持,如javascript,python,java,go等
  • APM Server:接收和转化来自APM Agent发来的数据
  • Elasticsearch:接收和存储来自APM Server的数据
  • Kibana APM UI:可视化性能数据

Install & Setup

Elastic APM提供SAAS版本,也可以自行安装(docker或者包安装),安装和运行部分请参考Elastic APM官方文档

...

Elastic APM应用

项目后台采用Django,利用Elastic APM的javascript agent,python的django agent能够很好的对项目的应用性能数据进行监控。

前端

前端的trace分为两部分:page-load自动的trace和用户行为自定义的trace

page-load的trace

page load的trace每次刷新页面会自动收集

  <!--
  * 替换$HOST_NAME,$SERVICE_FRONTENT_NAME, $PAGE_NAME
  * 需要注意的是serverUrl是APM server的地址和端口,这里配合nginx反向代理,不然会存在跨域问题
  * ngnix配置:
  location ^~ /apmserver/ {
      proxy_pass http://$HOST_NAME:8200/;
      ...
  }
  -->
  <script src="/web/js/elastic-apm-rum.umd.min.js"></script>
  <script>
  var apm = elasticApm.init({
    serviceName: '$SERVICE_FRONTENT_NAME',
    pageLoadTraceId: "{{ apm.trace_id }}",
    pageLoadSpanId: "{{ apm.span_id }}",
    pageLoadSampled: true,
    serverUrl: 'https://$HOST_NAME/apmserver',
    distributedTracingOrigins: ['https://$HOST_NAME']
  });
  </script>

  <!--在页面load完成的位置初始化进行数据收集-->
  elasticApm.setInitialPageLoadName($PAGE_NAME);

自定义的trace

收集在页面用户操作的trace,比如点击某个button,从后台获取数据,然后渲染。这里以jquery的ajax为例,在ajax里进行hook,大致如下:

var transaction = apm.startTransaction('E2E Perf Test', 'custom');
var httpSpan = transaction.startSpan($TYPE + ':' + $URL, 'http') //替换$TYPE: ajax type, $URL: ajax url

$.ajax({
  ...
  success: function(r){
    ...
    httpSpan.end()
    transaction.end()
    ...
  },
  error: function(xmlhttprequest, textstatus, message){
    ...
    apm.captureError(new Error('failed with status: ' + textstatus + ' message: ' + message));
    httpSpan.end()
    transaction.end()
    ...
  }
})

后台

django部分的配置也分为两部分:setting里全局通用trace配置和用户自定义trace配置

setting里全局通用trace配置

# settings.py
INSTALLED_APPS = (
    ...
    elasticapm.contrib.django
)

#替换$SERVICE_BACKEND_NAME,$KEY
ELASTIC_APM = {
   'SERVICE_NAME': '$SERVICE_BACKEND_NAME',
   'SECRET_TOKEN': '$KEY',
   'DEBUG': True,
   'DJANGO_TRANSACTION_NAME_FROM_ROUTE': True
}

MIDDLEWARE_CLASSES = (
    'elasticapm.contrib.django.middleware.TracingMiddleware',
    ...
)

# TEMPLATE里加入'elasticapm.contrib.django.context_processors.rum_tracing'

用户自定义trace配置

  • error trace

一般情况下,工程的后台代码都有exception统一抓获入口,为了能抓获后台error trace,在入口处,加上错误捕捉trace

from elasticapm import Client

#替换$SERVICE_BACKEND_NAME
client = Client({'SERVICE_NAME': '$SERVICE_BACKEND_NAME'})

#exception入口处加入
client.capture_exception()
  • function trace

在对应function加上apm span装饰器即可,如把一个名为_has_permission的function加入到trace

@elasticapm.capture_span()
def _has_permission(self, request):
    ...

演示

page-load的trace这里不展示了,这个是最基本的部分。elastic apm UI端能够展示非常丰富的应用性能数据,如span的占比,系统资源(cpu/memory/...)的利用率,TPS,时长等等。

下面我们展示一个自定义的trace:用户在web端选择一个dropdown列表的元素,页面出现Load等待,然后报错。按照传统的问题排查,我们得从前端到后端一步一步的debug。现在利用Elastic apm,从APM ui端,非常快速的就能定位到问题。

从这个waterfall图里,可以非常清晰的看到,整个操作从前端到后台,途经过的后台组件有mysql, redis等,全程花了2s多点,大部分耗时发生在_has_permission函数,后台有一处报错。进一步点击红色错误标志进行错误追踪:

错误原因是有一个全局变量未定义使用,再点击可以进入代码层:

那么对于操作耗时,apm已经非常准确的定位到是_has_permission引起的,我们进入该函数,可以看到,代码处有显式的睡眠2s的代码。到此,已经准确的定位和追踪到了出错误的代码处以及引起操作性能问题的原因。(ps: 代码只为演示)

总结

分布式系统架构的普及,软件体系架构变得越来越复杂。保证系统稳定健康的运行,了解系统的行为,分析系统的性能,在出现问题的时候,高效快速的定位和追踪问题,APM在这里扮演了重要的角色。各个厂商,各种APM产品也是不断的迭代进化,没有最好,只有更好。本文结合自己的一个实际体验,总结和整理了APM相关的一些基础知识和概念,以elastic APM为例作为一个入门介绍。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!