SkyWalking
1.是什么
skywalking是一个包含监控,追踪,并拥有故障诊断能力的分布式系统。它主要的作用是全链路监控,收集数据,分析处理数据,然后可视化呈现。这么说有点抽象,接下来画图来说
这是skywalking的架构。它通过Service(client agent)收集数据,然后传输给receiver cluster,然后接下来在内部部分需要原始数据或者需要进行整合的下发给aggregator,其他需要做es的发给es(Elasticsearch 是一个分布式、高扩展、高实时的搜索与数据分析引擎。它能很方便的使大量数据具有搜索、分析和探索的能力。充分利用Elasticsearch的水平伸缩性,能使数据在生产环境变得更有价值。Elasticsearch 的实现原理主要分为以下几个步骤,首先用户将数据提交到Elasticsearch 数据库中,再通过分词控制器去将对应的语句分词,将其权重和分词结果一并存入数据,当用户搜索数据时候,再根据权重将结果排名,打分,再将返回结果呈现给用户。)下面会介绍数据类型,一般分为三种,感到疑惑可以翻到第3点看一下
到这里只要懂它是什么,做了什么即可,下面会进一步介绍,深化理解。
2. 架构解析
2.1 数据收集
Skywalking 数据收集功能采用核心模块加插件的方式实现。核心模块实现了JVM数据收集、数据采样、数据发送等功能;而不同的插件用来实现各种中间件、RPC等组件的数据收集。
当核心模块使用插件采集到链路数据后,会将数据加入到本地缓存队列之中,使用生产-消费者模式进行异步发送数据。**这里其实就和我之前写的kafka和talos差不多了。**具体思路就是Core这边的核心模块收集数据,然后plugin loader作消息队列中间件把信息存入右边仓库内。
Agent介绍(还不是很明白)
那么具体的链路数据到底是怎么收集的呢? 不知道大家有没有听说过javaagent。它为我们提供了一种在运行期修改java字节码的能力。具体就是,在编译期,java编译器会将我们的源代码编译成java字节码,也就是.class文件。在运行期,JVM载入类文件时,会根据javaagent中的定义,动态修改类文件,从而达到字节码注入的目的。 Skywalking 使用javaagent的方式,在需要收集链路信息的地方,拦截对应的字节码,进行AOP修改,当某个调用链路运行至已经被SkyWalking代理过的方法时,SkyWalking会通过代理逻辑进行这些信息的收集、传递和上报。
3. 数据类型
数据类型主要分成三种:1.原始数据(链路数据,JVM数据),2.指标数据Metrics(P99,P95, QPS),3.采样数据(Slow service, slow method, slow SQL)。
原始数据,就是节点信息、链路数据、JVM数据等从数据采集端直接上传上来的数据;第二部分是P99、QPS这种通过链路数据进行统计后的指标数据;第三种是类似慢服务、慢方法、慢sql之类的根据链路信息计算出来的TopN采样数据由于每种数据类型的计算方法不同,所以他们的计算流程也稍有不同。我的任务主要聚焦于指标数据。
数据处理端主要分为两层来处理所有的数据,第一层,我们成为L1,接收从数据收集端发送来的原始数据,一方面将原始数据打入ES存储,一方面将原始数据放入内部的消息队列中。第二层,也就是L2层,从消息队列中取出原始数据,进行聚合、计算,生成指标数据与采样数据,再存入ES中
4. 数据处理端(服务端)的模块化设计
数据收集其实在客户端,由用户收集反馈
Skywalking将每个独立的功能都抽象成一个模块,设计统一的上层接口;所有的模块都ModuleManager中管理;模块间的调用都通过ModuleManager来获得其他模块的实现;而同样的模块可以有多种实现方式,比如配置模块,我们可以使用Apollo、Zookeeper等中间件来实现;存储模块则可以使用ES、Mysql等数据库来实现模块的各个实现之间没有耦合关系,全部通过模块定义中规定的上层接口来调用采用模块化的设计后,我们就可以跟我业务的使用场景,来定制化各个组件的实现方式,并且可以简单的开发属于我们自己的插件,比如开发Talos数据摄取方式等。
模块化设计的优点是:定义清晰,可以有多种实现,无耦合,自由插拔,便于拓展
模块端主要由4种模块结构构成,他们的关系如下图
以下代码都是抽象出来的,想要理解更多请自行参考源码
3.1 ModuleManager
这个类负责管理module。作为模块的管理器,其本身实现很简单,只有一个成员变量,三个共有方法成员变量loadedModules使用HashMap来存储所有初始化过得模块实现。由此可知,每个manager实现有且只有一个函数init用来初始化所有的模块。
那么要使用哪些模块呢?这个是通过传递一个配置参数applicationConfiguration来实现的在初始化中主要分为两部分逻辑,首先是每个模块的预处理部分,预处理后,会根据模块间的依赖,生成拓扑图来进行初始化剩下两个函数分别是has与find,分别用来判断模块实现是否存在与获得模块实现
注意这里用的是provider的prepare,而不是调用define的prepare。是因为这里的预处理是要初始化模块的服务,而define只是个定义。且在provider中如果需要会调用define.prepare的,所以不用担心。
public class ModuleManager implements ModuleDefineHolder {
//存放所有初始化过的模块,用于管理
private final Map<String, ModuleDefine> loadedModules = new HashMap<>();
//初始化
public void init(ApplicationConfiguration applicationConfiguration) {
...
//预处理
for (ModuleProvider provider: providers) {
provider.prepare();
}
//按照模块依赖初始化
BootstrapFlow bootstrapFlow = new BootstrapFlow(loadedModules);
bootstrapFlow.start(this);
bootstrapFlow.notifyAfterCompleted();
}
public boolean has(String moduleName) {...}
public ModuleProviderHolder find(String moduleName) {...}
}
3.2 ModuleDefine
我们再来看下模块定义类,它有切只有一个模块实现成员变量loadedProvider,这也再一次证实了一个模块同一时间只能有一个实现;此外模块定义类中还有一个特别重要的方法,就是services(),这个模块向外提供哪些接口服务,都是通过services来进行定义的比如说存储模块,就需要对外提供各个数据表的增删改查的服务;集群模块,就需要对外提供集群节点列表的服务
public abstract class ModuleDefine implements ModuleProviderHolder {
private ModuleProvider loadedProvider = null; //模块实现
private final String name; //模块名称
public ModuleDefine(String name) { this.name = name; }
public abstract Class[] services(); //对外提供的服务
//初始化
void prepare(ModuleManager moduleManager, ModuleConfiguration configuration
, ServiceLoader<ModuleProvider> moduleProviderLoader) {
...
}
}
3.3 ModuleProvider
服务实现模块中,拥有一个services成员变量,它就是用来存放在模块定义中规定的服务的实现还有一个接口是requiredModules,他用来规定模块的依赖列表
public abstract class ModuleProvider implements ModuleServiceHolder {
private ModuleManager manager;
private ModuleDefine moduleDefine;
//服务实现列表
private final Map<Class<? extends Service>, Service> services = new HashMap<>();
public abstract String name();
public abstract void prepare();
public abstract void start();
public abstract void notifyAfterCompleted();
public abstract String[] requiredModules(); //模块依赖列表
public final void registerServiceImplementation(Class<? extends Service> serviceType, Service service) {...}
void requiredCheck(Class<? extends Service>[] requiredServices) { ... }
...
}