Go 并发进阶(下):从零搭建 HTTP 服务
这是 Golang 学习系列的收官篇。我们把前面 9 篇的所有知识——结构体、接口、错误处理、Context、Testing、并发模式、sync 包——串起来,从零搭建一个生产级 HTTP 服务。写完这篇,你就能独立写出像样的 Go 后端了。 Q1:用标准库搭一个最小 HTTP 服务需要几行?A: 真的很少: 123456789101112131415161718192021222324252627282930313233343536package mainimport ( "encoding/json" "fmt" "net/http")type User struct { ID int `json:"id"` Name string `json:"name"`}func main() { // 注册路由 http.HandleFunc("/health", healthH...
Go GC 深度解析:从三色标记到 Green Tea(Go 1.26)
Go 1.26 正式默认启用 Green Tea GC(绿茶垃圾收集器)——这是 Go 垃圾回收机制自诞生以来最大的架构变革。本文用 10 个问题带你理解:它解决了什么、怎么做到的、对你写的代码意味着什么。 Q1:Go 的 GC 到底在做什么?为什么它这么重要?A: GC(Garbage Collector,垃圾回收器)的核心职责只有一个——自动回收不再使用的内存。 在 C/C++ 里你需要手动 malloc / free,忘了 free 就内存泄漏,free 太早就 use-after-free。Go 把这件事自动化了:你只管 make / new,运行时帮你找哪些内存已经没人引用了,然后回收。 但天下没有免费午餐。GC 要干活就得 暂停或挤占你的业务代码。所以 Go GC 的每一次演进,本质上都在做同一件事: 如何在”回收得足够快”和”对业务干扰足够小”之间找到更好的平衡点。 Go 1.26 的 Green Tea GC 是这个平衡点的又一次重大跳跃。 123456// 你写的代码就这么简单——GC 在幕后默默工作func proc...
Go 工程实践(中):Context 包
Context 是 Go 并发编程的核心。没有它,你的 goroutine 就像脱缰的野马——跑起来不知道怎么停,超时了也没法杀。这篇彻底搞懂 context。 Q1:Context 到底解决什么问题?A: 一个 goroutine 启动后,调用方面临三个问题: 问题 没有 Context 有 Context 取消任务 用户关了页面,后台 goroutine 还在跑 ctx.Cancel() 一键停掉 超时控制 数据库卡住了?请求永远不返回 ctx.Timeout() 自动取消 传值 goroutine 需要请求 ID、用户信息 ctx.Value() 跨层传递 123456789101112131415161718192021222324// 没有 context 的写法(别这么写)func fetchData() ([]User, error) { ch := make(chan []User) go func() { data := queryFromDB() // 如果 DB 慢了,这里会一直卡着...
Go 协程调度器:G-M-P 模型与调度策略
前两篇分别讲了 Channel 的底层(hchan 数据结构)和内存模型(happens-before 规则)。但还有一个更基础的问题没回答:goroutine 为什么这么轻量?调度器是怎么管理数万个 goroutine 的? 这篇深入 Go 调度器的 G-M-P 模型,结合 slg-go 项目的真实并发场景分析。 背景:slg-go 里的 Goroutine 开销在 slg-go 项目中,单个 Logic Node 进程的 goroutine 分布大致如下: 1234567891011121314151617181920212223242526// cmd/gate/main.go — Gateway 进程启动时的 goroutinefunc main() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() // 1. HTTP 服务(net/http 内部为每个请求分配一个 goroutine) gw.Start(ctx) // → 底层...
Go 内存模型:Happens-Before 规则与数据竞争实战
上一篇聊了 Channel 的底层实现——环形队列 + 等待队列 + 全局锁。但光知道 channel 怎么工作还不够,你还需要知道:两个 goroutine 对同一个变量的读写,到底什么时候是安全的? 这就是 Go 内存模型(Go Memory Model)要回答的问题。 背景:一个真实的数据竞争在 slg-go 战斗引擎的开发过程中,我们遇到过这样一个潜在问题: 123456789101112// internal/battle/engine/engine.go:85-95 — BattleEngine 结构体type BattleEngine struct { troopAttrs map[uint32]*TroopAttr // 只读,初始化后不变 terrainMods map[uint32]map[string]float64 // 只读 lua *luaBridge // 可变 scriptMu sync.RWMutex ...
Electron 实战:音色管理系统的 4 种模式设计
MiMo TTS Studio 的核心交互就是”选音色 → 输入台词 → 合成”。音色选择不是简单的一个 <select> 下拉框——它有 4 种完全不同的模式,每种模式的参数复杂度和用户心智模型都不同。本文从 UX 设计和代码实现两个角度拆解。 Q1:4 种音色模式分别是什么?各自解决什么问题?A: 1234567891011┌─────────────────────────────────────────────────────┐│ 音色设计面板 (VoiceDesignPanel) ││ ││ ┌─────────┐ ┌──────────┐ ┌──────────┐ ┌─────────┐ ││ │ 标准模式 │ │ 音色设计 │ │ 克隆模式 │ │ 导演模式 │ ││ │ Standard│ │ VoiceDesign│ │ VoiceClone│ │ Director│ ││ └───...
Electron 实战:浏览器 / Electron 双环境兼容的 IPC 封装
Electron 开发的一个痛点:前端代码只能在 Electron 里跑,调试 UI 必须启动完整的主进程。MiMo TTS Studio 通过一套 IPC 封装实现了 浏览器环境降级为 Mock 数据 的能力——在 Chrome 里打开 frontend/dist 就能调试绝大部分界面。 Q1:核心思路是什么?isElectron 标志位怎么工作的?A: 一句话概括:所有跨进程调用统一走一个 IPC 封装层,检测环境决定是真正通信还是返回 Mock 数据。 12345678910// utils/ipcRenderer.js — 整个封装层的入口// 尝试获取 Electron 的 ipcRenderer 对象const Renderer = (window.require && window.require('electron')) || window.electron || {};const ipc = Renderer.ipcRenderer || undefined;// 环境标志位——整个前端代码都通过...
Web Audio API + Canvas:音频波形可视化的前端实现
MiMo TTS Studio 生成语音后,需要直观展示音频波形,让用户判断语速、停顿和音量分布。实现方案是 Web Audio API 解码 + Canvas 条形图绘制,纯前端能力,不需要 Node.js 参与。 音频解码:从 buffer 到波形数据核心思路:将音频 buffer 解码为 PCM 数据,按固定间隔采样,归一化到 [0, 1]: 123456789101112131415161718192021222324252627282930313233343536// WaveformDisplay.vue — audioContext 创建一次并复用(单例)const audioContext = ref(null)const isAnalyzing = ref(false)async function analyzeAudio(src) { if (isAnalyzing.value) return isAnalyzing.value = true try { const arrayBuffer = await fetchAudi...
手写信号量:批量并发 TTS 生成的并发控制
MiMo TTS Studio 需要批量生成数十甚至上百条语音。每条请求独立,但不能无限制并发——API 有限流,本地内存也有上限。解决方案是手写信号量,把并发数控制在 3 条以内。 四种状态:台词的生命周期每条台词从创建到完成,经历四种状态: stateDiagram-v2 [*] --> pending pending --> generating: 开始合成 generating --> done: 成功 generating --> error: 失败 error --> pending: 重试 12345678// 每条台词的数据结构const line = { id: generateId('line'), text: '你好,世界', audioPath: '', status: 'pending', // 'pending' | 'generat...
三种音色模式:预置、设计、克隆的统一管理
MiMo TTS Studio 支持三种音色来源:预置音色(系统内置)、设计音色(文本描述生成)、克隆音色(参考音频复制)。三种模式的数据结构、API 调用方式完全不同,但需要在同一个角色管理界面中统一操作。 数据模型:Pinia Store 管理音色状态核心设计是用 ttsStore.js 管理当前音色状态,characterStore.js 管理角色列表。每个角色 = 一个音色配置 + 一组台词: 123456789// ttsStore.js — 简化示意const ttsMode = ref('standard') // 'standard' | 'voicedesign' | 'voiceclone'const voiceId = ref('mimo_default')const speed = ref(1.0)const emotion = ref('neutral')const styleDescription = ref('...