news 2026/10/8 14:00:26

ARM64离线部署Tendis单机版:镜像与compose全链路解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ARM64离线部署Tendis单机版:镜像与compose全链路解析

简介:面向ARM64架构CPU环境下离线部署Tendis 2.7.0单机版的运维与开发人员,这份工具包以docker-compose一键脚本方式完成部署、启动、停止、卸载与健康检测,特别适合内网隔离或无法访问外网镜像的生产环境。包内共19个文件,类型涵盖sh运维脚本、conf与tpl配置模板、yaml编排文件、dockerfile构建文件、离线镜像压缩包以及redis-cli等客户端和检测工具,整体体积约310.27MB,目录按scripts、conf、images、pkgs等模块组织,便于按需替换和问题排查。当前已有283人学习浏览,可作为ARM64平台部署Tendis单机版的高参考性方案。该工具支持自定义数据目录、端口和访问密码,主配置和数据目录均持久化,容器重启后配置不丢失;同时附带构建镜像所需的基础环境与Tendis二进制包,离线状态下也能完整还原部署链路,对信创适配或私有化交付场景尤为实用。即便缺少公网仓库,也能快速交付一个可用实例。

1. ARM64 离线部署 tendis 2.7.0 单机版:为什么这条链路值得抄

很多团队一听到“tendis 离线部署”,第一反应是去编译 RocksDB、到处找依赖源。实际把 2.7.0 单机版容器化以后,交付物料就一个镜像 tar 包加一个 compose 文件。这条链路里真正返工的坑都在镜像平台没对齐:x86 打包机上导出的镜像搬到 ARM64,一启动就报exec format error。下面按“镜像物料准备 → compose 参数 → 排错 → 真机验证”往下讲,适合手里有国产 ARM64 服务器、需要内网交付缓存/存储服务的同学。先花两分钟把原理聊透再动手,能少走一半弯路。

2. tendis 单机版在 ARM64 上的选型逻辑:协议兼容、RocksDB 与容器化边界

2.1 单机版不是玩具:存量 Redis 业务迁移的最短路径

tendis 是腾讯开源的高性能 KV 存储,走 Redis 协议,但底层数据落在 RocksDB,不像纯内存 Redis 那样受内存上限约束。业务侧用 redis-cli 能直接连,现有代码几乎不用改,只是数据全量刷在磁盘上,适合对数据可靠性要求更高、又不想动应用层的场景。单机版就是这个架构里最简单的一种形态:没有分片协商、没有跨节点选举、没有多角色协调,部署复杂度直接低一个数量级。

在 ARM64 离线场景里我优先选单机版,理由很直接。集群版一次要拉起多个角色,每个角色都要调线程、调内存水位,离线环境里监控和日志都不全,出了故障很难定位是哪个节点先崩。单机版一个容器就是一个服务,配置集中在 compose 文件里,数据目录独立挂出来,宿主机重启后只要 compose 文件和数据目录都在,一条docker-compose up -d就能原样恢复。

这里说一句大家关心的事:单机版能不能当生产。我的态度是,能用 Redis 的业务模型,tendis 单机版就能用。缓存穿透和一致性由业务层处理,tendis 管好存储底层;那些追求极高并发写入的堡垒型业务,才需要把集群版提上日程。

2.2 为什么离线场景绑定 docker-compose:镜像分发与可重演性

离线部署最大的问题是依赖不会自己出现。源码编译 tendis 要 g++、zlib、snappy、lz4、gflags 这些库,ARM64 源里缺一两个就会卡一整天。用镜像把运行环境和依赖全部锁进黑匣子,到目标机docker load完,剩下的动作就只是 compose 一键拉起。这就是容器化对离线部署最实在的价值,不是追“云原生潮流”,是省事。

选 docker-compose 而不是单条docker run,一是因为 compose 文件把端口、数据卷、内存限制、健康检查都写成一个声明文件,整份 yml 就是交付物;二是重演性好:只要镜像 tar 和 compose 文件一致,两台机器起出来的容器行为就一致。离线环境未必有私有镜像仓库,但绝大多数有 docker engine,把 docker-compose 插件一并拷进物料包,就能闭合成完整链路。

在准备物料时,要先把 docker engine 和 docker-compose 的版本关系搞清楚。compose v2 是官方插件,新装机器上docker compose子命令可用;老机器通常只认docker-compose这个独立二进制。离线装的时候不看版本就会翻车:脚本里写的是docker compose up -d,机器报docker: 'compose' is not a docker command,实际上是插件根本没装上。我一般会在物料包里同时准备两种方式,目标机上先确认docker compose version能打印出来,不行就退回docker-compose --version。

这套方案换到别的宿主环境也成立。比如 Windows Server 2022 上用 WSL containers 做离线交付,镜像准备、导入、编排的节奏完全相同,只是运行时名字不同。正因为 compose 层屏蔽了容器运行时的差异,离线部署这套流程才值得沉淀成固定脚本。

2.3 ARM64 兼容边界:官方镜像有没有 arm64 标签,先查 manifest 再动手

ARM64 不算新平台,但镜像平台机制真的容易给人挖坑。docker 镜像在拉取时会按 manifest list 挑平台,网络好的时候什么都不用管;离线物料准备阶段往往会忘掉--platform,在一台 x86 机器上打出 tar,搬到 ARM64 上启动直接报Exec format error。

我在任何一台打包机动手前,都会先看一眼 manifest:

docker manifest inspect 你的内网镜像地址:2.7.0

重点看输出里的platform段,arch 是不是arm64。如果仓库里根本没有 arm64 条目,--platform参数也救不了你,你拿到的永远是 x86 那份。这种情况常见做法是找一台联网的 ARM64 构建机把 Dockerfile 重打一遍,或者用 buildx 在 x86 上交叉构建。

docker pull --platform linux/arm64 你的内网镜像地址:2.7.0 docker image inspect 你的内网镜像地址:2.7.0 --format '{{.Architecture}}'

--platform只在客户端做选择,远程仓库给你的是不是 arm64,看Architecture字段才算数。有些镜像 tag 写得是 arm64,Architecture 仍然返回 amd64,这种“标签和实质不一致”的镜像就是源头上种下的雷。

我习惯在 CI 里用 QEMU 模拟 arm64 做一次冒烟,确认入口命令能跑起来。但我不会拿模拟器的结果当作 ARM64 原生结论。QEMU 模拟只能覆盖指令集行为,覆盖不了 RocksDB 对文件系统、io_uring 这类特性的真实表现,最终验证一定要放到真机上。

2.4 RocksDB 在单机版里的存在感:写放大、compaction 与磁盘预算

很多人把 tendis 当“Redis 换壳”看,忽略它底层是 LSM 树结构的 RocksDB。写路径先写 WAL 和 memtable,大批量写入时后台做 compaction 合并层文件,这个动作是持续的、占 CPU 和磁盘的。离线环境里业务刚接入时可能一切正常,跑几天之后磁盘空间掉得比预想快,写延迟偶尔抖一下,多半就是 compaction 在干活。

选单机版不代表不用关心存储底层。数据目录是 RocksDB 真正落盘的地方,我会在部署前确认两件事:宿主机上挂载给/data的文件系统是 ext4/xfs 这类常规日志文件系统,别拿 tmpfs 当数据卷;磁盘剩余空间至少是预估数据量的 3 倍,给 compaction 和临时文件留余地。这个预算参数在后续运维里最常改,离线交付时如果连磁盘预算都没谈,后面扩容空间都没着落。

至此选型逻辑已经清楚:单机版负责把部署复杂度压下来,docker 负责把依赖关进运行盒,RocksDB 相关的行为预判决定你要给数据卷多大空间。接下来进入物料准备环节。

3. 离线物料准备:在能联网的机器上制作 ARM64 镜像包

离线交付的第一步,是在能联网的机器上备齐三样东西:镜像 tar、docker-compose.yml、配置目录。镜像 tar 管运行时,compose 文件管启动方式,配置文件管服务的端口、日志级别和数据落盘位置。三者缺一样,都会在目标机上多耗一整天。

3.1 在联网机器上拉取适配 ARM64 的镜像:pull --platform 与 manifest 双保险

先在打包机上把镜像拉到本地。我所说的“打包机”可以是一台开发机或构建机,关键它要么和目标机同架构,要么能用 manifest 明确过滤 arm64。拉取命令:

docker pull --platform linux/arm64 你的内网镜像地址:tendis-2.7.0 docker image inspect 你的内网镜像地址:tendis-2.7.0 --format '{{.Architecture}}'

第一行显式指定目标平台,第二行是复盘动作。docker pull的--platform参数会从 manifest list 里选择匹配平台子镜像;如果目标仓库是单架构镜像,这一行可能拉下来 x86 或直接失败。docker image inspect的Architecture字段才是全部镜像层的真实架构,不是 tag 文本能骗过的。

为什么我要求“manifest 双保险”?因为很多内网镜像仓库是从外网地址搬运回来的,搬运时只搬了 x86 层,arm64 层丢了但 tag 没变。你在客户端加--platform没有报错,是因为 daemon 直接返回了唯一的单架构镜像。所以看 architecture 比看 tag 诚实得多。

检查字段确认是arm64之后再生成镜像包。万一仓库里确实没有 arm64,有两种补法:找一台 ARM64 机器重新构建,或者用 buildx 在 x86 上交叉构建。交叉构建需要 Dockerfile 里的基础镜像也支持 multi-arch,不建议离线场景一上来就碰,先把“有原装 arm64 镜像”的路径走通。

3.2 导出镜像 tar:save 不带平台信息,但 SHA256 能帮你防拷包损坏

镜像拉对了,接下来把它重新打包成本地文件:

docker save -o tendis-arm64-2.7.0.tar 你的内网镜像地址:tendis-2.7.0 sha256sum tendis-arm64-2.7.0.tar > tendis-arm64-2.7.0.tar.sha256

docker save把镜像的 manifest、layer 和 config 整体打成 tar,这个 tar 不附带肉眼可读的平台标签,架构信息藏在每一层的 config JSON 里。所以拷包时如果只靠文件名区分 arm64/x86,非常容易错,最好把文件重命名成tendis-arm64-2.7.0.tar这种可识别名字。

镜像 tar 的体积取决于基础镜像和层数,大压缩包在拷贝过程中更容易出问题。U 盘、网盘、scp 都可能静默丢数据,tar 包损坏到docker load时才会爆,报错又长得很像平台的错,很容易误导排查方向。所以我在打包之后必须生成 sha256 校验文件,并把校验动作写进交付说明。目标机上校验:

sha256sum -c tendis-arm64-2.7.0.tar.sha256

返回OK再继续,任何WARNING都说明包有问题,重新拷不要赌。这一步很便宜,但每次都能在 load 失败之前把问题挡下来。很多人 skip 它,结果在半路花费的时间比重新准备一次还多。

3.3 离线导入与预检:load 成功不代表能用,先看三个地方

目标机上第一个动作是把镜像 load 进去:

docker load -i tendis-arm64-2.7.0.tar docker images --format '{{.Repository}}:{{.Tag}} {{.ID}}'

load成功只代表数据包完整,不代表容器能跑在 ARM64 上。这里有两个隐含前提:镜像 config 里的架构与当前 CPU 一致,以及 daemon 能按镜像入口找到可执行文件。所以我在docker compose up之前一定会做三项预检。

第一项,docker images输出的镜像 ID 和 tar 的 sha256 对得上。第二项,确认 docker-compose 可用:新机器上跑docker compose version,老机器上跑docker-compose --version,哪个能出现就用哪个,脚本里不要写死。第三项,用一个临时容器验证平台:

docker run --rm --platform linux/arm64 你的内网镜像ID uname -m

输出aarch64就说明内核能认这个 ARM64 二进制。输出了别的架构,十有八九是 load 的 tar 本身就是 x86。这一步做完,才轮到 compose 上场。

3.4 物料目录建议:一个交付包里该有什么

离线交付不是丢一个 tar 给现场。我会把整个物料包按固定结构整理,复制到目标机后能直接操作:

tendis-offline/ ├── images/ │ ├── tendis-arm64-2.7.0.tar │ └── tendis-arm64-2.7.0.tar.sha256 ├── docker-compose.yml ├── conf/ │ └── tendis.conf └── README.md

images/放镜像和校验文件,docker-compose.yml就是目标机上执行的文件,conf/里放需要按机器调整的配置,README.md写清楚执行顺序和关键命令。目录结构能逼着你在交付前把所有变量想清楚。配置文件里如果只有默认值,现场还要自己试,离线环境里没人能上网查文档。

4. 编写 docker-compose.yml:单机版需要的 6 个参数

镜像包只是运行时,真正决定“一键”体验的是 compose 文件里的参数。tendis 单机版不依赖集群通信,所以不需要复杂的 service 编排,但端口、数据卷、内存、健康检查这几个参数缺一个都会在之后找补。

4.1 compose 文件骨架:镜像、端口、数据卷、启动命令

一份能直接跑起来的单机版 compose 文件大概长这样:

version: "3.8" services: tendis: image: 你的内网镜像地址:tendis-2.7.0 container_name: tendis-single platform: linux/arm64 ports: - "6379:6379" volumes: - ./data:/data - ./conf/tendis.conf:/etc/tendis/tendis.conf:ro command: ["tendis-server", "-f", "/etc/tendis/tendis.conf"] mem_limit: 4g cpus: "2" restart: unless-stopped healthcheck: test: ["CMD", "redis-cli", "-p", "6379", "ping"] interval: 10s timeout: 3s retries: 5 start_period: 20s

platform: linux/arm64是 compose 层面的平台约束,防止编排器在 multi-arch 镜像上挑错。ports把容器内 6379 映射到宿主机 6379,如果现场已有 Redis 占着端口,把左边宿主机端口改成 16379 就行,容器内不用动。volumes里有两条:/data是 RocksDB 数据落盘目录,tendis.conf以只读方式挂进容器,以后改配置不用重新打镜像。

command里写的是容器内路径,不是宿主机路径。这个细节我在排错环节还会重点提,第一次写跑起来报 no such file or directory 的,基本都是把路径写到了宿主机视角。restart: unless-stopped是为了让宿主机重启后 docker daemon 自动拉起 tendis,这是单机版在无人值守环境里的自恢复能力。

参数概览可以提炼成一张表,方便复制到自己的交付文档里:

配置项作用单机版建议
image + platform锁定 ARM64 镜像显式写 linux/arm64,不靠标签猜
ports业务访问入口6379:6379,冲突时改左端口
volumes数据与配置持久化/data 挂宿主目录,conf 只读挂载
command启动参数与配置路径指向容器内可用的配置文件路径
mem_limit / cpus资源水位4g / 2,按数据量上调
healthcheck服务就绪判断redis-cli ping + start_period

这 6 组参数是单机版 compose 文件的骨架。下面重点说最容易出问题的两类:资源和健康检查。

4.2 内存与 CPU 限制:别让 RocksDB 把容器撑爆

tendis 数据虽然落盘,运行期内存消耗照样不小。block cache、write buffer、compaction 线程都吃内存,不设mem_limit时容器可以吃满整台机器。离线 ARM64 机器通常还跑着别的服务,一个 tendis 把宿主机 OOM 拖挂是我见过多次的现场。

给单机版起步,经验值是mem_limit: 4g、cpus: "2"。如果业务读多写少,block cache 占主导,可以把内存给到 8g,CPU 维持 2 不变;如果写入量大、compaction 频繁,CPU 反而更值钱,把 cpus 升到 4,内存不动。cpus是 CPU 份额上限,不是独享核数,写"2"表示最多用两个核的算力。

RocksDB 相关的线程数、写缓冲区大小通常在 tendis.conf 里控制,不同小版本字段有差异。我一般先docker exec 容器名 cat /etc/tendis/tendis.conf | grep -iE 'thread|buffer'看当前版本有哪些字段,再到宿主机conf/tendis.conf修改后 restart。离线交付别把一个版本的默认配置当成标准答案,镜像版本和配置文件必须配套,否则 RocksDB 行为会和你预想完全不同。

4.3 healthcheck 与 depends_on:compose 不会替你等 tendis

depends_on只能保证容器的启动顺序,不能保证服务 ready。tendis 启动时要加载 RocksDB 目录、恢复 WAL,慢机器上可能到 10 秒以上,下游服务立刻去连必然 Connection refused。给 tendis 配一个healthcheck,再用condition: service_healthy把依赖关系绑牢:

depends_on: tendis: condition: service_healthy

healthcheck 命令本身很简单:

healthcheck: test: ["CMD", "redis-cli", "-p", "6379", "ping"] interval: 10s timeout: 3s retries: 5 start_period: 20s

start_period是启动宽限时间,容器刚开始运行的那段时间不会立刻判失败,给 RocksDB 恢复留下余量。retries: 5配合 10 秒间隔,连续五次失败才标记 unhealthy。这样编排器只在 tendis 真正返回 PONG 后才启动下游,业务侧一接入就是可用的,而不是先报一堆连接错误再人工等待。

配完 healthcheck 之后再谈一键部署才成立。否则一键 up 是起来了,应用层却在疯狂重试,等于把等待逻辑丢给了业务。

5. 单机版离线部署常见问题排查:从镜像起不来到数据丢失

5.1 现象一:容器启动报 Exec format error 或 bad linux arm64 image magic

现象:目标 ARM64 机器上docker-compose up -d之后容器秒退,docker logs只看见exec /usr/local/bin/tendis-server: exec format error。极少数老内核或压缩的启动环境下,还会出现类似bad linux arm64 image magic!的底层报错。在引导介质场景里也有“内核段既不是 arm64 image”的说法,本质上都是同一个问题:程序头的魔数和当前架构不匹配。

原因:镜像实际上是 x86 架构,或者镜像 config 里 Architecture 字段被误标成 amd64。exec format error出现在 load 之后、执行入口那一刻,很容易被误判成依赖缺失或权限问题,实际上跟依赖一点关系都没有。

解决:回到联网打包机,执行docker image inspect 镜像ID --format '{{.Architecture}}',确认这个字段是arm64。不是 arm64 就重新docker pull --platform linux/arm64再 save、再拷贝、再 load。在目标机上也可以用docker run --rm --platform linux/arm64 镜像ID uname -m验证,能输出 aarch64 才往下走。不要试图在目标机上用 QEMU 模拟或改启动参数糊弄过去,模拟器跑通不代表原生能用,绕过平台错误后面还会有更乱的错。

5.2 现象二:镜像 load 成功,compose up 报 no such file or directory

现象:docker load 显示 Loaded image 正常,docker-compose up -d显示容器创建成功,但容器状态 Exited,日志是could not open configuration file /root/tendis/conf/tendis.conf: no such file or directory。

原因:command 里的路径是容器内路径,但很多新手直接写了宿主机路径。另一个常见原因是宿主机上的./conf目录是空的,或者配置文件权限不足,容器内用户读不了。还有可能是镜像的入口脚本会先切到一个固定工作目录,而你挂载的数据卷恰好把那个目录占了。

解决:先docker run --rm 镜像ID ls /etc/tendis/把镜像里的真实路径列出来,再回头改 compose 的 command 和 volumes。配置文件挂载前先确认宿主机 conf 目录里确实有文件,docker compose exec tendis cat /etc/tendis/tendis.conf能打印内容,说明挂载关系没问题。这一步把容器内路径和宿主机路径的差异理清楚,后面就顺畅了。

5.3 现象三:容器删了重建,数据全丢

现象:用docker-compose down停服务,再up -d后 Redis 协议还能连,但 get 返回空,数据像回到昨天一样。

原因:数据目录根本没挂出来,tendis 在容器可写层里写 RocksDB。容器删除时可写层一起被销毁,数据就跟容器一起没了。还有一种更隐蔽的:volumes 挂到了宿主机/tmp这类会被系统清理的目录,表面看挂载了,实际目录随时可能被清。

解决:compose 里的 volumes 必须指向宿主机的长命目录,比如/data/tendis:/data。部署前执行ls -ld确认宿主机目录存在且权限对容器用户可写,必要时先拷一份配置文件进去再启动。操作上要特别小心docker-compose down -v,-v会删除匿名卷和命名卷,对运行了半年的单机版执行这个命令,等于给数据判死刑。

注意:对已经写入数据的数据卷,永远不要在清理命令里加-v。docker-compose down -v在部分 compose 版本里会连同匿名卷一起删除,生产数据没有回收站。

我一般把交付文档里的停止命令明确写成docker-compose stop,不写 down。stop 只停容器,down 会清网络和卷声明,这个习惯能避免不少误操作。

5.4 现象四:离线机器没有 docker-compose,脚本第一个命令就报错

现象:交付脚本里写的是docker compose up -d,目标机报docker: 'compose' is not a docker command。另一台机器可能是docker-compose: command not found,两个错还不一样。

原因:离线机器的 docker engine 是装系统时带的,compose v2 插件没随 engine 一起装。老机器只有 docker-compose 独立二进制,新机器则是docker compose子命令。交付前没有把 docker engine 和 docker-compose 的版本关系确认清楚,脚本里写死一个命令就会翻车。

解决:物料包准备阶段就把 compose 插件一起放进去。新版本机器把插件放到 docker CLI 插件目录:

mkdir -p /usr/local/lib/docker/cli-plugins cp docker-compose-linux-aarch64 /usr/local/lib/docker/cli-plugins/docker-compose chmod +x /usr/local/lib/docker/cli-plugins/docker-compose docker compose version

老版本机器则把独立二进制放到/usr/local/bin/docker-compose并加可执行权限。这里说的文件名是常见打包命名,你从内网软件源拿到的具体名字可能不同,但落盘目录和权限要求是一样的。自动化脚本里最好写一段检测逻辑:优先docker compose version,失败就退到docker-compose --version,两个都失败就停止并提示补装插件。

5.5 现象五:写入延迟升高、宿主机 Load 飙升

现象:部署前期一切正常,运行几周后偶发写延迟从 1ms 跳到 200ms 以上,系统 load 平均负载飙到核数的两倍。业务投诉缓存写入变慢。

原因:RocksDB compaction 在后台做大合并,磁盘 IO 和 CPU 同时打满;或者内存水位给得太高,write buffer 堆积后一次 flush 把 IO 打穿。离线环境没有集群分摊,单机版会独自承受这种尖峰。

解决:先用docker stats看容器 CPU 和内存是不是贴着限制跑,再进容器日志找 compaction 相关记录。调优方向上,把mem_limit和cpus往合理区间收,条件允许时给数据目录独立挂盘。要预期内接受写放大,不要在部署文档里写“无限容量”这种话。单机版要长期稳,一次性把数据和资源预算谈清楚,比事后调参更省心。

6. 真机验证与日常维护:从 redis-cli ping 到把后悔药留好

6.1 三步验证:启动日志、端口探测、真实读写

我部署完不开香槟也不马上交接,先跑三件事:

docker compose logs -f tendis redis-cli -p 6379 ping redis-cli -p 6379 set deploy:check ok redis-cli -p 6379 get deploy:check

第一步看日志里有没有 ready 之类的服务就绪字样;第二步确认 Redis 协议层通;第三步写一个键再读出来,验证数据真正落盘而非只进缓存。三步都过了,才把服务地址交给业务做联调。

6.2 数据备份与回滚:配置变更前把后悔药留好

tendis 单机版的数据就是 RocksDB 目录,没有 Redis 那种单独导出快照文件的接口用得顺手。常见做法是停机后直接cp -a /data/tendis /data/tendis.bak-日期,或者依赖宿主机磁盘快照。备份文件要放跟主库不同的盘,甚至不同的机器,否则磁盘坏了备份一起完蛋。

改配置前我习惯先复制一份原配置,加上日期后缀,再 restart。启动成功后观察写入和延迟,确认没问题再清掉备份。别把配置变更放在周五晚上做,一旦 RocksDB 因为参数不兼容起不来,现场没有上网查文档的窗口,周末就搭进去了。

6.3 别让 QEMU 模拟验证替代真机压测

前面提到 QEMU 模拟 arm64 只能做冒烟,真正验收一定在目标 ARM64 机器上跑一次十分钟左右的读写混合压测。主要看三组数字:redis-cli INFO 里 ops 吞吐、写入延迟 P99、以及docker stats的 CPU 波动。压测期间可以把日志级别打开,但别长期跑 debug,debug 日志会让 RocksDB 行为偏离真实负载,这也是我在 QEMU 验证里吃过亏的地方。

这套部署方案经历几次之后,我自己的固定习惯是:任何离线交付都先啃镜像平台确认,任何 compose 改动都先留备份,任何压测都只认真机结论。一次到位还是返工多次,差别就在这些习惯里。希望这些细节能在你的下一次离线交付里帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/8 13:53:43

基于人脸表情识别的课堂行为检测实战:从数据到专注度评分

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/8 13:53:43

工业物联网中RS485与UART串口通信硬核实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/8 13:53:28

微信小程序电子商城管理系统毕业设计全流程实战解析

每年这个时候,都有不少同学被毕业设计题目卡住。尤其是看到“基于微信小程序实现电子商城购物平台管理系统【附项目源码论文说明】”这种题,第一反应是“这还不简单?就是一个购物小程序嘛”,真正做起来才发现,里面藏着…

作者头像 李华
网站建设 2026/10/8 13:53:19

Vim命令高效学习法:从模式理解到核心组合实战

我平时被问得最多的一个问题是:“Vim的命令太多了,到底该怎么学?”说实话,这问题我每次听到都想反问一句:你学Vim是想把命令背完,还是想让自己在终端里改文件的速度快过脑子转一圈?如果目标是后…

作者头像 李华