<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Go 入门 on PlumePHP</title><link>https://plumephp.com/tags/go-%E5%85%A5%E9%97%A8/</link><description>Recent content in Go 入门 on PlumePHP</description><generator>Hugo</generator><language>zh-CN</language><lastBuildDate>Thu, 18 Dec 2025 10:16:00 +0800</lastBuildDate><atom:link href="https://plumephp.com/tags/go-%E5%85%A5%E9%97%A8/index.xml" rel="self" type="application/rss+xml"/><item><title>Go 2025 入门专题目录：从能写小程序到能维护小服务</title><link>https://plumephp.com/go-2025-beginner-learning-path/</link><pubDate>Thu, 18 Dec 2025 10:16:00 +0800</pubDate><guid>https://plumephp.com/go-2025-beginner-learning-path/</guid><description>&lt;p&gt;2025 年这一组 Go 入门文章已经从零散技巧慢慢长成了一个专题库。单篇文章适合解决一个具体问题：怎么解析表单、怎么写 &lt;code&gt;WaitGroup&lt;/code&gt;、怎么生成安全 Token、怎么做优雅关闭。但读者第一次打开时，未必知道应该先看哪一篇，也不容易判断哪些内容属于同一条学习线。&lt;/p&gt;</description></item><item><title>Go 入门：用 text/template 生成一份朴素报表</title><link>https://plumephp.com/go-text-template-reports-2025/</link><pubDate>Fri, 05 Dec 2025 17:21:00 +0800</pubDate><guid>https://plumephp.com/go-text-template-reports-2025/</guid><description>&lt;p&gt;不是所有输出都需要 HTML。很多内部工具会生成纯文本日报、邮件正文、Markdown 摘要、配置片段或工单说明。直接用 &lt;code&gt;fmt.Fprintf&lt;/code&gt; 拼字符串可以起步，但字段一多，换行和缩进就会变得难维护。Go 标准库的 &lt;code&gt;text/template&lt;/code&gt; 很适合这类场景。&lt;/p&gt;</description></item><item><title>Go 入门：WaitGroup 不只等待，还要收好结果</title><link>https://plumephp.com/go-waitgroup-result-collection-2025/</link><pubDate>Thu, 13 Nov 2025 09:49:00 +0800</pubDate><guid>https://plumephp.com/go-waitgroup-result-collection-2025/</guid><description>&lt;p&gt;&lt;code&gt;sync.WaitGroup&lt;/code&gt; 是 Go 并发入门必学工具。它的职责很简单：等一组 goroutine 做完。但真实代码里，等待只是第一步。你还要拿到每个任务的结果、处理错误、控制并发数量，并保证 channel 正确关闭。很多初学者会写出“能等完，但结果偶尔丢失或死锁”的代码。&lt;/p&gt;</description></item><item><title>Go 并发 map 入门：mutex、sync.Map 和单 goroutine 管理</title><link>https://plumephp.com/go-concurrent-map-patterns-2025/</link><pubDate>Tue, 28 Oct 2025 17:33:00 +0800</pubDate><guid>https://plumephp.com/go-concurrent-map-patterns-2025/</guid><description>&lt;p&gt;Go 的 map 很好用，但普通 map 不是并发安全的。多个 goroutine 同时读写同一个 map，可能触发 &lt;code&gt;fatal error: concurrent map writes&lt;/code&gt;，也可能产生数据竞争。初学者常常在 HTTP handler、缓存、在线用户列表里踩到这个问题。&lt;/p&gt;</description></item><item><title>Go 表单解析入门：FormValue、ParseForm 和 PostForm 怎么选</title><link>https://plumephp.com/go-http-form-parsing-2025/</link><pubDate>Thu, 16 Oct 2025 19:31:00 +0800</pubDate><guid>https://plumephp.com/go-http-form-parsing-2025/</guid><description>&lt;p&gt;不是所有 Web 请求都是 JSON。登录页、搜索框、后台表单、文件上传仍然会用表单。Go 标准库对表单解析支持很好，但几个 API 容易混：&lt;code&gt;FormValue&lt;/code&gt;、&lt;code&gt;PostFormValue&lt;/code&gt;、&lt;code&gt;ParseForm&lt;/code&gt;、&lt;code&gt;ParseMultipartForm&lt;/code&gt;。本文用登录表单讲清楚基本用法。&lt;/p&gt;</description></item><item><title>Go 领域错误入门：errors.Is 和 errors.As 如何服务业务判断</title><link>https://plumephp.com/go-domain-errors-is-as-2025/</link><pubDate>Tue, 07 Oct 2025 11:47:00 +0800</pubDate><guid>https://plumephp.com/go-domain-errors-is-as-2025/</guid><description>&lt;p&gt;Go 的 &lt;code&gt;error&lt;/code&gt; 很简单，但业务系统里的错误并不简单：订单不存在、库存不足、余额不够、状态不允许、外部支付超时。这些错误需要被上层判断，映射成 HTTP 状态码、用户提示、重试策略。只靠字符串比较会非常脆弱。&lt;/p&gt;</description></item><item><title>Go TLS 客户端证书入门：什么时候需要 mTLS</title><link>https://plumephp.com/go-tls-client-cert-basics-2025/</link><pubDate>Thu, 25 Sep 2025 10:26:00 +0800</pubDate><guid>https://plumephp.com/go-tls-client-cert-basics-2025/</guid><description>&lt;p&gt;普通 HTTPS 里，客户端验证服务器证书，确认自己连的是正确服务。mTLS，也就是双向 TLS，还要求客户端提供证书，服务器也验证客户端身份。内部服务调用、企业网关、支付接口、某些高安全 API 都可能要求客户端证书。&lt;/p&gt;</description></item><item><title>Go 入门：用 net/netip 处理 IP 地址更省心</title><link>https://plumephp.com/go-netip-address-basics-2025/</link><pubDate>Wed, 24 Sep 2025 15:33:00 +0800</pubDate><guid>https://plumephp.com/go-netip-address-basics-2025/</guid><description>&lt;p&gt;处理 IP 地址时，很多旧代码会使用 &lt;code&gt;net.IP&lt;/code&gt;。它能用，但类型是 &lt;code&gt;[]byte&lt;/code&gt;，可变、比较不方便，也容易在 IPv4 和 IPv6 之间绕晕。Go 1.18 引入了 &lt;code&gt;net/netip&lt;/code&gt;，提供了更现代的 &lt;code&gt;Addr&lt;/code&gt; 和 &lt;code&gt;Prefix&lt;/code&gt; 类型。对新代码来说，优先学 &lt;code&gt;netip&lt;/code&gt; 会更省心。&lt;/p&gt;</description></item><item><title>Go build tags 入门：为不同环境编译不同文件</title><link>https://plumephp.com/go-build-tags-environments-2025/</link><pubDate>Fri, 12 Sep 2025 10:09:00 +0800</pubDate><guid>https://plumephp.com/go-build-tags-environments-2025/</guid><description>&lt;p&gt;Go 的 build tags 可以让某些文件只在指定构建条件下参与编译。它常用于平台差异、可选功能、集成测试和少量环境差异。初学者可能会把它当成配置系统使用，这是要谨慎的。build tags 是编译期选择，不是运行时配置。&lt;/p&gt;</description></item><item><title>Go embed 模板布局入门：base、partial 和页面模板怎么组织</title><link>https://plumephp.com/go-embed-template-layouts-2025/</link><pubDate>Tue, 09 Sep 2025 15:18:00 +0800</pubDate><guid>https://plumephp.com/go-embed-template-layouts-2025/</guid><description>&lt;p&gt;Go 的 &lt;code&gt;html/template&lt;/code&gt; 可以渲染页面，&lt;code&gt;embed&lt;/code&gt; 可以把模板打进二进制。小型后台、内部工具、邮件预览页都很适合这种组合。问题是模板一多，如何组织 base layout、partial 和具体页面？如果随便 &lt;code&gt;ParseGlob&lt;/code&gt;，很快会乱。&lt;/p&gt;</description></item><item><title>Go 内存限制入门：GOMEMLIMIT、GOGC 和容器里的服务</title><link>https://plumephp.com/go-memory-limit-gogc-2025/</link><pubDate>Thu, 21 Aug 2025 10:36:00 +0800</pubDate><guid>https://plumephp.com/go-memory-limit-gogc-2025/</guid><description>&lt;p&gt;Go 有自动垃圾回收，但这不表示内存可以不管。容器里部署服务时，如果内存持续上涨，最终可能被 OOM kill。初学者常见误解是：只要没有内存泄漏，Go 会自动处理。现实是，GC 有策略，程序有峰值，容器有上限，你需要知道基本参数。&lt;/p&gt;</description></item><item><title>Go expvar 入门：给小服务加几个轻量指标</title><link>https://plumephp.com/go-expvar-metrics-basics-2025/</link><pubDate>Wed, 20 Aug 2025 15:27:00 +0800</pubDate><guid>https://plumephp.com/go-expvar-metrics-basics-2025/</guid><description>&lt;p&gt;很多小服务一开始没有完整监控系统，但也需要知道一些基本状态：请求数、错误数、队列长度、缓存命中次数。Go 标准库里的 &lt;code&gt;expvar&lt;/code&gt; 可以用很低成本暴露 JSON 指标。它不替代 Prometheus 这类监控体系，但很适合入门理解“服务应该给外界看哪些状态”。&lt;/p&gt;</description></item><item><title>Go 工作目录入门：为什么本地能读到文件，部署后却找不到</title><link>https://plumephp.com/go-working-directory-paths-2025/</link><pubDate>Thu, 07 Aug 2025 17:03:00 +0800</pubDate><guid>https://plumephp.com/go-working-directory-paths-2025/</guid><description>&lt;p&gt;“本地能跑，部署后找不到文件”是 Go 初学者常见问题。代码里写了 &lt;code&gt;templates/index.html&lt;/code&gt;，本地从项目根目录运行没事；部署成 systemd 服务后，工作目录变了，程序就报 &lt;code&gt;open templates/index.html: no such file or directory&lt;/code&gt;。问题不在 Go 编译，而在当前工作目录。&lt;/p&gt;</description></item><item><title>Go 入门：随机数别乱用，Token 要用 crypto/rand</title><link>https://plumephp.com/go-random-token-crypto-rand-2025/</link><pubDate>Wed, 06 Aug 2025 13:12:00 +0800</pubDate><guid>https://plumephp.com/go-random-token-crypto-rand-2025/</guid><description>&lt;p&gt;随机数是入门时很容易被低估的主题。抽一个测试样本、打乱列表、模拟掷骰子，用普通伪随机就可以；生成登录 Session、重置密码链接、邮箱验证 Token，就必须使用安全随机。两类需求看起来都叫“随机”，背后的要求完全不同。&lt;/p&gt;</description></item><item><title>Go 入门：用 slog 写可检索的结构化日志</title><link>https://plumephp.com/go-slog-structured-logging-2025/</link><pubDate>Mon, 28 Jul 2025 08:54:00 +0800</pubDate><guid>https://plumephp.com/go-slog-structured-logging-2025/</guid><description>&lt;p&gt;日志不是程序最后随手加的一行 &lt;code&gt;fmt.Println&lt;/code&gt;。线上排查问题时，日志经常是第一证据：哪个用户触发了请求、参数是什么、调用下游花了多久、错误发生在哪一层。纯文本日志能看，但不方便检索。结构化日志把关键信息放到字段里，日志平台可以按字段过滤、聚合和统计。&lt;/p&gt;</description></item><item><title>Go fs.FS 入门：让文件读取逻辑更容易测试</title><link>https://plumephp.com/go-fs-fs-testing-2025/</link><pubDate>Wed, 23 Jul 2025 08:39:00 +0800</pubDate><guid>https://plumephp.com/go-fs-fs-testing-2025/</guid><description>&lt;p&gt;很多代码会直接 &lt;code&gt;os.ReadFile(&amp;quot;config.json&amp;quot;)&lt;/code&gt;。这很简单，但测试时会依赖真实文件路径。Go 的 &lt;code&gt;io/fs&lt;/code&gt; 提供了 &lt;code&gt;fs.FS&lt;/code&gt; 接口，可以让函数从“文件系统”读取，而不关心这个文件系统来自磁盘、内存还是 embed。这样文件读取逻辑更容易测试。&lt;/p&gt;</description></item><item><title>Go 文件变化轮询入门：不用依赖也能做简单热加载</title><link>https://plumephp.com/go-file-watcher-polling-2025/</link><pubDate>Tue, 15 Jul 2025 16:29:00 +0800</pubDate><guid>https://plumephp.com/go-file-watcher-polling-2025/</guid><description>&lt;p&gt;监听文件变化常见做法是使用 fsnotify 这类库。但有些小工具不想引入依赖，只需要每隔几秒检查配置文件是否变了。这时轮询修改时间是一个简单可控的方案。它不够实时，但容易理解，也适合入门。&lt;/p&gt;</description></item><item><title>Go 数据库迁移入门：SQL 文件、版本表和上线顺序</title><link>https://plumephp.com/go-database-migrations-basics-2025/</link><pubDate>Tue, 08 Jul 2025 08:58:00 +0800</pubDate><guid>https://plumephp.com/go-database-migrations-basics-2025/</guid><description>&lt;p&gt;数据库表结构不会一开始就设计完。你会新增字段、加索引、拆表、修约束。初学项目常见做法是手工连数据库执行 SQL，短期快，长期危险：谁执行过、执行到哪一步、线上和本地是否一致，都说不清。数据库迁移的目标就是让结构变更有版本、有记录、可重复。&lt;/p&gt;</description></item><item><title>Go JSON 请求校验入门：不用框架也能写清楚规则</title><link>https://plumephp.com/go-json-schema-lite-validation-2025/</link><pubDate>Thu, 19 Jun 2025 19:40:00 +0800</pubDate><guid>https://plumephp.com/go-json-schema-lite-validation-2025/</guid><description>&lt;p&gt;写 JSON API 时，校验是绕不开的。字段缺失、字符串太长、枚举值不合法、嵌套数组为空，这些都应该在进入业务逻辑前拦住。很多项目会使用校验库，这没问题；但初学者最好先理解不用框架时规则应该放在哪里、错误怎么组织。&lt;/p&gt;</description></item><item><title>Go 入门：用 time.Ticker 做一个朴素限速器</title><link>https://plumephp.com/go-rate-limiter-ticker-2025/</link><pubDate>Wed, 18 Jun 2025 11:27:00 +0800</pubDate><guid>https://plumephp.com/go-rate-limiter-ticker-2025/</guid><description>&lt;p&gt;限速听起来像网关和高并发系统才需要的能力，其实入门项目也经常遇到。比如你要批量同步 5000 个客户资料到第三方 CRM，对方文档写着“每秒最多 5 次请求”。如果你开 50 个 goroutine 一口气打过去，很快就会收到 429，严重时还会被封禁。&lt;/p&gt;</description></item><item><title>Go os/exec 入门：调用外部命令时如何处理超时和输出</title><link>https://plumephp.com/go-os-exec-command-2025/</link><pubDate>Wed, 11 Jun 2025 13:48:00 +0800</pubDate><guid>https://plumephp.com/go-os-exec-command-2025/</guid><description>&lt;p&gt;Go 程序有时需要调用外部命令：图片转换、压缩文件、调用已有脚本、执行系统工具。标准库的 &lt;code&gt;os/exec&lt;/code&gt; 可以做到，但边界要清楚。最重要的是：不要把用户输入拼成 shell 命令；要设置超时；要处理 stdout 和 stderr。&lt;/p&gt;</description></item><item><title>Go TestMain 入门：为一组测试准备共享环境</title><link>https://plumephp.com/go-testmain-fixtures-2025/</link><pubDate>Tue, 03 Jun 2025 10:44:00 +0800</pubDate><guid>https://plumephp.com/go-testmain-fixtures-2025/</guid><description>&lt;p&gt;大多数 Go 测试只需要普通 &lt;code&gt;TestXxx&lt;/code&gt;。但有时一个包里的多组测试需要共享准备工作，比如创建临时目录、启动测试服务器、准备测试数据库连接。&lt;code&gt;TestMain&lt;/code&gt; 可以在一个测试包运行前后执行统一逻辑。&lt;/p&gt;</description></item><item><title>Go net/url 入门：安全地拼 URL 和查询参数</title><link>https://plumephp.com/go-net-url-query-2025/</link><pubDate>Tue, 27 May 2025 11:16:00 +0800</pubDate><guid>https://plumephp.com/go-net-url-query-2025/</guid><description>&lt;p&gt;很多 HTTP 客户端 bug 都来自手写 URL。用户输入里有空格、斜杠、中文、&lt;code&gt;&amp;amp;&lt;/code&gt;、&lt;code&gt;?&lt;/code&gt;，一旦直接字符串拼接，就可能生成错误 URL，甚至改变参数含义。Go 标准库的 &lt;code&gt;net/url&lt;/code&gt; 可以安全地处理路径和查询参数。&lt;/p&gt;</description></item><item><title>Go 后台任务重试入门：失败后怎么重试才不添乱</title><link>https://plumephp.com/go-background-job-retry-2025/</link><pubDate>Wed, 14 May 2025 11:22:00 +0800</pubDate><guid>https://plumephp.com/go-background-job-retry-2025/</guid><description>&lt;p&gt;后台任务失败后要不要重试？答案通常是“要，但不能乱重试”。发送通知、同步数据、生成报表、调用第三方接口都可能遇到临时失败。如果完全不重试，偶发抖动会变成用户可见失败；如果无限重试，可能把一个小问题放大成队列堵塞或外部服务雪崩。&lt;/p&gt;</description></item><item><title>Go worker 取消入门：后台循环如何听懂 context</title><link>https://plumephp.com/go-worker-context-cancel-2025/</link><pubDate>Fri, 09 May 2025 13:37:00 +0800</pubDate><guid>https://plumephp.com/go-worker-context-cancel-2025/</guid><description>&lt;p&gt;后台 worker 是 Go 服务里很常见的结构：消费队列、定时清理、同步外部数据、处理导入任务。很多 worker 写起来很简单，但停不下来。服务收到关闭信号后，HTTP server 已经退出，worker 还卡在 sleep、网络请求或 channel receive 上，进程迟迟不结束。&lt;/p&gt;</description></item><item><title>Go 入门：regexp 能做什么，不能替你做什么</title><link>https://plumephp.com/go-regexp-input-cleaning-2025/</link><pubDate>Tue, 29 Apr 2025 16:05:00 +0800</pubDate><guid>https://plumephp.com/go-regexp-input-cleaning-2025/</guid><description>&lt;p&gt;正则表达式很容易让人又爱又怕。它写得好，可以快速从文本里找出订单号、清理多余空白、判断一个字段是否像手机号；写得太贪，就会变成没人敢改的一串符号。Go 的 &lt;code&gt;regexp&lt;/code&gt; 包使用 RE2 语法，避免了某些语言里灾难性回溯的问题，但这不代表正则可以随便写。&lt;/p&gt;</description></item><item><title>Go HTTP 客户端上传文件入门：multipart 请求怎么构造</title><link>https://plumephp.com/go-http-client-multipart-upload-2025/</link><pubDate>Tue, 22 Apr 2025 14:05:00 +0800</pubDate><guid>https://plumephp.com/go-http-client-multipart-upload-2025/</guid><description>&lt;p&gt;前面很多教程会讲服务端如何接收文件上传，但客户端怎么上传也很常见：你的 Go 程序要把图片传给素材服务，把 CSV 传给导入接口，或者把日志包传给诊断系统。这类请求通常使用 &lt;code&gt;multipart/form-data&lt;/code&gt;。标准库可以完成，但写法比普通 JSON 请求稍微长一点。&lt;/p&gt;</description></item><item><title>Go HTTP 服务优雅关闭入门：停止接新请求，等旧请求收尾</title><link>https://plumephp.com/go-http-server-shutdown-drain-2025/</link><pubDate>Fri, 18 Apr 2025 08:42:00 +0800</pubDate><guid>https://plumephp.com/go-http-server-shutdown-drain-2025/</guid><description>&lt;p&gt;HTTP 服务发布或重启时，如果直接杀进程，正在处理的请求可能被中断，用户看到连接错误，后台写入也可能只完成一半。Go 的 &lt;code&gt;http.Server.Shutdown&lt;/code&gt; 可以帮助服务优雅关闭：停止接收新连接，等待已有请求完成，直到超时。&lt;/p&gt;</description></item><item><title>Go atomic 计数入门：比 mutex 更适合简单计数的场景</title><link>https://plumephp.com/go-atomic-counters-2025/</link><pubDate>Wed, 09 Apr 2025 15:44:00 +0800</pubDate><guid>https://plumephp.com/go-atomic-counters-2025/</guid><description>&lt;p&gt;并发计数是 Go 服务里很常见的小需求：请求数、错误数、当前连接数、后台任务数量。用 mutex 可以实现，但对单个整数计数来说，&lt;code&gt;sync/atomic&lt;/code&gt; 更轻便。较新的 Go 版本提供了类型化原子值，比如 &lt;code&gt;atomic.Int64&lt;/code&gt;，比老式函数更好读。&lt;/p&gt;</description></item><item><title>Go SQL 预编译入门：PrepareContext 什么时候值得用</title><link>https://plumephp.com/go-sql-prepared-statements-2025/</link><pubDate>Thu, 27 Mar 2025 14:26:00 +0800</pubDate><guid>https://plumephp.com/go-sql-prepared-statements-2025/</guid><description>&lt;p&gt;写 Go 数据库代码时，我们经常用 &lt;code&gt;ExecContext&lt;/code&gt; 和 &lt;code&gt;QueryContext&lt;/code&gt; 传 SQL 和参数。那 &lt;code&gt;PrepareContext&lt;/code&gt; 是做什么的？简单说，它会创建一个预编译语句，后续可以多次执行，适合同一条 SQL 重复执行的场景，比如批量插入、批量更新。&lt;/p&gt;</description></item><item><title>Go sync.Pool 入门：复用临时对象前先看清边界</title><link>https://plumephp.com/go-sync-pool-buffers-2025/</link><pubDate>Wed, 19 Mar 2025 09:22:00 +0800</pubDate><guid>https://plumephp.com/go-sync-pool-buffers-2025/</guid><description>&lt;p&gt;&lt;code&gt;sync.Pool&lt;/code&gt; 经常出现在性能优化文章里。它可以复用临时对象，减少分配压力。比如高频请求里不断创建 &lt;code&gt;bytes.Buffer&lt;/code&gt;，可以考虑用 pool 复用。但 &lt;code&gt;sync.Pool&lt;/code&gt; 也很容易被滥用：代码复杂了，性能却没明显提升，甚至因为对象没清理干净引入 bug。&lt;/p&gt;</description></item><item><title>Go 入门：CSV 导入导出别只会 strings.Split</title><link>https://plumephp.com/go-csv-reading-writing-2025/</link><pubDate>Wed, 12 Mar 2025 10:42:00 +0800</pubDate><guid>https://plumephp.com/go-csv-reading-writing-2025/</guid><description>&lt;p&gt;CSV 是最不起眼、也最容易出事故的数据格式。运营同事从后台导出一份表，财务系统给你一份结算明细，供应商发来一批商品编码，很多时候都是 CSV。初学者常犯的错误是用 &lt;code&gt;strings.Split(line, &amp;quot;,&amp;quot;)&lt;/code&gt; 处理。只要字段里出现逗号、换行或双引号，这个办法马上失效。&lt;/p&gt;</description></item><item><title>Go context 值入门：请求范围数据可以放，业务参数不要乱塞</title><link>https://plumephp.com/go-context-values-boundary-2025/</link><pubDate>Thu, 06 Mar 2025 10:47:00 +0800</pubDate><guid>https://plumephp.com/go-context-values-boundary-2025/</guid><description>&lt;p&gt;&lt;code&gt;context.Context&lt;/code&gt; 是 Go 服务开发里绕不开的类型。它主要负责取消、超时和请求范围的数据。前两者很好理解，第三个“请求范围的数据”最容易被滥用。很多初学者会把业务参数、配置、数据库连接甚至 service 都塞进 context，最后代码变得像隐形全局变量。&lt;/p&gt;</description></item><item><title>Go HTTP gzip 压缩入门：什么时候压缩响应，什么时候不要压</title><link>https://plumephp.com/go-http-gzip-middleware-2025/</link><pubDate>Mon, 24 Feb 2025 18:05:00 +0800</pubDate><guid>https://plumephp.com/go-http-gzip-middleware-2025/</guid><description>&lt;p&gt;HTTP 响应压缩是一个常见优化。JSON、HTML、CSS、文本日志这类内容通常能压缩很多，网络传输更省；图片、视频、已经压缩过的 zip 文件再压缩意义不大，还会浪费 CPU。Go 标准库有 &lt;code&gt;compress/gzip&lt;/code&gt;，可以写一个简单中间件理解压缩流程。&lt;/p&gt;</description></item><item><title>Go JSON 流式响应入门：边查边写，避免一次性占满内存</title><link>https://plumephp.com/go-json-stream-response-2025/</link><pubDate>Tue, 18 Feb 2025 09:51:00 +0800</pubDate><guid>https://plumephp.com/go-json-stream-response-2025/</guid><description>&lt;p&gt;很多列表接口一开始会把所有数据查出来，放进切片，再 &lt;code&gt;json.NewEncoder(w).Encode(slice)&lt;/code&gt;。数据少时没问题，数据一多，内存会飙升，用户也要等全部数据准备好才能收到响应。对于导出类接口，可以考虑流式 JSON 响应：边查、边编码、边写给客户端。&lt;/p&gt;</description></item><item><title>Go sync.Once 入门：懒加载资源时只初始化一次</title><link>https://plumephp.com/go-sync-once-lazy-init-2025/</link><pubDate>Mon, 10 Feb 2025 16:18:00 +0800</pubDate><guid>https://plumephp.com/go-sync-once-lazy-init-2025/</guid><description>&lt;p&gt;Go 服务里经常有一些资源只需要初始化一次：解析模板、加载规则、创建昂贵对象、读取本地配置。你可以在程序启动时全部准备好，也可以第一次使用时再懒加载。懒加载时最怕并发：多个请求同时进来，发现资源还没初始化，于是重复执行。&lt;code&gt;sync.Once&lt;/code&gt; 就是用来保证某段初始化逻辑只执行一次的。&lt;/p&gt;</description></item><item><title>Go 入门：Cookie、Session 和一个简单登录状态</title><link>https://plumephp.com/go-http-cookie-session-basics-2025/</link><pubDate>Fri, 07 Feb 2025 14:18:00 +0800</pubDate><guid>https://plumephp.com/go-http-cookie-session-basics-2025/</guid><description>&lt;p&gt;登录状态是 Web 开发绕不开的话题。很多初学者一听到 Session，就会把它想成某种神秘机制。其实从 HTTP 角度看，Cookie 是浏览器保存并自动带回服务端的一小段数据；Session 是服务端用这段数据找到用户状态的一种做法。Cookie 在客户端，Session 数据通常在服务端。&lt;/p&gt;</description></item><item><title>Go http.ServeContent 入门：文件下载、修改时间和断点续传</title><link>https://plumephp.com/go-servecontent-downloads-2025/</link><pubDate>Tue, 28 Jan 2025 10:12:00 +0800</pubDate><guid>https://plumephp.com/go-servecontent-downloads-2025/</guid><description>&lt;p&gt;很多 Go 初学者写文件下载接口时，会直接 &lt;code&gt;os.ReadFile&lt;/code&gt; 把文件读进内存，再 &lt;code&gt;w.Write&lt;/code&gt; 返回。文件很小时这样能跑，但报表、日志包、图片、压缩文件一大，就会浪费内存，也错过了 HTTP 已经提供的能力：修改时间、Range 请求、缓存协商、内容长度。标准库的 &lt;code&gt;http.ServeContent&lt;/code&gt; 正是为这类场景准备的。&lt;/p&gt;</description></item><item><title>Go 入门：用 flag 写一个不慌不忙的命令行配置</title><link>https://plumephp.com/go-cli-flag-config-2025/</link><pubDate>Fri, 24 Jan 2025 09:36:00 +0800</pubDate><guid>https://plumephp.com/go-cli-flag-config-2025/</guid><description>&lt;p&gt;很多人第一次写 Go 程序，是从一个 HTTP 服务开始。但在真实团队里，命令行小工具同样常见：清理过期文件、导入一批 CSV、生成报表、检查接口是否可用。小工具看起来简单，最容易写成“先能跑再说”的样子：路径写死在代码里，超时时间散落在函数里，出了错只打印一行“failed”。等这个工具被同事拿去每天跑，问题就来了。&lt;/p&gt;</description></item><item><title>Go httptest.Server 入门：给 HTTP 客户端写可信测试</title><link>https://plumephp.com/go-httptest-server-client-2025/</link><pubDate>Thu, 16 Jan 2025 09:34:00 +0800</pubDate><guid>https://plumephp.com/go-httptest-server-client-2025/</guid><description>&lt;p&gt;很多 Go 初学者会给 HTTP handler 写测试，却不知道怎么给 HTTP 客户端写测试。比如你的服务要调用一个用户中心接口，代码里有 &lt;code&gt;http.Client&lt;/code&gt;、URL、请求头和 JSON 解码。测试时不可能真的去调用线上用户中心，也不应该为了单元测试启动一整套依赖。标准库里的 &lt;code&gt;net/http/httptest&lt;/code&gt; 提供了一个很实用的工具：&lt;code&gt;httptest.Server&lt;/code&gt;。&lt;/p&gt;</description></item><item><title>Go 时区处理入门：time.Location、UTC 和用户本地时间</title><link>https://plumephp.com/go-timezone-location-basics-2025/</link><pubDate>Thu, 09 Jan 2025 11:08:00 +0800</pubDate><guid>https://plumephp.com/go-timezone-location-basics-2025/</guid><description>&lt;p&gt;时间处理是后端里最容易“本地没问题，线上出事故”的部分。尤其是预约、提醒、账单、报表这类功能，用户看到的是本地时间，系统存储和计算却最好使用统一时间。Go 的 &lt;code&gt;time&lt;/code&gt; 包很强，但初学者如果只会 &lt;code&gt;time.Now()&lt;/code&gt; 和 &lt;code&gt;Format&lt;/code&gt;，很容易在时区上踩坑。&lt;/p&gt;</description></item><item><title>Go 配置分层入门：默认值、环境变量和启动校验</title><link>https://plumephp.com/go-env-config-layering-2024/</link><pubDate>Thu, 19 Dec 2024 19:04:00 +0800</pubDate><guid>https://plumephp.com/go-env-config-layering-2024/</guid><description>&lt;p&gt;配置是后端服务里最容易被低估的部分。初学者常常先把端口、数据库地址、超时时间写死在代码里，等部署到不同环境时再匆忙改成环境变量。结果是默认值散落各处，启动时不校验，线上才发现某个配置拼错了。&lt;/p&gt;</description></item><item><title>Go 测试替身入门：用小接口写出容易测试的业务代码</title><link>https://plumephp.com/go-test-doubles-interfaces-2024/</link><pubDate>Thu, 24 Oct 2024 09:46:00 +0800</pubDate><guid>https://plumephp.com/go-test-doubles-interfaces-2024/</guid><description>&lt;p&gt;很多初学者一听到“单元测试”，就想到 mock 框架。其实 Go 里最常用、最舒服的测试替身，往往只是一个小结构体。你定义一个刚好够用的小接口，测试里手写 fake 或 spy，就能验证业务逻辑，而不用真的发邮件、调支付或连外部 API。&lt;/p&gt;</description></item><item><title>Go 日志脱敏入门：不要把密码、Token 和身份证写进日志</title><link>https://plumephp.com/go-log-redaction-basics-2024/</link><pubDate>Thu, 17 Oct 2024 11:28:00 +0800</pubDate><guid>https://plumephp.com/go-log-redaction-basics-2024/</guid><description>&lt;p&gt;日志是排查问题的基础，但日志也很容易泄露敏感信息。密码、验证码、session ID、访问 token、身份证号、银行卡号、完整手机号，如果进入日志系统，就可能被更多人看到，也很难彻底删除。初学者常见错误是为了调试方便，把整个请求体或结构体直接打印出来。&lt;/p&gt;</description></item><item><title>Go 黄金文件测试入门：适合输出很长的文本和 JSON</title><link>https://plumephp.com/go-golden-file-tests-2024/</link><pubDate>Tue, 08 Oct 2024 10:12:00 +0800</pubDate><guid>https://plumephp.com/go-golden-file-tests-2024/</guid><description>&lt;p&gt;有些函数的输出很长，比如生成 Markdown、渲染配置文件、格式化 JSON、生成 SQL。你当然可以在测试里写一个很长的字符串常量，但测试文件会变得难读。黄金文件测试，也叫 golden file test，就是把期望输出放在单独文件里，测试时读取文件并比较。&lt;/p&gt;</description></item><item><title>Go 泛型集合入门：自己写 Set 和 Stack 时要注意什么</title><link>https://plumephp.com/go-generic-set-stack-2024/</link><pubDate>Wed, 18 Sep 2024 15:52:00 +0800</pubDate><guid>https://plumephp.com/go-generic-set-stack-2024/</guid><description>&lt;p&gt;Go 有了泛型以后，很多人第一反应是写集合库。虽然实际项目里不要急着造大而全的数据结构库，但写一个小 &lt;code&gt;Set&lt;/code&gt; 和 &lt;code&gt;Stack&lt;/code&gt; 很适合理解泛型：类型参数怎么写，&lt;code&gt;comparable&lt;/code&gt; 是什么，方法接收者怎么设计，零值能不能直接用。&lt;/p&gt;</description></item><item><title>Go 部分更新 API 入门：区分未提交、空值和清空字段</title><link>https://plumephp.com/go-json-patch-style-update-2024/</link><pubDate>Thu, 05 Sep 2024 17:19:00 +0800</pubDate><guid>https://plumephp.com/go-json-patch-style-update-2024/</guid><description>&lt;p&gt;创建接口比较简单，用户提交什么就创建什么。更新接口尤其是部分更新，会遇到一个麻烦问题：字段没提交、提交空字符串、提交 &lt;code&gt;null&lt;/code&gt;，这三件事含义可能不同。比如昵称没提交表示不改，提交空字符串表示设置为空字符串，提交 &lt;code&gt;null&lt;/code&gt; 表示清空昵称。&lt;/p&gt;</description></item><item><title>Go 1.23 迭代器入门：range over func 能解决什么问题</title><link>https://plumephp.com/go-iterators-range-over-func-2024/</link><pubDate>Fri, 30 Aug 2024 15:33:00 +0800</pubDate><guid>https://plumephp.com/go-iterators-range-over-func-2024/</guid><description>&lt;p&gt;Go 1.23 让 &lt;code&gt;range&lt;/code&gt; 可以遍历函数形式的迭代器。对初学者来说，这个特性第一眼可能有点陌生：既然我们已经能遍历切片、map、channel，为什么还需要遍历函数？答案通常和“惰性生成”和“避免一次性装进内存”有关。&lt;/p&gt;</description></item><item><title>Go 信号处理入门：让 CLI 和服务收到 Ctrl+C 后体面退出</title><link>https://plumephp.com/go-signal-shutdown-cli-2024/</link><pubDate>Wed, 14 Aug 2024 11:09:00 +0800</pubDate><guid>https://plumephp.com/go-signal-shutdown-cli-2024/</guid><description>&lt;p&gt;你在终端里按 &lt;code&gt;Ctrl+C&lt;/code&gt;，程序会收到中断信号。如果程序只是立刻退出，可能会留下半写入文件、未完成请求、没有关闭的连接。对小脚本来说这不一定严重，但对长期运行的服务、消费者、同步工具来说，体面退出很重要。&lt;/p&gt;</description></item><item><title>Go 配置热重载入门：哪些配置能重载，哪些不该重载</title><link>https://plumephp.com/go-config-reload-boundary-2024/</link><pubDate>Tue, 06 Aug 2024 09:43:00 +0800</pubDate><guid>https://plumephp.com/go-config-reload-boundary-2024/</guid><description>&lt;p&gt;配置热重载听起来很诱人：不用重启服务，改配置就生效。但不是所有配置都适合热重载。日志级别、开关、限流阈值通常可以；数据库连接地址、监听端口、加密密钥就要谨慎。入门阶段最重要的是先划清边界。&lt;/p&gt;</description></item><item><title>Go channel 流水线入门：生产、处理、取消和关闭怎么配合</title><link>https://plumephp.com/go-channel-cancellation-pipeline-2024/</link><pubDate>Mon, 22 Jul 2024 08:49:00 +0800</pubDate><guid>https://plumephp.com/go-channel-cancellation-pipeline-2024/</guid><description>&lt;p&gt;channel 是 Go 很有代表性的特性，但很多初学者会把它写成“能跑就行”的并发代码：谁关闭 channel 不清楚，错误怎么返回不清楚，取消后 goroutine 会不会退出也不清楚。短期看只是偶尔卡住，长期看就是 goroutine 泄漏和线上不稳定。&lt;/p&gt;</description></item><item><title>Go 内存缓存入门：TTL、并发安全和清理策略</title><link>https://plumephp.com/go-inmemory-cache-ttl-2024/</link><pubDate>Tue, 16 Jul 2024 16:07:00 +0800</pubDate><guid>https://plumephp.com/go-inmemory-cache-ttl-2024/</guid><description>&lt;p&gt;不是所有缓存都需要 Redis。很多小型服务只想把低频变化的配置、字典表、权限规则在进程内缓存几分钟，减少数据库查询。Go 里写一个简单内存缓存并不难，但要注意并发安全、过期时间和清理策略。&lt;/p&gt;</description></item><item><title>Go 构建版本信息入门：把 commit、版本号和构建时间写进程序</title><link>https://plumephp.com/go-build-version-info-2024/</link><pubDate>Tue, 09 Jul 2024 14:28:00 +0800</pubDate><guid>https://plumephp.com/go-build-version-info-2024/</guid><description>&lt;p&gt;程序上线后，最常见的排查问题之一是：现在跑的到底是哪一个版本？代码仓库里修了 bug，不代表服务器已经部署了。镜像构建成功，不代表线上实例都更新了。把版本号、Git commit 和构建时间写进程序，是一个很小但很有用的习惯。&lt;/p&gt;</description></item><item><title>Go 分页 API 入门：limit、cursor 和稳定排序</title><link>https://plumephp.com/go-pagination-api-2024/</link><pubDate>Thu, 27 Jun 2024 13:25:00 +0800</pubDate><guid>https://plumephp.com/go-pagination-api-2024/</guid><description>&lt;p&gt;列表接口一开始很容易写成“查全部”。数据少时没问题，数据变多后就会慢、占内存，还会让前端一次拿到太多无用数据。分页是 API 的基本功。Go 写分页不难，难的是把参数校验、排序稳定性和响应结构设计清楚。&lt;/p&gt;</description></item><item><title>Go 文件上传入门：限制大小、校验类型和安全保存</title><link>https://plumephp.com/go-file-upload-server-2024/</link><pubDate>Tue, 18 Jun 2024 18:26:00 +0800</pubDate><guid>https://plumephp.com/go-file-upload-server-2024/</guid><description>&lt;p&gt;文件上传看起来只是接收一个 multipart 表单，但真正写到服务里，会遇到不少边界：用户上传超大文件怎么办，文件名里带路径怎么办，扩展名能不能信，保存失败如何处理，旧文件要不要删除。初学者最容易写出能跑但不安全的代码。&lt;/p&gt;</description></item><item><title>Go 邮件模板入门：把验证码和通知邮件渲染清楚</title><link>https://plumephp.com/go-email-template-rendering-2024/</link><pubDate>Mon, 03 Jun 2024 10:31:00 +0800</pubDate><guid>https://plumephp.com/go-email-template-rendering-2024/</guid><description>&lt;p&gt;邮件模板听起来像前端工作，但后端经常要负责生成验证码、邀请链接、账单通知和任务提醒。很多初学项目一开始直接用 &lt;code&gt;fmt.Sprintf&lt;/code&gt; 拼 HTML，写到第二封邮件时就会变得难维护。Go 的 &lt;code&gt;html/template&lt;/code&gt; 很适合渲染 HTML 邮件，因为它会自动转义用户数据。&lt;/p&gt;</description></item><item><title>Go 密码重置 Token 入门：随机数、过期时间和一次性使用</title><link>https://plumephp.com/go-password-reset-token-2024/</link><pubDate>Tue, 28 May 2024 08:52:00 +0800</pubDate><guid>https://plumephp.com/go-password-reset-token-2024/</guid><description>&lt;p&gt;找回密码看起来是一个普通功能：用户输入邮箱，系统发送链接，用户点开后设置新密码。真正实现时，最关键的是 token。它必须足够随机，不能长期有效，最好一次性使用，数据库里也不应该明文保存。&lt;/p&gt;</description></item><item><title>Go database/sql 上下文入门：QueryContext、超时和连接释放</title><link>https://plumephp.com/go-sql-query-context-2024/</link><pubDate>Thu, 23 May 2024 11:21:00 +0800</pubDate><guid>https://plumephp.com/go-sql-query-context-2024/</guid><description>&lt;p&gt;&lt;code&gt;database/sql&lt;/code&gt; 是 Go 标准库里非常重要的包。它的 API 看起来不复杂，但初学者常常在几个地方踩坑：查询没有超时、忘记关闭 &lt;code&gt;Rows&lt;/code&gt;、把 &lt;code&gt;context.Background()&lt;/code&gt; 写在仓储层、扫描错误处理不完整。短期看只是代码能不能跑，长期看会影响连接池、请求取消和线上稳定性。&lt;/p&gt;</description></item><item><title>Go CSV 导入入门：逐行读取、校验和错误报告</title><link>https://plumephp.com/go-csv-import-validation-2024/</link><pubDate>Fri, 10 May 2024 17:45:00 +0800</pubDate><guid>https://plumephp.com/go-csv-import-validation-2024/</guid><description>&lt;p&gt;CSV 导入是很多后台系统都会遇到的功能。用户从表格软件导出一份文件，上传到系统里批量创建用户、商品或订单。Go 标准库的 &lt;code&gt;encoding/csv&lt;/code&gt; 能完成大部分基础工作，但真正难的是边界：表头不对怎么办，某一行邮箱为空怎么办，转换失败怎么告诉用户，文件很大时能不能逐行处理。&lt;/p&gt;</description></item><item><title>Go 结构化错误入门：错误码、包装和 HTTP 映射</title><link>https://plumephp.com/go-structured-error-types-2024/</link><pubDate>Mon, 29 Apr 2024 08:39:00 +0800</pubDate><guid>https://plumephp.com/go-structured-error-types-2024/</guid><description>&lt;p&gt;Go 的错误处理很直接：函数返回 &lt;code&gt;error&lt;/code&gt;，调用方检查它。但项目变大后，如果所有错误都只是 &lt;code&gt;fmt.Errorf(&amp;quot;bad request&amp;quot;)&lt;/code&gt;，HTTP 层就很难判断该返回 400、404 还是 500；日志里也很难分清用户错误和系统错误。结构化错误的目标不是复杂化，而是让错误携带稳定语义。&lt;/p&gt;</description></item><item><title>Go 临时文件入门：os.CreateTemp、MkdirTemp 和清理责任</title><link>https://plumephp.com/go-temp-files-safe-cleanup-2024/</link><pubDate>Wed, 17 Apr 2024 18:40:00 +0800</pubDate><guid>https://plumephp.com/go-temp-files-safe-cleanup-2024/</guid><description>&lt;p&gt;临时文件经常出现在后端任务里：下载一个文件后解析，生成报表后上传，图片转换时落盘，或者调用某个只接受文件路径的外部工具。很多初学者会手写 &lt;code&gt;/tmp/demo.txt&lt;/code&gt;，这在并发和安全上都不稳。Go 标准库提供了 &lt;code&gt;os.CreateTemp&lt;/code&gt; 和 &lt;code&gt;os.MkdirTemp&lt;/code&gt;，应该优先使用它们。&lt;/p&gt;</description></item><item><title>Go 超时和重试入门：context、退避和幂等性一起看</title><link>https://plumephp.com/go-timeout-retry-backoff-2024/</link><pubDate>Thu, 11 Apr 2024 14:08:00 +0800</pubDate><guid>https://plumephp.com/go-timeout-retry-backoff-2024/</guid><description>&lt;p&gt;写后端服务时，外部调用失败是常态。网络会抖，依赖会慢，网关会返回 502，数据库也可能短暂不可用。初学者最容易写出两种极端代码：一种完全不重试，偶发错误直接暴露给用户；另一种无脑重试很多次，把一个小抖动放大成更大的压力。正确做法不是“永远重试”或“永不重试”，而是把超时、退避和幂等性一起考虑。&lt;/p&gt;</description></item><item><title>Go JSON API 入门：Handler、请求解析和统一响应怎么组织</title><link>https://plumephp.com/go-http-json-api-structure-2024/</link><pubDate>Tue, 26 Mar 2024 09:37:00 +0800</pubDate><guid>https://plumephp.com/go-http-json-api-structure-2024/</guid><description>&lt;p&gt;Go 标准库写 JSON API 并不难，难的是写到第十个接口时还能保持清楚。很多初学项目一开始把所有逻辑塞进 handler：解析 JSON、校验字段、查数据库、拼响应、写错误。前两个接口还行，接口多了以后，错误格式不统一，测试也不好写。&lt;/p&gt;</description></item><item><title>Go io.Pipe 入门：边生成边上传的流式处理</title><link>https://plumephp.com/go-io-pipe-streaming-2024/</link><pubDate>Mon, 18 Mar 2024 10:57:00 +0800</pubDate><guid>https://plumephp.com/go-io-pipe-streaming-2024/</guid><description>&lt;p&gt;很多导出功能会先把全部数据生成到内存或临时文件，再上传到对象存储。数据少时没问题，数据大时就会占用很多内存或磁盘。Go 的 &lt;code&gt;io.Pipe&lt;/code&gt; 可以把写入端和读取端连接起来：一边生成，一边被另一边读取，适合流式处理。&lt;/p&gt;</description></item><item><title>Go 限流入门：用令牌桶保护一个 HTTP 接口</title><link>https://plumephp.com/go-rate-limit-token-bucket-2024/</link><pubDate>Thu, 07 Mar 2024 20:17:00 +0800</pubDate><guid>https://plumephp.com/go-rate-limit-token-bucket-2024/</guid><description>&lt;p&gt;限流不是大公司才需要的功能。一个公开表单、一个登录接口、一个 AI 调用入口，如果没有任何限制，很容易被误用、刷爆或拖垮下游依赖。对 Go 初学者来说，限流最值得先理解的是“保护边界”：限制请求进入系统的速度，而不是等数据库或外部服务扛不住后再处理错误。&lt;/p&gt;</description></item><item><title>Go singleflight 思路入门：避免缓存击穿时重复查询</title><link>https://plumephp.com/go-singleflight-cache-miss-2024/</link><pubDate>Wed, 21 Feb 2024 09:18:00 +0800</pubDate><guid>https://plumephp.com/go-singleflight-cache-miss-2024/</guid><description>&lt;p&gt;缓存能减轻数据库压力，但缓存失效那一刻也可能制造压力。比如热门用户资料过期，突然来了 200 个请求，如果每个请求都发现缓存没有，然后同时查数据库，数据库会被瞬间打满。这类问题常被称为缓存击穿。&lt;/p&gt;</description></item><item><title>Go HTML 模板安全入门：自动转义、模板函数和页面渲染边界</title><link>https://plumephp.com/go-template-security-html-2024/</link><pubDate>Fri, 16 Feb 2024 16:42:00 +0800</pubDate><guid>https://plumephp.com/go-template-security-html-2024/</guid><description>&lt;p&gt;很多人第一次用 Go 写 Web 页面，会在 &lt;code&gt;text/template&lt;/code&gt; 和 &lt;code&gt;html/template&lt;/code&gt; 之间犹豫。两个包 API 很像，名字也很像，但用途不一样。只要输出的是 HTML 页面，通常就应该使用 &lt;code&gt;html/template&lt;/code&gt;。它最重要的能力是自动上下文转义：用户输入出现在 HTML 文本、属性、JavaScript 或 URL 位置时，模板引擎会按位置做不同处理，降低 XSS 风险。&lt;/p&gt;</description></item><item><title>Go Cookie 和 Session 入门：登录态到底放在哪里</title><link>https://plumephp.com/go-cookie-session-basics-2024/</link><pubDate>Thu, 08 Feb 2024 13:56:00 +0800</pubDate><guid>https://plumephp.com/go-cookie-session-basics-2024/</guid><description>&lt;p&gt;做 Web 应用时，登录态是绕不开的话题。很多初学者会问：用户登录后，是不是把用户 ID 放到 Cookie 里？Session 和 Cookie 有什么区别？退出登录时要删什么？这些问题看似简单，但如果边界没想清楚，很容易留下安全隐患。&lt;/p&gt;</description></item><item><title>Go 1.22 循环变量入门：为什么子测试和 goroutine 更不容易踩坑了</title><link>https://plumephp.com/go-122-loop-variable-subtests-2024/</link><pubDate>Mon, 29 Jan 2024 10:18:00 +0800</pubDate><guid>https://plumephp.com/go-122-loop-variable-subtests-2024/</guid><description>&lt;p&gt;Go 里有一个经典坑：在循环里启动 goroutine，或者在表驱动测试里写 &lt;code&gt;t.Run&lt;/code&gt;，最后所有闭包都拿到了同一个循环变量。很多老 Go 程序员都会下意识写一行 &lt;code&gt;tt := tt&lt;/code&gt;，这行代码看起来有点奇怪，但它曾经非常重要。到了 Go 1.22，循环变量的语义发生了调整，很多这类问题会自然消失。&lt;/p&gt;</description></item><item><title>Go httptrace 入门：看清一次 HTTP 请求慢在哪里</title><link>https://plumephp.com/go-httptrace-client-debug-2024/</link><pubDate>Thu, 25 Jan 2024 15:36:00 +0800</pubDate><guid>https://plumephp.com/go-httptrace-client-debug-2024/</guid><description>&lt;p&gt;写 Go 服务时，调用外部接口慢是很常见的问题。日志里只看到“请求花了 2 秒”，但这 2 秒到底花在哪里？是 DNS 慢、建立连接慢、TLS 握手慢、服务端处理慢，还是响应体下载慢？如果只能靠猜，排查会很痛苦。&lt;/p&gt;</description></item><item><title>Go HTTP Basic Auth 入门：用中间件保护内部页面</title><link>https://plumephp.com/go-basic-auth-middleware-2024/</link><pubDate>Thu, 11 Jan 2024 09:24:00 +0800</pubDate><guid>https://plumephp.com/go-basic-auth-middleware-2024/</guid><description>&lt;p&gt;很多小型 Go 服务都有一个内部页面：查看任务队列、触发一次同步、检查缓存状态。它不一定值得接入完整登录系统，但也不能裸奔在公网。HTTP Basic Auth 是一个简单选择。浏览器会弹出用户名密码框，请求头里带上凭据，服务端验证后再允许访问。&lt;/p&gt;</description></item><item><title>Go 子测试组织入门：表驱动、t.Run 和测试命名怎么写</title><link>https://plumephp.com/go-table-tests-subtests-2023/</link><pubDate>Mon, 04 Dec 2023 14:27:00 +0800</pubDate><guid>https://plumephp.com/go-table-tests-subtests-2023/</guid><description>&lt;p&gt;Go 的测试工具很朴素，没有复杂的断言 DSL，也没有必须学习一整套框架的压力。你只要创建一个 &lt;code&gt;xxx_test.go&lt;/code&gt; 文件，写一个 &lt;code&gt;func TestXxx(t *testing.T)&lt;/code&gt;，再运行 &lt;code&gt;go test&lt;/code&gt;，就已经能开始了。也正因为它朴素，很多初学者会在项目变大后遇到另一个问题：测试越写越散，失败信息不清楚，新增一个场景要复制一段函数，最后大家都不太愿意补测试。&lt;/p&gt;</description></item><item><title>Go 基准测试和分配入门：用 -benchmem 看清楚优化是否值得</title><link>https://plumephp.com/go-benchmark-allocations-2023/</link><pubDate>Fri, 13 Oct 2023 09:44:00 +0800</pubDate><guid>https://plumephp.com/go-benchmark-allocations-2023/</guid><description>&lt;p&gt;刚开始写 Go 的时候，很多人会把“性能优化”理解成一种直觉游戏：我觉得这个写法少了一层函数调用，应该更快；我觉得这个循环里用了 &lt;code&gt;append&lt;/code&gt;，应该会慢；我觉得字符串拼接换成 &lt;code&gt;strings.Builder&lt;/code&gt;，一定更专业。直觉有时候能帮你发现问题，但它不能替代测量。尤其在 Go 里，编译器、逃逸分析、运行时分配器都在悄悄做事，很多看上去“聪明”的写法，实际效果可能很小，甚至更差。&lt;/p&gt;</description></item></channel></rss>