Outbox Pattern

解决了什么问题? 分布式系统通过消息队列等手段互相解耦带来的状态不一致, 如数据落库成功但是kafka消息发送失败等情况 是什么? 利用本地数据库事务的原子性,将数据入库和消息入库(outbox-records)变为原子性操作。 异步启动一个 poller() worker 定时从 outbox-records 中取出消息尝试发送, 消息至少被成功发送一次, 保证了分布式系统数据的一致性。 问题 高并发场景下会导致更高的延迟 消息接收端需要做幂等性校验 发送失败/超时不能自动回退数据库操作

June 6, 2026

Golang Select

快速路径 运用 select 中的 default 分支可以实现无锁(非阻塞)的快速检查 select { case v, ok := <- ch: if !ok { // ch 已经被关闭, 直接返回 return io.EOF } return v // ch 缓冲区中有内容, 直接取出返回 default: // ch 缓冲区中无内容,select 走 default 分支防止阻塞 } select { // 阻塞等待 case v, ok := <- ch: if !ok { // ch 已经被关闭, 直接返回 return io.EOF } return v // ch 缓冲区中有内容, 直接取出返回 case <- ctx.Done(): // 等待超时/取消 return ctx.Err() } 在阻塞等待之前加入快速路径主要是因为 golang 中的 select 具有随机性, 在高并发场景下可能会出现 <- ctx.Done() 分支永不被进入。 详细的源码解析见下文 select 语句源码 #filepath: src/runtime/select.go // case 对应的结构体 type scase struct { // chan -> case 关联的 channel c *hchan // data element // 发送: 待发送数据的源地址 (case ch <- v elem 指向 v 源地址) // 接收: 目标变量的地址(堆、栈)(case v := <- ch elem 指向 v 的地址) elem unsafe.Pointer } #filepath: src/runtime/select.go // select 语句经编译器编译后被转化为 selectgo 的函数调用 func selectgo( cas0 *scase, // scase 数组的首地址,存放在 goroutine 栈上 order0 *uint16, // [2*ncases]uint16 数组首地址,也分配在栈上 // 为什么有了 cas0 还需要 order0? 详见下文 pc0 *uintptr, // (race detector 构建) [ncases]uintptr 数组首地址;否则 nil;与本章节无关 nsends int, // 发送 case 的数量 nrecvs int, // 接收 case 的数量 block bool, // true=阻塞(无 default),false=非阻塞(有 default) ) (int, bool) // 返回值:选中的 case 索引 + 是否成功接收到值 // NOTE: In order to maintain a lean stack size, the number of scases // is capped at 65536. // 将裸指针转换为具有最大合法边界的数组 // 防止后续写入栈外内存 cas1 := (*[1 << 16]scase)(unsafe.Pointer(cas0)) order1 := (*[1 << 17]uint16)(unsafe.Pointer(order0)) ncases := nsends + nrecvs scases := cas1[:ncases:ncases] // array[:end:max] 限制新切片最大容量为 ncases pollorder := order1[:ncases:ncases] // 轮询顺序 注意:轮询顺序是随机的,并非顺序轮询,详见下文 lockorder := order1[ncases:][:ncases:ncases] // channel 加锁顺序 selecctgo 如何决定轮询顺序? ...

June 6, 2026

Linux Cgroup

新版内核普遍采用 v2 版本的 cgroup 是什么 cgroup 既 Controll group, 是 linux 对外提供的文件接口,用来控制进程资源。 $ mount -t cgroup2 cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursiveprot,memory_hugetlb_accounting) 使用 新建 group 直接在 /sys/fs/cgroup 下新建目录 $ cd /sys/fs/cgroup/ $ sudo mkdir test $ ls ./test cgroup.controllers cpu.stat memory.peak cgroup.events cpu.stat.local memory.pressure cgroup.freeze cpu.uclamp.max memory.reclaim cgroup.kill cpu.uclamp.min memory.stat cgroup.max.depth cpu.weight memory.swap.current cgroup.max.descendants cpu.weight.nice memory.swap.events cgroup.procs ... 重点关注 cgroup.procs 这个文件, 其中的内容(pid)既我们想要控制的进程号。 控制cpu占用率 $ while true; do : ; done & [1] 50274 # 50274 为对应pid $ top -b -n 1 -p 50274 | grep "50274" | awk '{print $9}' 99.7 # 此时没有限制,死循环占用100%的cpu $ echo 50274 | sudo tee cgroup.procs # 直接使用当前shell的进程号, 这样子随后开启的子进程也会受到控制 $ echo 2000 10000 | sudo tee cpu.max # 10000 微秒内 `$$` 进程只能占用 2000 微秒 $ top -b -n 1 -p 50274 | grep "50274" | awk '{print $9}' 19.9 # 20% 控制内存 测试程序 ...

June 6, 2026