hiredis-0.13.1 实战拆解:C 客户端库编译、同步异步接口与避坑指南
简介hiredis-0.13.1 是一份面向 C 语言开发者的轻量级 Redis 客户端库源码包适合需要在服务端程序中嵌入高性能 Redis 通信能力的中高级工程师尤其适用于高并发、低延迟的命令收发与协议解析场景。压缩包共 32 个文件约 54KB以 13 个 .h 头文件与 13 个 .c 源文件为主体覆盖连接管理、协议读写、字典与动态字符串等核心模块另含 Makefile、README.md、CHANGELOG.md、COPYING 及 .travis.yml 等构建与说明文件并附带 libev、libevent、glib、libuv 等适配器示例便于快速对照接入。目前已有 228 人学习下载。通过阅读源码与示例读者可掌握 Redis 协议的解析流程、异步接口的调用方式以及多事件循环适配思路为排查连接异常、优化内存与 CPU 占用提供可参考的实现依据。1. hiredis-0.13.1一个 C 客户端库的实战拆解如果你正在用 C 或 C 写服务端程序需要跟 Redis 打交道那 hiredis 大概率是你绕不开的一个库。它足够轻轻到编译完只有几百 KB它也足够老0.13.1 这个版本号在不少存量项目里还能翻到。很多人第一次接触它是因为某个中间件源码里引了hiredis/hiredis.h然后发现这东西既没有连接池也没有自动重连甚至连超时都要自己处理——但恰恰是这种“什么都不替你做”的设计让它在高并发场景下反而比很多封装库更可控。这篇笔记拆的是 hiredis-0.13.1 这个具体版本从编译参数、同步接口、异步接口到协议解析的边界把能复现的步骤和踩过的坑都摆出来。适合手里有 C 项目、需要直连 Redis 或者要读中间件源码的从业者。2. 编译与链接从源码到可执行文件2.1 为什么选 0.13.1 而不是更新版本hiredis 的版本迭代不算激进0.13.1 属于 0.13 分支的维护版本。很多存量项目锁死在这个版本原因通常有两个一是构建系统里已经写死了hiredis-0.13.1的路径升级会触发连锁改动二是这个版本的 API 已经覆盖了绝大多数同步和异步场景没有引入后续版本里那些需要重新适配的接口调整。常见做法是如果项目没有用到 Redis 6 之后的 ACL 或 RESP3 特性0.13.1 完全够用。我一般会先确认项目里有没有依赖redisAsyncSetConnectCallback之外的异步回调如果没有就优先保持版本一致避免链接期出现符号冲突。从源码构建是最稳的方式因为发行版仓库里的 hiredis 版本往往跟你的目标版本对不上。源码包解压后根目录下有一个 Makefile直接 make 就能出静态库和动态库。但这里有个细节默认编译出来的libhiredis.so带版本号如果你要嵌到自己的构建系统里最好显式指定安装路径否则make install会往/usr/local/lib里塞跟系统里已有的版本打架。# 解压后进入源码目录 tar -xzf hiredis-0.13.1.tar.gz cd hiredis-0.13.1 # 指定安装前缀避免污染系统默认路径 make PREFIX/opt/hiredis-0.13.1 make PREFIX/opt/hiredis-0.13.1 install # 确认产物 ls /opt/hiredis-0.13.1/lib # 应看到 libhiredis.a、libhiredis.so、libhiredis.so.0.13 等上面这段命令里PREFIX是 Makefile 支持的变量用来控制安装根目录。make阶段会先编译出hiredis.o、net.o、sds.o等目标文件再链接成静态库和动态库。make install会把头文件拷到include/hiredis/库文件拷到lib/。注意libhiredis.so是一个软链接指向libhiredis.so.0.13而libhiredis.so.0.13又指向libhiredis.so.0.13.1。如果你在容器里构建记得把lib目录加到LD_LIBRARY_PATH否则运行时会报error while loading shared libraries。2.2 链接时容易翻车的两个参数编译自己的程序时链接命令里-lhiredis的顺序很关键。GCC 的链接器是从左到右解析符号的如果-lhiredis放在源文件前面链接器在处理源文件时还没看到库就会报一堆undefined reference to redisConnect。正确顺序是源文件在前库在后。另外如果用的是静态库libhiredis.a还需要额外链接-lpthread因为 hiredis 内部用了 pthread 做异步事件循环的锁保护。动态库则不需要显式加-lpthread因为.so的依赖关系已经记录在 ELF 的DT_NEEDED里了。# 动态链接源文件在前库在后 gcc -o demo demo.c -I/opt/hiredis-0.13.1/include \ -L/opt/hiredis-0.13.1/lib -lhiredis # 静态链接需要补上 pthread gcc -o demo_static demo.c -I/opt/hiredis-0.13.1/include \ /opt/hiredis-0.13.1/lib/libhiredis.a -lpthread参数说明-I指定头文件搜索路径-L指定库文件搜索路径-l指定库名去掉lib前缀和.so/.a后缀。静态链接时直接写.a文件的完整路径就不需要-L和-l了。如果编译时报undefined reference to pthread_create说明漏了-lpthread如果运行时提示找不到libhiredis.so.0.13检查LD_LIBRARY_PATH是否包含/opt/hiredis-0.13.1/lib。提示在 CMake 项目里用find_library(HIREDIS_LIB hiredis PATHS /opt/hiredis-0.13.1/lib NO_DEFAULT_PATH)可以精确锁定版本避免找到系统里其他版本的 hiredis。3. 同步接口实战连接、命令与回复解析3.1 建立连接与超时控制hiredis 的同步 API 核心就三个函数redisConnect、redisCommand、redisFree。redisConnect返回一个redisContext指针里面封装了 socket 文件描述符、读写缓冲区和一个err字段。如果连接失败redisConnect不会返回 NULL而是返回一个err被置位的 context所以判断连接是否成功不能只看指针是否为空必须检查c-err。#include stdio.h #include hiredis/hiredis.h int main(void) { // 连接本地 Redis端口 6379超时 1.5 秒 struct timeval timeout {1, 500000}; // 1.5 秒 redisContext *c redisConnectWithTimeout(127.0.0.1, 6379, timeout); if (c NULL || c-err) { if (c) { printf(连接错误: %s\n, c-errstr); redisFree(c); } else { printf(分配 context 失败\n); } return 1; } printf(连接成功Redis 版本: %d.%d.%d\n, c-version / 10000, (c-version % 10000) / 100, c-version % 100); redisFree(c); return 0; }这段代码里redisConnectWithTimeout比redisConnect多了一个struct timeval参数用来控制connect()系统调用的超时。如果不设超时在网络不通的情况下connect()会阻塞默认的 TCP 超时时间可能长达几十秒。c-err是错误码c-errstr是可读的错误描述。c-version是 Redis 服务端返回的版本号编码方式是major*10000 minor*100 patch所以0x000703对应 7.3。注意redisFree会关闭 socket 并释放 context不要重复调用。3.2 命令发送与回复类型判断redisCommand是一个可变参数函数用法类似printf。它会把命令按 Redis 协议编码后发给服务端然后阻塞等待回复返回一个redisReply指针。redisReply的type字段决定了怎么读数据REDIS_REPLY_STATUS是简单状态REDIS_REPLY_STRING是字符串REDIS_REPLY_INTEGER是整数REDIS_REPLY_ARRAY是数组REDIS_REPLY_NIL表示空。每次用完redisReply必须调用freeReplyObject否则内存会持续增长。// 发送 SET 命令 redisReply *reply redisCommand(c, SET %s %s, user:1001, alice); if (reply NULL) { printf(命令发送失败: %s\n, c-errstr); // 这里通常意味着连接已断开需要重建连接 } else { printf(SET 回复类型: %d, 内容: %s\n, reply-type, reply-str); freeReplyObject(reply); } // 发送 GET 命令 reply redisCommand(c, GET user:1001); if (reply ! NULL reply-type REDIS_REPLY_STRING) { printf(GET 结果: %s\n, reply-str); } freeReplyObject(reply); // 发送 INCR 命令返回整数 reply redisCommand(c, INCR counter:pageview); if (reply ! NULL reply-type REDIS_REPLY_INTEGER) { printf(当前计数: %lld\n, reply-integer); } freeReplyObject(reply);参数说明redisCommand的格式化字符串里%s会被替换成 C 字符串%b用于二进制安全字符串需要额外传长度参数%d用于整数。reply-str只在type为REDIS_REPLY_STRING或REDIS_REPLY_STATUS时有效reply-integer只在REDIS_REPLY_INTEGER时有效。如果type是REDIS_REPLY_ARRAYreply-elements是元素个数reply-element[i]是第 i 个元素的redisReply指针。一个常见的翻车点是对REDIS_REPLY_NIL的回复直接读reply-str结果拿到 NULL 指针程序崩溃。所以读之前一定先判type。3.3 管道模式减少往返延迟如果你要连续发多条命令而且不关心中间结果可以用管道模式。redisAppendCommand把命令写入输出缓冲区但不发送redisGetReply才真正发送并读取回复。这样多条命令可以一次性发出去减少网络往返次数。在批量写入场景下管道模式能把吞吐量提升好几倍。// 管道模式先追加三条命令 redisAppendCommand(c, SET key1 val1); redisAppendCommand(c, SET key2 val2); redisAppendCommand(c, GET key1); // 再依次读取回复 redisReply *r1, *r2, *r3; redisGetReply(c, (void**)r1); redisGetReply(c, (void**)r2); redisGetReply(c, (void**)r3); printf(r1: %s\n, r1-str); printf(r2: %s\n, r2-str); printf(r3: %s\n, r3-str); freeReplyObject(r1); freeReplyObject(r2); freeReplyObject(r3);注意redisGetReply的第二个参数是void**需要强制转换。如果某条命令的回复是数组或嵌套结构同样要按type逐层解析。管道模式不会自动处理错误如果中间某条命令失败后续的redisGetReply可能返回 NULL此时要检查c-err并重建连接。4. 异步接口与事件循环ae 与 libevent 的取舍4.1 异步连接的回调注册顺序hiredis 的异步 API 以redisAsyncConnect为入口返回redisAsyncContext。跟同步版本不同异步连接不会阻塞连接结果通过回调通知。这里有一个血泪经验redisAsyncSetConnectCallback和redisAsyncSetDisconnectCallback必须在事件循环启动之前注册否则连接建立或断开时回调不会被触发。另外异步 context 的生命周期由事件循环管理不能手动redisAsyncFree否则会跟事件循环里的引用计数冲突。#include hiredis/hiredis.h #include hiredis/async.h #include hiredis/adapters/libevent.h #include event2/event.h void onConnect(const redisAsyncContext *c, int status) { if (status ! REDIS_OK) { printf(异步连接失败: %s\n, c-errstr); return; } printf(异步连接成功\n); } void onDisconnect(const redisAsyncContext *c, int status) { if (status ! REDIS_OK) { printf(异常断开: %s\n, c-errstr); } else { printf(正常断开\n); } } void onSetReply(redisAsyncContext *c, void *reply, void *privdata) { redisReply *r reply; if (r NULL) { printf(SET 命令无回复\n); return; } printf(SET 回复: %s\n, r-str); } int main(void) { struct event_base *base event_base_new(); redisAsyncContext *ac redisAsyncConnect(127.0.0.1, 6379); if (ac NULL || ac-err) { printf(异步连接创建失败\n); return 1; } // 绑定事件循环必须在连接回调注册之前或同时完成 redisLibeventAttach(ac, base); // 注册连接与断开回调 redisAsyncSetConnectCallback(ac, onConnect); redisAsyncSetDisconnectCallback(ac, onDisconnect); // 发起异步命令 redisAsyncCommand(ac, onSetReply, NULL, SET async_key async_val); // 启动事件循环 event_base_dispatch(base); event_base_free(base); return 0; }这段代码里redisLibeventAttach把 hiredis 的异步 context 挂到 libevent 的event_base上之后所有 socket 读写都由 libevent 驱动。redisAsyncCommand的第二个参数是回调函数第三个参数是私有数据会原样传给回调。回调函数的签名固定为void (*)(redisAsyncContext*, void*, void*)其中第二个参数实际是redisReply*需要自己转换。注意异步回调里的redisReply不需要手动freeReplyObjecthiredis 会在回调返回后自动释放。4.2 ae 适配器与 libevent 适配器的差异hiredis 自带一个极简的事件循环ae也提供了 libevent、libev、glib 的适配器。选哪个取决于你项目里已经用了什么。如果项目本身就在用 libevent直接redisLibeventAttach最省事如果项目没有任何事件循环用自带的ae可以少引一个依赖。但ae的功能比较基础不支持定时器嵌套和信号处理复杂场景下还是 libevent 更稳。适配器头文件适用场景注意事项aeadapters/ae.h无现有事件循环简单异步不支持多线程事件循环libeventadapters/libevent.h已有 libevent 的项目需链接 -leventlibevadapters/libev.h已有 libev 的项目需链接 -levglibadapters/glib.h已有 GLib 主循环需链接 -lglib-2.0选择适配器时还要注意线程安全。hiredis 的异步 context 不是线程安全的如果多个线程要共用一个连接必须加锁或者每个线程独立连接。常见做法是每个工作线程维护自己的redisAsyncContext通过线程本地存储隔离。5. 避坑与排查五个真实踩过的坑5.1 连接池缺失导致的 fd 耗尽现象服务运行一段时间后redisConnect开始返回Connection refused或Cannot assign requested addressnetstat看到大量TIME_WAIT状态的连接。原因hiredis 本身不提供连接池每次redisConnect都新建一个 TCP 连接用完redisFree关闭。在高频短连接场景下端口会被快速耗尽内核的TIME_WAIT回收速度跟不上。解决在应用层自己维护一个连接池或者复用同一个redisContext做多次命令。如果必须短连接调整内核参数net.ipv4.tcp_tw_reuse和net.ipv4.ip_local_port_range但这只是缓解根本方案还是连接复用。5.2 异步回调里阻塞导致事件循环卡死现象异步命令的回调函数里执行了一个耗时操作比如写日志文件或查数据库之后所有 Redis 命令的回复都延迟很高甚至超时。原因hiredis 的异步回调是在事件循环线程里同步执行的回调不返回事件循环就无法处理下一个 socket 事件。整个连接上的所有命令都会被阻塞。解决回调里只做数据拷贝和状态标记把耗时逻辑丢到工作线程或任务队列里。如果必须同步处理改用同步 API 并放在独立线程里。5.3 二进制数据被截断现象用redisCommand(c, SET %s %b, key, bin_data, bin_len)写入二进制数据读出来发现长度不对或者内容被截断。原因%s格式符遇到\0就停止而二进制数据里经常包含\0。必须用%b并显式传入长度。解决写入时用%b读取时不要用strlen(reply-str)判断长度而要用reply-len。redisReply的len字段记录了字符串的实际字节数对二进制安全。5.4 超时设置对同步命令无效现象设置了redisConnectWithTimeout的超时但某条redisCommand还是卡了很久。原因redisConnectWithTimeout只控制connect()阶段的超时不控制命令读写阶段的超时。如果 Redis 服务端处理慢或者网络中断redisCommand里的read()会一直阻塞。解决同步 API 没有内置的命令级超时需要用setsockopt设置SO_RCVTIMEO和SO_SNDTIMEO或者改用异步 API 配合定时器。常见做法是封装一层带超时的redisCommand用poll或select监听 fd 可读后再读。5.5 版本不匹配导致的符号冲突现象编译通过运行时崩溃gdb显示redisConnect内部调用了错误的地址或者redisFree释放了非法内存。原因系统里存在多个版本的 hiredis链接时找到了旧版本的.so但头文件用的是新版本的。结构体布局不一致导致内存访问越界。解决用ldd检查可执行文件实际链接的libhiredis.so路径确保跟编译时用的头文件版本一致。在构建系统里显式指定-L和-Wl,-rpath把运行时库搜索路径锁死。6. 进阶技巧用 redisCommandArgv 做二进制安全封装redisCommand的格式化字符串虽然方便但在处理二进制 key 或 value 时容易踩坑。hiredis 提供了redisCommandArgv直接传参数数组和长度数组完全绕过格式化解析是二进制安全场景下的首选。我一般会封装一个redis_command_bin函数把 key 和 value 的指针与长度分开传内部拼成argv数组后调用redisCommandArgv。redisReply *redis_command_bin(redisContext *c, const char *cmd, const char *key, size_t key_len, const char *val, size_t val_len) { const char *argv[3]; size_t argvlen[3]; argv[0] cmd; argvlen[0] strlen(cmd); argv[1] key; argvlen[1] key_len; argv[2] val; argvlen[2] val_len; return (redisReply*)redisCommandArgv(c, 3, argv, argvlen); }这个封装里argv和argvlen是一一对应的redisCommandArgv的第二个参数是参数个数。注意argv里的指针在函数返回前必须保持有效因为 hiredis 会立即把它们编码进输出缓冲区不会持有引用。如果 key 或 val 是栈上的临时缓冲区调用完就可以释放。返回值同样是redisReply*用完记得freeReplyObject。验证这个封装是否二进制安全可以写一个测试构造一个包含\0的 value写入后用GET读出来比较reply-len和原始长度是否一致。如果一致说明整条链路没有截断。我习惯在每次升级 hiredis 版本后都跑一遍这个测试因为不同版本对%b和redisCommandArgv的处理细节可能有微调。还有一个容易忽略的点redisCommandArgv在管道模式下也有对应的redisAppendCommandArgv用法类似只是不立即读取回复。如果你在做批量二进制写入用redisAppendCommandArgv配合redisGetReply能兼顾吞吐和二进制安全。从那以后我每次在项目里引入 hiredis都会先跑一遍二进制读写测试和超时测试确认版本和链接路径没问题再往下写业务逻辑。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

图像浮雕效果实现原理:从梯度计算到物理光照建模

图像浮雕效果实现原理:从梯度计算到物理光照建模

1. 项目概述:一张图如何“立起来”?浮雕效果不是滤镜,而是光与形的数学游戏“图像浮雕效果的实现”——这八个字背后藏着的,不是Photoshop里点几下就出来的装饰性滤镜,而是一套融合了微分几何、光照建模和离散信号处理…

2026/10/16 15:43:04 阅读更多 →
手写Transformer代码笔记:从Attention原理到可调试实现

手写Transformer代码笔记:从Attention原理到可调试实现

1. 项目概述:这不是一篇论文复述,而是一份可运行、可调试、可教学的代码笔记“Attention is all you need”——这句标题在深度学习领域早已不是一句口号,而是Transformer架构诞生的宣言式烙印。但现实是,绝大多数人第一次读完这篇…

2026/10/16 15:43:04 阅读更多 →
Blender导入Panda3D EGG模型:插件使用与避坑指南

Blender导入Panda3D EGG模型:插件使用与避坑指南

简介:Blender插件blender-egg-importer面向Blender建模师与Panda3D游戏开发者,用于将Panda3D的.egg模型直接导入Blender,并与YABEE等导出器互操作,保留原始模型的层级结构和法线信息。资源包体积仅21KB,包含4个文件&am…

2026/10/16 15:43:04 阅读更多 →

最新新闻

InternVL2-Pretrain-Models的Pixel Shuffle与mlp1投影器:图像特征如何压缩成LLM的词汇

InternVL2-Pretrain-Models的Pixel Shuffle与mlp1投影器:图像特征如何压缩成LLM的词汇

【免费下载链接】InternVL2-Pretrain-Models 项目地址: https://ai.gitcode.com/OpenGVLab/InternVL2-Pretrain-Models 点击查看 免费下载 InternVL2-Pretrain-Models 是 InternVL 2.0 多模态大语言模型 Stage-1 阶段的多模态预训练模型仓库,提供从 Int…

2026/10/16 16:24:17 阅读更多 →
EN 300 132-1接口A1标准解读:电压暂降、冲击电流与保护器件选型实战

EN 300 132-1接口A1标准解读:电压暂降、冲击电流与保护器件选型实战

简介:《ETSI EN 300 132-1 V2.1.1(2019-03)》是欧洲电信标准协会发布的环境工程与信息通信技术(ICT)设备电源接口标准,其第1部分规定了交流电输入接口的电压范围、电流等级、插头插座类型、过载及接地保护等…

2026/10/16 16:23:15 阅读更多 →
NASA CEA程序全解析:从化学平衡到火箭比冲计算实战

NASA CEA程序全解析:从化学平衡到火箭比冲计算实战

简介:CEA(Chemical Equilibrium with Applications)是NASA开发的热化学平衡计算程序,能模拟火箭发动机燃烧室内的高温高压化学反应,求解平衡组分、温度分布及焓熵等热力学参数,常被用于不同燃料/氧化剂组合…

2026/10/16 16:23:15 阅读更多 →
云原生入门:用Minikube跑通自愈型Hello World

云原生入门:用Minikube跑通自愈型Hello World

简介:本资源是一份系统化、分层级的云原生技术学习路线图,面向开发者、运维工程师及希望转型云原生领域的IT从业者,解决技术栈庞杂、学习路径不清晰、各组件关系难把握等核心痛点。路线图PDF文件共1个,大小1.29MB,内容…

2026/10/16 16:23:15 阅读更多 →
职业心理测评系统源码怎么选?二次开发避坑指南

职业心理测评系统源码怎么选?二次开发避坑指南

简介:这是一套面向职业心理测评场景的完整管理系统源码,适合需要搭建或二次开发测评平台的开发者、企业HR与就业指导机构。系统围绕职业倾向、性格特征、工作适应力等维度,覆盖用户认证、测评数据采集、结果生成、反馈展示,以及用…

2026/10/16 16:23:15 阅读更多 →
免停电免布线的开关柜局放在线监测:工程落地与选型要点

免停电免布线的开关柜局放在线监测:工程落地与选型要点

前阵子到一个工厂配电房看现场,配电柜的母线室局部放电量已经连续三次巡检超过告警线,运维负责人跟我说得很直白:柜子不敢停,负荷没法切;柜内二次小室塞满了,电缆沟和桥架也走不了线,厂里一年只…

2026/10/16 16:23:15 阅读更多 →

日新闻

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

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

2026/10/16 0:00:05 阅读更多 →
信号与系统频域分析作业解题指南

信号与系统频域分析作业解题指南

1. 作业背景与核心要求解析信号与系统作为电子信息类专业的核心基础课,其作业设计往往直接关联课程的核心知识框架。2026年春季第六次作业的布置,通常对应着课程进展到频域分析的关键阶段。根据常规教学进度推测,本次作业可能涉及以下核心内容…

2026/10/16 0:00:05 阅读更多 →
Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

1. 从一个"四不像"的练手项目说起如果你已经看完了Django的官方教程前几章,跟着敲完了投票应用,大概会有一种"我好像会了,又好像什么都不会"的割裂感。教程里每个知识点都是拆开的——这一章讲模型,下一章讲视…

2026/10/16 0:00:05 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/16 14:23:36 阅读更多 →