定位
An APM(application performance monitor) system, especially designed for microservices, cloud native and container-based architectures.
应用性能监控系统,主要针对微服务云原生容器化架构系统。
功能
- 支持多种语言Agent
- 轻量高效,无需部署大数据平台和大量服务器
- 模块化,UI、存储、集群管理都有多种机制可选
- 支持 Webhook 告警
- 自带可视化
架构
包含采集(Agent)、收集处理(OAP – Observability Analysis Platform)、存储、UI 四个部分。
处理流程
如何采集?
agent + 插件化,例如dubbo采集由dubbo-plugin支持,还有tomcat-plugin等等,实现代码无侵入、拓展性好可插拔
如何开发插件?
简言之 skywalking 利用的是 bytebuddy
动态生成字节码技术和 AOP 概念,通过生成对应组件的字节码暴露 hook(进行方法拦截等),以继承的方式引用自定义拦截器,调用自定义拦截器组织组件的调用信息等,由 SW 进行传输、采集。
bytebuddy
较之 ASM,屏蔽了字节码底层的细节,封装了提供易上手的 API,降低使用成本。
开发者只需要声明需要拦截的类名(可通过规则匹配)以及定义拦截器、实现中的逻辑即可,拦截的维度包括类静态方法、实例方法和构造方法。
参考 dubbo-2.7.x-plugin 插件:
org.apache.skywalking.apm.plugin.asf.dubbo.DubboInstrumentation
// 继承 ClassInstanceMethodsEnhancePluginDefine 说明只增强非静态方法
public class DubboInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
// 声明要增强的组件具体的 class,用于匹配要拦截目标类
private static final String ENHANCE_CLASS = "org.apache.dubbo.monitor.support.MonitorFilter";
// 声明拦截器类,包含具体要执行的拦截逻辑
private static final String INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.asf.dubbo.DubboInterceptor";
...
@Override
public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
// 不拦截构造器
return null;
}
@Override
public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
return new InstanceMethodsInterceptPoint[] {
new InstanceMethodsInterceptPoint() {
@Override
public ElementMatcher<MethodDescription> getMethodsMatcher() {
// 声明拦截方法名为 invoke 的方法,也是用于匹配
return named("invoke");
}
@Override
public String getMethodsInterceptor() {
// 拦截器类名
return INTERCEPT_CLASS;
}
@Override
public boolean isOverrideArgs() {
return false;
}
}
};
org.apache.skywalking.apm.plugin.asf.dubbo.DubboInterceptor
public class DubboInterceptor implements InstanceMethodsAroundInterceptor {
@Override
public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
MethodInterceptResult result) throws Throwable {
Invoker invoker = (Invoker) allArguments[0];
Invocation invocation = (Invocation) allArguments[1];
RpcContext rpcContext = RpcContext.getContext();
boolean isConsumer = rpcContext.isConsumerSide();
URL requestURL = invoker.getUrl();
AbstractSpan span;
final String host = requestURL.getHost();
final int port = requestURL.getPort();
boolean needCollectArguments;
int argumentsLengthThreshold;
if (isConsumer) {
final ContextCarrier contextCarrier = new ContextCarrier();
span = ContextManager.createExitSpan(generateOperationName(requestURL, invocation), contextCarrier, host + ":" + port);
//invocation.getAttachments().put("contextData", contextDataStr);
//@see https://github.com/alibaba/dubbo/blob/dubbo-2.5.3/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/RpcInvocation.java#L154-L161
CarrierItem next = contextCarrier.items();
while (next.hasNext()) {
next = next.next();
// 消费端:利用 dubbo attachment 传递 trace 等信息 (其他组件也是类似 例如 httpClient 通过 header 传递、 RocketMq 通过 properties 传递等等)
rpcContext.setAttachment(next.getHeadKey(), next.getHeadValue());
if (invocation.getAttachments().containsKey(next.getHeadKey())) {
invocation.getAttachments().remove(next.getHeadKey());
}
}
needCollectArguments = DubboPluginConfig.Plugin.Dubbo.COLLECT_CONSUMER_ARGUMENTS;
argumentsLengthThreshold = DubboPluginConfig.Plugin.Dubbo.CONSUMER_ARGUMENTS_LENGTH_THRESHOLD;
} else {
ContextCarrier contextCarrier = new ContextCarrier();
CarrierItem next = contextCarrier.items();
while (next.hasNext()) {
next = next.next();
// 服务端:从 dubbo attachment 获取 trace 等信息
next.setHeadValue(rpcContext.getAttachment(next.getHeadKey()));
}
span = ContextManager.createEntrySpan(generateOperationName(requestURL, invocation), contextCarrier);
span.setPeer(rpcContext.getRemoteAddressString());
needCollectArguments = DubboPluginConfig.Plugin.Dubbo.COLLECT_PROVIDER_ARGUMENTS;
argumentsLengthThreshold = DubboPluginConfig.Plugin.Dubbo.PROVIDER_ARGUMENTS_LENGTH_THRESHOLD;
}
Tags.URL.set(span, generateRequestURL(requestURL, invocation));
collectArguments(needCollectArguments, argumentsLengthThreshold, span, invocation);
span.setComponent(ComponentsDefine.DUBBO);
SpanLayer.asRPCFramework(span);
}
@Override
public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
Object ret) throws Throwable {
...
dealException()
ContextManager.stopSpan();
}
}
目前 skywalking-java 有 v1, v2 两套 API 用于自定义插件拦截器,v2 增加了
MethodInvocationContext
用于在 beforeMethod 和 afterMethod/handleMethodException 间传递变量,高并发场景下性能更优。
traceId 生成原理
采用类似 snowflow 算法本地生成,相比分布式生成需要网络请求,性能更高。但是需要解决时钟回拨问题,采用的方案是每次记录生成的时间戳,下次生成比较上一次的时间戳,如果小于上次时间,则使用另一个自增字段作为时间戳。
Process_ID(random).Current_Thread_Id.Timestamp * 10000 + Seq_Num (ThreadLocal)
采样频率可配置
全部采样在数据量较大的情况下可能会影响性能,所以默认设置了三秒采样三次,并且如果发现上游携带了 TraceContext ,那么会强制下游也采集,保证采样了的链路的完整性。
数据模型
Trace (仅是逻辑概念,通过多个 TraceSegment 中属性 relatedGlobalTraceId
串联维护)
|- TraceSegment 1
|- Span 1
|- Span 2
|- TraceSegment 2
对比
- 相比于 zipkin(twitter开源)、pinpoint
类别 skywalking zipkin pinpoint cat 接入方式 agent,代码无侵入 引入依赖 agent 引入依赖(手动埋点) agent到collector的协议 grpc http, mq thrit http/tcp 颗粒度 方法级 类级 方法级 代码级 报警 有 无 有 有 安装部署 较复杂 简单 复杂 较复杂 性能影响 小 中 高 中 存储 es, h2 (默认es) cassandra, es, mysql hbase mysql, hdfs 支持语言 Java/C/C++/Python/Go/Node.js/PHP/.Net/nginx Java Java Java/C/C++/Python/Go/Node.js Trace查询 有 无 有 无 - 相比于 Prometheus
a monitoring system and time series database.
简单地理解 Prometheus,它是一个时序数据库,按照时间收集了很多 metric(指标)。
metric 可以理解为带标签的 key-value 键值对,它形如
key{label1="...", label2="..."} => 100
,也就是说 Prometheus 按时间按类型收集了很多值,排列起来最终就是监控数据。具体的格式可参照下图(图片来源:《剖析Prometheus的内部存储机制》):这种 metric 数据结构,占存储空间很小,性能很高,兼容适配也很简单,相比其他 APM 工具根据日志进行解析要优越很多。
主要区别
- Prometheus 收集数据基于 pull 模式,通过HTTP周期性抓取被监控组件的状态,任意组件只要提供对应的HTTP接口并且符合Prometheus定义的数据格式,就可以接入Prometheus监控。
- Prometheus 有自己的语言,叫做
PromQL
,用于查询监控数据。(Skywalking(9.4.0开始)提供了 PromQL 服务,已经支持 PromQL 的第三方系统或可视化平台(如 Grafana),可以通过它获取指标。) - UI 需要依赖其他组件 例如 Grafana
- Skywalking接入简单,Prometheus接入需要引入依赖帮助提供 exporter,例如 micrometer
- sw适合快速搭建监控系统,pm 更加灵活,拓展性更强。
- 另外 sw 是java编写,pm 是go语言编写
可观测系统分类
类型 | Metrics | Tracing | Logging |
---|---|---|---|
含义 | 指标聚合(例如某段时间qps吞吐量,cpu负载等等) | 系统调用链追踪 | 日志采集、存储、分析 |
系统 | Prometheus, Zabbix | Cat, Zipkin, Skywalking, Google Dapper | ELK |
各种监控系统能力并不是完全隔离,而是会存在交叉,只是侧重点不同,但最终发展方向是实现全面、即三种能力的整合。
例如 Skywalking 其实同时具备三者能力,但重点深入 Tracing and Metrics。
SW 目前实现自定义指标接入 以及 PromQL查询并集成Grafana看板