一些 Go Web 开发笔记
原文: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/<ID>case 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 GOMEMLIMIT=250MiB
我喜欢用 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 新版本的发布说明。
相关文章:

一些 Go Web 开发笔记
原文:Julia Evans - 2024.09.27 在过去的几周里,我花了很多时间在用 Go 开发一个网站,虽然不知道它最终会不会发布,但在这个过程中我学到了一些东西,想记录下来。以下是我的一些收获: Go 1.22 现在有了更…...

[Go语言快速上手]初识Go语言
目录 一、什么是Go语言 二、第一段Go程序 1、Go语言结构 注意 2、Go基础语法 关键字 运算符优先级 三、Go语言数据类型 示例 小结 一、什么是Go语言 Go语言,通常被称为Golang,是一种静态类型、编译型的计算机编程语言。它由Google的Robert Gr…...

基于STM32的智能风扇控制系统设计
引言 本项目将基于STM32微控制器设计一个智能风扇控制系统,通过温度传感器实时检测环境温度,并根据预设的温度范围自动调节风扇的转速。该系统展示了STM32的PWM输出、传感器接口以及自动控制应用的实现。 环境准备 1. 硬件设备 STM32F103C8T6 开发板…...

OpenCV 形态学相关函数详解及用法示例
OpenCV形态学相关的运算包含腐蚀(MORPH_ERODE),膨胀(MORPH_DILATE),开运算(MORPH_OPEN),闭运算(MORPH_CLOSE),梯度运算(MORPH_GRADIENT),顶帽运算(MORPH_TOPHAT),黑帽运算(MORPH_BLACKHAT),击中…...

Kafka学习笔记(三)Kafka分区和副本机制、自定义分区、消费者指定分区
文章目录 前言7 分区和副本机制7.1 生产者分区写入策略7.1.1 轮询分区策略7.1.2 随机分区策略7.1.3 按key分区分配策略7.1.4 自定义分区策略7.1.4.1 实现Partitioner接口7.1.4.2 实现分区逻辑7.1.4.3 配置使用自定义分区器7.1.4.4 分区测试 7.2 消费者分区分配策略7.2.1 RangeA…...

华为 HCIP-Datacom H12-821 题库 (31)
🐣博客最下方微信公众号回复题库,领取题库和教学资源 🐤诚挚欢迎IT交流有兴趣的公众号回复交流群 🦘公众号会持续更新网络小知识😼 1. 默认情况下,IS-IS Level-1-2 路由器会将 Level-2 区域的明细路由信息发布到Lev…...
占位,凑满减
占位,凑满减...
SpringBoot校园资料平台:从零到一的构建过程
1系统概述 1.1 研究背景 如今互联网高速发展,网络遍布全球,通过互联网发布的消息能快而方便的传播到世界每个角落,并且互联网上能传播的信息也很广,比如文字、图片、声音、视频等。从而,这种种好处使得互联网成了信息传…...
czx前端
一、盒模型 标准盒模型:box-sizing: content-box。 外边距边框内边距内容区。 IE盒模型,怪异盒模型:box-sizing: border-box。 外边距内容区(边框内边距内容区)。 二、CSS特性 继承性: 父元素的字体大小…...

Perforce演讲回顾(上):从UE项目Project Titan,看Helix Core在大型游戏开发中的版本控制与集成使用策略
日前,Perforce携手合作伙伴龙智一同亮相Unreal Fest 2024上海站,分享Helix Core版本控制系统及其协作套件的强大功能与最新动态,助力游戏创意产业加速前行。 Perforce解决方案工程师Kory Luo在活动主会场,带来《Perforce Helix C…...

【含文档】基于Springboot+Andriod的成人教育APP(含源码+数据库+lw)
1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 系统定…...

CentOS7系统配置Yum环境
新安装完系统的服务器往往缺少我们常用的依赖包,故需要设置好yum源,方便软件安装,以下是CentOS7为例,系统安装后yum默认安装。 //备份之前的配置文件 mv /etc/yum.repos.d /etc/yum.repos.d.bak mkdir -p /etc/yum.repos.d 1…...

pyqt打包成exe相关流程
1、首先是安装pyinstaller, 在cmd中输入以下安装命令: pip3 install pyinstaller -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple/ 2、安装完毕之后,下一步就是找到你要打包的工程,打包的logo放置如下位置: 3、将log…...

设计模式、系统设计 record part02
软件设计模式: 1.应对重复发生的问题 2.解决方案 3.可以反复使用 1.本质是面向对象 2.优点很多 1.创建型-创建和使用分离 2.结构型-组合 3.行为型-协作 571123种模式 UML-统一建模语言-Unified Modeling Language 1.可视化,图形化 2.各种图(9…...

github双重验证(2FA)启用方法
一、双重验证-2FA 在去年看到过说github启用双重验证的通知,觉得做为一个普通开发者,可能没有这么快会要求启用。结果,今天早晨一来就收到了邮件,要求说在11月底完成2FA的认证,否则权限受限。真是无了语。所谓2FA好理…...

《Linux从小白到高手》理论篇:Linux的系统服务管理
值此国庆佳节,深宅家中,闲来无事,就多写几篇博文。本篇详细深入介绍Linux的系统服务管理。 系统服务通常在系统启动时自动启动,并在后台持续运行,为系统和用户提供特定的功能。例如,网络服务、打印服务、数…...
SQL中如何进行 ‘’撤销‘’ 操作-详解
在 SQL 中,撤销已经执行的操作通常涉及两个主要的概念:事务控制和回滚操作。 ### 1. 事务控制 在支持事务的数据库管理系统(如 MySQL 的 InnoDB 引擎)中,您可以使用事务来确保数据的完整性。事务可以确保一系列的操作…...

Hadoop之WordCount测试
1、Hadoop简介: Hadoop是Apache旗下的一个用Java语言实现的开源软件框架,是一个开发和运行处理大规模数据的软件平台。 Hadoop的核心组件包括Hadoop分布式文件系统(HDFS)和MapReduce编程模型。HDFS是一个高度容错的系统…...

Vue和axios零基础学习
Vue的配置与项目创建 在这之前要先安装nodejs 安装脚手架 官网 Home | Vue CLI (vuejs.org) 先运行,切换成淘宝镜像源,安装速度更快 npm config set registry http://registry.npm.taobao.org 创建项目 用编译器打开一个空文件,在终端输入…...

STM32新建工程-基于库函数
目录 一、创建一个新工程 二、为工程添加文件和路径 三、创建一个main.c文件,并调试 四、修改一些配置 五、用库函数进行写程序 1、首先加入一些库函数和头文件 2、编写库函数程序 一、创建一个新工程 我这里选择STM32F103C8的型号,然后点击OK。 …...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错
出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上,所以报错,到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本,cu、torch、cp 的版本一定要对…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...

接口自动化测试:HttpRunner基础
相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具,支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议,涵盖接口测试、性能测试、数字体验监测等测试类型…...

【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看
文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...