走进SkyWalking-插件概念[#2]

Python09

走进SkyWalking-插件概念[#2],第1张

[TOC]

在解析SkyWalking的源码之前,我们先来了解下SkyWalking中的插件机制,那它的作用是什么?为什么先要去了解它呢?我们知道SkyWalking是用来监控应用程序的系统,那它必然需要收集我们应用程序中需要监控的相关数据,那问题就来了,它怎么知道我们系统中需要收集哪些数据?所以插架的作用就在于此,由于对监控的应用程序的不确定性,进而提供了可扩展的插件机制来满足实际的业务监控需求。

为什么先要去了解它?因为对监控数据的来源收集是应用监控的基础,也是第一步,也就是SkyWalking中的探针部分,同时它也是我们解析Agent启动源码中重要的一环。

在SkyWalking的文档及源文件中,相信你能看到官方已经提供了很多常见框架的不同版本的通用插件,这里面一些由官方提供,一些由社区开发者Pull;但是如果你想在项目中重度使用SkyWalking来做APM,仅仅使用这些插件还是不够的,你必然需要封装符合自己业务监控需求的插件,相应的,你必须的知道插件的机制以及如何开发。

工欲善其事必先利其器,开发插件前,我们先得具备SkyWalking的插件中的基础概念以及开发规范的要点。

它是上下文管理器,是SkyWalking中的核心API,负责提供所有主要的API,像下文中Span和Context的一些列操作都是由它封装并提供方法,同时负责维护Context上下文,内部是通过 ThreadLocal 来进行多线程的隔离和维护的,在后面插件开发的过程中会经常使用到这个类的。

跨度,它是系统中完成的单个工作单元,通俗的说一条完整的调用链路就是由多个Span单元组成的,通过下图相信你会更加直观的了解它,

这是 /storage/waste 的一条调用链路,下面的每一行就代表一个Span,上面的 test 是自定义的插件拦截定义的Span。

OpenTracing的规范中定义了一个Span需要包含操作名称、开始和结束时间、跨度标签、跨度日志、 SpanContext ,其中SpanContext的作用是携带跨进程的边界数据,由 traceId 、 spanId 、键值对数据组成。

而SkyWalking的Span概念与之类似,同时提供了一些扩展,我们先来看下Span的类图结构,

首先 AsyncSpan 定义了一个顶层接口,

能看到里面定义了两个方法,它们是在异步场景下使用的API,当Span中的组成信息需要在另一个线程中进行设置时,像标签、日志、属性等,就可以使用它们。使用的方式也很简单,比如在A线程的Span里面调用 prepareForAsync() 方法,然后再B线程中拿到Span进行设置,完成后调用 asyncFinish() 方法来结束调用即可。

再来看它的子类接口 AbstractSpan ,它里面就定义了Span的属性组成,像操作名称OperateName、标签Tag、日志Log、组件Component、类别Layer等等。其中

而下面的实现子类中,其中 NoopSpan 是一个空实现,没有任何实际操作,用来存放 IgnoredTracerContext 的,我们主要看 AbstractTracingSpan 下三个重要的实现子类:

我们再定义Span的时候只需要根据上面的描述创建相应的Span即可,其中 EntrySpan 和 ExitSpan 注意要设置Component和Layer值。这里创建Span的方式也很简单,利用 ContextManager 提供的API来完成,

Context通常代表着上下文,相信看过一些框架源码,像Spring等,一定对它很熟悉;那我们来看看SkyWalking中的Context是怎么定义的,先看类图,

这里 AbstractTracerContext 是一个顶层接口,代表着跟踪上下文的管理器, IgnoredTracerContext 刚才我们提到过,源码注释中对其说明是表示一个被忽略的上下文,主要作用是用一个字段 int stackDepth 来维护堆栈深度,它通常在Span中的OperateName定义为空时会被创建;而 TracingContext 才是核心跟踪逻辑控制器,它保存着一些基本的信息个状态以外,还存放着上下文中传播的 Span 和跨度深度等重要数据。

我们在创建 Span 的时候方法内部会自动帮我创建 AbstractTracerContext 并初始化,所以不用担心,主要在下面两种情况下会需要我们进行传递处理:

通过上面我们知道了SkyWalking中插件的作用、自定义插件中需要定义的哪些基本信息和规范,以及如何在不同的场景下进行上下文的传播,那接下来就是如何进行插件开发了,下一篇我们继续。

身未动,心已远。

把一件事做到极致就是天分!

你好,手工装各种库用gem,rvm是用来管理ruby的,ruby的其中一个“程序”叫rubygems,简称 gem,是个ruby版本管理器,bundle是rails框架里面安装Gemfile指定的各种库的工具。 gem和bundle是装的同意的东西,rvm是装ruby的。,而用来管理项目 的gem的,叫bundle.完全是不同的东西,他们相同的只是都可以管理gem

它采用纯 Python 实现,是一个分布式用户负载测试的工具。 使用基于 Requests 库的客户端发起请求,使编写脚本大大简化在模拟并发方面摒弃进程和线程,完全基于时间驱动,采用协程(gevent)提供的非阻塞 IO 和 coroutine 来实现网络层的并发请求。因此单台压力机也能产生数千并发请求数。

Locust官网

特点:

支持Python版本: 3.6, 3.7, 3.8

安装方式: pip install locust -i https://pypi.tuna.tsinghua.edu.cn/simple

案例

--- 一个用户下一步具体要执行什么动作,是随机的,也就是 locust 会从动作列表中随机选择一个

--- @task 接受一个参数,表示权重,数值越大,这个动作被选中的概率就越高

然后打开浏览器, 直接在浏览器输入 http://localhost:8089 打开UI界面, 界面如下:

locust -f locustfile.py --headless -u 100 -r 10 -t 10s

运行多个进程的Locust, 使用 --master 命令启动主进程, 使用 --worker 启动从属进程

主进程控制从进程并收集从进程的测试结果从进程负责执行测试,并反馈结果给主进程。

启动无界面主进程

locust -f locustfile.py --master --headless --master-bind-host=127.0.0.1 --master-bind-port=8090 -t 15s -u 5 -r 1

启动后,如果当前没有从进程的话会显示等待从进程。当从进程启动后便可以执行脚本了。

启动无界面的从进程:

locust -f locustfile.py --worker --master-host=127.0.0.1 --master-port=8090 --headless

从进程启动后将开始执行测试,主进程将会统计结果。

对于 web 网站来说,通常情况下,网站的一个页面中用户能够执行多个动作,并且这些动作通常是为了某个特定的目标,比如注册页面,用户可以执行的动作: 验证用户名,手机号码是否重复,更新图形验证码,获取手机验证码等。这些动作的目标就是为了完成用户注册。我们可以把这些动作组合在一起。

在 locust 中我们通过 TaskSet 类来组合多个动作,然后在 HttpUser 对象中引入 taskset ,引入方式是在 tasks 属性列表中,添加 taskset 类。

taskset 也是可以嵌套的,我们在 taskset 的 tasks 属性中添加要嵌套的其他 taskset 类。类似于: 网站首页包含用户登录页面,用户登录页面包含用户注册页面这种关系

我们在 HttpUser 以及 TaskSet 下定义动作后,locust 是从这些动作中随机选择一个动作来执行,有时候我们希望用户执行的动作是有序的,比如:

1.验证用户名是否重复

2.验证手机号码是否重复

3.验证获取图形验证码

4.获取手机验证码

5.注册

这时候就需要通过 SequentialTaskSet 来定义动作集合,他会按照定义 task 定义的顺序来一次调用。

我们需要判断一个请求是失败还是成功,需要给请求参数携带 catch_response=True ,这样请求方法就会返回一个上下文管理器

这个上下文管理器返回的是响应对象,我们通过调用响应对象的 failure(消息) 来标记本次请求失败,

通过 catch_response=True 参数来进行断言。