云舟观测:Arkit数据解析插件详解
off999 2025-01-21 20:36 32 浏览 0 评论
1. 概述
在云舟观测产品中,Arkit作为统一监控Agent,是整个产品的基石,它支持采集多种操作系统(如Linux和Windows)上的系统指标和进程信息,同时还能读取多种数据源,包括原始文件、消息队列和网络数据等,并将这些数据发送到各种目标存储系统中。此外,Arkit提供了丰富的数据解析配置选项,能够灵活地满足不同场景下的数据解析需求。无论是简单的日志解析,还是复杂的结构化数据处理,Arkit都能轻松应对,确保数据的准确性和完整性,为后续的数据分析和监控提供了坚实的基础。
2. 数据采集与解析
Arkit目前支持包括文件在内的多种数据源,以及上百种监控指标的采集,同时内置了多种解析方式,支持在Arkit本地进行一些数据解析:通过在采集端进行的边缘计算,利用节点的算力对数据进行加工变换。这样一方面可以减少数据冗余,降低发送数据的网络及存储消耗;另一方面也是对数据进行不同形式的扩展和预处理,降低后续处理环节的复杂度——这一部分也是本文将要介绍的重点内容。最后,采集到的数据支持支持发送到多种目标端。
在数据采集模块读取到原始数据后,既可以将原始数据直接发送给数据目标,也可以配置数据解析模块,将采集到的数据进行各种处理。例如:
● 数据格式解析(CSV解析,JSON解析、Grok解析,Nginx日志解析)等
● 自定义插件解析:允许用户通过自定义插件的方式完成日志解析(插件下发并热更新使用,无需重启Arkit)
在自定义插件解析中,云舟观测提供了WEB端代码编辑器以及模板代码样例,用户可以使用golang开发自己的插件,并将插件代码提交到后台进行编译,通过后插件创建成功;然后可以使用“测试”功能,输入测试文本数据验证插件对数据的解析是否正确;在创建采集任务配置解析方式时,可以选择"自定义插件"类型,并使用之前创建成功的插件。
3. 插件功能
Arkit本身是用go语言开发的,因此的插件选型时也需要选择基于go语言的动态代码实现方案。go语言作为一门强类型、静态编译的语言,有着极佳的性能,但是也相应的缺少了一些灵活性。为了实现一些需要灵活处理的、插件类的业务需求,调研了golang插件开发的一些方案:
首先是嵌入一些相应的脚本引擎,分别可以动态执行lua、js和python等脚本语言,并获取执行结果继续使用。但是这类方案有个致命的缺点:代码执行效率远远低于原生go代码,在需要进行海量日志采集和处理的流程中,解析效率低,进而影响采集发送效率是个严重的问题。
其次是golang原生plugin功能,也是Arkit最终采用的方案,下面将会重点介绍。为了配合插件的使用,并为用户提供自行编写、修改、分发插件的入口,云舟观测和Arkit插件模块的大致架构如下图所示:
3.1 Plugin的起源
Go Plugin 机制是在 Go 1.8 版本引入的一项重要特性。它的设计目标是为 Go 程序提供一种动态加载代码的能力,使得程序可以在运行时动态扩展功能而无需重新编译整个程序。这个特性的诞生主要基于以下几个需求:
● 微服务架构下的动态扩展需求
● 插件化架构的实现需求
● 热更新/热部署的需求
● 第三方功能模块的动态集成需求
Go Plugin 机制为 Go 程序提供了高效的动态加载能力,其核心优势包括:零拷贝的内存映射设计、· 高效的符号解析机制、优化的类型检查系统、· 接近原生的调用性能。
3.2 第一个demo插件代码示例:
```go
// plugin-impl.go
package main
import "fmt"
func Hello() string {
return "Hello from golang plugin"
}
// 插件必须有 main 包和 main 函数
func main() {}
```编译插件:
```bash
go build -buildmode=plugin -o plugin.so plugin-impl.go
```使用插件:
```go
// main.go
package main
import (
"fmt"
"plugin"
)
func main() {
// 加载插件
p, err := plugin.Open("plugin.so")
if err != nil {
panic(err)
}
// 查找符号
symbol, err := p.Lookup("Hello")
if err != nil {
panic(err)
}
// 类型断言
hello := symbol.(func() string)
fmt.Println(hello())
}
```3.3 内部实现机制
Go Plugin 的实现主要涉及以下几个关键部分:
1. 符号表管理
● 插件编译时会生成导出符号表
● 运行时通过符号表查找和解析函数/变量
● 使用 ELF(Linux)/Mach-O(macOS) 格式存储符号信息
2. 内存管理
● 插件代码被加载到独立的内存空间
● 通过内存映射实现代码共享
● 支持垃圾回收
3. 类型系统
● 插件和主程序必须使用相同的 Go 版本编译
● 类型信息在运行时进行匹配
● 保证类型安全
3.4 核心数据结构
```go
// plugin 包内部的关键结构
type Plugin struct {
pluginpath string // 插件文件路径
syms map[string]interface{} // 符号表
loaded bool // 是否已加载
}
// 动态链接信息
type _dl_info struct {
dli_fname *byte // 文件名
dli_fbase *byte // 基地址
dli_sname *byte // 符号名
dli_saddr *byte // 符号地址
}
```3.5 Plugin的性能优势
Go Plugin 具有优异的性能,这得益于以下几个方面:
- 零拷贝设计:Plugin 使用内存映射技术,将插件代码直接映射到进程地址空间,这样做的好处是:避免了数据拷贝开销、减少了内存占用、提高了代码加载速度。
```go
// 简化的内存映射实现
func mmap(file *os.File, size int) ([]byte, error) {
return syscall.Mmap(
int(file.Fd()),
0,
size,
syscall.PROT_READ|syscall.PROT_EXEC,
syscall.MAP_SHARED,
)
}
```2. 符号解析优化:Plugin 采用了高效的符号解析机制
● 延迟加载:只有在实际使用时才解析符号、避免了不必要的符号解析开销
● 符号缓存:已解析的符号会被缓存、重复使用时直接从缓存获取
● 哈希表实现:使用哈希表存储符号信息,查找复杂度为 O(1)
3. 类型校验优化:Plugin 的类型校验也做了性能优化,优化措施包括:类型信息缓存、延迟检查、批量验证。
```go
// 类型信息缓存
var typeCache = sync.Map{}
func lookupType(name string) (reflect.Type, bool) {
if t, ok := typeCache.Load(name); ok {
return t.(reflect.Type), true
}
return nil, false
}
```4. 性能测试数据:以下是一个简单的性能测试对比,可以看到:Plugin 调用的性能损耗仅比直接调用多 26%。
```go
func BenchmarkPluginCall(b *testing.B) {
p, _ := plugin.Open("plugin.so")
fn, _ := p.Lookup("Hello")
hello := fn.(func() string)
b.ResetTimer()
for i := 0; i < b.N; i++ {
hello()
}
}
func BenchmarkDirectCall(b *testing.B) {
for i := 0; i < b.N; i++ {
Hello()
}
}
```
```shell
BenchmarkPluginCall-8 10000000 158 ns/op
BenchmarkDirectCall-8 10000000 125 ns/op
```4. Arkit自定义插件实践
如果内置插件不能满足数据的解析需求,云舟观测还提供了自定义插件,用户可以使用go编写插件,实现灵活的数据解析和转换需求。
● 接口定义:
```go
type PluginParser interface {
Parse(string, map[string]string) map[string]interface{}
}
type pluginImp struct{}
var Plugin pluginImp
```● Parse方法:用户可以自行实现Parse方法对原始数据进行解析。Parse方法的输入为单行原始数据,输出支持两种类型:单行处理后的日志 以及 处理后的json格式(KV)数据。
示例1:输出单行日志——向日志行中添加主机名称
```go
package main //请勿修改
//请勿使用golang SDK之外的package
import (
"os"
)
//=====customization area begin, be my guest.
var hostname string
func getHostname() string {
hostname, err := os.Hostname()
if err != nil {
hostname = "unknown"
}
return hostname
}
/*
解析器插件必须实现下面的方法
输入:
line: 单行日志数据(不会为空)
params: 使用插件时传入的参数,KV均为string(若使用插件时没有传入参数,可能为空)
输出: string(处理后的文本数据)
*/
func parse2String(line string, params map[string]string) string { //请勿修改本方法签名(方法名,输入参数类型,输出参数类型)
//=====查询使用params参数 begin,例如:是否存在名为paramXKey的参数
//if paramXValue, ok := params["paramXKey"]; ok {
//若查询到,则可以自行使用该参数的值:paramXValue
//}
//=====查询使用params参数 end
if hostname == "" {
hostname = getHostname()
}
return hostname + " " + line
}
//=====customization area end, sayonara.
```示例2:输出json格式(KV)数据——解析nginx原始日志中的所有字段:
```go
package main //请勿修改
//请勿使用golang SDK之外的package
import (
"regexp"
"strings"
)
//=====custom area begin, be my guest.
var regex = regexp.MustCompile(`^"([^"]*)"\s+(\d+)\s+(\d+)\s+"([^"]*)"\s+"([^"]*)"\s+"([^"]*)"\s+([\d]*)\s+([\d.]*)\s+([0-9|.|:]*)\s+([\d]+)\s+([\d.]*)云舟观测:Arkit数据解析插件详解 - 今日头条 )
//日志格式
//上半部分:$remote_addr - $remote_user [$time_local]
//下半部分:"$request" $status $body_bytes_sent "$http_host" "$http_referer" "$http_user_agent" $request_length $request_time $upstream_addr $upstream_response_time
/*
解析器插件必须实现下面的方法
输入:
line: 单行日志数据(不会为空)
params: 使用插件时传入的参数,KV均为string(若使用插件时没有传入参数,可能为空)
输出: map对象(处理后的JSON数据)
*/
func parse2JSON(line string, params map[string]string) map[string]interface{} { //请勿修改本方法签名(方法名,输入参数类型,输出参数类型)
results := make(map[string]interface{}, 1)
halfArr := strings.Split(line, "] ")
if len(halfArr) != 2 {
results["raw"] = line
return results
}
arr1 := strings.Fields(halfArr[0])
if len(arr1) != 5 {
results["raw"] = line
return results
}
results["remote_addr"] = arr1[0]
results["remote_user"] = arr1[2]
results["time_local"] = strings.ReplaceAll(arr1[3], "[", "") + " " + arr1[4]
d := regex.FindStringSubmatch(halfArr[1])
if len(d) != 12 {
results["raw"] = halfArr[1]
return results
}
results["request"] = d[1]
results["status"] = d[2]
results["body_bytes_sent"] = d[3]
results["http_host"] = d[4]
results["http_referer"] = d[5]
results["http_user_agent"] = d[6]
results["request_length"] = d[7]
results["request_time"] = d[8]
results["upstream_addr"] = d[9]
results["upstream_status"] = d[10]
results["upstream_response_time"] = d[11]
return results
}
//=====custom area end, sayonara.
```5. 局限性
尽管 Go Plugin 性能优异,但也存在一些限制:
- 平台限制:实现基于操作系统的动态链接库机制。目前仅支持 Linux,不支持 Windows
- 版本依赖:插件和主程序必须使用相同的 Go 版本;依赖的三方库版本需要一致
- 调试复杂性:插件代码调试相对困难;错误追踪不够直观
关于云舟观测
云舟观测是由360智汇云推出的一款一站式数据采集与监控观测产品,可以对基础设施、应用性能,以及云原生下业务指标和日志进行全面的监控和观测,构建全链路的可观测性服务,帮助用户及时发现和解决系统及应用性能问题,提高系统的稳定性和可靠性。
更多技术和产品文章,请关注
如果您对哪个产品感兴趣,欢迎留言给我们,我们会定向邀文~
360智汇云是以"汇聚数据价值,助力智能未来"为目标的企业应用开放服务平台,融合360丰富的产品、技术力量,为客户提供平台服务。目前,智汇云提供数据库、中间件、存储、大数据、人工智能、计算、网络、视联物联与通信等多种产品服务以及一站式解决方案,助力客户降本增效,累计服务业务1000+。智汇云致力于为各行各业的业务及应用提供强有力的产品、技术服务,帮助企业和业务实现更大的商业价值搜索“360智汇云”
- 上一篇:Linux 下用 awk 分析日志文件实例
- 下一篇:405状态码Nginx解决办法
相关推荐
- sql数据库自学(数据库入门必看——《sql基础教程》)
-
SQLServer数据库基础知识:1.数据库是由数据组成的,这些数据可以被组织成有序的数据结构,以支持特定的应用程序。2.数据库管理系统(DBMS)是一种软件工具,用于创建、管理和操作数据库。...
- 无线网连接不可上网怎么回事
-
可能有几下几方面原因:1、无线路由器网络参数设置错误,无法拨通ISP运营商的局端设备,无法接入互联网;2、宽带线路出现故障,路由器无法拨通ISP运营商的局端设备,无法连通;3、宽带DNS服务器由于某种...
- 恢复大师app下载(恢复大师app下载软件)
-
是真的。开心手机恢复大师是一款苹果手机数据恢复软件,可以恢复删除的微信聊天记录、短信、通讯录、备忘录、qq聊天记录等17种数据。我测试了一下,确实是可以恢复的。而且开心手机恢复大师是可以免费试用的,是...
- windowsxp下载网站(windows xp download)
-
目前无法下载因为红色警戒XP电脑版是一款已经停止开发的游戏,官方已经停止了对其的支持和更新。虽然网上有一些模拟器可以运行该游戏,但是安装和使用相对困难,而且可能存在版权问题。建议玩家选择其他同类型的游...
- 没人用过的激活码没过期(没人用过的激活码没过期可以用吗)
-
迷你世界并不存在什么激活码的。《迷你世界》是一款高度自由的休闲类3D沙盒游戏,有着非常方便快捷的多人联机模式,只要有网络就能和各个地方的小伙伴们一起玩。这里没有等级和规则限制,没有规定的玩法,只有随心...
- 2017年联想笔记本电脑有几款
-
17年的笔记本电脑可以勉强安装一下win10系统试试。关键看你的内存有多少,内存大于4个G的话可以安装win10速度不会太慢。最好是安装win7系统,这样能发挥你这台电脑的所有的性能,你用起来也会感觉...
- 当前显卡排名(当下显卡排行)
-
101、Irispro5802、Iris62002、Iris52004、UHD630/6205、HD6306、HD5307、HD46008、HD44009、HD420010、HD40...
- win10专业版激活变成企业版(win10专业版激活变成企业版怎么办)
-
win10永久激活密钥很少,一旦网上有分享,等你拿到时就超过期限了,一般是要购买。激活win10系统可以使用激活工具:win10激活工具下载一、win10专业版产品密钥NXRQM-CXV6P-PBGV...
- ghostwinxp下载纯净版(ghost win7纯净版下载)
-
可以下载的,现在官网和其他网站上都可以下载xp原版的。可以通过以下步骤下载我的世界游戏到xp系统中:1.首先打开你的浏览器软件,搜索关键字“我的世界xp版下载”,找到可靠下载地址;2.从下载页面下...
- 惠普完整版驱动(惠普最新驱动)
-
惠普官方的标准操作:HP1050安装驱动步骤:一:准备:拿出驱动光盘放入光驱或到HP官网下载完整版驱动。二:不要插USB数据线或插上线打印机电源不要开,安装完整版驱动,当程序提示插入USB数据线时,插...
- 浏览器最好用的(浏览器最好用的插件)
-
一、谷歌浏览器谷歌浏览器是公认最好用的,这个可以从市场占有率看出端倪,超过三分之二的用户使用谷歌浏览器。Chrome浏览器以简洁快速著称,不管是普通用户还是开发人员,chrome浏览器都是首选。Chr...
- fast路由器6位初始密码(fast路由器的密码)
-
答:fast路由器初始密码是admin;新款的迅捷无线路由器,管理界面没有初始密码。查看迅捷无线路由器底部标签,标签上标注了admin,说明初始密码就是admin;如果没有,说明该路由器没有初始密码。...
- 硬盘恢复软件哪个好(硬盘 恢复软件)
-
迷你兔数据恢复工具:支持恢复硬盘丢失的数据Pc3000数据恢复软件是一款非常专业的硬盘修复工具,能够对电脑硬盘资料数据进行修复,通过使用这个软件可以解决硬盘数据丢失故障,是一个用户进行硬盘资料修复好帮...
- 十大品牌监控摄像头排名(十大品牌监控摄像头排名第一)
-
答:1、华为/HUAWEI9.92、小米/MI9.63、罗技/Logitech9.64、海康威视/HIKVISION9.25、乔安/Jooan9.26、普联/TP-LINK9.27、乐橙8.98、萤石...
欢迎 你 发表评论:
- 一周热门
-
-
抖音上好看的小姐姐,Python给你都下载了
-
全网最简单易懂!495页Python漫画教程,高清PDF版免费下载
-
Python 3.14 的 UUIDv6/v7/v8 上新,别再用 uuid4 () 啦!
-
飞牛NAS部署TVGate Docker项目,实现内网一键转发、代理、jx
-
python入门到脱坑 输入与输出—str()函数
-
宝塔面板如何添加免费waf防火墙?(宝塔面板开启https)
-
Python三目运算基础与进阶_python三目运算符判断三个变量
-
(新版)Python 分布式爬虫与 JS 逆向进阶实战吾爱分享
-
失业程序员复习python笔记——条件与循环
-
系统u盘安装(win11系统u盘安装)
-
- 最近发表
- 标签列表
-
- python计时 (73)
- python安装路径 (56)
- python类型转换 (93)
- python进度条 (67)
- python吧 (67)
- python的for循环 (65)
- python格式化字符串 (61)
- python静态方法 (57)
- python列表切片 (59)
- python面向对象编程 (60)
- python 代码加密 (65)
- python串口编程 (77)
- python封装 (57)
- python写入txt (66)
- python读取文件夹下所有文件 (59)
- python操作mysql数据库 (66)
- python获取列表的长度 (64)
- python接口 (63)
- python调用函数 (57)
- python多态 (60)
- python匿名函数 (59)
- python打印九九乘法表 (65)
- python赋值 (62)
- python异常 (69)
- python元祖 (57)
