跳至主要内容

博文

Go并发控制的常见用法和注意事项

Go 并发控制:常见用法与踩坑指南 Go 的并发模型基于 CSP(Communicating Sequential Processes)理念,核心口号是「不要通过共享内存来通信,而要通过通信来共享内存」。但实际项目中, sync 包和共享内存的用法同样常见。这份文档把常见工具和典型坑点放在一起,方便对照查阅。 一、基础并发原语 1. goroutine go func () { doSomething() }() 启动成本很低(初始栈约 2KB,按需增长),但 不代表可以无限制启动 。 go 关键字只是把函数丢给调度器,不保证执行顺序,也不保证主协程会等它结束。 常见坑:主协程提前退出 func main () { go fmt.Println( "hello" ) // main 函数直接结束,goroutine 可能还没来得及执行 } 主协程退出时,其余 goroutine 会被直接杀死,不会等待。必须用 WaitGroup 、 channel 或其他同步手段显式等待。 2. channel ch := make ( chan int ) // 无缓冲,发送阻塞直到有人接收 ch := make ( chan int , 10 ) // 有缓冲,容量满了才阻塞 无缓冲 channel 是同步点,发送方和接收方必须「握手」。 有缓冲 channel 只在缓冲区满/空时才阻塞,容易掩盖设计问题(比如用大缓冲区掩盖生产消费速度不匹配)。 常见坑一:向已关闭的 channel 发送数据 close (ch) ch <- 1 // panic: send on closed channel 常见坑二:重复关闭 channel close (ch) close (ch) // panic: close of closed channel 原则: 由发送方关闭 channel,且只关闭一次 ;接收方永远不要关闭。如果有多个发送方,用 sync.Once 或专门的协调协程来关闭。 常见坑三:nil channel 导致永久阻塞 对 nil channel 的收发操作会永远阻塞(这在 select 里其实是一个有用的技巧,用来临时...
最新博文

goroutine 泄露

Go 语言里的 goroutine 泄露(goroutine leak) ,它通常发生在: goroutine 已经没有实际工作可做、也无法退出,但仍然被 Go runtime 保留。 常见情况有这几种: 1. 永久阻塞在 channel func foo () { ch := make ( chan int ) go func () { x := <-ch // 一直等,但没人发送 fmt.Println(x) }() } 如果后续没人向 ch 发送数据,这个 goroutine 就会一直阻塞。 2. channel 没有消费者 ch := make ( chan int ) go func () { ch <- 1 // 一直卡在这里 }() 如果没有其他 goroutine 接收: <-ch 这个 goroutine 就无法继续执行,也无法退出。 3. 忘记关闭 channel / 退出条件 典型例子: func worker (ch <- chan int ) { go func () { for { x := <-ch fmt.Println(x) } }() } 如果 ch 永远不关闭,且没有其他退出机制,这个 goroutine 可能永久运行。 更常见的写法是: for x := range ch { fmt.Println(x) } 然后由生产者: close (ch) 让消费者自然退出。 4. select 一直等不到退出信号 go func () { for { select { case x := <-ch: fmt.Println(x) } } }() 如果 ch 永远没有数据,这个 goroutine 会一直活着。 通常应该加一个退出机制: go func () { for { select { ...

优惠券系统架构设计

优惠券系统架构设计文档 版本:v1.0 适用场景:电商/O2O 类平台的优惠券发放、领取、叠加计算与核销 目录 背景与目标 需求概述 总体架构 核心领域模型 规则引擎设计 叠加规则设计详解 最优叠加方案计算(核心算法) 数据存储设计 服务拆分与 API 设计 高并发与一致性保障 可扩展性设计 监控与可观测性 总结与后续演进 1. 背景与目标 优惠券系统看似简单(发一张券、抵扣一点钱),但在实际业务中会迅速演变成规则最复杂、最容易出资损事故的模块之一,原因是: 券的类型多(满减、折扣、立减、免邮、赠品券……),每种的计算方式不同; 券与券之间存在复杂的叠加/互斥关系,且这些关系会随运营活动频繁调整; 用户手里可能同时持有多张可用券,系统需要在毫秒级内帮用户/自动挑出"最划算"的组合; 一旦规则出错(该互斥的没互斥、该叠加的没叠上),直接影响 GMV 和资损。 本设计的核心目标是: 把"规则判定"与"组合寻优"两个能力做成独立、可配置、可插拔的引擎层 ,业务方(运营)只需要配置规则,不需要改代码就能上线新的叠加策略。 2. 需求概述 2.1 功能性需求 能力 说明 券模板管理 创建满减/折扣/立减/免邮/赠品等类型的券模板,设置门槛、适用范围、有效期、库存 发放与领取 支持主动领取、系统发放(下单赠送、会员权益)、定向发放 可用券查询 给定订单(商品、金额、店铺),查出用户当前可用的券列表 叠加规则判定 判断任意两张/多张券是否可以同时使用 最优叠加方案计算 在所有可用券中,自动计算出使订单实付最低(或折扣最大)的合法组合 核销 下单时锁定券、支付成功后核销、订单取消/退款后回滚 规则配置化 运营后台可视化配置叠加规则、互斥组,无需发版 2.2 非功能性需求 低延迟 :查询可用券 + 计算最优组合需在结算页同步返回,目标 P99 < 100ms; 强一致性 :券的锁定/核销/库存扣减不能超卖、不能被重复核销; 高并发 :大促场景下领券、查询是脉冲式流量; 可扩展 :新增券类型、新增叠加规则不应侵入核心链路代码; 可审计 :每一次核销、每一次规则判定要可追溯...

推广员分销系统设计难点与避坑指南

推广员分销系统设计难点与避坑指南 一、系统概述 这是一个典型的"CPS(按销售付费)分销返佣系统",核心业务闭环是: B 端(商家) 设置商品分佣比例 ↓ 推广员(C端用户/达人) 生成专属海报/链接 ↓ 其他用户扫码/点击 → 浏览 → 下单支付 ↓ 系统识别归因关系 → 计算佣金 → 冻结期 → 结算入账 → 提现 要求: 多租户 (多个B端商家共用一套系统,数据/配置互相隔离)+ 高并发 (大促、直播带货等场景下瞬时流量高)。 下面按照"分佣规则 → 归因跟踪 → 资金安全 → 数据一致性 → 性能 → 风控 → 多租户隔离 → 可观测性"的顺序,逐一拆解每个环节的设计难点和坑,并给出建议方案。 二、核心难点逐项分析 1. 多租户数据隔离——最容易埋雷的基础设施问题 难点: - 隔离方案怎么选:独立库(强隔离但运维成本高)、共享库独立Schema、共享库共享表+ tenant_id (成本低但隔离弱)。分销系统这种中小租户量大、单租户数据量不算特别大的场景,业界主流是 共享库共享表 + tenant_id 分片字段 。 - 最大的坑 :业务代码里每条 SQL 都要手动带 tenant_id ,一旦某个新人开发漏写了 WHERE 条件,就是跨租户数据泄露的严重事故(比如商家A能看到商家B的推广员佣金数据)。 建议方案: - 不要依赖"自觉",在 ORM/DAO 层做 强制拦截 (如 MyBatis 拦截器、Hibernate Filter),自动为所有 SQL 注入 tenant_id 条件,业务层无法绕过。 - tenant_id 作为分库分表的分片键之一,但要注意大小租户体量不均衡导致的 数据倾斜 ——可以给超大租户单独分配库表,中小租户共享。 - 所有缓存 Key、消息队列 Topic/Tag、定时任务、导出文件都要带租户维度,避免"隐性"的跨租户污染。 2. 分佣规则引擎——规则多、优先级复杂、还要支持历史快照 难点: - 分佣比例可能存在多级配置:商品级 > 类目级 > 店铺级 > 平台默认,甚至叠加活动期间的临时加佣。规则合并逻辑如果散落在各处代码里,后期极难维护。 - 最容易被忽略的坑 :商家...

Go语言GC详解

垃圾回收(GC)到底在干什么——从零开始讲清楚 这份文档从"为什么需要GC"讲起,一步步搭到"为什么并发GC需要写屏障和短暂STW",尽量把每个术语在第一次出现时就解释清楚。 1. 内存管理要解决的问题 程序运行时会不断申请内存(比如 new 一个对象、 append 一个切片)。申请之后,早晚有一天这块内存不再需要了,就应该被收回,否则内存会一直涨,最终把机器耗尽。 回收内存的方式,历史上只有两条路: 手动管理 (C/C++ 的 malloc / free ):程序员自己决定什么时候释放。问题很直接——忘了释放 → 内存泄漏;释放早了 → 其他地方还在用这块内存,程序直接崩溃或读到脏数据(这类 bug 叫 use-after-free,是很多安全漏洞的根源)。 自动管理 (Go、Java、Python、JS……):语言运行时自己判断哪些内存还在用、哪些可以收回。 这个"自动判断"的机制,就是 GC(Garbage Collector,垃圾回收器)。 GC 要解决的核心问题只有一个: 在程序运行的过程中,怎么知道一块内存现在还有没有人在用? 2. GC 怎么判断"这块内存还有没有人用" 严格来说,"这个对象以后还会不会被用到"这个问题,在数学上是 无法精确判定 的(跟停机问题是一类问题)。所以所有实用的 GC 都退而求其次,换了一个 可以被严格证明、计算代价也可控 的替代问题: 不问"这个对象以后有没有用",只问"从程序当前能直接访问到的地方出发,沿着指针能不能走到这个对象"。 能走到,就叫 可达(reachable) ,判定为存活;走不到,就判定为垃圾,可以回收。这个概念叫 可达性分析(reachability analysis) ,是几乎所有追踪式 GC(Go、Java、C#、Python 用的都是这一类)共同的理论基础。 "程序当前能直接访问到的地方"叫 根(roots) ,具体包括: 所有 goroutine 栈上的局部变量 全局变量 CPU 寄存器里当前正持有的指针 下面这张图展示了可达性分析的基本样子:根对象出发,能顺着指针链条走到的 A...

Gin框架架构设计与调用链分析

Gin 框架架构详解 基于 gin-gonic/gin 的核心设计,聚焦模块划分、调用链与关键实现。 1. 整体架构概览 Gin 本质上是在 Go 标准库 net/http 之上做的一层轻量封装,核心思路只有两条: 用 Radix Tree(基数树)做路由匹配 ,取代标准库 ServeMux 的线性/前缀匹配,换来更快的路由查找。 用责任链模式(Chain of Responsibility)组织中间件与业务 Handler ,通过一个 Context.Next() 递归调用链把所有处理函数串起来。 分层结构大致如下: ┌─────────────────────────────────────────────┐ │ net/http.Server │ ← Go 标准库,负责 TCP/HTTP 协议解析 └───────────────────┬───────────────────────────┘ │ 实现 http.Handler 接口 ┌───────────────────▼───────────────────────────┐ │ gin.Engine │ ← 框架入口 + 路由树容器 + 全局配置 │ ┌───────────────────────────────────────────┐ │ │ │ RouterGroup (匿名嵌入) │ │ ← 路由分组、中间件注册 │ └───────────────────────────────────────────┘ │ └───────────────────┬───────────────────────────┘ │ ServeHTTP → handleHTTPRequest ┌───────────────────▼───────────────────────────┐ │ methodTrees(每个 HTTP 方法一棵树) │ ← Radix Tree ...

Python学习指南:从Go和PHP开发者的视角

Python 从入门到精通:写给 Go / PHP 开发者 本文假设你已经熟练掌握 Go 和/或 PHP,重点不是从零讲解"什么是变量""什么是函数",而是 用你已有的知识做类比 ,把精力集中在 Python 与 Go/PHP 真正不同的地方——尤其是类型系统、并发模型、包生态和"Pythonic"的编程习惯。 目录 核心定位差异:三门语言分别在解决什么问题 环境搭建与工具链 基础语法速成 类型系统:动态但可以强类型提示 核心数据结构 函数:从简单到高级 面向对象编程的 "Python 方式" 错误处理机制 迭代器、生成器与惰性求值 装饰器与上下文管理器 并发与并行模型(重点对比 Goroutine) 包管理与生态系统 代码风格与工程规范 测试 常用框架速览 性能优化与部署注意事项 进阶学习路线图 1. 核心定位差异:三门语言分别在解决什么问题 维度 Go PHP Python 设计哲学 简单、显式、编译期安全 Web 请求-响应模型,"能跑就行" 可读性优先,"应该只有一种明显的做法" 类型系统 静态强类型,编译期检查 动态弱类型(PHP 8 起类型提示增强) 动态强类型,可选静态类型提示(type hints) 执行方式 编译为原生二进制 解释执行(通常配合 PHP-FPM) 解释执行(CPython 字节码 + 虚拟机) 并发模型 Goroutine + Channel,语言原生 天然无状态、多进程模型,协程需额外扩展 GIL 限制下的多线程 + asyncio 协程 典型场景 后端服务、基础设施、CLI 工具 Web 应用(尤其传统 LAMP 架构) 数据科学、AI/ML、脚本自动化、Web 后端 哲学关键词 gofmt ,一切从简 实用主义,历史包袱重 import this (Python 之禅),"电池自带" 给你的心智模型 :如果说 Go 是"给你一套工具,逼你按规矩来",PHP 是"给你自由,出了问题算你倒霉",那 Pyt...