实时捕捉与追溯:得物基于 eBPF 打造云上网络连接异常摄像头
近期我们容器 SRE 团队基于 eBPF 技术建设网络连接异常感知能力,灰度上线过程中发现了生产环境 10+ 以上的应用配置错误、程序 Bug 等问题。在和应用负责同学同步风险过程中,大家都挺好奇我们如何实现在对应用无侵入的情况下发现服务连接异常的。本篇文档将尝试从技术角度和大家聊聊我们是如何通过内核超能力 eBPF 为以摄像头式的实现应用连接异常感知和检测。
背景
在云上生产环境中,相信大家一定遇到过应用偶发访问中间件或下游服务异常的场景,这些异常究竟是云厂商虚拟网络环境的抖动还是应用自身连接设置不当,亦或网络策略变更所导致,我们总是不能快速便捷地做出初步判断,此类问题的排查多半需要具有丰富网络经验的 SRE 同学在主机上使用 tcpdump 抓包分析;但与此同时,网络异常问题多半具有偶发性,我们不仅需要在捉摸不定的场景下捕获到异常信息,同样也需要能够基于应用信息进行历史异常数据追溯,偶发性和可追溯性叠加,这无疑进一步增加了此类问题排查难度。
业内有句万金油的大法:一切问题都皆可归结于网络偶发抖动导致,至于到底是否是网络抖动导致的,云商一般也会是惜字如金“指标正常,没有异常”,这类问题往往最后被时间所淹没。
那么,是否可参考城市道路上架设摄像头的模式,即可实现网络异常实时查看又可进行历史异常追溯呢?经过一番调研,我们注意结合 Linux 内核观察和业内流行的 eBPF 技术可实现网络质量摄像头的能力,本文尝试以网络连接异常场景建设网络摄像头能力进行展开,关键技术给与介绍和实践验证。
方案调研
经过业内相关技术调研,我们发现 Linux 内核的跟踪机制配合 eBPF 技术可实现灵活的网络事件采集和过滤能力,并且可实现多个网络事件的联合分析和汇聚,这个章节我将分别给与介绍。为简单起见,Linux 内核跟踪机制我仅以跟踪点机制进行介绍(实际场景中内核中还有动态跟踪机制 kprobe 更加灵活,但跨内核移植性略差)。
可观测基石: Linux 内核网络跟踪点
内核跟踪点(Tracepoint) 是内核开发人员提前在代码中预定义的探测点,开发者在这些跟踪点上注入自己定义的处理函数,以便在事件发生时进行数据采集。内核跟踪点的优势在于是内核开发者预先定义好的,位置和语义明确,使用时不需要修改内核代码。通过Linux 内核中的 tracefs
文件机制,使用者可以方便地启用或禁用特定的跟踪点,按需采集自己感兴趣的数据。Linux 内核跟踪点可在目录 /sys/kernel/debug/tracing/events/
中按照类别查看
网络跟踪点介绍
网络相关的跟踪点主要位于在 /sys/kernel/debug/tracing/events/
目录下的 net
/sock
/ tcp
等子目录,其中 tcp
目录下跟踪主要和 TCP 网络协议相关,sock 目录下为网络资源对象 socket
相关的跟踪点。如果本地安装了性能分析工具 perf
,则可以使用 perf list 'tcp:*' 'sock:inet*'
命令查看当前内核相关的 跟踪点(此处为 5.10 内核版本,不同内核版本跟踪点数量略有不同):
sock:inet_sock_set_state [Tracepoint event]tcp:tcp_retransmit_skb [Tracepoint event]tcp:tcp_retransmit_synack [Tracepoint event]tcp:tcp_rcv_space_adjust [Tracepoint event]tcp:tcp_probe [Tracepoint event]tcp:tcp_send_reset [Tracepoint event]tcp:tcp_receive_reset [Tracepoint event]tcp:tcp_destroy_sock [Tracepoint event]
这里我们对常用的网络相关跟踪点功能给与简单介绍,结合这些跟踪点可实现不同场景下的网络质量监控和诊断:
网络跟踪点实践
Linux 内核中可基于 tracefs
机制直接控制跟踪点的启用和停止,方便我们可以快速进行测试和数据搜集,这里以 sock/inet_sock_set_state
跟踪点观察 Socket 对象状态变化为例演示:
echo "1" > /sys/kernel/debug/tracing/events/sock/inet_sock_set_state/enablecat /sys/kernel/debug/tracing
etcd-3825 [006] ..s2. 2198.949362: inet_sock_set_state: family=AF_INET protocol=IPPROTO_TCP sport=46142 dport=32766 saddr=127.0.0.1 daddr=127.0.0.1 saddrv6=::ffff:127.0.0.1 daddrv6=::ffff:127.0.0.1 oldstate=TCP_ESTABLISHED newstate=TCP_CLOSE_WAIT
....echo "0" > /sys/kernel/debug/tracing/events/sock/inet_sock_set_state/enable
上述样例输出中,我们可以看到进程 etcd 进程中的某个连接状态从 TCP_ESTABLISHED
到 TCP_CLOSE_WAIT
的变化。该跟踪点打印数据的格式,可在文件/sys/kernel/debug/tracing/events/sock/inet_sock_set_state/format
中查看。
内核可编程:Linux 内核超能力 eBPF
尽管我们在上述场景进行了验证,可以获取到文本数据,但是仍然存在较多难以解决的问题,比如我们需要基于特定条件过滤和多个跟踪点场景进行关联分析,基于 tracefs
的方式可能会面临采集信息低效,而且无法实现多更重点场景数据关联合并聚合分析场景。此时,我们就需要赋予内核编程能力的超能力 eBPF 技术来进行高效和灵活的采集和聚合分析能力。
eBPF 超能力
eBPF 是一项革命性的技术,起源于 Linux 内核,它可以在特权上下文中(如操作系统内核)运行沙盒程序,其可用于安全有效地扩展内核的功能,而无需通过更改内核源代码或加载内核模块的方式来实现。
eBPF 自 2014 年引入到内核以后,经历了快速的发展和完善,当前已经成为顶级的内核模块。应用场景也从最初的网络数据包过滤扩展到内核可观测、安全、网络和调度等多个领域。eBPF 可实现动态地编程内核以实现高效的网络、可观测性、追踪和安全性。eBPF 相对于内核,犹如 JavaScript 与 HTML 本身,eBPF 技术赋予了内核的可编程能力,带来了众多的创新可能。
就 eBPF 技术而言,其具有以下特性:
-
高性能:eBPF 通过 JIT 编译以及运行在内核空间中,可以大幅提高内核中数据处理能力;
-
安全:eBPF 程序经过严格的验证,不会导致内核崩溃,并且只能由特权用户进行修改;
-
灵活:功能和用例的修改、增加即时加载到内核并生效,而不需要重启或者打补丁。
eBPF 技术原理
eBPF 技术原理简单而言就是在内核中基于事件运行用户自定义的程序,并且能通过提供的 map/perf 等机制提供用户空间与内核空间的双向数据交换。工作原理的核心流程如下图所示:
-
编译: 用户可基于场景编写特定用途的 eBPF 程序,借助于 LLVM(clang)套件将其编译为通用的 eBPF 字节码(有点类似 Java 字节码);
-
加载: 通过系统调用将编译后的字节码通过系统调用
bpf()
加载到内核对应的事件 Hook 点; -
验证:内核会针对加载的 eBPF 字节码进行各种安全检查,确保程序运行时的安全性,避免对内核造成灾难;验证通过后,将 eBPF 字节码通过 JIT 优化为二进制指令(默认启用),并与特定的事件相关联;
-
触发运行:在内核对应的事件触发时,就会运行对应的 eBPF 程序,基于上下文数据进行处理或将处理后的数据发送至用户空间的程序进行进一步处理分析。
如果你想进一步了解 eBPF 技术,推荐进一步阅读BPF 技术概览文章。
使用 eBPF 注意事项
用于 eBPF 是在内核事件发生时触发的运行的程序,其需要保障安全和高效:
-
不能访问内核任务函数,不同的场景下只能访问特定场景下的上下文信息,比如网络类型的 eBPF 程序只能访问到网络相关上下文信息;
-
eBPF 程序运行的栈只有 512 个字节,能够运行的指令数量也最大只能为 100 万条指令。
上述限制都是为了保障在内核事件触发后不会影响内核安全和内核运行效率。在介绍完内核网络跟踪点和 eBPF 基础技术后,我们进入到整体方案设计阶段。
架构设计和实现
方案架构设计
在整体架构上,我们可将程序部署在 K8s 容器集群或单机 ECS 上。当 K8s 调度到单机或部署到单机上,需要运行用户空间程序 net-collector
和内核事件处理程序 bpf_collector
,核心流程如下:
-
用户空间程序
net-collector
用于加载 BPF 程序至内核中; -
当内核中有网络连接异常信息时,内核中的
bpf_collector
程序就会被触发; -
bpf_collector
程序将采集到数据发送到用户空间程序net-collector
; -
用户空间程序
net-collector
在进行数据过滤和聚合后将数据写入到本地日志记录文件中; -
单机部署的日志采集 agent 将数据采集到日志平台;
-
最后,我们可通过可视化界面完成日志平台保存数据的消费处理和架构化展示,以应用维度提供异常展示。
部署在单机 eBPF 程序,就像核心路口的摄像头一样,可时时刻刻按照约定规则采集信息,我们既可以实时查看采集的信息,还可以通过日志平台进行回溯查看。
方案实现
以网络连接超时为例,我们尝试采集环境中所有新建连接的网络重传事件,这里有两个关键点:
-
连接初始阶段,这时 TCP 状态位于 SYN_SENT 状态;
-
在连接状态出现了数据包的重传,基于上述网络跟踪点章节介绍,我们适合选取跟踪点
tcp:tcp_retransmit_skb
附着编写的 eBPF 程序即可。
单机采集端的实现,我们需要编写两部分程序:
-
用户空间程序
net-colloctor
:用于将 eBPF 程序加载到内核,并接受在内核事件触发 eBPF 程序传递的数据,进行采集、聚合和分析,并写入到日志文件记录中; -
内核中运行的 eBPF 程序
bpf-collector
:内核程序附着与tcp:tcp_retransmit_skb
跟踪点,在网络连接重传事件发生时候,采集网络上下文信息,并将其发送至用户空间程序net-colloctor
进行分析。
内核空间 eBPF 程序
内核中的 eBPF 程序给与简单说明,核心包括 3 个步骤:
SEC("tracepoint/tcp/tcp_retransmit_skb") // 与跟踪点对应的声明
int handle_tcp_retransmit_skb(struct trace_event_raw_tcp_event_sk_skb *ctx)
{struct event event = {};const struct sock *sk = ctx->skaddr;// 步骤 ①char state = 0;state = BPF_CORE_READ(sk, __sk_common.skc_state); if (state != TCP_SYN_SENT){return 0;}// 步骤 ②event.family = BPF_CORE_READ(sk, __sk_common.skc_family); event.ts_us = bpf_ktime_get_ns() / 1000;event.type = RESTRANSMIT;event.sport = BPF_CORE_READ(ctx, sport);event.dport = BPF_CORE_READ(ctx, dport);switch (event.family){case AF_INET:bpf_probe_read_kernel(&event.saddr, sizeof(ctx->saddr), BPF_CORE_READ(ctx, saddr));bpf_probe_read_kernel(&event.daddr, sizeof(ctx->daddr), BPF_CORE_READ(ctx, daddr));break;case AF_INET6:bpf_probe_read_kernel(&event.saddr, sizeof(ctx->saddr_v6), BPF_CORE_READ(ctx, saddr_v6));bpf_probe_read_kernel(&event.daddr, sizeof(ctx->daddr_v6), BPF_CORE_READ(ctx, daddr_v6));break;default:}// 步骤 ③bpf_perf_event_output(ctx, &dw_net_col_events, BPF_F_CURRENT_CPU, &event, sizeof(event));return 0;
}
上述代码中的核心步骤介绍如下:
步骤 1:获取到触发重传函数上下文中的 TCP Sock 对象的状态信息,这里我们仅采集 TCP_SYN_SENT 状态的,否则就直接返回;
步骤 2:基于内核跟踪点的 Famliy 信息来区分是 IPv4 或 IPv6,获取到必要的信息并填充到 event 对象,event 对象是用户空间程序与内核 BPF 程序公用的数据结构;
步骤 3:将内核中封装好的数据发送到用户空间。
这里需要注意的是,不同内核版本的跟踪点结构体可能会发生变化。为了同时适配多个内核版本,我们使用可以跨多个内核版本移植方案 CO-RE。CO-RE 全称为 Compile Once – Run Everywhere
,中文翻译为一次编译、到处运行,当前社区已经提供在低版本支持 CO-RE 的实现,我们在 4.19 内核中由于没有默认支持 CO-RE,这需要进行一些特定的处理步骤。关于 CO-RE 详细介绍可参考这篇文档。
用户空间程序
用户空间的程序主要用于加载 BPF 程序到内核,并读取 BPF 程序运行过程中上报的数据。程序使用 go 开发,基于库 cilium/ebpf
。简化代码如下所示:
func main() {flag.Parse()var btfSpec *btf.Specvar err error// 步骤 ①// Load pre-compiled programs and maps into the kernel.var opts ebpf.CollectionOptionsopts.Programs.KernelTypes = btfSpecobjs := bpfObjects{}if err := loadBpfObjects(&objs, &opts); err != nil {log.Fatalf("loading objects: %v", err)}defer objs.Close()// 步骤 ②tpRestranSkb, err := link.Tracepoint("tcp", "tcp_retransmit_skb", objs.HandleTcpRetransmitSkb, nil)if err != nil {log.Fatalf("opening tracepoint tcp/tcp_retransmit_skb %s", err)}defer tpRestranSkb.Close()// 步骤 ③rd, err := perf.NewReader(objs.DwNetColEvents, os.Getpagesize()*10)if err != nil {log.Fatalf("creating perf event reader: %s", err)}defer rd.Close()// 步骤 ④var event bpfEventfor {record, err := rd.Read()if err := binary.Read(bytes.NewBuffer(record.RawSample), binary.LittleEndian, &event); err != nil {continue}eventHandler.Handle(&event)}
}
上述代码已经经过简化,上述核心步骤介绍如下:
步骤 1:加载内核运行的 eBPF 程序到内存对象中,完成内存初始化并加载到内核;
步骤 2:将加载到内核的 eBPF 程序与我们定义的跟踪点关联,此处关联后当事件触发时就可运行 eBPF 程序;
步骤 3:打开与 eBPF 程序约定的通信 perf 通道,后续可以持续读取数据;
步骤 4:循环读取 perf 通道中的数据,然后调用 eventHandler.Handle(&event)
函数进行后续的各种分析和处理,这里主要是数据过滤、聚合分析和缓存等各种我们特定的业务处理。
容器集群部署
在单机上运行 BPF 程序需要特权模式 privileged: true
和挂载宿主机相关目录 /lib/modules
和/sys/kernel/debug
。同时考虑在版本升级和变更的灰度,我们还需要将部署策略 updateStrategy
设置为type: OnDelete
。 部署文件主要设置内容如下所示:
template:spec:containers:- name: mainimage: repoin.shizhuang-inc.net/component/net_conn_collector:0.1imagePullPolicy: IfNotPresentsecurityContext:privileged: truecapabilities:add:- SYS_ADMINvolumeMounts:- name: modules-hostmountPath: /lib/modulesreadOnly: false- name: debug-fs-hostmountPath: /sys/kernel/debugreadOnly: falsevolumes:- name: modules-hosthostPath:path: /lib/modules- name: debug-fs-hosthostPath:path: /sys/kernel/debugupdateStrategy:type: OnDelete
效果展示
当在部分 K8s 集群灰度上线后,我们可直观针对主机上网络连接异常情况基于主机和上下游分析。基于原始数据,我们与 CMDB 数据整合打通,通过图形化的方式展示异常链路,可快速定位到应用上下游,大幅提升了问题定位效率。
相关文章:

实时捕捉与追溯:得物基于 eBPF 打造云上网络连接异常摄像头
近期我们容器 SRE 团队基于 eBPF 技术建设网络连接异常感知能力,灰度上线过程中发现了生产环境 10 以上的应用配置错误、程序 Bug 等问题。在和应用负责同学同步风险过程中,大家都挺好奇我们如何实现在对应用无侵入的情况下发现服务连接异常的。本篇文档…...

ubuntu14.04图形界面配置
Ubuntu系统启动,输入用户密码后,屏幕显示彩色背景,但是始终不能进入图形界面。 如果你也遇到过这种情况,可以参照以下方法解决(在 ubuntu14.04 验证)。 同时按下 alt ctrl F1,屏幕出现 tty1&a…...

51单片机-第八节-蜂鸣器
一、什么是蜂鸣器? 蜂鸣器是一种将电信号转换为声音信号的器件,常用来产生设备的按键音、报警音等提示信号。 蜂鸣器按驱动方式可分为有源蜂鸣器和无源蜂鸣器: 有源蜂鸣器:内部自带振荡源,将正负极接上直流电压即可…...

Windows命令查看WiFi密码
查看所有已保存的WiFi网络 (以管理员身份)输入以下命令 netsh wlan show profiles查看某个WiFi网络的密码 netsh wlan show profile name"你的网络名" keyclear在输出中,在关键内容(Key Content)字段下找…...

不同环境下RabbitMQ的安装-2 ARM架构、X86架构、Window系统环境下安装RabbitMQ
ARM架构、X86架构、Window系统环境下RabbitMQ的安装 RabbitMQ安装1 Erlang语言介绍2 安装Erlang2.1 ARM架构的CentOS虚拟机中安装Erlang2.2 X86架构的CentOS虚拟机中安装Erlang2.3 Windows系统安装Erlang2.3.1 下载Erlang2.3.2 安装Erlang2.3.3 配置Erlang2.3.4 检测Erlang 3.安…...

C++(week16): C++提高:(六) Qt提高
文章目录 四、Qt的元对象系统1.元对象和MOC(1)自省 和 反射(2)Qt是怎样支持元对象系统的?(3)支持元对象系统的三个要求(4)元对象系统的功能(5)动态属性 2.信号和槽机制(1)信号与槽机制的基本原理(2)自定义信号、自定义槽函数①自定义信号②自定义槽③关联 connect (…...

go 时间转时间戳的时区设置问题
昨天遇到一个问题,在完成时间转换时间戳,在后续测试中发现转换后的时间戳转成时间后,时间发生错误,时间和转换时间不一致问题 如下: package mainimport ("fmt""time" )func main() {Start : &q…...

MySQL 常见日志清理策略
前言: MySQL 数据库服务器使用多种类型的日志来记录操作和事件,这对于故障诊断、审计和性能分析非常重要。然而,这些日志文件会随着时间的推移而不断增长,可能会占用大量的磁盘空间。因此,定期清理这些日志是必要的&a…...

3大管人绝招让你的手下心服口服
3大管人绝招让你的手下心服口服 一:差异化管理,玩弄人性 谁赞成,谁反对,看清楚谁顺从自己,谁反对自己之后,接下来要做的便是区别对待。 给听话的下属最好的资源、最轻松简单的工作、最高的待遇…...

useImperativeHandle 是什么?你可以理解为 vue3 的 expose
useImperativeHandle 确实类似于 Vue 3 的 expose,两者都用于控制子组件向父组件暴露的接口。 在 React 中,useImperativeHandle 需要与 forwardRef 一起使用,原因如下: 转发引用:forwardRef 允许父组件将 ref 传递给…...

《Techporters架构搭建》-Day05 属性校验
属性校验 前言Validated基础用法集合校验分组校验嵌套校验自定义校验器 源码地址 前言 在项目开发过程中,经常遇到需要对传递的参数进行校验,比如某个参数字段是否为空、值的取值是否在约定范围、格式是否合法等等,最原始的写法,…...

HTTP的场景实践
HTTP的场景实践:任选一个浏览器,对于其涉及的请求中的缓存策略展开具体分析 1. 强缓存: Cache-Control用于指定缓存的最长有效时间。 Expires用于指定资源过期的日期。 2. 协商缓存: ETag用于标识资源的唯一标识符,…...

MySQL:表的设计原则和聚合函数
所属专栏:MySQL学习 💎1. 表的设计原则 1. 从需求中找到类,类对应到数据库中的实体,实体在数据库中表现为一张一张的表,类中的属性对应着表中的字段 2. 确定类与类的对应关系 3. 使用SQL去创建具体的表 范式࿱…...

介绍springmvc-水文
Spring MVC 是一个基于 Java 的开源 Web 框架,它是 Spring Framework 的一部分。Spring MVC 提供了一个架构,用于开发灵活、可扩展的 Web 应用程序。 Spring MVC 的主要特点包括: 基于模型-视图-控制器(MVC)的架构&am…...

uni-app学习笔记
一、下载HBuilder https://www.dcloud.io/hbuilderx.html 上述网址下载对应版本,下载完成后进行解压,不需要安装,解压完成后,点击HBuilder X.exe文件进行运行程序 二、创建uni-app项目 此处我是按照文档创建的uni-ui项目模板…...

Windows Server修改远程桌面端口
新建入站规则 填写端口 允许连接 修改远程桌面端口 winR打开注册表 计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\Wds\rdpwd\Tds\tcp修改PortNumber为新端口 计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\Wi…...

界面组件Kendo UI for Vue 2024 Q2亮点 - 发布一系列新组件
随着最新的2024年第二季度发布,Kendo UI for Vue为应用程序开发设定了标准,包括生成式AI集成、增强的设计系统功能和可访问的数据可视化。新的2024年第二季度版本为应用程序界面提供了人工智能(AI)提示,从设计到代码的生产力增强、可访问性改…...

达梦数据库 逻辑备份还原
达梦的逻辑备份还原 1.背景2.要求3.实验步骤3.1 相关术语3.2 dexp逻辑导出3.2.1 使用dexp工具3.2.2 dexp相关参数含义3.2.3 四种级别导出3.2.3.1 FULL3.2.3.2 OWNER3.2.3.3 SCHEMAS3.2.3.4 TABLES 3.2.4 使用范例3.2.4.1 环境准备3.2.4.2 dexp逻辑导出 3.3 dimp逻辑导入3.3.1 使…...

Stable Diffusion绘画 | 图生图-上传重绘蒙版
上传重绘蒙版,可以弥补局部重绘的缺点,能够更精细的修改画面中的指定区域 使用PS制作的蒙版图片为耳朵下方区域,可以为图片中的女生带上不同款式的耳环。 参数配置: 调整提示词: 生成图片如下所示: 调整提…...

打开Office(word、excel、ppt)显示操作系统当前的配置不能运行此应用程序最全解决方案!
我以前用过分区助手把office从c盘挪到d盘了,从那以后office就用不了了,然后我就删了(貌似没删干净)。 最近由于有使用word的需求,所以我从学校官网找到正版软件的安装包,按照步骤重新卸载电脑中office残留…...

猫头虎 分享已解决Bug || TypeError: Cannot read property ‘map‘ of undefined 解决方案
🐯 猫头虎 分享已解决Bug || TypeError: Cannot read property map of undefined 解决方案 摘要: 今天猫头虎带大家深入探讨在前端开发中常见的一个令人头疼的问题:TypeError: Cannot read property map of undefined。这个错误通常出现在我…...

大模型快速部署,以浪潮源2.0为例
step1: 申请PAI-DSW试用 step2:魔塔社区授权 由于本地授权一直失败,于是采用了魔塔免费平台实例进行学习。 搭建好之后,打开就有相关页面了: demo搭建: 按照官方提示的步骤进行搭建,内容如下:…...

Python知识点:使用FastAI进行快速深度学习模型构建
使用FastAI构建深度学习模型非常方便,尤其是对于快速原型开发和实验。以下是一个使用FastAI构建深度学习模型的完整示例,涵盖数据准备、模型训练和评估。 安装依赖 首先,确保你安装了FastAI库和其他必要的库: pip install fast…...

Nginx配置全局https
Nginx配置全局https 要在 Nginx 中配置将 HTTP (80 端口) 请求重定向到 HTTPS (443 端口),可以在 Nginx 的配置文件中添加以下配置。假设你已经配置好了 HTTPS 相关的证书和密钥。 打开你的 Nginx 配置文件,通常是 /etc/nginx/nginx.conf。 在配置文件…...

DBAPI 如何对SQL查询出的日期字段进行统一格式转换
DBAPI 如何对SQL查询出的日期字段进行统一格式转换 mysql有一张订单表,有两个datetime类型的字段create_time update_time 新建一个API,SQL内容是查询所有数据 访问API发现日期字段默认返回时间戳格式 如果修改成自己想要的年月日格式,就要使…...

C:每日一题:字符串左旋
题目:实现一个函数,可以实现字符串的左旋 例如:ABCD左旋一个字符就是BCDA;ABCD左旋两个字符就是CDAB; 1、解题思路: 1.确定目标旋转k个字符,我们要获取字符串的长度 len,目的是根…...

深兰科技荣获2024年度金势奖“AI出海先锋品牌”金奖
近日,由金势奖组委会、凤凰网、营销国际协会等国内外知名机构、集团共同主办的“第四届未来营销大会暨锐品牌盛典”在上海举行。大会揭晓了第四届“金势奖锐品牌大赏”奖项的评选结果,深兰科技凭借自身在机器人产品出口和海外市场开拓等出海全球化发展方…...

服务器启动jar包的时候报”no main manifest attribute“异常(快捷解决)
所以,哥们,又出现问题咯.没事,我也出现了,哈哈哈哈哈,csdn感觉太麻烦了,所以搞了一篇这个. 没得事,往下看,包解决的. 希望可以帮助到各位,感谢阅览! 小手点个赞,作者会乐烂哈哈哈哈哈哈😆😆😆😆…...

部分控件的setText文案没有出现在retranslateUi()中,多语言切换不生效问题
问题:在designer中设计UI,我从其他ui文件copy了部分控件,新ui文件重新编译生成后,setText()并没有出现在新文件的retranslateUi()函数中,导致多语言切换不生效。 void retranslateUi(QWidget * …...

ubuntu系统下安装LNMP集成环境的详细步骤(保姆级教程)
php开发中集成环境的安装是必不可少的技能,而LNMP代表的是:Linux系统下Nginx+MySQL+PHP这种网站服务器架构。今天就给大家分享下LNMP的安装步骤。 1 Nginx安装 在安装Nginx前先执行下更新命令: sudo apt-get update 接下来开始安装Nginx, 提示:Could not get lock /v…...