建设网站有哪些,永久持续更新,广西桂林网站建设公司,页游网站如何做推广原文#xff1a;Julia Evans - 2024.09.27
在过去的几周里#xff0c;我花了很多时间在用 Go 开发一个网站#xff0c;虽然不知道它最终会不会发布#xff0c;但在这个过程中我学到了一些东西#xff0c;想记录下来。以下是我的一些收获#xff1a;
Go 1.22 现在有了更…
原文Julia Evans - 2024.09.27
在过去的几周里我花了很多时间在用 Go 开发一个网站虽然不知道它最终会不会发布但在这个过程中我学到了一些东西想记录下来。以下是我的一些收获
Go 1.22 现在有了更好的路由支持
我一直没有动力去学习任何 Go 的路由库比如 gorilla/mux、chi 等所以我一直是手动处理路由的像这样 // DELETE /records:case r.Method DELETE n 1 p[0] records:if !requireLogin(username, r.URL.Path, r, w) {return}deleteAllRecords(ctx, username, rs, w, r)// POST /records/IDcase r.Method POST n 2 p[0] records len(p[1]) 0:if !requireLogin(username, r.URL.Path, r, w) {return}updateRecord(ctx, username, p[1], rs, w, r)但显然从 Go 1.22 开始Go 的标准库现在有了更好的路由支持因此代码可以像这样重写 mux.HandleFunc(DELETE /records/, app.deleteAllRecords)mux.HandleFunc(POST /records/{record_id}, app.updateRecord)不过它也需要一个登录中间件所以可能更像这样使用 requireLogin 作为中间件 mux.Handle(DELETE /records/, requireLogin(http.HandlerFunc(app.deleteAllRecords)))内置路由的一个坑带斜杠的重定向
我遇到了一个烦人的问题如果我为 /records/ 创建了一个路由那么对 /records 的请求会被重定向到 /records/。
遇到的问题是当我向 /records 发送 POST 请求时它会被重定向到对 /records/ 的 GET 请求这导致 POST 请求的请求体被移除从而破坏了请求。幸运的是Xe Iaso 写了一篇关于同样问题的博文这让我更容易调试这个问题。
我认为解决方案就是使用像 POST /records 这样的 API 端点而不是 POST /records/这看起来本身也是一个更常见的设计。
sqlc 自动为我的数据库查询生成代码
我有点厌倦了为我的 SQL 查询写那么多样板代码但并不想学习 ORM因为我知道自己想写什么 SQL 查询也不太想了解 ORM 如何将它们转换为 SQL 查询的规则。
但后来我发现了 sqlc它可以将像这样的查询
-- name: GetVariant :one
SELECT *
FROM variants
WHERE id ?;编译成这样的 Go 代码
const getVariant -- name: GetVariant :one
SELECT id, created_at, updated_at, disabled, product_name, variant_name
FROM variants
WHERE id ?
func (q *Queries) GetVariant(ctx context.Context, id int64) (Variant, error) {row : q.db.QueryRowContext(ctx, getVariant, id)var i Varianterr : row.Scan(i.ID,i.CreatedAt,i.UpdatedAt,i.Disabled,i.ProductName,i.VariantName,)return i, err
}我喜欢这种方式因为如果我不确定该为某个 SQL 查询编写什么 Go 代码我可以直接写出我想要的查询然后读取生成的函数它会准确告诉我如何调用它。对我来说这比翻阅 ORM 文档去搞清楚如何构建我想要的 SQL 查询要容易得多。
阅读了 Brandur 的 2024 年 sqlc 笔记后我对使用这种方式来处理我的小项目更有信心。那篇文章提供了一个非常有用的案例展示了如何使用 CASE 语句有条件地更新表中的字段例如当你有一个包含 20 列的表并且只想更新其中 3 列时。
sqlite 小贴士
有人在 Mastodon 上给我发了这篇文章优化 sqlite 以用于服务器。我的项目很小对性能没有太多顾虑但我从中得出的主要结论是
为数据库的写入操作准备一个专用的对象并对它运行 db.SetMaxOpenConns(1)。我通过惨痛的教训了解到如果不这样做两个线程同时尝试写入数据库时会出现 SQLITE_BUSY 错误。如果我想让读取速度更快可以有两个单独的数据库对象一个用于写入一个用于读取。
那篇文章中还有更多看起来有用的建议比如“COUNT 查询很慢”和“使用 STRICT 表”不过我还没有尝试这些。
另外有时如果我有两个表并且知道永远不需要在它们之间进行 JOIN我就会把它们放在不同的数据库中这样就可以独立连接它们。
Go 1.19 引入了一种设置 GC 内存限制的方法
我在内存相对较少的虚拟机VM上运行所有 Go 项目比如 256MB 或 512MB。我遇到了一个问题应用程序不断被 OOM内存不足终止这让我很困惑——难道我有内存泄漏吗到底怎么回事
经过一些谷歌搜索我意识到或许我并没有内存泄漏只是需要重新配置垃圾收集器默认情况下根据Go 垃圾收集器指南Go 的垃圾收集器允许应用程序分配的内存达到当前堆大小的 2 倍。
Mess With DNS 的基本堆大小大约是 170MB而 VM 上的可用内存大约只有 160MB如果内存翻倍它就会被 OOM 终止。
在 Go 1.19 中添加了一种方法可以告诉 Go “嘿如果应用程序开始使用这么多内存运行一次 GC”。于是我将 GC 内存限制设置为 250MB似乎这样做后应用程序被 OOM 终止的次数减少了
export GOMEMLIMIT250MiB我喜欢用 Go 做网站的几个原因
过去 4 年里我断断续续地用 Go 做一些小网站比如 nginx playground这种方式对我来说很适用。我喜欢它的原因是
只有一个静态二进制文件部署时只需复制这个二进制文件。如果有静态文件我可以用 embed 把它们嵌入到二进制文件里。内置了一个可以在生产环境中使用的 web 服务器所以我不需要配置 WSGI 等东西来让它工作。我可以把它放在 Caddy 后面或者直接在 fly.io 上运行。Go 的工具链非常容易安装我只需 apt-get install golang-go 之类的命令然后 go build 就能构建我的项目。开始发送 HTTP 响应所需记住的东西非常少——基本上就是一些像 Serve(w http.ResponseWriter, r *http.Request) 这样的函数读取请求并发送响应。如果我需要记住某个具体细节只需要查看这个函数就可以了而且 net/http 是标准库的一部分所以你不需要安装任何库就可以开始创建网站。我真的很喜欢这一点。Go 是一个比较系统层面的语言所以如果我需要运行像 ioctl 之类的操作也很容易做到。
总的来说它给我的感觉是Go 让项目变得容易上手你可以用 5 天时间开发一个项目放下 2 年然后再捡起来写代码也不会有太多问题。
相比之下我尝试学习 Rails 几次了我真的想喜欢 Rails——我用 Rails 做过几个小型网站每次都觉得非常神奇。但最终每次我回到这些项目时我都不记得任何东西是如何工作的最后只能放弃。相比之下虽然我的 Go 项目里充满了很多重复的样板代码但至少我可以读懂代码搞清楚它是怎么工作的。
我还没有搞明白的事情
一些我在 Go 中还没有做过的事情
渲染 HTML 模板通常我的 Go 服务器只是 API我会用 Vue 做前端单页应用。我在 Hugo 中大量使用过 html/template过去 8 年我一直用 Hugo 写这个博客但我还不确定对它的感觉。我从没做过真正的登录系统通常我的服务器根本不需要用户。我从没尝试过实现 CSRF跨站请求伪造。
总的来说我不确定如何实现安全敏感的功能所以我不会启动那些需要登录/CSRF 等功能的项目。我猜这可能是框架派上用场的地方。
很高兴看到 Go 添加的新功能
我在这篇文章中提到的两个 Go 功能GOMEMLIMIT 和路由都是过去几年里添加的而我在它们发布时没有注意到。这让我觉得应该更密切关注 Go 新版本的发布说明。