当前位置:首页 > 技术分析 > 正文内容

关于我的Go语言Web开发心得与笔记

ruisui884个月前 (03-07)技术分析15


去年我曾经用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/ 请求处理  
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版本开始,标准库对路由的支持有了显著提升,代码得以简化为:

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/。这导致POST请求被错误地转换为GET请求,因为请求体在重定向过程中被丢弃。幸运的是,Xe Iaso的一篇博客文章为我提供了解决这一问题的线索。

我认为,避免这个问题的最佳方法是使用POST /records这样的API端点,而不是POST /records/,这看起来也更符合常规设计。

三、sqlc:自动生成数据库查询代码的神器

编写大量的SQL查询样板代码让我感到厌倦,但我又不想使用ORM,因为我喜欢直接控制SQL查询。这时,我发现了sqlc这个工具。它可以将SQL查询:

-- 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 Variant  
    err := row.Scan(  
       &i.ID,  
       &i.CreatedAt,  
       &i.UpdatedAt,  
       &i.Disabled,  
       &i.ProductName,  
       &i.VariantName,  
    )  
    return i, err  
}

我喜欢sqlc的原因是,它可以根据我编写的SQL查询自动生成Go代码,让我无需在ORM文档中苦苦寻找如何构造所需的SQL查询。

四、SQLite技巧分享

在Mastodon上,我读到了一篇名为《为服务器优化SQLite》的文章。虽然我的项目规模较小,对性能要求不高,但这篇文章仍然给了我很多启发:

  1. 我创建了一个专门用于写入数据库的对象,并设置了db.SetMaxOpenConns(1)。我发现,如果不这样做,当两个线程同时尝试写入数据库时,我会遇到SQLITE_BUSY错误。
  2. 为了提高读取速度,我考虑使用两个独立的数据库对象,一个用于写入,一个用于读取。

文章中提到的其他技巧也很有用(如优化COUNT查询、使用STRICT表等),但我还没有尝试过。

另外,如果我有两个表且永远不会进行JOIN操作,我会将它们放在单独的数据库中,以便独立连接。

五、Go 1.19:设置GC内存限制的新方法

我在内存有限的虚拟机(如256MB或512MB)上运行Go项目时,遇到了应用程序因内存不足而被杀死的问题。经过调查,我发现这可能是由于垃圾回收器配置不当导致的。

在Go 1.19中,我们可以设置GC内存限制来避免这种情况。我将GC内存限制设置为250MB后,应用程序因内存不足而被杀死的情况大大减少:

export GOMEMLIMIT=250MiB 

六、我为何偏爱用Go语言制作网站

我之所以对Go语言情有独钟,尤其在网站开发领域,主要得益于以下几个方面的优势:

  • 部署高效便捷:Go语言的部署过程极为简便,只需一个静态二进制文件即可完成。更令人惊喜的是,通过embed功能,静态资源也能轻松嵌入到二进制文件中,进一步简化了部署流程。
  • 内置强大的Web服务器:Go语言内置了功能完善的Web服务器,直接适用于生产环境,无需额外配置WSGI或其他中间件,从而大大提高了开发效率和灵活性。
  • 工具链安装简便:Go语言的工具链安装过程同样简便快捷,只需执行如apt-get install golang-go的命令,随后使用go build即可轻松构建项目,无需复杂的配置过程。
  • HTTP响应处理简洁:借助Serve(w http.ResponseWriter, r *http.Request)函数,Go语言能够轻松读取HTTP请求并发送响应,大大简化了Web开发的复杂度,提高了开发效率。
  • 标准库支持丰富:net/http包作为Go语言的标准库之一,无需额外安装任何第三方库,即可直接用于网站开发,为开发者提供了极大的便利。
  • 系统级编程能力强大:作为系统级语言,Go在处理如ioctl等底层命令时同样表现出色,展现了其强大的跨领域应用能力。

总的来说,Go语言不仅让我在短时间内迅速开发出功能完善的网站,即便在长时间搁置后,也能轻松回归并继续开发。相比之下,我曾尝试学习Rails,但总是感到困惑和挫败。而对于充满重复样板代码的Go项目,我反而觉得更加容易理解和维护。这无疑是Go语言在网站开发领域中的独特魅力所在。

扫描二维码推送至手机访问。

版权声明:本文由ruisui88发布,如需转载请注明出处。

本文链接:http://www.ruisui88.com/post/2585.html

标签: go embed
分享给朋友:

“关于我的Go语言Web开发心得与笔记” 的相关文章

Excel VBA 主界面/一步一步带你设计【收费管理系统】02

本文于2023年5月14日首发于本人同名公众号,更多文章案例请关注微信公众号:Excel活学活用!☆本期内容概要☆用户窗体设置:主界面、主页设计上期我们分享了EXCEL读取ACCESS数据库表,实现系统登录的功能【Excel VBA 访问带密码保护的Access数据库/用户窗体设置/EXCEL用户+...

vue中如何在自定义组件上使用v-model和.sync

自定义事件tips推荐始终使用 kebab-case 的事件名。(v-on会将事件名自动转换为小写,避免匹配不到)changeData ×change-data √自定义组件的v-model用法:父组件定义数据源(不需要定义修改数据的方法),在子组件标签上通过v-model="data...

如何在GitLab上回退指定版本的代码?GitLab回退指定版本问题分析

在Git中,回退到指定版本并不是删除或撤销之前的提交,而是创建一个新的提交,该提交包含指定版本的内容。这意味着您需要将当前代码更改与指定版本之间的差异进行比较,并将其合并到一个新的提交中。如果您没有更新本地代码,并且您希望将 GitLab 仓库回退到指定版本,您可以使用以下命令:git fetchg...

Acustica Audio 发布模拟Roland Jupiter 双声道合成器插件 TH2

福利: Acustica Audio 发布模拟Roland Jupiter 风格的双声道合成器插件 TH2 免费下载 意大利 Acustica Audio 公司发布布模拟Roland Jupiter 风格的双声道合成器插件 TH2 ,灵感来源于Acustica Audio的THING-8系列,它是...

Vue页面传参详解

一、两种方式方法1:name跳转页面this.$router.push({name:'anotherPage',params:{id:1}})另一页面接收参数方式:this.$route.params.id示例:控制台展示:方法2:path跳转页面this.$router.push(...

VUE 技术栈

官网链接:https://cn.vuejs.org/什么是vue:渐进式JavaScript 框架vue-cli链接:https://cli.vuejs.org/vue-cli安装:npm install -g @vue/clivue -V创建一个项目:vue create xxxxxx模版语法:文...