如其他大中型互联网应用一样,微博平台由众多的分布式组件构成,用户通过浏览器或移动客户端的每一个HTTP请求到达应用服务器后,会经过很多个业务系统或系统组件,并留下足迹(footprint)。但是这些分散的数据对于问题排查,或是流程优化都帮助有限。对于这样一种典型的跨进程/跨线程的场景,汇总收集并分析这类日志就显得尤为重要。另一方面,收集每一处足迹(footprint)的性能数据,并根据策略对各子系统做流控或降级也是确保微博平台高可用的重要因素。要能做到追踪每个请求的完整调用链路;收集调用链路上每个服务的性能数据;通过计算性能数据和比对性能指标(SLA)再回馈到控制流程(control flow)中,基于这些目标就诞生了微博的Watchman系统。在业界,Twitter的Zipkin和淘宝的鹰眼系统也是类似的系统。
这样的系统通常有几个设计目标:
低侵入性(non-invasivenss):作为非业务组件,应当尽可能少侵入或者不侵入其他业务系统,保持对使用方的透明性,可以大大减少开发人员的负担和接入门槛。
灵活的应用策略(application-policy):可以决定所收集数据的范围和粒度。
时效性(time-efficient):从数据的收集和产生,到数据计算/处理,再到展现或反馈控制,都要求尽可能得快速。
决策支持(decision-support):这些数据数据是否能在决策支持层面发挥作用,特别是从DevOps的角度。
Watchman系统架构图
对于这些设计目标,Watchman系统是怎么样做的呢?
既然要追踪调用链路要收集数据,通常的做法就是通过代码埋点来记录日志。这样一方面要求在所有需要收集数据的地方侵入代码进行修改,并且(可能)引入新的依赖。比如淘宝的鹰眼系统在跨进程的远程调用两侧(stub和skeleton)通过埋点记录数据并传递请求上下文(request-context)。
watchman-runtime组件利用字节码增强的方式在载入期织入增强逻辑(load-time weaving),为了跨进程/线程传递请求上下文,对于跨线程watchman-enhance组件通过javaagent的方式在应用启动并载入class时修改了JDK自身的几种线程池(ThreadPool或几类Executor)实现,在客户代码提交(execute或submit)时对传入的runnable/callable对象包上具有追踪能力的实现(proxy-pattern),并且从父线程上去继承或初始化请求上下文(request-context);如下图所示: