skywalking源码的搭建
zh
上面有两种编译方式
第一种是通过git clone源码
出现上面的提示是网络原因导致下载失败,可以参考下面的解决办法解决
哔哩哔哩上面直击痛点:一招搞定GitHub开源项目下载加速! - 1.开源项目下载优化(Av94251133,P1).mp4
接下来我们要切换到tag 为v 8.2.0的代码
执行下面的两个命令
接下来我们进入到skywalking的目录执行下面的两个命令
千万要注意上面的两个操作不能出现错误
完成上面的动作之后我们就可以开始skywalking的编译了
执行编译的命令如下
第二在编译的时候一定要注意maven地址的下载,不能仅仅只配置阿里云的仓库的地址,还需要配置官方仓库的下载地址,因为有的插件在阿里云上面没有必须到官网上面去下载,这里maven的配置仓库地址如下
<!-- 阿里云仓库 --> <mirror> <id>alimaven</id> <mirrorOf>central</mirrorOf> <name>aliyun maven</name> <url>http://maven.aliyun.com/nexus/content/repositories/central/</url> </mirror> <!-- 中央仓库1 --> <mirror> <id>repo1</id> <mirrorOf>central</mirrorOf> <name>Human Readable Name for this Mirror.</name> <url>http://repo1.maven.org/maven2/</url> </mirror> <!-- 中央仓库2 --> <mirror> <id>repo2</id> <mirrorOf>central</mirrorOf> <name>Human Readable Name for this Mirror.</name> <url>http://repo2.maven.org/maven2/</url> </mirror>
在编译的过程中报如下的错误
[ERROR] Failed to execute goal org.xolstice.maven.plugins:protobuf-maven-plugin:
0.6.1:compile (grpc-build) on project apm-network: protoc did not exit cleanly.
Review output for more information. -> [Help 1]
第一种网上的说法是升级maven的版本到3.6.3,我们升级了之后还是存在问题
最终的解决办法是重启电脑之后,在执行mvn命令居然成功了
在编译skywalking的web模块的时候,npm的仓库我们需要修改下,不能使用官网的,使用官网的整个下载会非常的缓慢
我们进入到apm-webapp模块,修改pom.xml文件,我们使用淘宝的地址
这里即使弄成了淘宝的地址也会报错,那么如何解决了
参考这篇博客:https://blog.csdn.net/smooth00/article/details/106921934
我们手动编译前端工程
首先我们要先安装成功npm工具
我们进入G:\skywalking-8.2.0-code-1206\skywalking-ui目录下
我们执行下面的命令,我们将npm仓库的地址设置设为
npm config set registry "http://registry.npmjs.org/"
我们不使用淘宝的镜像仓库的地址
接下来我们执行npm install命令
执行的过程中报下面的错误
PS D:\workspace\podcast-oms> npm install --registry=https://registry.npm.taobao.org npm WARN tarball tarball data for postcss@5.2.18 (sha1-ut+hSX1GJE9jkPWLMZgw2RB4U8U=) seems to be corrupted. Trying one more time. npm ERR! path D:\workspace\podcast-oms\node_modules\.staging\postcss-8e12407f\lib\parser.js npm ERR! code EPERM npm ERR! errno -4048 npm ERR! syscall unlink npm ERR! Error: EPERM: operation not permitted, unlink 'D:\workspace\podcast-oms\node_modules\.staging\postcss-8e12407f\lib\parser.js'npm ERR! { [Error: EPERM: operation not permitted, unlink 'D:\workspace\podcast-oms\node_modules\.staging\postcss-8e12407f\lib\parser.js'] npm ERR! cause: npm ERR! { Error: EPERM: operation not permitted, unlink 'D:\workspace\podcast-oms\node_modules\.staging\postcss-8e12407f\lib\parser.js' npm ERR! errno: -4048, npm ERR! code: 'EPERM', npm ERR! syscall: 'unlink', npm ERR! path: npm ERR! 'D:\\workspace\\podcast-oms\\node_modules\\.staging\\postcss-8e12407f\\lib\\parser.js' }, npm ERR! stack: npm ERR! 'Error: EPERM: operation not permitted, unlink \'D:\\workspace\\podcast-oms\\node_modules\\.staging\\postcss-8e12407f\\lib\\parser.js\'', npm ERR! errno: -4048, npm ERR! code: 'EPERM', npm ERR! syscall: 'unlink', npm ERR! path: npm ERR! 'D:\\workspace\\podcast-oms\\node_modules\\.staging\\postcss-8e12407f\\lib\\parser.js', npm ERR! parent: 'postcss-minify-gradients' } npm ERR! npm ERR! The operation was rejected by your operating system. npm ERR! It's possible that the file was already in use (by a text editor or antivirus), npm ERR! or that you lack permissions to access it. npm ERR! npm ERR! If you believe this might be a permissions issue, please double-check the npm ERR! permissions of the file and its containing directories, or try running npm ERR! the command again as root/Administrator (though this is not recommended).
It's possible that the file was already in use (by a text editor or antivirus)我们把
It's possible that the file was already in use (by a text editor or antivirus)
使用了上面的办法还是没有解决问题,我们使用阿里的cnpm来解决
https://developer.aliyun.com/mirror/NPM?from=tnpm
我们进入到skywalking-ui
执行下面的命令
npm install -g cnpm --registry=https://registry.npm.taobao.org
看到下面的就表示执行成功了
接下来要在skywalking-ui模块下执行cnpm-install命令
接下来我们执行打包命令执行npm run build命令,执行成功之后打包就成功了
会在skywalking-ui下面生成一个dist目录
独立编译成的UI dist文件,也是可以放到apm-webapp中打包的,可以将上图dist中的文件拷贝到apm-webapp\target\classes\public下,然后修改apm-webapp\pom.xml,将npm install和build过程都注释了:
接下来进入到skywalking源码目录执行下面的编译命令进行编译
mvn clean package install -Denforcer.skip=true -Dmaven.test.skip=true -Dcheckstyle.skip=true
就可以进行打包了,但是在打包的过程中会报错
会报下面的错误
[WARNING] The assembly descriptor contains a *nix-specific root-relative-referen
因为当前是在windows环境,在binary,xml中写的确认是linux环境的路径,解决的办法就是
手动的apache-skywalking-apm-8.2.0的源码下面创建dist目录,在dist目录下面创建下面的目录,将上面编译完成的文件按照binary,xml定义的路径拷贝到下面的文件夹中
接下来我们要在idea中启动skywalking集群的源码
接下来我们使用idea打开项目
https://my.oschina.net/mingshashan/blog/3167233?from=groupmessage
然后查看设置生成的源代码(主要是看potobuf文件编译生成的源代码)
-
apm-protocol/apm-network/target/generated-sources/protobuf 选中这个目录下面的
grpc-java
和java
,然后右键选择Mark Directory As
-->Generated Sources Root
如下图所示 -
oap-server/server-core/target/generated-sources/protobuf目录的
grpc-java
和java
文件夹Mark Directory As-->Generated Sources Root
-
oap-server/server-receiver-plugin/receiver-proto/target/generated-sources/protobuf 目录的
grpc-java
和java
文件夹Mark Directory As-->Generated Sources Root
-
oap-server/exporter/target/generated-sources/protobuf目录的
grpc-java
和java
文件夹Mark Directory As-->Generated Sources Root
-
oap-server/server-configuration/grpc-configuration-sync/target/generated-sources/protobuf目录的
grpc-java
和java
文件夹Mark Directory As-->Generated Sources Root
-
oap-server/oal-grammar/target/generated-sources目录的
grpc-java
和java
文件夹Mark Directory As-->Generated Sources Root
这里只需要导入上面的几个目录作为源码目录,如果多导入就会代码报错,下面我就是多导入了下面的目录结果在编译的时候代码就发生了异常,antlr4这个目录是不能被导入成源码的
第二个代码的源码中包下面的错误
我用的idea版本是2018的版本,我安装了lombook的插件,但是本地使用的maven版本是3.6.3版本,我把maven版本更换成3.3.9版本就好了
第二种是直接从apache skywalking下载的源码,直接编译
这里我采用的是第二种直接从从apache skywalking下载的源码,直接编译
进入到skywalking源码目录执行下面的编译命令进行编译
mvn clean package install -Denforcer.skip=true -Dmaven.test.skip=true -Dcheckstyle.skip=true
在编译的过程中报probuffer协议编译失败,无论如何都搞不定,结果重启电脑之后,再次编译就好了、
首先skywalking中存在前端
一定要保证npm 和node已经安装成功
上面保存的原因是node-8.17.0-win-x64.zip文件下载失败,我们需要手动下载node-8.17.0-win-x64.zip放在对应的maven库中,接下来在编译的时候报错
我们需要将npm仓库的地址换成国内的仓库地址
并且需要把 apm-webapp工程的pom文件中npm的下载地址改成国内的,不然访问在编译过程中会因为访问不了国外的仓库而报错
上面的编译成功之后,我们需要将代码 导入到eclipse中
我们在eclipse中需要将编译之后的文件夹设置为源码
eclipse设置源码的方式如所示
- 设置 gRPC 的自动生成的代码目录,为源码目录 :
- 将
apm-protocol/apm-network/target/generated-sources/protobuf
目录下面grpc-java
和java
目录右键设置为Generated Rources Root
。 - 将
oap-server/server-core/target/generated-sources/protobuf
目录下面grpc-java
和java
目录右键设置为Generated Rources Root
。 - 将
oap-server/server-receiver-plugin/skywalking-istio-telemetry-receiver-plugin/target/generated-sources/protobuf
目录下面grpc-java
和java
目录右键设置为Generated Rources Root
。
IDEA 运行
skywalking的源码中存在lombok组件,我们需要安装lombok的插件
-
首先我们需要安装IntelliJ IDEA中的lombok插件,打开IntelliJ IDEA后点击菜单栏中的File-->Settings,或者使用快捷键Ctrl+Alt+S进入到设置页面。
-
我们点击设置中的Plugins进行插件的安装,在右侧选择Browse repositories...,然后在搜索页面输入lombok变可以查询到下方的Lombok Plugin,鼠标点击Lombok Plugin可在右侧看到Install按钮,点击该按钮便可安装。
-
我们在安装页面可以看到lombok具体支持的所有注解,在安装过程中有Downloading Plugins的提示,安装过程中进度条会变化。需要提醒的是,在安装过程中一定要保证网络连接可用且良好,否则可能会安装失败。安装成功后我们可以看到右侧的Restart按钮,此时可先不操作,因为我们还有后续的配置工作。安装完成后我们再回到Plugins,此时在右侧可以搜索到lombok,而安装前是不行的。
END
lombok插件的使用
-
使用前我们需要说明的是安装的插件只是一个调用,就像我们使用maven插件一样,本机需要安装maven才行。我们在使用lombok前也需要添加lombok的依赖。lombok的版本一直在更新,大家可以在百度搜索框输入lombok maven找到最新的依赖版本。
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.10</version></dependency>
-
接下来我们编辑一个实体类Student,添加三个属性,最后在类上添加@Data属性,这个注解可以帮我们在.class文件中生成类中所有属性的get/set方法、equals、canEqual、hashCode、toString方法等。
apm-agent-core是skywalking的核心类,打包会生产skywalking-agent.jar这个包
微内核架构
SkyWalking Agent 采用了微内核架构(Microkernel Architecture),那什么是微内核架构呢?微内核架构也被称为插件化架构(Plug-in Architecture),是一种面向功能进行拆分的可扩展性架构。在基于产品的应用中通常会使用微内核架构,例如,IDEA、Eclipse 这类 IDE 开发工具,内核都是非常精简的,对 Maven、Gradle 等新功能的支持都是以插件的形式增加的。
如下图所示,微内核架构分为核心系统和插件模块两大部分。
idea在编译skywalking的时候,发现项目的java源码没有编译的情况
第一要保证idea的jdk设置正确
项目无法编译运行的问题要设置上图保证maven正确要编译项目
在上图展示的微内核架构中,内核功能是比较稳定的,只负责管理插件的生命周期,不会因为系统功能的扩展而不断进行修改。功能上的扩展全部封装到插件之中,插件模块是独立存在的模块,包含特定的功能,能拓展核心系统的功能。通常,不同的插件模块互相之间独立,当然,你可以设计成一个插件依赖于另外一个插件,但应尽量让插件之间的相互依赖关系降低到最小,避免繁杂的依赖带来扩展性问题。
最终所有插件会由内核系统统一接入和管理:
首先,内核系统必须知道要加载哪些插件,一般会通过配置文件或是扫描 ClassPath 的方式(例如前文介绍的 SPI 技术)确定待加载的插件;
之后,内核系统还需要了解如何使用这些插件,微内核架构中需要定义一套插件的规范,内核系统会按照统一的方式初始化、启动这些插件;
最后,虽然插件之间完全解耦,但实际开发中总会有一些意想不到的需求会导致插件之间产生依赖或是某些底层插件被复用,此时内核需要提供一套规则,识别插件消息并能正确的在插件之间转发消息,成为插件消息的中转站。
由此可见微内核架构的好处:
测试成本下降。从软件工程的角度看,微内核架构将变化的部分和不变的部分拆分,降低了测试的成本,符合设计模式中的开放封闭原则。
稳定性。由于每个插件模块相对独立,即使其中一个插件有问题,也可以保证内核系统以及其他插件的稳定性。
可扩展性。在增加新功能或接入新业务的时候,只需要新增相应插件模块即可;在进行历史功能下线时,也只需删除相应插件模块即可。
SkyWalking Agent 就是微内核架构的一种落地方式。在前面的课时中我已经介绍了 SkyWalking 中各个模块的功能,其中 apm-agent-core 模块对应微内核架构中的内核系统,apm-sdk-plugin 模块中的各个子模块都是微内核架构中的插件模块。
SkyWalking Agent 启动流程概述
此前,在搭建 SkyWalking 源码环境的最后,我们尝试 Debug 了一下 SkyWalking Agent 的源码,其入口是 apm-agent 模块中 SkyWalkingAgent 类的 premain() 方法,其中完成了 Agent 启动的流程:
初始化配置信息。该步骤中会加载 agent.config 配置文件,其中会检测 Java Agent 参数以及环境变量是否覆盖了相应配置项。
查找并解析 skywalking-plugin.def 插件文件。
AgentClassLoader 加载插件。
PluginFinder 对插件进行分类管理。
使用 Byte Buddy 库创建 AgentBuilder。这里会根据已加载的插件动态增强目标类,插入埋点逻辑。
使用 JDK SPI 加载并启动 BootService 服务。BootService 接口的实现会在后面的课时中展开详细介绍。
添加一个 JVM 钩子,在 JVM 退出时关闭所有 BootService 服务。
SkywalkingAgent.premain() 方法的具体实现如下,其中省略了 try/catch 代码块以及异常处理逻辑:
复制代码
public static void premain(String agentArgs,
Instrumentation instrumentation) throws PluginException {
// 步骤1、初始化配置信息
SnifferConfigInitializer.initialize(agentArgs);
// 步骤2~4、查找并解析skywalking-plugin.def插件文件;
// AgentClassLoader加载插件类并进行实例化;PluginFinder提供插件匹配的功能
final PluginFinder pluginFinder = new PluginFinder(
new PluginBootstrap().loadPlugins());
// 步骤5、使用 Byte Buddy 库创建 AgentBuilder
final ByteBuddy byteBuddy = new ByteBuddy()
.with(TypeValidation.of(Config.Agent.IS_OPEN_DEBUGGING_CLASS));
new AgentBuilder.Default(byteBuddy)...installOn(instrumentation);
// 这里省略创建 AgentBuilder的具体代码,后面展开详细说
// 步骤6、使用 JDK SPI加载的方式并启动 BootService 服务。
ServiceManager.INSTANCE.boot();
// 步骤7、添加一个JVM钩子
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
public void run() { ServiceManager.INSTANCE.shutdown(); }
}, "skywalking service shutdown thread"));
}
了解了 SkyWalking Agent 启动的核心步骤之后,本课时剩余部分将对每个步骤进行深入分析。
初始化配置
在启动 demo-webapp 和 demo-provider 两个 demo 应用的时候,需要在 VM options 中指定 agent.confg 配置文件(skywalking_config 参数),agent.config 配置文件中的配置项如下:
复制代码
# 当前应用的服务名称,通过Skywalking Agent上报的Metrics、Trace数据都会
# 携带该信息进行标识
agent.service_name=${SW_AGENT_NAME:Your_ApplicationName}
在 SnifferConfigInitializer.initialize() 方法中会将最终的配置信息填充到 Config 的静态字段中,填充过程如下:
将 agent.config 文件中全部配置信息填充到 Config 中相应的静态字段中。
解析系统环境变量值,覆盖 Config 中相应的静态字段。
解析 Java Agent 的参数,覆盖 Config 中相应的静态字段。
SnifferConfigInitializer.initialize() 方法的具体实现如下:
复制代码
public static void initialize(String agentOptions) {
// 步骤1、加载 agent.config配置文件
InputStreamReader configFileStream = loadConfig();
Properties properties = new Properties();
properties.load(configFileStream);
for (String key : properties.stringPropertyNames()) {
String value = (String)properties.get(key);
// 按照${配置项名称:默认值}的格式解析各个配置项
properties.put(key, PropertyPlaceholderHelper.INSTANCE
.replacePlaceholders(value, properties));
}
// 填充 Config中的静态字段
ConfigInitializer.initialize(properties, Config.class);
// 步骤2、解析环境变量,并覆盖 Config中相应的静态字段
overrideConfigBySystemProp();
// 步骤3、解析 Java Agent参数,并覆盖 Config中相应的静态字段
overrideConfigByAgentOptions(agentOptions);
// 检测SERVICE_NAME和BACKEND_SERVICE两个配置项,若为空则抛异常(略)
IS_INIT_COMPLETED = true; // 更新初始化标记
}
步骤 1 中的 loadConfig() 方法会优先根据环境变量(skywalking_config)指定的 agent.config 文件路径加载。若环境变量未指定 skywalking_ config 配置,则到 skywalking-agent.jar 同级的 config 目录下查找 agent.confg 配置文件。
将 agent.config 文件中的配置信息加载到 Properties 对象之后,将使用 PropertyPlaceholderHelper 对配置信息进行解析,将当前的“${配置项名称:默认值}”格式的配置值,替换成其中的默认值,demo-provider 解析结果如下图所示:
完成解析之后,会通过 ConfigInitializer 工具类,将配置信息填充到 Config 中的静态字段中,具体填充规则如下:
在接下来的 overrideConfigBySystemProp() 方法中会遍历环境变量(即 System.getProperties() 集合),如果环境变 是以 "skywalking." 开头的,则认为是 SkyWalking 的配置,同样会填充到 Config 类中,以覆盖 agent.config 中的默认值。
最后的 overrideConfigByAgentOptions() 方法解析的是 Java Agent 的参数,填充 Config 类的规则与前面两步相同,不再重复。
到此为止,SkyWalking Agent 启动所需的全部配置都已经填充到 Config 中,后续使用配置信息时直接访问 Config 中的相应静态字段即可。
dubbo 插件
这里要注意这里拦截点是连接类的实例方法所以继承的类是ClassInstanceMethodEnhancePluginDefine
如果是拦截类的静态方法,就要集成另外的类,如下
下面的具体的拦截的实现
结论
skywalking中witnessClass机制
witness机制的原理,列如dubbo有1.0 2.0两个版本
1.0中原理的拦截的MonitorFilter类对应的方法是invoke方法
2.0中原理的拦截的MonitorFilter类对应的方法变成了name方法
现在我们为dubbo1.0 编写了skywalking一个插件dubbo1.0 -jar
我们为dubbo2.0 编写了skywalking一个插件dubbo2.0 -jar
我们如何实现dubbo1.0 -jar只拦截invoke方法
dubbo2.0 -jar只拦截name方法
这就通过witnessClass机制来实现,dubbo1.0 -jar的插件中项目组需要实现witnessClass方法,在改方法中返回一直方法,改方法只有dubbo1.0这个版本有,列如方法为aa
项目启动的时候,classload会对dububbo 1.0这个项目组运行的版本进行字节码解析看是否存在aa方法,如果aa方法与skywalking agent中witnessClass方法中定义的aa方法一致,就使用dubbo1.0 -jar只拦截invoke方法
列如sping存在多个版本
agent后后端通信的设置
一种是是明文通信
一种是ssl通信
agent后后端的token认证
grpc和后端的oap重连的时候是随机选择一个后端的oap server 实例来进行连接
应用通过grpc向opa注册注册的时候会携带当前的应用名称,注册成功之后会将当前应用的ID返回给agent,agent会存在在缓存中
当前服务实例也需要进行注册,注册成功之后,oap会给当前的实例返回一个id,然后将这个ID缓存起来
注册成功之后,agengt需要通过定时任务不断向后端发送心跳包,说明当前的实例是存在的
Tracesegement的核心概念
skywalking将一个进程中的所有span封装到一个Tracesegement中
一个Tracesement中包含多个span
span的定义如下
span中提供了下面的方法
概念3:TraceSegment中存在一个字段TraceSegmentref字段,记录了TraceSegment与TraceSegment的相互关联关系
概念4:上面的概念介绍完成之后接下来介绍context对象来实现对TraceSegment的管理和实现对span的管理
在上面创建entryspan之后,会将当前的TraceSegment上传到oap中,entryspan一般是分为两种,一个中进程传播,一种是线程传播,接下来讲讲跨进程传递,需要将Trace的信息上下文进行跨进程传播
跨线程传播如下
11、接下来在发送TracesSegment上报给oap的时候用到了DateCarrier组件,DateCarrier组件中会存储要发送的TracesSegment
接下来我们重点讲解下几个重要的概念
我们来查看下一个案例contextManger如何进行管理的
讲讲TraceSegment中的采样,看是否需要将当前的审判添加到TraceSegment中
当TraceContext调用stopSpan关闭最后一个span的时候,会调用finish方法关闭改span所在的TraceSegment,于此同时会调用TracContext的listen通过改segment被关闭了的通知,这个时候就会将TraceSegment发送给oap的后端
TraceSegmentServiceClient主要负责将TraceSegment通过DataCarrier将TraceSegment序列化发送给后端的oap
整个序列化profuffer协议格式如下所示
这里序列化之后将segement发送给后端oap,发送的过程是阻塞性的
lombok
posted on 2020-11-24 19:01 luzhouxiaoshuai 阅读(2454) 评论(0) 编辑 收藏 举报