应用监控 eBPF 版:实现高效协议解析的技术探索

2024-01-24 07:20

本文主要是介绍应用监控 eBPF 版:实现高效协议解析的技术探索,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

作者:彦鸿

引言

随着 Kuberentes 等云原生技术的飞速发展,带来了研发与运维模式的变革。企业软件架构由单体服务向分布式、微服务演进。随着业务发展,多语言、多框架、多协议的微服务在企业中越来越多,软件架构复杂度越来越高,如何快速通过可观测工具快速定位出问题对研发人员至关重要。为满足全场景、端到端的应用监控需求,应用实时监控服务 ARMS 推出应用监控 eBPF 版,通过 eBPF 技术完善整个应用监控体系。应用监控 eBPF 版提供无侵入、语言无关的可观测能力。

详细产品介绍: 多语言应用监控最优选,ARMS 应用监控 eBPF 版正式发布

使用 eBPF 来进行可观测性需要进行应用层协议解析,但云上微服务软件架构中的应用层协议往往比较复杂,这也给协议解析带来了不小的挑战。传统的协议解析方式存在 CPU、内存占用高,错误率高等问题,在应用监控 eBPF 版中,我们提出一种高效的协议解析方案,实现对应用层协议的高效解析。

eBPF 技术简介

eBPF(扩展的 Berkeley 包过滤器)是一种强大的技术,允许开发人员在 Linux 内核中安全地运行预编译的程序,而不改变内核源码或加载外部模块 [ 1] 。这一独特的能力使得 eBPF 成为构建现代、灵活且高效的应用监控工具的理想选择。

图片

图 2.1 eBPF 示意图

在可观测性方面,eBPF 优势尤为突出:

  • 实时性: eBPF 能够实时捕获和分析数据,为开发者提供即时的性能反馈。
  • 精确性: 通过精细的 hook 函数(hook points),eBPF 可以在系统的具体点进行监控,从而准确地收集所需数据。
  • 灵活性: 开发者可以编写定制的 eBPF 程序来监控特定事件,使其能够适应各种复杂的监控需求。
  • 低开销: eBPF 程序直接在内核空间运行,避免了传统监控工具中频繁的用户空间和内核空间之间的上下文切换。
  • 安全性: eBPF 程序在执行前必须通过内核的严格检查,确保不会危及系统安全。

传统的协议解析方案

图片

图 3.1 传统协议解析方案架构图

3.1 传统方案的解析流程

基于 eBPF 来做数据抓取和协议解析的传统方案主要分为:

  • 数据采集
  • 数据传递
  • 协议解析

其中数据采集主要在内核态,数据传递介于内核态和用户态,协议解析在用户态进行。具体的,数据采集的流程为 eBPF 使用 kprobe 或 tracepoint 方式,从内核中抓取到流量事件即 event,这些事件中有控制层面的事件如从 connect、close 等系统调用处采集到的事件。也有数据层面的事件,如从 read、write 等系统调用处采集到的事件。待数据采集到内核事件后,我们需要将数据从内核态传递至用户态去做进一步的处理。在 eBPF 中,我们采用 perf buffer(一种特殊 eBPF Map)来做数据传递。数据存放到整个 perf buffer 后,在用户态进行协议解析。

3.2 传统方案中存在的问题

传统的解析方案中 CPU、内存占用过高,在高流量场景下错误率较高,主要体现在以下三个方面:

  • 高内存占用: 数据采集中无法筛选协议,导致大量无关数据占满 perf buffer,引发内存过高。
  • 事件丢失风险: 高 QPS 导致 perf buffer 迅速填满,处理不及时会丢失事件,特别是控制层事件可能因数据层事件过多而丢失。
  • 解析效率低: 需要遍历尝试所有支持协议才能找到正确的协议,导致大量无效解析,增加 CPU 负担。

实现高效协议解析的技术探索

4.1 高效的协议解析方案流程

鉴于上文所述传统方案中存在的问题,本文提出一种高效的协议解析方案,本文所述方案主要分为四部分:

  • 数据采集
  • 协议推断
  • 事件分流
  • 协议解析

其中协议解析由分为:

  • 连接维护
  • 数据分帧
  • 协议解析
  • 请求-响应匹配

图片

图 4.2 本文所述协议解析框架图

如图 4.2 所示,eBPF 首先在内核态中采集到数据,根据协议帧头进行协议推断。根据协议推断的结果,可以初步判断改数据帧是否是所支持的协议。如果判断为“是”,才传递至用户态进行进一步解析,否则不进行处理。进行简单的事件过滤后,本文根据事件的类型进行事件分流。

如控制事件放到 control events perf buffer 中,数据事件放到 data event perf buffer。事件传递至用户态后控制事件将用于连接维护,数据事件根据其数据流向,分别放入发送队列和接收队列中。然后周期性的从对队列中的数据进行分帧处理,这样可以很好的解决单发多收、多发单收、多发多收等场景。从接受队列或发送队列(也可以理解为数据流)中拆解出单独的帧数据后将会通过按照内核态中推断的协议类型去匹配对应的协议解析器进行进一步解析。分别解析出请求与响应后,需要去匹配请求和响应,完成一个完成的可观测记录,即 record,后续也将通过 record 来生成可观测中的 Span。

本章后续小节将会重点讲解图 4.2 中的关键流程,即协议推断器(protocol infer)、协议解析进行详解。

4.2 协议推断(protocol infer)

顾名思义,协议推断主要用于在采集到数据包时,通过协议的协议帧头来推测是否是支持的协议类型。如果是支持的类型则将数据传递至用户态进行进一步处理。

以 MySQL5.7 协议为例:在 MySQL5.7 协议中,如果第一帧数据为 MySQL 的命令帧,如图 4.3 所示,命令帧有以下几种类型,具体见 MySQL 官方文档协议 [ 2]

图片

图 4.3 MySQL 命令帧

但在这里,具体是不是真的是 MySQL 协议还到用户态解析时进一步确认。基于此,我们在内核中先通过简单的判读进行推断,简易的推断代码如下:

static __inline enum protocol_type_t infer_mysql(const char* buf, size_t count) {static const uint8_t query = 0x03;static const uint8_t connect = 0x0b;static const uint8_t stmtPrepare = 0x16;static const uint8_t stmtExecute = 0x17;static const uint8_t stmtClose = 0x19;if (buf[0] == connect || buf[0] == query || buf[0] == stmtPrepare || buf[0] == stmtExecute ||buf[0] == stmtClose) {return request;}return unknown;
}

4.3 协议解析(conn tracker)

整个协议解析流程主要是在 conn tracker 组件中进行,其主要的能力有:

  • 连接维护
  • 数据分帧
  • 协议解析
  • 请求-响应匹配

具体的,在长连接场景下每次数据传输的基本元数据信息,如 source ip、 source port、dest ip、dest port 等信息总是相同的。如图 4.4 所示,如果我们能够在用户态维护其连接信息,那这部分连接相关的元数据信息就不必每次都放入 perf buffer 中,只用传递连接 ID 即可,进一步降低网络带宽。

图片

图 4.4 conn tracker 连接维护图

其次有部分协议,如 MySQL 协议,有部分 MySQL 相关信息,如版本号,编码等信息只在初次建立连接时候会发送包信息,如果用户态没有维护连接信息,则这部分元数据信息将无法解析。

上文提到的内核中采集到的数据会放置接收队列、发送队列两个队列中,也可以理解为数据流。从整个数据流中分解出每一帧的数据是进行协议解析的前提。基本思路是根据每种协议的结束帧标识来做判断,如 MySQL 响应的 EOF 帧信息。图 4.5 所示为 MySQL 协议分帧示意图。

图片

图 4.5  MySQL 协议分帧示意图

分解出每一帧的数据后,就按照各个协议进行协议解析即可。

图片

图 4.6 MySQL 协议解析示意图

在可观测中,我们需要有一个完整的请求-响应记录。以 MySQL 协议为例,由于 MySQL 协议是按照时间序有序的,请求的时间序和响应的时间序能进行对应,响应总是以 EOF 结束,EOF 帧为以下形式。

图片

图 4.7 MySQL Response 结束帧(EOF)

参考 MySQL 官方文档 [ 2]

图片

图 4.8 MySQL 请求-响应匹配示意图

总结

基于 eBPF 因其高性能,低开销,无侵入等特点近年来成为可观测性的研究热点。基于 eBPF 来进行应用监控必须进行协议解析。当前传统的协议解析方案存在 CPU、内存开销大,错误率高等问题。基于此本文提出一种高效的协议解析框架,并在阿里云应用实时监控服务 ARMS  “应用监控 eBPF 版” [ 1] 中正式发布。成功接入后将会出现如下的应用监控展示大盘,以下是展示示意图。

使用的测试项目 github 地址:alibabacloud-microservice-demo [ 3]

图片

图 5.1 应用监控 eBPF 版-概览

图片

图 5.2 应用监控 eBPF 版-数据库分析

图片

图 5.2 应用监控 eBPF 版-应用拓扑

具体接入流程见下方链接,仅需一分钟即可无死角监控您的应用。详细情况可进钉钉群(群号:35568145)进行交流。

目前,应用监控 eBPF 版处于免费使用阶段,并于近期将会推出网络监控、数据库分析、CPU Profiling [ 1] 等能力。欢迎开发者体验与使用。点击阅读原文,立即体验。

参考链接:

[1] 多语言应用监控最优选,ARMS 应用监控 eBPF 版正式发布

[2] https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_com_init_db.html

[3] https://github.com/aliyun/alibabacloud-microservice-demo

这篇关于应用监控 eBPF 版:实现高效协议解析的技术探索的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/638928

相关文章

C++中unordered_set哈希集合的实现

《C++中unordered_set哈希集合的实现》std::unordered_set是C++标准库中的无序关联容器,基于哈希表实现,具有元素唯一性和无序性特点,本文就来详细的介绍一下unorder... 目录一、概述二、头文件与命名空间三、常用方法与示例1. 构造与析构2. 迭代器与遍历3. 容量相关4

Java中Redisson 的原理深度解析

《Java中Redisson的原理深度解析》Redisson是一个高性能的Redis客户端,它通过将Redis数据结构映射为Java对象和分布式对象,实现了在Java应用中方便地使用Redis,本文... 目录前言一、核心设计理念二、核心架构与通信层1. 基于 Netty 的异步非阻塞通信2. 编解码器三、

C++中悬垂引用(Dangling Reference) 的实现

《C++中悬垂引用(DanglingReference)的实现》C++中的悬垂引用指引用绑定的对象被销毁后引用仍存在的情况,会导致访问无效内存,下面就来详细的介绍一下产生的原因以及如何避免,感兴趣... 目录悬垂引用的产生原因1. 引用绑定到局部变量,变量超出作用域后销毁2. 引用绑定到动态分配的对象,对象

SpringBoot基于注解实现数据库字段回填的完整方案

《SpringBoot基于注解实现数据库字段回填的完整方案》这篇文章主要为大家详细介绍了SpringBoot如何基于注解实现数据库字段回填的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解... 目录数据库表pom.XMLRelationFieldRelationFieldMapping基础的一些代

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关

Java 虚拟线程的创建与使用深度解析

《Java虚拟线程的创建与使用深度解析》虚拟线程是Java19中以预览特性形式引入,Java21起正式发布的轻量级线程,本文给大家介绍Java虚拟线程的创建与使用,感兴趣的朋友一起看看吧... 目录一、虚拟线程简介1.1 什么是虚拟线程?1.2 为什么需要虚拟线程?二、虚拟线程与平台线程对比代码对比示例:三

一文解析C#中的StringSplitOptions枚举

《一文解析C#中的StringSplitOptions枚举》StringSplitOptions是C#中的一个枚举类型,用于控制string.Split()方法分割字符串时的行为,核心作用是处理分割后... 目录C#的StringSplitOptions枚举1.StringSplitOptions枚举的常用

Python函数作用域与闭包举例深度解析

《Python函数作用域与闭包举例深度解析》Python函数的作用域规则和闭包是编程中的关键概念,它们决定了变量的访问和生命周期,:本文主要介绍Python函数作用域与闭包的相关资料,文中通过代码... 目录1. 基础作用域访问示例1:访问全局变量示例2:访问外层函数变量2. 闭包基础示例3:简单闭包示例4

Python实现字典转字符串的五种方法

《Python实现字典转字符串的五种方法》本文介绍了在Python中如何将字典数据结构转换为字符串格式的多种方法,首先可以通过内置的str()函数进行简单转换;其次利用ison.dumps()函数能够... 目录1、使用json模块的dumps方法:2、使用str方法:3、使用循环和字符串拼接:4、使用字符