跳转到正文
zeno's blog

归档

按时间整理的全部文章。

2026 60
六月 1
  • 云原生基础:Twelve-Factor App 作为应用设计契约

    Twelve-Factor App(2011, Heroku)定义了 SaaS 应用与运行平台之间的契约:声明式配置、无状态进程、环境无关的构建产物、日志作为事件流。它不是银弹——环境变量存密钥有安全隐患、严格无状态忽略了合理的本地缓存、Admin Processes 在 K8s 时代已过时——但其核心洞察(代码与配置分离、进程与状态分离、构建与运行分离)至今仍是云原生应用的设计基线。

五月 13
四月 16
三月 8
  • hyper(三):与 axum、tower 的架构关系

    hyper 处理 HTTP 协议 (字节流 ↔ Request/Response), tower 提供中间件组合框架 (Service + Layer), axum 在两者之上添加路由和类型安全的请求提取. 三者的分工是: hyper 管"线路上的字节", tower 管"请求的处理流水线", axum 管"开发者体验". 理解这个三层架构是在 Rust 中写 HTTP 服务的基础.

  • hyper(二):Body trait 与请求响应体

    httpbody::Body trait 是 hyper 生态中所有 HTTP body 的统一抽象, 通过 pollframe 方法按帧返回数据 (data frames) 或 trailers, 实现零拷贝流式传输. hyper 1.0 将入站 body 固定为 Incoming 类型, 出站 body 由开发者从 Full<Bytes>, Empty<Bytes>, BoxBody 等中选择 -- 这种入站/出站类型分离是从 0.14 迁移时最大的心智模型变化.

  • hyper(一):底层 HTTP 实现与 1.0 迁移

    hyper 是 Rust 生态中 HTTP/1.1 和 HTTP/2 的底层协议实现, 不是 Web 框架. 1.0 版本做了破坏性重构: 移除内置高层 Server/Client 到 hyper-util, Body 从具体类型变为 trait, 定义了自己的 Service trait (&self 而非 &mut self, 无 poll_ready). hyper 是 axum、reqwest、tonic 的底层引擎.

  • 可观测性(二):Rust 可观测性体系的架构理解

    文档 observability.md 的代码太多,这篇只讲架构,帮助理解"为什么这样设计"。

  • axum(三):中间件与生产实践-tower 原生的 Web 应用

    axum 不发明中间件系统——Router::layer() 直接接受任何 tower::Layer。tower-http 的 Trace/Compression/CORS/Timeout 全部开箱即用。from_fn 让你用普通 async 函数写中间件而不需要实现 Service + Layer。注意 .layer() 只影响它之前添加的路由,顺序是洋葱模型(后加的在外层)。

  • 可观测性(一):三大支柱-Logs、Traces、Metrics

    出了问题 │ ┌──────────────┼──────────────┐ ▼ ▼ ▼ Metrics Traces Logs "有问题" "哪里慢" "为什么错"

  • axum(二):Handler 与 Extractor-编译期请求解析的魔法

    axum 的 handler 是普通 async fn,参数类型决定如何从 HTTP 请求中提取数据。FromRequestParts 提取头部/路径/query(不消耗 body),FromRequest 提取 body(只能有一个,必须放最后)。所有验证在编译期完成——类型不匹配编译器直接拒绝,不等到运行时 500。这背后是 allthetuples! 宏为 0~16 个参数生成的 17 个 Handler trait blanket impl。

  • axum(一):设计哲学-类型驱动的零成本 Web 框架

    axum 是 tokio 官方的 Web 框架,由 David Pedersen 在 2021 年创建。它的核心设计是:不写自己的中间件系统,直接用 tower::Service;不用宏做路由,用函数调用;不用运行时反射做参数提取,用编译期 trait 解析。整个 crate #![forbid(unsafe_code)]。

二月 9
  • 微服务(四):No-mock 趋势与测试策略

    Clean Architecture 做对了的话,大部分层根本不需要 mock:domain 层零依赖直接测,repository 层必须用真实基础设施(testcontainers),use case 层视编排复杂度决定。Mock 只在外部不可控系统(支付网关、短信)和故障注入场景下合理。

  • 微服务(三):多实例共享数据库为什么不需要额外同步

    多个无状态服务实例访问同一个 PostgreSQL,不需要分布式锁或 Redis 互斥。数据库的 MVCC + 事务 + 约束已经处理了并发控制。你唯一需要操心的是业务层"先读后写"的竞态,而这个问题单实例多 goroutine 也一样存在,解决方案全在 SQL 层面(原子 SQL、乐观锁、SELECT FOR UPDATE、唯一约束)。

  • Tokio:定时器、时间轮与精度

    tokio 的定时器用的是分层时间轮(Hierarchical Timing Wheel),不是 min-heap。

  • 微服务(二):完整微服务集群的分层系统

    一个完整的微服务集群,从来不只是“一堆 RPC 服务实例”。至少要同时看见四层:平台层负责调度和运行,流量层负责入口和东西向治理,业务层承载真正的领域逻辑,状态层保存持久数据与异步事件;可观测性、配置、密钥、发布流水线则是横切支撑层。少看任何一层,架构判断都会失真。

  • tokio(四):陷阱与生产最佳实践

    tokio 最常见的陷阱分为两类:阻塞运行时(在 async 上下文中做同步操作)和取消安全(Future 在 .await 点被 drop 导致状态丢失)。本文列举 10 个高频陷阱和对应的生产级修复方案,以及 runtime 配置、优雅关闭、可观测性和错误处理的最佳实践。

  • 微服务(一):拆分-按运维特征和组织边界画线

    拆分的本质是让不同团队能独立部署和运维各自的代码。拆分依据的优先级:领域边界 > 团队所有权 > 数据所有权 > 运维特征差异 > 变更频率。大多数团队应该止步于模块化单体,只在出现可度量的痛点时才提取服务。拆错的代价远大于不拆的代价。

  • tokio(三):I/O、定时器、同步原语与 select!

    tokio 的 I/O 驱动将 mio 的 Poll 包装为与调度器集成的 Reactor,通过 Waker 机制在 fd 就绪时唤醒对应 task。定时器使用六级分层时间轮(每级 64 slot),所有操作 O(1)。同步原语(Mutex / channel / Semaphore)专为 async 设计,核心区别是等待时不阻塞线程。select! 宏是 tokio 最强大也最危险的组合器,理解其取消语义是正确使用的前提。

  • tokio(二):任务系统-spawn、取消与协作式调度

    tokio::spawn 将一个 Future 包装为运行时调度的任务(task),任务是非抢占式的——它只在 .await 点主动让出执行权。tokio 通过 coop budget(128 次操作预算)实现软抢占,通过 JoinSet / TaskTracker 提供结构化并发,通过 drop JoinHandle 实现取消。理解任务生命周期是避免 tokio 大部分 pitfall 的关键。

  • tokio(一):运行时-从 mio 到异步运行时的完整设计

    tokio 是 Rust 的异步运行时,提供工作窃取调度器、I/O 驱动、定时器、同步原语和任务管理。它在 Rust 异步生态中的角色等同于 Asio 的 iocontext + strand + timerqueue + 线程池的总和,但 Rust 选择了分层架构(mio → Future trait → tokio)而非 Asio 的单库大一统模式。理解 tokio 需要先理解它为什么不能只用 mio,以及 Rust async/await 的设计如何塑造了运行时的形态。

一月 13
  • DDD(二):领域事件

    领域事件是系统里已经发生的一个事实,用过去时描述。它的本质作用是解耦聚合之间的协作。

  • DDD(一):领域驱动设计概览

    没有 DDD 的项目: 代码按技术分层: controllers/ models/ services/ utils/ 业务逻辑散落在 controller 里、service 里、甚至 SQL 里 新人来了看不懂业务,改一个功能要改 5 个文件 "这个扣血逻辑在 PlayerService 里还是 CombatService 里?两边都有一点"

  • mio(七):陷阱与常见错误

    mio 的 7 个致命陷阱:不 drain 数据导致事件丢失、不处理 WouldBlock、忘记 deregister 导致资源泄漏、reregister 的覆盖语义、跨 Poll 使用 Source、在 poll 线程做阻塞操作、忽略 EINTR。建在 mio 之上的抽象还有 3 个额外陷阱。所有这些都源于 mio 的核心设计决策:边缘触发 + 非阻塞 + 手动管理。

  • mio(六):代码示例-TCP Echo Server

    mio 官方提供的 TCP server 示例约 130 行,展示了手动事件循环的完整模式:Token 分发、WouldBlock 处理、edge-triggered drain、连接池管理。同样的功能用 tokio 约 15 行。这个对比不是在批评 mio——它精确地展示了 mio 的定位:它是操作系统事件通知的薄封装,不是应用框架。

  • 整洁架构(四):微服务时代的落地-从理想到妥协

    国内大厂(腾讯、字节等)在微服务实践中大量借鉴了 Clean Architecture 的思想,但几乎没有团队完整照搬同心圆模型。实际落地是在敏捷迭代压力下,选择性应用依赖反转原则——核心域严格分层,CRUD 边缘服务直接怼。

  • mio(五):关键设计决策

    mio 的每一个设计决策都是为 Rust 的 ownership 系统量身定做的。Token 替代回调是为了避免 closure 生命周期问题;强制边缘触发是为了简化 API 和提高性能;强制 non-blocking 是为了避免意外阻塞事件循环;没有全局状态是为了让 Poll 的生命周期完全由用户控制。

  • 整洁架构(三):方法设计-每层用自己的语言命名和传参

    Handler 说协议语言(HTTP 动词 + 资源),Service 说业务语言(领域动词 + 意图),Repository 说存储语言(CRUD + 查询条件),Domain Entity 说规则语言(不变量 + 状态转换)。如果两层的方法签名几乎一样,说明有一层在当透传中间人,不该存在。

  • mio(四):为什么不直接用 mio

    mio 刻意只做事件通知,不提供 timer、buffer 管理、async/await、任务调度。直接用 mio 写服务器代码就像用 epoll_wait 的 C 代码一样繁琐。tokio 在 mio 之上叠加了完整的异步运行时。这不是 mio 的缺陷——这是 Unix 哲学的 "do one thing well"。

  • 整洁架构(二):核心原则-依赖方向永远从外向内

    Clean Architecture 只有一条铁律:内层不能 import 外层的任何东西。通过接口定义在消费方(依赖反转),实现内层调用外层能力、却不依赖外层实现。其余分层、命名都是这条规则的推论。

  • mio(三):平台后端-epoll、kqueue、IOCP

    mio 在三个平台上的封装策略完全不同。Linux epoll 和 macOS kqueue 都是 Reactor 模型的原生映射,mio 的封装很薄。Windows IOCP 是 Proactor 模型,mio 必须用 AFD(Auxiliary Function Driver)这个底层驱动来模拟 Reactor 语义——这是 mio 中最复杂的代码路径。

  • 整洁架构(一):概览-依赖方向与层级职责

    Clean Architecture 只有一条铁律:依赖只能从外向内,内层不知道外层的存在。

  • mio(二):核心架构-Poll、Token、Interest、Events

    mio 的核心由 6 个类型组成:Poll(事件循环)、Registry(注册表)、Token(事件标识)、Interest(监听意图)、Events(事件集合)、Source trait(可轮询的 I/O 源)。整个 API 没有一个回调函数、没有一个 closure——全靠整数 Token 做事件分发。这是有意为之的设计决策,为了与 Rust 的 ownership 模型兼容。

  • mio(一):Rust 异步生态的 Reactor 基石

    mio 是 Rust 生态中对操作系统 I/O 事件通知机制(epoll/kqueue/IOCP)的薄封装,由 Carl Lerche 创建,当前版本 1.2.1。它刻意只做一件事:事件就绪通知。没有 timer、没有 buffer、没有 async/await。tokio 和整个 Rust 异步生态建在 mio 之上。它与 C++ Asio 的核心差异:Asio 选 Proactor 是因为要向 IOCP 对齐;mio 选 Reactor 是因为 Rust 语言特性(ownership + async/await)让 Reactor 模式更自然,而 IOCP 的适配代价由 mio 在内部消化。

2025 44
十二月 3
  • Go 服务:从接口契约到分层实现

    写业务代码"无从下手"的本质是逻辑还在脑子里碎片化。解法是固定一条拆解路径——先定接口契约(入参出参),再定领域模型(存什么),最后分层填充业务逻辑(Handler → Service → Repository)。每一层只做一件事,卡壳时用伪代码注释占位,逐步替换为真实代码。

  • Go 服务:容量评估与扩容决策

    Go 服务本身几乎不是瓶颈(纯 CPU 可达 10 万+ RPS),真正的瓶颈在数据库查询、RPC 调用等 I/O。决策核心是:用 CPU 利用率 × P99 延迟的组合判断该优化代码还是加机器,用连接池指标定位隐藏瓶颈。

  • Go 网络(一):goroutine-per-connection 模型与生产实践

    Go 的网络编程模型是 goroutine-per-connection:每个连接一个 goroutine,写同步阻塞风格的代码,runtime 的 netpoller 在底层用 epoll/kqueue 实现异步 I/O。关键在于超时管理(Deadline 是绝对时间不是超时)和连接池配置(MaxIdleConnsPerHost 默认 2 是生产环境的坑)。

十一月 4
  • C++ 竞赛:ACM 模式 I/O 的组合拳

    ACM 模式写 C++ 的 I/O 模板只有两行:iosbase::syncwith_stdio(false); cin.tie(nullptr);——关掉 C stdio 同步和 cin 对 cout 的 tie,把默认慢的 cin/cout 提到接近 scanf/printf 的速度;然后用 cin >> 读 token、getline 读整行、stringstream 切分变长字段 这三招覆盖 95% 的输入格式;剩下 5% 的极限数据量(> 10^7 整数)用 fread + 手写 parseInt 解决。最容易踩的坑是 cin >> 和 getline 混用时换行符残留、scanf 读 double 用错 %f、输出循环里写 endl 拖慢几十倍。

  • C++ STL:迭代器如何解耦算法与容器

    STL 的灵魂是 Alexander Stepanov 的泛型编程思想:算法只依赖迭代器,容器只提供 begin/end——N 个算法 × M 个容器从需要 N×M 份代码变成 N+M 份。vector 扩容用 moveifnoexcept 保证强异常安全,std::sort 用 introsort 保证最坏 O(n log n),unorderedmap 因 ABI 包袱锁死为链地址法(比 absl::flathash_map 慢 2-3 倍)——这些实现选择背后都有性能与兼容性的深层权衡。

  • Go Redis:go-redis/v9 的连接池、Pipeline 与 Hook

    github.com/redis/go-redis/v9 是 Redis 官方维护的 Go 客户端(截止 2026-04 最新 v9.18.0),提供类型安全的命令接口、channel-based 连接池、Pipeline/Transaction 批处理、Pub/Sub、Lua 脚本、Hook 中间件链、以及对 Standalone/Cluster/Sentinel/Ring 四种部署模式的统一抽象。掌握它的连接池调优、Pipeline 正确使用、redis.Nil 错误处理和 Context 超时行为是生产可靠性的关键。

  • Go PostgreSQL:pgx 的架构设计与生产实践

    github.com/jackc/pgx/v5 是纯 Go 实现的 PostgreSQL 驱动和工具集。它直接实现 PostgreSQL 线协议(不依赖 libpq),提供原生 API 和 database/sql 兼容层双模式,内置连接池(pgxpool)、自动 prepared statement 缓存、COPY 批量导入、LISTEN/NOTIFY、70+ 类型映射。相比 lib/pq,pgx 更快、功能更全、维护更活跃,是 2026 年 Go + PostgreSQL 的唯一正解。

十月 8
  • C++ 协程:语言机制、陷阱与实现边界

    C++20 协程的本质是编译器把带 coawait/coyield/coreturn 的函数重写成状态机,把跨 suspend 存活的变量打包到一个堆上的 frame——这是语言级 stackless 协程,不是 Goroutine 式 stackful。标准库只给了 coroutinehandle / suspendalways / suspendnever 这几块地基,不给 Task<T>、不给 Generator(C++23 才加)、不给 executor、不给调度器。要在网络服务器里用,必须自己实现 Task<T>(严格遵守 symmetric transfer 否则链式 coawait 直接栈溢出)+ IoUringAwaiter(绑定到 reactor)。三大杀手级陷阱:协程参数必须按值传(引用参数跨 suspend 必悬挂)、finalsuspend 必须 suspend_always(否则 double-free)、热路径不能依赖 HALO,需要自定义 operator new。

  • Linux I/O(二):io_uring 的双环模型与工程边界

    iouring 的本质不是"更快的 epoll",而是用户态和内核态共享两个 mmap 环(SQ 提交环 / CQ 完成环)+ 完成模型(submit then wait for completion),统一覆盖文件 / socket / timer / futex 的异步。相较 epoll 的 readiness 模型,它在存储 I/O(libaio 无法 buffered)和syscall 密集场景(SQPOLL 下热路径零 syscall)有质变;对已优化过的 epoll 网络栈提升只有 10–30%。工程上的真实门槛是内核版本下限 ≥ 5.15、容器 seccomp 默认屏蔽、安全 CVE 历史重、多云厂商禁用,以及一条必须铭刻的规则:CQE 的完成顺序和 SQE 的提交顺序无关,必须靠 userdata 关联。

  • C++ 网络编程:epoll、Reactor 与 one loop per thread

    C++ 标准库到 C++20 都没有网络库(Networking TS 因 executor 重构被无限期搁置)。Linux 生产方案是 epoll ET + Reactor 模式 + one loop per thread:主 Reactor accept 连接分发到工作线程池,每个工作线程独占 EventLoop 处理自己的连接,通过 eventfd 跨线程唤醒、timerfd 统一定时器、sharedptr + weakptr 管理 TcpConnection 生命周期。muduo 是国内 C++ 服务器开发事实上的学习范本。

  • Linux I/O(三):BSD socket 编程手册

    socket API 的本质是把"可寻址的双向通信"塞进 fd 抽象(everything is a file),代价是必须通过 setsockopt/shutdown/getsockopt 等元操作补齐 TCP 状态机的语义;生产代码的复杂度集中在短读/短写循环、EAGAIN/EINTR 重试、SIGPIPE 屏蔽、字节序转换、TIMEWAIT 与 SOREUSEADDR 的误解这五个点上。

  • GORM(三):生产环境最佳实践

    GORM 在生产环境中最常翻车的地方不是功能缺失,而是默认行为与开发者直觉不一致:零值更新被吞、Session 条件污染、软删除隐式过滤、连接池耗尽。本篇系统梳理连接池四参数调优公式、Logger/PreparedStmt/DBResolver 配置、12 个真实生产陷阱(附正反代码)、性能优化策略、以及 GORM/sqlc/sqlx/Ent/Bun 五种方案的选型决策矩阵。

  • Linux I/O(一):epoll 高性能的本质与使用要点

    epoll 把 select/poll 的 O(n) 轮询重构为「注册一次 + 设备就绪时回调插入链表」的事件驱动模型;正确使用的核心是 ET 必须配非阻塞 + 循环到 EAGAIN、EPOLLERR/EPOLLHUP 无需请求但必须处理、close 前必须显式 EPOLLCTLDEL(防 dup 场景的悬垂监听)。

  • GORM(二):关联与预加载

    GORM 支持四种关联类型(Belongs To / Has One / Has Many / Many To Many)和多态关联。默认是懒加载(不自动加载关联),用 Preload 做 eager loading(2 条 SQL 解决 N+1),用 Joins 做单条 JOIN 查询(适合按关联字段过滤)。Association Mode 提供 Find/Append/Replace/Delete/Clear/Count 六种操作管理关联关系。

  • GORM(一):核心用法与设计决策

    GORM 是 Go 生态最主流的 ORM,通过 struct tag 映射模型、method chaining 构建查询、自动事务保证写入一致性。核心概念:gorm.Model 提供 ID/时间戳/软删除,Session/Statement 架构解决链式调用的状态污染问题,Save() 更新全字段而 Updates() 只更新非零值字段。v1.30.0+ 引入 Generics API 提供类型安全。

九月 5
  • Go 数据库:sqlc 的 SQL-first 类型安全方案

    sqlc 是一个编译器:输入 schema.sql(DDL)+ query.sql(带注解的 DML),输出类型安全的 Go 代码。不是 ORM,不是 query builder,是 SQL-first 的代码生成。

  • C++ 工程化(二):命名规范没有统一标准但有底线规则

    C++ 没有统一的命名规范,主流风格(STL/Google/LLVM/Qt/Unreal)各自为政——这是 C++ 40 年多时代叠加的历史遗留问题。但"没有统一标准"不等于"怎么写都行":C++ 标准对保留标识符有硬性规定,违反就是 UB;宏必须全大写也是跨风格的唯一共识。实操铁律是:进入已有 codebase 严格 follow,新建项目选一个成熟 style guide 一以贯之,并用 clang-format 强制执行——风格选择的质量远不如风格一致性的质量重要。

  • Go RPC:gRPC、HTTP/2 与 proto 契约

    gRPC 用 .proto 文件定义服务契约,代码生成器产出强类型的 server 接口和 client stub,通过 HTTP/2 实现单连接多路复用、头部压缩和四种通信模式(unary/server-stream/client-stream/bidi-stream)。核心价值不是"比 REST 快",而是契约即代码、跨语言一致、streaming 原生支持。

  • C++ 工程化(一):Modern CMake 的核心是 target-based 与传递性语义

    CMake 不是构建系统而是构建系统生成器(读 CMakeLists.txt → 生成 Makefile / Ninja / VS 工程 / Xcode 工程)。Old-Style CMake 靠 includedirectories、adddefinitions 这种全局命令驱动,无作用域、无传递性、无法组合;Modern CMake(3.15+ 是真正的基线)把一切挂在 target 上,用 PUBLIC/PRIVATE/INTERFACE 显式表达"这个使用需求是只给我自己用,还是要传递给 consumer"。掌握这三个关键字 + generator expressions($<...>)+ CMakePresets.json + FetchContent,就写得出 2026 年的 idiomatic CMake。

  • Go 工具链:Protobuf 的字段编号、Varint 与二进制序列化

    Protobuf 用字段编号(而非字段名)标识数据、用 Varint 压缩整数、省略零值字段,实现了比 JSON 小 2-5 倍、快 5-10 倍的序列化性能,同时通过字段编号机制天然支持 schema 前向/后向兼容演化。

八月 10
  • Go 工具链:Buf 如何替代 protoc 工作流

    Buf 用一个 CLI 统一了 protobuf 的构建、lint、breaking change 检测、代码生成、格式化和依赖管理。它替代的不是 protobuf 本身,而是 protoc + 手动管理插件 + Makefile/shell 脚本的传统工作流。核心价值:buf.yaml 定义模块和规则,buf.gen.yaml 定义代码生成,buf generate 一个命令完成所有事。

  • 现代 C++(五):内存模型、atomic、thread 与 future

    C++11 之前没有语言级内存模型,多线程代码在编译器 + CPU 双重乱序下不可能正确(Hans Boehm 2005 论文证明)。C++11 一次性引入内存模型、std::atomic、std::thread、std::future 四件套,给了 C++ 官方并发语义。六种 memory order 是理解的核心——在 x86 上 release/acquire 几乎免费、seq_cst 要 mfence,在 ARM 上差距更大,这正是暴露细粒度 order 的意义。

  • 现代 C++(四):模板元编程从 SFINAE 到 Concepts

    C++ 模板元编程(TMP)的本质是把编译器当成纯函数式解释器:类型是值、模板是函数、偏特化是模式匹配。SFINAE 是语言规则的副作用被滥用成约束机制的历史遗留,C++17 if constexpr + C++20 Concepts 把"编译期分支"和"约束表达"从巫术升级为一等公民,错误信息从 200 行模板栈变成一行说明。业务代码 95% 用不到深度 TMP,但读标准库和调试模板错误必须看得懂。

  • Go 基础:正则表达式、自动机与 regexp 的线性时间保证

    正则表达式的核心分歧在引擎实现:backtracking 引擎(PCRE/Java/Python/JS)用递归回溯,最坏指数时间,存在 ReDoS 风险;automata-based 引擎(RE2/Go regexp)用 NFA/DFA 模拟,保证线性时间,但牺牲了 backreference 和 lookaround。Go 的 regexp 包基于 RE2,选择了安全和可预测性,代价是表达力略弱。理解引擎原理比记语法重要得多。

  • 现代 C++(三):Concepts、Ranges、Coroutines、Modules 如何重定义 C++

    C++20 是继 C++11 之后最大的一次升级,核心是四个范式级特性同时落地:Concepts 把模板错误从 200 行报错变成一行说明,Ranges 用惰性 view + 管道语法重构了整个 STL 算法接口,Coroutines 让异步代码写得像同步(但标准库没给 Task,需要三方库),Modules 终于告别了头文件——每一个单独拎出来都足以改变一整类编程风格。

  • Go 基础:interface 与 first-class function 如何消解 GoF 模式

    Go 没有继承、没有 abstract class,用隐式 interface + first-class function + channel 替代了 GoF 23 个模式中的绝大多数。真正需要手写的模式只有 Functional Options、Middleware Chain 和 Decorator 堆叠,其余要么是语言内置(Iterator = range,Singleton = sync.Once),要么是 Java 遗产在 Go 里直接不需要。

  • 现代 C++(二):if constexpr、optional 与 variant 如何降低认知负担

    C++17 不是革命而是善后——它把 Boost.Optional/Variant/Any/Filesystem/string_view 吸收进标准库,用 if constexpr 和折叠表达式干掉 SFINAE 地狱,用结构化绑定消除 std::tie 的笨重,用 CTAD 让模板参数不再冗余;日常业务代码的认知负载一次性大幅下降,是应届生和资深的分水岭。

  • Go 基础:interface 的底层实现-eface 与 iface

    梳理 Go interface 的底层表示,重点看 eface、iface、itab 和运行时类型元数据如何协作完成方法派发。

  • 现代 C++(一):C++11/14 为什么是现代 C++ 的起点

    C++11/14 的核心升级不是加了几个语法糖,而是把值语义 + RAII + 移动 + lambda 组合成新范式:资源用智能指针自动管理、返回大对象用移动不用拷贝、行为用 lambda 就地编写——日常 C++ 的认知负载一次性大幅下降,Bjarne 本人说"像一门新语言"不是营销话术。

  • Go 基础:错误处理与 Errors Are Values

    Go 将错误视为普通值(error 是一个只含 Error() string 方法的 interface),通过多返回值强制调用者显式处理,而非像 Java/Python 那样用异常机制隐式传播。Go 1.13 引入 error wrapping(%w、errors.Is、errors.As),Go 1.20 引入 errors.Join 支持多错误合并。这套机制的核心优势是:控制流可见、错误可编程、无隐式传播。代价是 if err != nil 样板代码多,但 Go 团队已于 2025 年 6 月正式宣布不再追求语法层面的错误处理变更。

七月 8
  • Go 运行时(三):netpoller 如何用 epoll 与 gopark 跑异步 I/O

    Go 运行时在创建网络 socket 时将其设为非阻塞,当 Read/Write 返回 EAGAIN 时,通过 gopark 挂起 goroutine(不占 OS 线程),将 fd 注册到 epoll/kqueue(边缘触发),就绪后通过调度器的 findRunnable 或 sysmon 唤醒 goroutine 重试——用户写的是同步代码,运行时跑的是异步 I/O。

  • Go 运行时(二):并发三色标记清除收集器

    Go 使用并发、非移动、三色标记清除垃圾收集器。设计哲学是用内存换低延迟(STW < 1ms),只暴露两个调参旋钮(GOGC + GOMEMLIMIT)。Go 1.26 起默认启用 Green Tea GC,marking 阶段性能提升 10-40%。

  • Go 运行时(一):GMP 调度模型

    Go 运行时通过 G(goroutine)、M(OS thread)、P(logical processor)三级抽象实现 M:N 线程调度。P 的引入(Go 1.1)解决了全局锁竞争,work-stealing 保证负载均衡,异步抢占(Go 1.14+,SIGURG 信号)消除了 CPU 密集型 goroutine 的饥饿问题。Go 1.25 起 GOMAXPROCS 自动感知容器 cgroup CPU 限制。

  • Go 并发(五):并发模式与最佳实践

    Go 的并发编程远不止 goroutine + channel。生产级代码需要掌握 context 传播与取消树、errgroup 有界并发、worker pool 模式、graceful shutdown 全流程、race detector 的 CI 集成,以及至少十种常见反模式的识别与规避。本文是"如何在生产环境写出正确并发 Go 代码"的完整指南。

  • Go 并发(四):无锁编程层次-从 CAS 到消除共享

    CAS 既是乐观锁也是无锁编程,不矛盾——"乐观锁"描述并发策略,"无锁"描述实现方式(不用 mutex)。比 CAS 更激进的方向是 FAA(一次成功)→ 分片(消除共享)→ RCU(读者零开销)。

  • Go 并发(三):内存模型、happens-before 与同步语义

    Go 内存模型定义了 happens-before 偏序关系,它是 sequenced-before(单 goroutine 内语句顺序)和 synchronized-before(跨 goroutine 同步操作)的传递闭包。只有通过 happens-before 关联的写操作才保证对读操作可见。2022 年(Go 1.19)修订正式赋予 sync/atomic 顺序一致性语义,与 C++ seq_cst、Java volatile 对齐。任何存在 data race 的 Go 程序行为未定义——不存在"良性竞争"。

  • Go 并发(二):sync 包与并发原语

    sync 包的每个原语都围绕同一个核心模式:fast path 用 atomic 无锁操作,slow path 用 runtime semaphore 休眠/唤醒。Mutex 通过 normal/starvation 双模式平衡吞吐与尾延迟;sync.Map 在 Go 1.24 重写为 HashTrieMap(并发哈希字典树);sync.Pool 利用 per-P 本地存储 + victim cache 实现两轮 GC 缓冲。理解这些内部结构,才能在正确的场景选择正确的原语。

  • Go 并发(一):Channel 内部机制与使用模式

    Channel 是 Go CSP 并发模型的核心原语,底层由 hchan 结构体实现(环形缓冲区 + 互斥锁 + 两条等待队列)。发送/接收的关键优化是直接传输——当对端已在等待时,数据绕过缓冲区直接拷贝到对方栈上。select 通过随机轮询序(防饥饿)+ 地址排序加锁(防死锁)实现多路复用。理解这些内部机制对写出正确、高效的并发代码至关重要。

六月 6
  • asio(六):十大陷阱与生产实践

    Asio 最常见的 bug 源头是对象生命周期(异步操作引用了已析构的对象)和缓冲区生命周期(buffer 底层内存在操作完成前被释放)。strand 不是锁但胜似锁——用错了比不用更危险。timer 取消的语义和直觉不一致。这篇列出 10 个陷阱和 7 条生产 checklist。

  • asio(五):操作系统I/O多路复用-epoll、kqueue、IOCP如何被统一

    Asio 通过编译期条件选择 reactor 实现(epollreactor / kqueuereactor / winiocpiocontext),每种实现共享统一的内部接口(registerdescriptor、startop、cancelops、run、interrupt)。Linux/macOS 是 Reactor 之上模拟 Proactor,Windows 是原生 Proactor 直通 IOCP。io_uring 作为第四种后端在 Boost 1.78+ 可选启用。

  • asio(四):异步模型演进-从回调到C++20协程

    Asio 的异步操作只写一次实现,通过 asyncresult<CompletionToken, Signature> trait 自动适配回调、stackful 协程、stackless 协程、C++20 协程和 future。这是 Asio 最精妙的设计——一个 asyncread 实现同时支持五种异步风格,新增风格只需特化一个 trait。

  • asio(三):组件拆分-strand、timer、socket、buffer如何协作

    Asio 的组件设计遵循一个原则:I/O 对象不拥有资源(缓冲区、线程),只持有操作系统句柄和执行器引用。strand 解决多线程序列化问题,timer 集成进 reactor 的等待超时,socket 分层抽象协议差异,buffer 是纯粹的指针+长度对。这些组件通过共享同一个 io_context 和执行器模型自然组合。

  • asio(二):io_context-事件循环的核心引擎

    iocontext 不只是一个事件循环,它是异步操作的调度器:管理完成事件队列、分发 handler 到调用 run() 的线程、与操作系统的 I/O 多路复用器集成。Boost 1.66 将 ioservice 拆分为 io_context(执行上下文)+ executor(轻量句柄),是为了解耦 I/O 对象与具体的执行环境。

  • asio(一):Proactor模式-为什么Asio不用Reactor

    Proactor 模式以「操作完成通知」为核心,恰好是 Windows IOCP 的原生语义;而 Linux epoll/macOS kqueue 是 Reactor(就绪通知),可以在其上模拟 Proactor。反过来在 IOCP 上模拟 Reactor 极其别扭。Asio 选 Proactor 作为公共 API,是唯一能同时高效映射所有主流操作系统的选择。