服务拓扑串联难?eBPF为滴滴可观测带来解题新思路

news2024/10/6 18:33:24

上篇文章我们讲到可观测性在滴滴的实践与落地,更多关注的是不同观测信号之间的关联关系。那服务与服务之间的关系又如何串联,业界当前爆火的 ebpf 又在滴滴有着怎样的应用,本文为你揭晓。

背景

业务介绍:业务接口调用观测

滴滴可观测平台除了负责滴滴 MTL 能力的建设,还涉及更偏向业务侧的数据及服务接口调用观测。

关于接口调用拓扑观测,这里先解释下以免引起歧义。如下图描述了一个调用关系:

167ef0f3c91c5d3530bc61c8b920bd46.png

一次请求、响应过程

这里用[caller=A, caller-func=/a, callee=B, callee-func=/b],简写成[A, /a, B, /b],以及 [A, /a, C, /c]来描述A服务的/a触发后调用B:/b以及C:/c的动作。在获取到足够多的接口调用数据时,通过给定某个业务的若干个调用入口(如上述示例中的[A, /a]),通过对接口调用链路的不断串联,可以梳理出该业务若干个重要的调用链路。

调用链路的构建对于服务稳定性保障有重要意义,无论是容灾放火、业务按需扩容、高峰期业务状态巡检护堤等均依赖于核心调用链路的构建。从经验上来看,在实际故障处理以及容量评估时,接口级的调用拓扑比服务级或者容器/物理机级的调用拓扑要有效很多。

e6bfc8fede2641554a4c4f614049968a.png

一般来说,接口粒度的服务拓扑可以通过调用日志或者调用 metric 来进行串联。滴滴可观测早些时候采用调用日志+调用 metric 相结合的方式生成服务接口调用拓扑。后来随着统一服务治理的推进,业务上报 metric 完全可以覆盖调用日志里的调用关系,且生成接口拓扑的成本大幅降低,因此就接口拓扑生成这一场景而言,已经调整为基于服务调用的 metric 数据来生成。

78c39b4ce5f0832ceccc58e872cfd324.png

通过metric串联接口拓扑的示意图

业务问题:服务接口拓扑的校验

看起来,通过接口调用 metric 来串联调用链路是一种通用的方式,但是其生成结果显然存在如下的问题:

  • 已生成的数据缺少校验方式。由于数据是业务方代码上报的,即使引入了通用的SDK,caller-func 信息也只能依赖于代码调用时主动传入。从实践经验来看,caller-func 的漏传错传问题比较明显。

  • 调用关系校验、生成成本高昂。依赖业务代码上报,意味着代码需要遵循相当的规范。较为核心的调用链路,推动代码的变更相对容易,业务配合度较高。但非核心的调用链路或已经稳定运行许久的遗留项目,代码的规范化变更是较难推动的。而手动添加则需要对项目进行人工梳理,对于存在近千个调用的链路而言,没有实际操作空间。

上述两个问题是使用 metric 串联业务接口拓扑时常见的问题。

以滴滴可观测的实践来看,当核心链路的复杂度达到以千计的量级,即使有专门的团队推动业务调用链路的 metric 接入治理,也会有相当比例的调用关系缺失或者错误。

67c5dd8b239d9d9faacc71a94d6fc3b2.png    

  理想情况下的正常结果        

062efd796b77e096a35a4b2af42d90d5.png

metric 信息错误时可能的结果

针对服务接口拓扑校验的问题,滴滴可观测通过探索,形成了基于eBPF(后文如无其他说明,简称BPF)技术进行服务接口拓扑无侵入采集的方案。通过 metric+BPF 采集相结合的方式,实现了接口拓扑数据的准确性验证、缺失数据补充。同时,进一步探索了可观测更深层次使用 BPF,如 MTL 的融合。

方案

BPF介绍

BPF 最早是伯克利包过滤器(Berkely Packet Filter)的简称,内核自3.15开始对 BPF 进行扩展,通过增加 BPF 程序寄存器个数、扩充 BPF 程序可使用内存以及增加多个BPF事件使得 BPF 具备高可定制性。为了和扩展前的 BPF进行区分,将3.15之前的BPF称为 cBPF(classic BPF),扩展后的 BPF 称为 eBPF (extended BPF),而 BPF 也从一种缩写更多的成为了一种技术的代称。

截至4.18版本的内核,BPF支持的部分事件类型及其简要介绍如下:

01b34802129aac4507339e7eee7967ea.png

本文涉及的内容有 uprobe 以及 kprobe,大多数的内核函数都可以通过 kprobe 来进行 hook。而在用户自定义程序中,符号表中存在的函数也均可通过 uprobe 进行 hook。

kprobe 和 uprobe 触发时,只能获取目标函数的参数或者堆栈信息。如下面一段代码是通过 bpftrace 来观测 /bin/bash 并通过获取 readline 返回值来观测用户 bash 命令的示例。

 
 
#!/usr/bin/bpftrace


BEGIN
{
  printf("开始观测bash...\n使用Ctrl-C停止\n");
}


uretprobe:/bin/bash:readline
{
  printf("cmd: %s\n", str(retval));
}

其中,bash 源码对 readline 的定义如下,参照目标函数的源码可以更好理解BPF 的逻辑。

/* Read a line of input. Prompt with PROMPT. A NULL PROMPT means none. */
extern char *readline (const char *);

执行后,当出现目标内核函数执行时,触发如下:

$ sudo bpftrace ./bashreadline.bt
Attaching 2 probes...
开始观测bash...
使用Ctrl-C停止
cmd: ls -l
cmd: pwd
cmd: crontab -e
cmd: clear

eBPF 在3.15内核引入后,其功能不断扩展。比较重大的一个扩展是在4.18内核中引入了BTF(BPF Type Format),BTF 技术使得 BPF 字节码的加载、使用变得更加简单。

BPF的开发

原生的 BPF 实现各种功能一般是使用受限的C语言调用 bpf-helpers 函数,而后使用 LLVM 将其编译成 BPF-code 字节码,通过系统调用进行加载。原生的C语言编写方式较为繁琐,iovisor 项目推出了 bcc 库来增强 BPF 的开发便捷度,同时维护了支持 one-liner风格、极具易用性的 bpftrace 工具。业内知名的 cilium 也维护了一个 cilium-ebpf。除了bcc、bpftrace、cilium-ebpf,亦有 长于全生产周期支持的 coolbpf、在 libc 基础上使用 rust 提供 BPF 支持的 aya 等工具。

08a499756970fdfeb6ab0543ced6d028.png

BPF生态,图源自ebpf.io

使用BPF解决服务接口拓扑问题

上一章节提到服务接口拓扑中无法对生成的拓扑数据进行校验,这样的问题目前在滴滴可观测是通过 BPF 来解决。这里通过一个简单的示例以及使用 bpftrace 脚本构建的解决方案来展示下效果。

示例:简单的golang服务

这里给出一个基于go1.16的简单的golang服务。从处理代码中可知,这里的四元组是 [local, /handle, local, /echo]。为了方便示例说明,这里的"handle"的逻辑和请求下游的逻辑是串行的,没有使用"goroutine"。这一点很重要,后面会进行说明。

func echo(c *gin.Context) {
  c.JSON(http.StatusOK, &Resp{
    Errno: 0,
    Errmsg: "ok",
  })


  return
}


/* 
s := http.Server{
  Addr: "0.0.0.0:9932",
}
r := gin.Default()
r.GET("/echo", echo)
r.GET("/handle", handle)
s.Handler = r
*/
func handle(c *gin.Context) {
  client := http.Client{}
  req, _ := http.NewRequest(http.MethodGet,
    "http://0.0.0.0:9932/echo", nil)
  resp, err := client.Do(req)
  if err != nil {
    fmt.Println("failed to request", err.Error())
    c.JSON(http.StatusOK, &Resp{
    Errno: 1,
    Errmsg: "failed to request",
  })
    return
  }


  respB, err := ioutil.ReadAll(resp.Body)
  if err != nil {
    fmt.Println("read resp failed")
    c.JSON(http.StatusOK, &Resp{
      Errno: 2,
      Errmsg: "failed to read request",
    })
    return
  }


  defer resp.Body.Close()


  fmt.Println("resp: ", string(respB))
  c.JSON(http.StatusOK, &Resp{
    Errno: 0,
    Errmsg: "request okay",
  })


    return
}

采集的逻辑及执行效果:

uprobe:./http_demo:net/http.serverHandler.ServeHTTP
{
  $req_addr = sarg3;
  $url_addr = *(uint64*)($req_addr+16);
  $path_addr = *(uint64*)($url_addr+56);
  $path_len = *(uint64*)($url_addr+64);


  // 在http请求触发处,依据pid将caller_func存储起来
  @caller_path_addr[pid] = $path_addr;
  @caller_path_len[pid] = $path_len;
  @callee_set[pid] = 0;
}


uprobe:./http_demo:"net/http.(*Client).do"
{
  // 依据 pid 获取 caller 信息
  printf("caller: \n caller_path: %s\n",
  str(@caller_path_addr[pid], @caller_path_len[pid]));
  $req_addr = sarg1;


  // 获取 callee 信息
  $addr = *(uint64*)($req_addr);
  $len = *(uint64*)($req_addr + 8);
  printf("callee: \n method: %s\n", str($addr, $len));


  $url_addr = *(uint64*)($req_addr + 16);
  $addr = *(uint64*)($url_addr + 40);
  $len = *(uint64*)($url_addr + 48);
  printf(" host: %s\n", str($addr, $len));


  $addr = *(uint64*)($url_addr + 56);
  $len = *(uint64*)($url_addr + 64);
  printf(" url: %s\n\n", str($addr, $len));


  @callee_set[pid] = 1
}


uprobe:./http_demo:"net/http.(*response).finishRequest"
{
  // 如果没有下游请求,单独输出
  if (@callee_set[pid] == 0){
    printf("caller: \n caller_path: %s\n",
    str(@caller_path_addr[pid], @caller_path_len[pid]));
    printf("callee: none\n\n");
    @callee_set[pid] = 1;
  }
}

使用采集脚本进行采集,结果如下:

# 启动采集
$ bpftrace ./http.bt
Attaching 2 probes... # 未触发请求前,停止在这里
caller: # 触发请求后,输出
caller_path: /handle
callee:
  method: GET
  host: 0.0.0.0:9932
  url: /echo
caller:
  caller_path: /echo
  callee: none


# 开始服务
$ ./http_demo &
# 触发请求
$ curl http://0.0.0.0:9932/handle

可以看到,bpftrace 脚本实现了对目标服务接口调用四元组的采集,而这是在目标服务未进行任何代码变更的情况下进行的,BPF 展示了其在可观测领域的魅力。

实际的方案覆盖及效果

通过上面的示例,展示了使用 BPF 进行接口拓扑观测的主要思路。需要说明的是,示例里使用的是 pid 作为 caller_map 里的 key,但在实际的工程中,由于 golang goroutine 与 pid 并非一一对应的,需要使用 goid 来作为 key。

同时,由于 handleFunc 里会使用新的 goroutine 来发起下游的请求,BPF 也需要对 goid 的派生关系进行维护,以避免某个 goid 关联的 caller 信息丢失。这样一来, 对于 golang 服务而言,实际的处理思路就很明确了。

5d0f24fc3e4cf1f4862455fa2c65f9e2.png

BPF观测服务拓扑的方案示意

上图是滴滴可观测现行的 golang 接口调用观测 BPF 方案,对方案进行总结,其核心在于:

  • 信息采集。包括 caller-func,callee,callee-func 等信息,均需要通过合适的 hook 点选择来获取。

  • 信息关联。基于 golang 服务的特性,使用 goid 进行关联。这就使得 caller 信息能够和 callee 信息相关联,以获取四元组。

目前滴滴可观测基于这样的思路,完成了对 golang 和 PHP 服务的覆盖。从实践结果来看,该方案对目标服务有效覆盖率约 80%。目标监控核心调用链路,经对 BPF 新增四元组的人工确认,无异常四元组。与基于 metric 的数据相对比,在部分核心调用链路,新增四元组调用可达20%。

问题

丢掉的关联性

上述方案确实是目前能够想到较为直观的方案。其中信息采集部分问题不大,虽使用了 uprobe,引入了对目标函数参数的依赖,但是就实际生产环境上使用的go1.10~go1.20而言,除了 go1.17 引入的函数调用规约需要适配外,其他必要的信息基本上没有变化。

信息关联部分比较麻烦,现有方案里是通过维护 goroutine 的派生关系来实现 caller 信息和 callee信息的关联,但现实往往不尽如意。比如,从实际的工程来看,下面的代码是会出现的:

/*用法1:通过channel来传递request。这种场景下,事件间的关联性丢失,无法形成四元组*/


var reqChan = make(chan *http.Request, 10)


func handle(w http.ResponseWriter, req *http.Request) {
  io.WriteString(w, "Hello, World\n")
  reqChan <- req // 这里通过channel来传递请求
  return
}


func handleReq() {
  for {
    select {
    case req, ok := <-reqChan:
      if !ok {
        log.Println("channel closed")
        return
      }


      log.Println("received, ", req.Host, req.Method)
      // do some stuff
      // 即使这里存在下游请求,也无法和caller关联起来。
    }
  }
}


func main() {
  go handleReq()
  http.HandleFunc("/hello", handle)
  http.ListenAndServe("0.0.0.0:9999", nil)
  return
}


type GoroutinePool interface {
  Start() (error, bool)
  AddTask(func())
  Stop() (error, bool)
}


var pool GoroutinePool


func handle(w http.ResponseWriter, req *http.Request) {
  io.WriteString(w, "Hello, World\n")


  pool.AddTask(func() {
    // 这里由于采用了goroutine池,goroutine间的派生关系  会丢失,事件无法有效串联
    handleReq(req)
  })
  return
}


func handleReq(req *http.Request) {
  log.Println("received, ", req.Host, req.Method)
  // do some stuff
}


func main() {
  // init pool
  // pool = New()
  http.HandleFunc("/hello", handle)
  http.ListenAndServe("0.0.0.0:9999", nil)
 return
}

上述的两个场景由于无法获取 goroutine 的派生关系,现有的方案将无法获取四元组,类似的问题会影响 BPF 的采集效果。从现有经验来看,golang 工程中受类似代码影响的四元组占比在20%以内。

uprobe:适配的复杂性

经过上节的介绍,可知滴滴可观测是基于 uprobe 构建的服务接口拓扑观测方案。

BPF uprobe 的使用具有处理数据高效、整体方案直观的特点。由于 uprobe 更接近于用户的代码,因此对于用户感知较强的问题更加得心应手,如框架中慢函数调用等。

但大多数的项目使用更多的是 kprobe,比如 bpftrace 中的很多实用工具。deepflow 的观测能力大都是在 kprobe 的基础上构建的,kindling 涉及网络数据处理的内容也是基于 kprobe 进行处理的。

目前在实际使用中,完全按照 uprobe 构建方案的项目仍属少数。究其原因, uprobe 的使用存在如下两个缺点:

  • 通用性较差。通过方案介绍可知,基于 uprobe 的方案和语言(甚至是框架)是强相关的。且在目标程序符号表不存在的情况下,uprobe 无法进行工作。这意味着如果目标使用场景不明确,使用 uprobe 就需要对每个具体的场景进行适配,整体的投入、产出将会很低。

  • 性能问题。uprobe 触发时,会涉及到用户态和内核态的两次切换,这意味着单次执行 uprob 时,其性能开销很高(单个 uprobe 的触发耗时在1us左右,而单个 kprobe 的触发耗时则在100ns左右)。当被 hook 的函数频繁触发时,目标进程的性能将会很差。

尽管 uprobe 存在上述所说的缺陷,滴滴可观测仍然选择了基于 uprobe 来构建方案,主要因为 uprobe 的开发效率更快,成本更低。

使用 uprobe 来开发,所见即所得。数据不存在退化,关键信息无须从传输层报文中获取。不仅节省了开发时间,处理的复杂性也大大降低:考虑一个长 http 报文,uprobe 可以直接从目标函数获取需要的数据,比如 URL 信息,而 kprobe 则会触发多次,且需要对报文进行解析以获取所需要的信息。就目前来看,滴滴可观测的 ebpf-agent 线上实际 CPU 开销常态在单核的10%以下(一般的业务进程,含 PHP 进程,路由 nginx 服务CPU会高些),对目标进程的性能影响几乎不会被感知。

展望

用户态VM的需求

滴滴可观测使用了大量的 uprobe ,在离线环境上,单个物理机常态运行1500多个 uprobe 的 hook 点。将来随着 BPF 功能的延伸,uprobe hook 点的数量还会增加。大量的 uprobe 放到内核中,不仅对内核造成稳定性压力,而且由于BPF VM 运行在内核态,使得 uprobe 触发时会导致程序触发内核态和用户态的2次切换,对目标进程的函数执行造成延迟。

这两点都让用户态的 VM 使用无法避免。只有将 uprobe 切换到用户态的 VM 执行,uprobe 的耗时才能降下来,大规模使用 uprobe 才不会对目标服务造成太大的影响。

基于BPF的MTL融合方案

当我们重新审视 bpf-helpers 时可以看到这样一个有意思的函数:

long bpf_probe_write_user(void *dst, const void *src, u32 len)
 
Description
Attempt in a safe way to write len bytes from the buffer src to dst in memory. 
It only works for threads that are in user context, 
and dst must be a valid user space address.
 
This helper should not be used to implement any kind of security mechanism because of TOC-TOU attacks, 
but rather to debug, divert, and manipulate execution of semi-cooperative processes.
 
Keep in mind that this feature is meant for experiments, 
and it has a risk of crashing the system and running programs.  
Therefore, when an eBPF program using this helper is attached, 
a warning including PID and process name is printed to kernel logs.
 
Return 0 on success, or a negative error in case of failure.

这个函数的功能就强大了,意味着 BPF 的数据可以直接写入目标进程的空间,扩充了 BPF 的使用范围。而在 MTL 融合的过程中, 比较棘手的问题是 trace 信息无法有效关联到 metric 以及 log 中。

6be523942eb231c6c92decda3b653670.png

原始的MTL融合方案

如上图所示,当 metric 或者 log 上报时没有上报正确的 trace 信息,则 metric 及 log 将无法关联到 trace 中。

而如果每个请求的处理链路被 BPF 正常维护,且 BPF 维护了该请求的 trace 信息,metric 和日志在生成时,自然就可以和trace关联起来。下图分别展示了  BPF 增强的三种方案:

9b262538a697116074be395554056a3f.png

BPF增强的MTL融合方案

af38c1e1da6e866c66d4a0731859b26d.png

BPF+SDK的MTL融合方案

8c99dddf7437a840df2d6e32a5bad29c.png

BPF为主的MTL融合方案

总结

有了各种观测采集手段,收集了大量的观测数据。这些数据是直接事无巨细地交付给用户,还是按指定维度聚合后展示,聚合使用什么样的计算引擎,spark 还是 flink?

下篇文章将为您呈现滴滴的可观测团队是如何实现数据计算的,敬请期待。


云原生夜话

你期待eBPF技术能够解决可观测的哪些问题?欢迎在评论区留言,如需与我们进一步交流探讨,也可直接私信后台。

作者将选取1则最有意义的留言,送出滴滴元气牛仔托特包,9月21日晚9点开奖。

c286217af04dfe0e7adccc1a12f7c7cd.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1011702.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于Linux对MySQL数据库的安全加固指南(超实用--实战版)

&#x1f468;‍&#x1f393;博主简介 &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01; &#x1f40b; 希望大家多多支…

零基础学前端(四)重点讲解 CSS

1. 该篇适用于从零基础学习前端的小白 2. 初学者不懂代码得含义也要坚持模仿逐行敲代码&#xff0c;以身体感悟带动头脑去理解新知识 3. 初学者切忌&#xff0c;不要眼花缭乱&#xff0c;不要四处找其它文档&#xff0c;要坚定一个教授者的方式&#xff0c;将其学通透&#xff…

柏曼的护眼台灯怎么样?明基、书客、柏曼护眼台灯测评对比

台灯是最常见的一种照明灯具&#xff0c;基本每家每户都有着一台&#xff0c;大多数是给孩子学习使用的。而且现在的孩子学习压力都非常繁重&#xff0c;导致很多孩子早早就开始近视了。不少家长也开始重视孩子的视力健康&#xff0c;给孩子挑选护眼台灯。不过市面上的护眼台灯…

抖去推爆款视频生成器怎么制作开发?--短视频矩阵系统研发

在当今的数字时代&#xff0c;短视频已经成为一种非常受欢迎的内容形式。人们通过观看短视频来获取娱乐、学习和营销信息。然而&#xff0c;制作优秀的爆款视频并不容易&#xff0c;这需要创意、技能和时间。为了简化这一过程&#xff0c;抖去推推出了一款爆款视频生成器&#…

无CDN场景下的传统架构接入阿里云WAF防火墙的配置实践

文章目录 1.配置网站接入WAF防火墙1.1.配置网站接入方式1.2.填写网站的信息1.3.WAF防火墙生成CNAME地址 2.配置WAF防火墙HTTPS证书3.修改域名DNS解析记录到WAF防火墙4.验证网站是否接入WAF防火墙 传统架构接入WAF防火墙非常简单&#xff0c;配置完WAF网站接入后&#xff0c;将得…

KPM算法

概念 KMP&#xff08;Knuth–Morris–Pratt&#xff09;算法是一种字符串匹配算法&#xff0c;用于在一个主文本字符串中查找一个模式字符串的出现位置。KMP算法通过利用模式字符串中的重复性&#xff0c;避免无意义的字符比较&#xff0c;从而提高效率。 KMP算法的核心思想是…

【持续记录】深度学习环境配置

1080面对Transformer连勉强也算不上了&#xff0c;还是要去用小组公用的卡 完整记一个环境配置&#xff0c;方便后面自用✍️ nvidia-smi查看GPU信息 ** CUDA版本12.2 conda -V查询conda版本 22.9.0 新建conda环境 准备装python3.8 conda create --name caiman python3.8.2激…

Apollo自动驾驶平台的未来展望:从智能出行到城市管理

引言&#xff1a; 自动驾驶技术已经从科幻概念变为现实&#xff0c;而Baidu的Apollo自动驾驶平台正处于这一技术浪潮的前沿。但Apollo的潜力远远不止于此&#xff0c;它有可能引领整个城市的未来发展。本文将探讨Apollo自动驾驶平台在未来的展望&#xff0c;从智能出行到城市管…

VS Code用AI写代码:Codeium插件

文章目录 Codeiumchat代码生成 Codeium Codeium是基于边缘计算的代码AI工具&#xff0c;提供超过70种编程语言的代码补全、对话、搜索等功能&#xff0c;相当霸道。 在插件栏搜索到Codeium之后&#xff0c;需要科学上网安装&#xff0c;安装完成后会提示注册。注册之后&#…

这应该是Linux用户与用户组最详细的知识了吧!

【微|信|公|众|号&#xff1a;厦门微思网络】 Linux学习专栏​ 1、用户和用户组文件 在 linux 中&#xff0c;用户帐号&#xff0c;用户密码&#xff0c;用户组信息和用户组密码均是存放在不同的配置文件中的。 在 linux 系统中&#xff0c;所创建的用户帐号和其相关信息 (密…

考虑可再生能源消纳的建筑综合能源系统日前经济调度模型(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

基于智能远程监考方案,云上组考打造考试新范式

热潮兴起&#xff0c;「云上组考」是怎样炼成的&#xff1f; 疫情以来&#xff0c;改变了很多场景形态&#xff0c;“考试”是其中之一。 越来越多的学校开始采用云上组考模式&#xff0c;提高考试效率&#xff0c;节省人力、物力成本&#xff0c;规范考试管理&#xff0c;引发…

掌握Linux服务器:构建、管理和优化稳健的互联网基础设施

&#x1f482; 个人网站:【工具大全】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 引言 Linux服务器是构建…

基于SpringBoot+Vue+MySQL实现的高校固定资产管理系统源代码+数据库

基于 Vue 和 SpringBoot 的高校固定资产管理系统 完整代码下载地址&#xff1a;高校固定资产管理系统 软件简介 基于 Vue 和 SpringBoot 的高校固定资产管理系统&#xff0c;用于实现高校对固定资产的管理需求&#xff0c;包含资产品类、资产单位、资产仓库、资产供应商、资…

分布式文件系统的新兴力量:揭秘Alluxio的元数据管理机制【文末送书】

文章目录 写在前面01 分布式文件系统元数据的常见类型1.1 文件&#xff08;inode&#xff09;元数据1.2 数据块&#xff08;block&#xff09;元数据1.3 Worker元数据 02 分布式文件系统元数据的存储模式2.1 元数据存储在堆上&#xff08;HEAP模式&#xff09;2.2 元数据存储在…

智安网络|提升企业网络安全:避免成为勒索软件攻击的目标

勒索软件是当前网络世界中一种威胁严峻的恶意软件类型&#xff0c;勒索软件攻击近年来不断增加&#xff0c;越来越多的企业成为勒索软件攻击的目标。这种攻击方式通过加密敏感数据&#xff0c;并勒索企业支付赎金以解锁数据&#xff0c;给企业造成严重的经济损失与声誉风险。企…

GDB之call、print手动调用函数(十一)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

SSM - Springboot - MyBatis-Plus 全栈体系(八)

第二章 SpringFramework 四、SpringIoC 实践和应用 4. 基于 配置类 方式管理 Bean 4.4 实验三&#xff1a;高级特性&#xff1a;Bean 注解细节 4.4.1 Bean 生成 BeanName 问题 Bean 注解源码&#xff1a; public interface Bean {//前两个注解可以指定Bean的标识AliasFor…

【计算机视觉 | 图像模型】常见的计算机视觉 image model(CNNs Transformers) 的介绍合集(六)

文章目录 一、Co-Scale Conv-attentional Image Transformer&#xff08;CoaT&#xff09;二、Pyramid Vision Transformer v2(PVTv2)三、Class-Attention in Image Transformers&#xff08;CaiT&#xff09;四、PoolFormer五、ScaleNet六、VoVNet七、Siamese U-Net八、Single…

MT4和MT5的共同点,anzo capital昂首资本说一个,没人有意见吧

相信很多交易者对MT4和MT5都不会陌生&#xff0c;但您了解他们背后之间的关系吗?今天anzo capital昂首资本就和各位交易者一起聊聊&#xff0c;没人有意见的MT4和MT5的共同点。 其实谈起MT4和MT5&#xff0c;就不得不聊聊他们背后的公司MetaQuotes&#xff0c;MetaQuotes 是…