让 AI 把我的 PHP 博客重写成 Go
作者尝试将一个运行近20年的古老PHP博客系统重构为Go语言。项目启用了Claude Code的Superpowers插件,通过结构化问答明确了技术选型:采用Go的Gin框架、GORM作为ORM,并构建Vue 3 SPA前端,保持与原MySQL数据库100%兼容。AI助手在确认需求后,自动生成了包含项目结构、API设计等详细规划文档,并利用子代理驱动开发模式执行了约22个开发任务,最终生成一个约35MB的单文件可执行程序,集成了前端SPA。 实现过程并非一帆风顺,主要挑战在于处理历史遗留数据。最复杂的是对UBB标记语法的解析与渲染,因内容已含HTML实体转义且标签存在嵌套,作者编写了34个测试用例才覆盖所有边界情况。此外,需为三代不同的旧URL格式实现301重定向以保持外链有效,并调整了附件链接的解析逻辑以适配反向代理路径。数据库中的标签词频统计也因数据陈旧而改为通过关联查询实时计算。 最终项目产出包括约2800行Go后端代码和2000行Vue前端代码,实现了完整的REST API、JWT认证、防盗链等40余个端点。作者评价整个过程耗时约两三小时,主要负责需求反馈与测试,AI则负责编码、构建与部署,认为这种人机协作模式展现了AI在复杂工程任务中的实用潜力。
git submodule 与 subtree 的异同
最近有开发者在整理代码仓库、尝试将代码与数据分离时,借助 `filter-repo` 等工具,引发了关于究竟该用 `git submodule` 还是 `git subtree` 的思考。这篇文章就深入对比了这两个看似功能相似、实则内核与适用场景迥异的 Git 功能。 两者最核心的差异在于代码的“存在形式”。`submodule` 像是一个精确的指针,它只在你的主仓库中记录一个指向特定子仓库提交的链接。因此,主仓库保持精简,但每次克隆或拉取后,你都需要额外执行 `git submodule init` 和 `update` 来同步子模块内容,管理上更为“显式”。 相反,`subtree` 则采用“拿来主义”,它将子仓库的代码内容直接合并到主仓库的指定目录下,代码成为主仓库历史的一部分。你无需额外步骤就能看到并编辑全部代码,操作更直接,但代价是主仓库的历史记录会膨胀,且后续同步上游更新时可能产生更复杂的合并。 这种差异直接决定了它们的适用场景。如果你的子项目是清晰分离、需要独立版本管理且上游更新频繁的组件(例如共享库),`submodule` 提供了干净的隔离。若你只是希望将某个外部项目的某次快照代码嵌入你的项目,或对代码的便捷访问和单一仓库管理的需求高于历史清洁度,那么 `subtree` 的“一体化”方案会更简单省心。文章通过一个真实的代码整理场景,清晰地剖析了这两种方案的优劣与选择依据,能帮助开发者在项目管理和代码组织时做出更合适的决策。
C++ 中的 main 定义
这篇讲的是 C++ 最新标准里一个看似微小却影响行为的细节调整:关于 `main` 函数的定义。 作者直接指出了核心变化——根据最新的 C++ 标准草案,程序入口函数 `main` 不再允许被 `extern "C"` 这样的 linkage-specification 修饰。文章引用了标准原文,明确了这一点。对于很多习惯了在某些项目或编译器下这样写来避免名字修饰的开发者来说,这可能会造成困惑或编译错误。 文章进一步解释了背景:在旧标准和实际编译器实现中,`main` 作为程序入口点,其链接约定有着特殊的隐含处理。允许用 `extern "C"` 修饰是一种非正式的宽容行为,但新标准明确了这一行为是不符合规定的。文章通过展示错误用法与正确用法的代码对比,清晰地指明了正确做法——只需遵循基本的 `main` 函数签名即可。 这提醒我们,依赖编译器的“非标准特性”或历史宽容行为存在风险。随着标准演进,这些模糊地带正在被澄清。对于需要严格符合标准的代码,尤其是跨平台项目,留意此类细节更新很有必要。
我对比特币的理解
这篇讲的是作者如何理解当前争议很大的比特币。文章没有直接下定论,而是用两个生动的故事作为切入点,层层剖析比特币的价值本质和价格逻辑。 作者首先通过“熊猫便便”的比喻,解释了价值如何从“局部共识”演变为“全局共识”,从而论证比特币并非“废纸”,而是一种已被部分人群认可的资产。接着,他指出其价格核心取决于交易与投资两方面的需求,并类比货币发行原理,说明了供需关系如何影响定价。 文章进一步拆解了比特币的需求来源:既有应对全球通胀的“类黄金”属性,也包含了散户投机的狂欢。作者通过一个具体的杠杆交易算账案例,直观地说明了投机的高风险,预判其长期投机属性会减弱,价格将趋于稳定。最后,文章也冷静地指出了监管政策与密码安全(包括潜在的量子计算威胁)两大核心风险。 整体来看,作者提供了一个理解比特币的系统性框架,从价值形成、定价机制到风险收益,有助于读者在狂热的市场讨论中建立自己的分析视角。
fbx 到 gltf 转换问题
这篇文章讲述了游戏引擎团队在迁移到 glTF 格式时,为解决美术工具导出支持不足与 FBX 私有格式带来的转换问题所经历的系列尝试。作者从项目背景出发,详细记录了团队评估和使用四个不同方案的过程:先是发现 Assimp 工具臃肿、编译问题多且存在链接失败 bug,因而放弃;继而尝试自行编写 FBX 解析模块,但意识到数据转换的繁杂性远超预期,非长期维护之选;接着采用 Facebook 发布的 FBX2glTF,却在对方停止维护后陷入 bug 无法修复的困境;最终转向 Blender,利用其优秀的命令行脚本支持、官方 glTF 插件以及详尽的 FBX 文档,形成了稳定可靠的工作流。文章不仅分享了具体的技术选型思路与踩坑经验,也反映了从私有格式走向开放标准过程中的现实挑战与务实解决策略。
Golang socket 里面奇怪的 pipe 使用
这篇讲的是一个Go语言代理服务器在排查文件描述符时遇到的蹊跷事。 作者日常监控发现,一个TCP连接数两万多的服务,在系统的`/proc/pid/fd`目录下却有五万多个pipe文件描述符,数量远超socket本身。这不符合直觉,于是开始深挖源码。 根因最终指向了Go在Linux下对`net.Conn.readFrom`方法的优化。为了减少用户态内存拷贝,Go会尝试使用`splice`系统调用在内核态直接完成数据传输。而`splice`要求一方必须是管道,因此其实现略显“绕”:每次`readFrom`操作都会先通过`pipe2`创建一对临时管道,再分别进行`splice`操作,用完即关。这完美解释了那些额外pipe的来源。 作者也指出,尽管这种“管道中转”的实现看起来不甚优雅,但在像代理这样`readFrom`生命周期较长的场景中,其性能收益依然可观,因此通常无需优化。文章通过一次具体的生产现象,清晰揭示了Go网络库中一个精巧但隐蔽的内核级优化机制。
Raft 为什么不能直接 commit 前任的日志?
这篇讲的是 Raft 共识协议中一个容易被忽略但至关重要的设计细节:为什么 Leader 不能直接提交前任任期的日志,而必须通过提交本任期的新日志来“隐式”提交。 作者从 Raft 的几项基本原则出发,进行逻辑推演。他指出,一旦日志被 commit,对状态机的影响就不可撤销;而未 commit 的日志则可能被同一 index 不同 term 的新日志替换。核心目标是让所有节点最终提交相同的日志。 问题在多个 Leader 交替时浮现。例如,前两任 Leader 针对同一 index 产生的日志均未形成多数派,第三任 Leader 可能继承其中任一个,这就会导致另一条日志被替换。文章强调,只有 commit 自己任期的日志才能确保它“永不丢失”。这是因为现任 Leader 永远不会撤销自己任期的日志,且新当选的 Leader 一定包含上一个任期多数派中的最新日志。因此,确认本任期日志已复制到多数节点,就能保证它被所有后续 Leader 继承。 这个推理解释了 Raft 论文中反例背后的深层原理,揭示了“隐式提交”机制是如何在日志可能被覆盖的复杂场景下,依然坚定地维护日志一致性的。
实现 go 的 goroutine 本地存储又一种方式
这篇讲的是Go语言中goroutine本地存储的一种新颖实现方案。 作者指出,Go本身没有提供便捷的goroutine本地存储,虽然可以通过`context`传递数据,但这要求在调用链上处处传递,侵入性较强。他发现Go标准库中用于性能剖析的`pprof`包里,隐藏着一个可以携带数据的`label`机制。 基于此,作者设计了一个巧妙的方案:利用其中一个label,通过一些底层技巧将一个`map`结构“塞”进去,从而在单个goroutine中携带所需的本地数据。同时,为了与标准库中基于`context`操作label的逻辑兼容,还做了相应的处理,防止数据被意外覆盖。 通过这种方式,作者将对原有pprof功能的干扰降到了最低。为此,他编写并开源了一个简洁的库:`github.com/xiezhenye/gls`,为需要goroutine本地状态的场景提供了一个侵入性较低的新选择。
如何迁移一个Git仓库
作者从一次 Git 仓库迁移实践出发,指出不少人在操作时会遇到问题。文章系统梳理了三种主要的迁移方法,并剖析了其中的门道。 最常见的直接 `git push` 方法,其实只能迁移本地已有的分支引用,远端的其他分支和标签都会丢失。为了解决这个问题,文章介绍了两种更可靠的方案。一种是使用“裸仓库”(`--bare`),它只包含版本库数据而没有工作目录,非常适合用于纯粹的数据中转。另一种是“镜像仓库”(`--mirror`),它比裸仓库更进一步,保持了与源仓库的同步能力,适合需要后续持续更新的场景。 作者最终推荐使用裸仓库的方法进行一次性迁移,因为它操作直接且彻底。对于需要长期保持同步的场景,则可以选择镜像仓库。这篇文章清晰地对比了不同方法的原理与适用情况,能帮助读者根据自身需求选择最合适的迁移路径。
树莓派 Raspbian 家长控制
这篇讲的是用树莓派给家里的智能电视、机顶盒“立规矩”的实战方案。核心思路很直接:利用树莓派作为网关,通过`iptables`日志监控特定设备(比如孩子的电视)的网络访问行为。 具体实现上,作者提供了一套`ash`脚本。脚本会实时监听日志,一旦发现被监控设备有访问请求,就立刻给它“开绿灯”——在防火墙上放行。同时,它会创建一个定时文件,1小时后自动触发“拦截”操作,再过1小时则“清理”所有相关规则,恢复初始状态。这样,设备每天就被默认限制为“1小时使用,1小时冷却”的循环。 方案最巧妙的地方在于,它巧妙地利用了Linux的日志系统和定时文件,将访问控制变成了一个无需手动干预的自动化状态机。作者也提到了,这套基于`ash`的脚本稍作修改后,也能适配到OpenWRT等路由器系统上,扩展了它的使用场景。对于想动手控制孩子屏幕时间的家长或有类似定时控制需求的技术爱好者,这是一个非常具体的参考。
视频的容器与格式
这篇梳理视频格式领域的基础文章,从“编码”与“容器”两个核心维度展开。作者将视频编码(如H.264、H.265)比作内容的“压缩标准”,决定了画面质量与文件大小;而容器(如MP4、MKV)则是装载这些内容的“打包盒子”。文章依次介绍了从经典的MPEG-1/2到目前主流的H.264、代表趋势的H.265等编码技术的演进与特点,并对比了MOV、AVI、MKV等主流容器的优劣——例如MKV因其超强的包容性而被称为“万能容器”,能封装几乎任何格式的音视频流。 对于需要处理或选择视频格式的开发者、创作者而言,文章提供了清晰的脉络:H.264+MP4是当下兼容性最广的选择,而H.265则代表了在同等画质下更高效压缩的未来方向。无论是理解DVDRip中的MPEG-2,还是分辨RMVB文件背后的RealVideo编码,这篇文章都给出了直观的解答。
图解4种git合并分支方法
这篇讲的是Git分支合并的“四重奏”。作者从“在Git里改变历史是可能的”这个有趣的比喻切入,把抽象的版本控制操作讲得像在不同宇宙间穿梭。 文章图解了四种核心合并方法:最常用的merge其实有三种模式——fast-forward在无分叉时直接移动指针,效率最高;`--no-ff`会强制保留合并节点,让历史更清晰;`squash`则把多个提交压成一个,适合整理功能分支。除此之外,还介绍了rebase,它通过重写提交历史来创造线性、干净的版本树;以及灵活的cherry-pick,可以像摘樱桃一样精确选择某个提交合入当前分支。 作者通过动态示意图,清晰展示了每种操作对提交历史树的影响,比如rebase会将原本分叉的提交“移植”成直线。这种可视化对比,能帮助开发者快速理解不同策略的差异:merge适合保留完整上下文,rebase擅长维护主线整洁,而cherry-pick在修复特定问题时无往不利。 掌握这些方法的区别,就像拿到了Git历史管理的完整工具箱,面对再复杂的分支拓扑也能从容应对。
使用 defer 还是不使用 defer?
这篇讲的是Go语言中defer语句的“爱恨情仇”——从最初赞赏它能简化资源清理代码,到发现其性能开销,再到最终理解其适用边界的全过程。 文章首先展示了defer的魅力:它能让锁的获取与释放成对出现,代码清晰且不易出错,因此在标准库中被广泛使用。然而,性能测试揭示了一个关键事实:使用defer释放锁(70.4 ns/op)比直接调用(19.3 ns/op)慢数倍,多个defer叠加时开销更大。这引出了核心矛盾:defer带来的代码简洁性,是以几十纳秒的性能损耗为代价的。 文章进一步探讨了Go官方对此的优化(如1.8版本的改进),并引用了实际案例(如Prometheus项目)。结论并非一刀切地否定defer,而是提出了务实的平衡点:对于大多数业务代码,defer的便利性远胜于其微小开销;但对于高并发下的“热路径”,通过pprof观察到defer成为瓶颈时,手动管理资源释放则是更优选择。简单说,defer并非免费,但它的代价在绝大多数场景下完全值得。
Mac下处理PC以^M结尾的文本
这篇文章解决的是在 Mac 系统下处理来自 Windows 的文本文件时,行尾出现多余 `^M` 字符(回车符)的常见问题。作者首先清晰地对比了不同系统的行末符差异:Unix/Linux 使用换行符 `\n`,老版本 Mac OS 使用回车符 `\r`,而 Windows 则使用回车换行组合符 `\r\n`。这种不匹配正是导致文本在 Mac 终端或编辑器中显示异常的根源。 文章接着提供了非常直接且实用的解决方案。作者引用 Stack Overflow 上的讨论,指出使用 `awk` 命令并指定记录分隔符 `RS` 为 `\r\n`,就能正确解析并处理这类文件,例如 `awk -v RS='\\r\\n' foo.log`。这个方法比手动替换字符更高效,也更精准。 对于开发者而言,理解这些底层差异并在处理跨平台数据时选择合适的工具,是提升效率、避免“踩坑”的关键。本文从现象到原理再到具体命令,提供了一次简明而完整的技术点拨。
图解python中赋值、浅拷贝、深拷贝的区别
这篇讲的是Python开发者经常遇到的“坑”:当你对一个列表或字典进行赋值、浅拷贝或深拷贝时,它们背后到底发生了什么?为什么修改一个会影响另一个,而有时又不会? 文章通过三段清晰的代码示例,逐步拆解了这三种操作的本质。赋值只是创建了同一个对象的另一个引用,两者完全绑定。浅拷贝会创建一个新容器,但容器内的元素仍然是原对象的引用,所以修改嵌套的可变对象(如内部的列表)依然会互相影响。而深拷贝则会递归地复制所有层级,创建出一个完全独立的副本,彻底切断了与原对象的关联。文章还特别指出了关键差异点:对于不可变类型(如字符串),修改会直接替换为新对象;而对于可变类型(如列表),修改操作在原对象上进行。此外,像数字、字符串这类非容器类型,实际上不存在拷贝操作。 作者通过内存地址的直观对比,把抽象的概念变得很具体。理解这些区别,能帮你避免在传递复杂数据结构时,因误操作而导致数据被意外修改的麻烦。
折腾 Python logging 的一些记录
这篇讲的是 Python logging 模块的深度“折腾”与实战技巧。作者从 logging 的官方流程图和源码出发,清晰地拆解了从日志请求发出,到经过 Logger、Filter、Handler 层层处理,最终格式化输出的完整链路。 文章的亮点在于,它没有停留在理论层面,而是基于对这套机制的理解,分享了如何巧妙地扩展功能。比如,利用 Filter 不仅能过滤还能**改写** LogRecord 的特性,为日志添加了项目相对路径(`relpath`)。文章也指出了配置中的一个“坑”:自定义 Filter 无法通过 `fileConfig` 文件配置,必须使用 `dictConfig` 或 Python 字典。 更进一步,作者将这套扩展思路应用到了实际工程中。通过 Filter 动态地向 LogRecord 注入上下文,成功地为 Flask 请求和 Celery 任务日志串联上了关键的 `request_id` 和 `task_id`。文章还提到了用装饰器自动记录函数调用参数与返回值,并处理了其中容易出错的日志定位问题。 整体而言,这不仅是一次对 logging 内部机制的剖析,更是一份如何将其“驯服”并服务于复杂应用场景的实践指南,对想深入理解或定制 Python 日志系统的开发者很有启发。
一起来学 Spring 2.X
这是一份针对 Spring Boot 2.x 的全面学习指南,由作者唐亚峰在其个人博客上连载。系列文章从最基础的构建第一个 Spring Boot 工程讲起,为读者铺设了一条清晰的学习路径。 整个系列系统性地覆盖了 Spring Boot 2.x 开发中的核心技术栈。作者不仅详细解释了配置管理、日志框架这些基础内容,还深入到整合 Thymeleaf 模板、使用 JdbcTemplate、Spring Data Jpa 以及 MyBatis 进行数据库访问的实战环节。对于进阶需求,文章进一步探讨了如何集成 Lettuce Redis 做缓存、利用 Spring Cache 二级缓存、接入 RabbitMQ 消息队列(包括延迟队列的实现),以及使用 Swagger 进行接口在线调试。 除了核心功能集成,系列也关注应用运维与工程化实践。例如,通过 Actuator 与 Spring Boot Admin 实现服务监控与管理,配置定时任务,实现文件上传与全局异常处理,以及借助 Liquibase 进行数据库版本管理。在安全与性能方面,讲解了整合 Shiro 安全框架,使用本地锁与分布式锁防止重复提交,并探讨了分布式限流方案的优雅实现。甚至包括 JDK8 日期格式化这种实用细节和 WebSocket 聊天室搭建这样的趣味课题。 这个系列最大的特点是循序渐进且内容扎实,每一讲都聚焦一个明确的主题并提供可运行的示例,非常适合希望从零开始或系统性巩固 Spring Boot 2.x 开发技能的读者作为案头参考。
sproxy开发体验
作者分享了开发sproxy代理工具时的一些实战经验。起初是为了解决内网服务需要统一通过代理访问公网的需求,他用Go编写了支持多种协议的sproxy。在后续迭代中,为了能对接Shadowsocks等服务,只需利用golang.org/x/net/proxy库并借助环境变量配置,就能轻松增加SOCKS5链式代理支持。 这次经历虽只涉及少量代码改动,却让他收获了多个实用的排坑心得。例如,在Mac上调试监听443端口的程序时,因权限不足,他通过端口重定向巧妙地解决了问题。更关键的是,他发现本地DNS解析可能被污染,导致调试时访问特定网站不通,将域名解析环节调整到SOCKS5代理之后进行则可解决此问题。文章还简要提及了dnscrypt等更复杂的DNS安全方案,以及对SOCKS5协议特性和Go语言调试环境的观察。 这些来自一线开发的具体细节与思考,对于同样在处理网络代理、开发环境调试的开发者来说,提供了不少可直接参考的路径和启发。
使用 gka 一键生成帧动画
这篇介绍了一个名为 gka 的开源工具,旨在解决前端开发者在处理序列帧动画时的手工繁琐问题。传统做法下,从设计师拿到一组图,需要手动重命名文件、计算 CSS keyframes 百分比,若使用合图还得逐帧计算位置数据,一旦设计稿修改,整个过程就变得异常痛苦。 gka 提供了一键式的解决方案。你只需提供图片文件夹路径和前缀,它就能自动完成批量重命名、生成对应的 CSS 动画代码以及预览 HTML 文件。工具的核心优势在于其性能优化,内置了图片压缩、合图模式以及相同帧复用功能,能有效减小最终体积。同时,它支持多种输出模板,灵活性强。 从文中的示例可以看出,运行一条简单的命令后,工具能迅速输出一个包含预览页、CSS 文件和规范图片的整洁文件夹。它把设计师交付的、杂乱的图片序列,高效地转化成了可直接部署的动画代码,显著节省了前端开发者的重复劳动时间。
自动人脸识别基本原理
这篇讲的是人脸识别近40年来的核心算法演进。作者开篇就点明,这个领域融合了计算机视觉、机器学习等多学科知识,算法难以统一分,通常根据输入数据分为基于静态图像和视频图像两大类。 文章重点对比了三类经典的静态图像识别算法。特征脸方法通过主成分分析将人脸投影到一个低维子空间进行匹配,思路直观,但得到的特征在区分不同类别时未必最优。弹性图匹配则更进一步,它用图结构表示人脸,节点编码局部纹理,边记录几何关系,这种方法对光照和姿态变化有一定鲁棒性,但计算代价过高影响了实用。3D形态模型则另辟蹊径,尝试用三维模型参数来描述人脸的形状和纹理,从而更好地处理姿势和光照变化。 针对视频人脸识别,文章梳理了三个发展阶段。早期方法本质是“跟踪后识别”,利用多帧投票来提高稳定性。随后发展出融合声音、步态等信息的多模态系统。最新的方向则是同时在空间和时间维度上建模,直接利用视频中连续的动态特征进行识别。文章也坦诚地指出了视频场景下面临的图像质量低、人脸尺寸小等严峻挑战,这为后续研究指明了方向。