我开始学习Go语言了
概述
由于我身为一个业余者,在过去一个月里开始使用Golang来开发应用程序,我决定将我的感悟和实际调研的结果与大家分享,希望能对大家有一点参考价值。
暫時先分享一下當前的目錄結構。
左边是根目录,右边是src目录下。对于是否要创建名为src的目录,我一直很犹豫,但最终还是决定创建。
IDE, 开发环境,执行环境。
基於以下原因,我選擇了使用VSCode作為IDE。
– VSCode擁有Golang開發團隊所提供的插件(最初是由Microsoft維護的,但現在已轉交給Golang開發團隊),因此實際上VSCode是標準的選擇。
– 雖然也有JetBrains的GoLand可供使用,但它是付費軟體,所以此次我選擇略過(雖然提供30天的免費試用)。
– 如果使用Intellij Ultimate,還有AWS Toolkit可供使用。
Golang应用程序最终会被编译成一个Linux的ELF格式二进制文件,因此可以轻松地进行Docker化。
在寻找用于运行Golang应用程序的Docker镜像时,我发现了由Google维护的Distroless镜像系列,看起来很不错,所以我决定使用它们。参考网站
此外,我使用了Docker的多阶段构建功能,将构建和运行应用程序的操作都写在了一个简单的Dockerfile中。
FROM golang:1.17.3-bullseye as builder ## -- buildするイメージ
COPY src/ /go/src/
WORKDIR /go/src
RUN GOOS=linux GOARCH=amd64 go build -o hoge main.go
FROM gcr.io/distroless/base:latest ## -- アプリケーション実行するイメージ & 最終的に出来上がるイメージ
COPY --from=builder /go/src/hoge /
ENTRYPOINT [ "/hoge" ]
关于包的构成和架构
作为一位长期从事Java API开发的人来说,我对基于Spring框架的MVC模式非常熟悉。但是,由于Golang没有像Spring这样强大的框架,因此我决定回归编程的本质,考虑采用干净的架构。
根据完整的清洁架构层,并根据目录结构图进行切割。由于这是我第一次接触清洁架构,也不太自信能够很好地解释,因此请参考下面的文章。
对于目录结构和架构的选择,我们团队经过讨论后决定了现在的结构。我们有不断尝试和探索的先驱者们,没有一个确切的答案。虽然我们起初采用了干净架构的分层方式,但后来进行了修正。如果将来遇到问题,我们会再次考虑。
在中文中,可以这样表达:Web框架是Gin。
选择架构之后就是选择框架。
尽管GitHub上的星星数量并不是唯一的指标,但Gin确实拥有最多的星星数量,而且它的开发活跃并且有很多可以参考的日文网站,所以我选择了Gin,认为它很不错。
我认为Gin的特点还有以下几个方面。
-
- 速い
- 軽量
JetBrains先生在调查报告中也获得了第一名,这是链接。
对于各种框架的比较也可以参考一下。
编译
由于Golang支持的操作系统/ CPU架构以及下面的命令,因此可以根据不同的环境进行编译。
go tool dist list
...(一部省略)
linux/386
linux/amd64
linux/arm
linux/arm64
...(一部省略)
这次我们的正式环境预计采用AWS的ECS Fargate,最近刚刚发布了支持arm64的版本,因此我们决定准备linux/amd64和linux/arm64的容器。(实际使用哪个还未确定,但据说arm64更节能并能节约费用。)
关于编译选项的现场和开发用途
-race:类似于检查线程安全性(实际上有一些文章说如果添加它会使应用程序变慢,所以检查只在开发环境中进行,不添加到编译用于生产环境)
-w -s:不添加此选项会包含调试信息,所以在生产环境中不添加。
// 開発用
go build -race -o hoge main.go
// 本番用
go build -o hoge main.go -ldflags "-w -s"
在进行交叉编译时需要注意事项。无法使用-race选项进行交叉编译。详细信息请点击此处。
例如:在amd64服务器上进行arm64编译时。
关于Makefile
在Go语言中,使用Makefile作为编译器语言是主流趋势。
将构建命令和环境配置命令统一放在Makefile中非常方便。
如果将开发环境和生产环境的编译选项进行区分,并将其整理到Makefile中,就可以通过一个make命令实现执行。
# disable symbol table and dwarf
GO_LDFLAGS_SYMBOL:=
ifeq ($(HOGE_ENV),prod)
GO_LDFLAGS_SYMBOL:=-w -s
endif
GO_LDFLAGS:=$(GO_LDFLAGS_SYMBOL)
# race detector
GO_BUILD_RACE:=-race
ifeq ($(HOGE_ENV),prod)
GO_BUILD_RACE:=
endif
# go build
GO_BUILD:=$(GO_BUILD_RACE) -ldflags "$(GO_LDFLAGS)"
.PHONY: build
build:
GOOS=linux GOARCH=amd64 go build -o hoge $(GO_BUILD) main.go
# 開発環境用build
HOGE_ENV=dev make build
# 本番環境用build
HOGE_ENV=prod make build
使用图书馆
蝰蛇
支持JSON、YAML、TOML等配置文件的库。
由于有很多可供参考的资源,我们在这里做简略介绍,只介绍一下具体的使用方法。我们是与go:embed结合使用的。
go:embed提供了将静态文件嵌入到已编译的ELF格式二进制文件中的功能。
├── config.go
└── yaml
├── config.prod.yml
└── config.dev.yml
database:
postgres:
name: hoge
host: localhost
port: 5432
user: user
pass: password
如果假设在上述的目录结构中保存了每个环境的设置(如数据库设置),那么config.go的内容将如下。
type Config struct {
Database struct {
Postgres struct {
Name string
Host string
Port string
User string
Pass string
}
}
}
var (
//go:embed yaml/*
staticYamlDir embed.FS
)
func LoadConfig() *Config {
setDefaultEnv()
hogeEnv := viper.GetString("env")
fileName := "yaml/config." + hogeEnv + ".yml"
viper.SetConfigName("config." + hogeEnv)
viper.SetConfigType("yaml")
viper.AddConfigPath("config/yaml")
b, err := staticYamlDir.ReadFile(fileName)
if err != nil {
panic("Failed to read config.")
}
if err := viper.ReadConfig(bytes.NewReader(b)); err != nil {
panic("Failed to load config.")
}
var c Config
if err := viper.Unmarshal(&c); err != nil {
panic("Failed to unmarshal config.")
}
return &c
}
func setDefaultEnv() {
viper.SetEnvPrefix("hoge")
viper.BindEnv("env")
viper.AutomaticEnv()
viper.SetDefault("env", "dev")
}
通过设置名为HOGE_ENV的环境变量,可以在应用程序启动时加载不同的配置文件,因此可以将环境特定的设置分别存储在不同文件中进行管理。
sqlboiler (SQL锅炉)
这是一个ORM库。如何使用将被省略。
虽然有着著名的GORM等,但我们选择了sqlboiler,理由如下。
-
- schemaからコード生成がやりやすい
-
- 静的に型付けされており、実行時にリフレクションを使う必要がないため高速
-
- migration機能はないがflywayとかでカバーできる
-
- 生queryがかける
- relationもサポート
空气
Golang 是一种编译型语言,因此在开发过程中需要频繁进行修改、构建和执行的循环操作。
air 是一款可以自动化该循环过程的所谓热重载库,能够提高在本地进行开发的效率。
感受
尽管无法像Java那样通过注解自动完成一些操作,但我认为Golang的特点是代码编写起来比较直接。(尽管Java本身有强大的Spring框架作为武器,所以不仅仅是语言本身的比较)
优点:
-
- 新卒でもコード見ればある程度理解できるようなわかりやすさ
-
- 軽量、高速
- クラウドやコンテナ技術、マイクロサービスなどと親和性が高い
麻煩之处:Golang没有异常的概念,必须始终进行if err != nil的判断。
Go语言因为其简洁的设计理念与其他语言有所区别,并获得了广泛的支持,所以可能有些地方使用起来会有些困难,但感觉未来还是会进一步变得更加友好。
最后
在学习一门新语言时,网络上有很多零散的信息,但实际尝试后可能会发现有些方法已经过时,或者是使用反例来构建。因此,建议最先阅读官方文档。特别是对于Golang而言,官方文档相当完善且易读,敬请参阅。
-
- オフィシャルドキュメント
Effective go ※これとかは必ず読んで見てください。
我对Golang的重要特性——goroutine和channel很感兴趣,但目前尚未接触过。未来我希望能够攻克这些领域。
结束了。