Go语言入门

GO语言介绍

最近在学习Golang,Go 语言被设计成一门应用于搭载 Web 服务器,存储集群或类似用途的巨型中央服务器的系统编程语言。

对于高性能分布式系统领域而言,Go 语言无疑比大多数其它语言有着更高的开发效率。它提供了海量并行的支持,这对于游戏服务端的开发而言是再好不过了。

1:特点

  • 编译型语言,有比较充分的编译期检查。
  • 强类型语言,安全,速度快。
  • 严格区分值和指针。
  • 机器码目标编译器,不依赖虚拟机。
  • 类C逻辑,很多特性与C语言一脉相承,又有很多现代化改进。
  • 支持多返回值
  • 有GC。
  • 协程模型,屏蔽了os线程概念,抽象了多线程软件开发。
  • 基于消息传递的通信方式。
  • 支持反射。
  • defer善后处理方式。
  • 支持嵌入C语言,汇编语言
  • 跨平台,arm,mips,x86,win,linux,macos,freebsd,设计本身对新平台扩展也很友好。
  • 原生支持跨平台交叉编译。
  • 用tags代替了C中的宏,更易读,易管理。
  • 统一代码风格,全世界的go代码读起来都像自己写的。
  • 编译速度非常快,在编译成机器码的语言中来说,有当年delphi的感觉。
  • 完善的工具链,编译,调试,测试,文档,性能分析,代码管理,模块管理,版本管理,全部自带,不依赖第三方。
  • 面向工程的语言,没有太多新潮的功能。
  • 默认UTF-8编码

2:优势

  • 有C基础的话,学习成本极低。
  • 自带功能非常丰富的标准库,许多高级功能基本不需要找三方库。
  • 语言和编译期已经稳定,版本升级很谨慎,持续保持兼容性。
  • 运行速度快占用资源少,运行速度不输主流的java/c#,占用资源却少很多。
  • 不需要考虑内存泄露的C语言?
  • 实施简单,通常只需要copy一个可执行文件
  • 与C语言结合机制简单,相比JNI。
  • 机器码目标程序增加了破解和反编译难度。
  • 只有一套标准和知识,起码目前还没有分裂。
  • 交叉编译特别简单方便。

3:劣势

  • 没有真正的面向对象,有类C风格基本的对象和接口组合功能。
  • 错误处理比较原始(官方认为这样很好),逻辑控制需要耗费更多精力。
  • 包依赖要求比较严格的C风格,管理不当容易产生循环依赖错误。
  • 没有动态特性,一切都在编译期定了。
  • 需要精细粒度控制资源的领域无法取代C。
  • 编译时要求拥有全部源代码,没开放类似c的lib功能。
  • 三方包著名的较少,选择困难,很多问题还要自己来。
  • 官方没有提供GUI开发包,虽然有一些开源实现,但还不适合任复杂图形界面开发。
  • 移动前端开发还不成熟。
  • 自带模板引擎功能很弱,不是很好用,一般需要自行扩展。
  • 相比java,没那么适合堆集木。
1
2
3
4
5
6
7
pip install when-changed
# 监控当前文件夹变动并且执行命令
when-changed -v -r -1 -s ./ go run main.go
# 笔者喜欢自己 alias 一个快捷命令放到 zshrc/bashrc 里,比如
alias go_monitor_run="when-changed -r -v -1 . go run "
# 这样就可以直接执行如下命令,修改完之后自动执行代码看效果啦
go_monitor_run main.go

Go module

1、go module介绍

在早期编写Go项目时,需要将Go项目代码依赖的所有第三方包放入GOPATH目录下,这是方式的缺点在于不支持版本管理,同一个依赖包只能存在一个版本的代码。而多个项目下可能会分别使用不同版本的依赖包。

在Go1.11版本中,发布了Go module的依赖管理方式,可以帮助开发人员更方便地管理项目依赖,并且保证依赖的版本管理和代码的可复用性。Go module在Go1.14 版本开始推荐在生产环境使用,于Go1.16版本默认开启。

具体来说,Go module将依赖包版本信息和程序代码本身实现分离管理,每个Go module都会有一个go.mod文件,该文件包含了module的依赖包列表以及对应的版本信息,当一个module需要引用其他依赖包时,会根据go.mod文件中的信息去下载对应的依赖包和对应版本的代码供程序使用。

2、go module相关命令

命令 说明
go mod init 初始化项目依赖,生成go.mod文件
go mod download 下载 go.mod 文件中指明的所有依赖
go mod tidy 检查项目中的依赖关系,项目文件中引入的依赖与 go.mod 进行比对
go mod graph 打印出模块依赖图,显示项目中所有模块及其之间的依赖关系
go mod edit 编辑 go.mod 文件
go mod vendor 将当前项目的依赖项复制到项目的 vendor 目录中,以便离线构建和依赖管理
go mod verify 校验项目中的依赖项是否被篡改过,以保证项目依赖的完整性和安全性
go mod why 显示指定模块依赖关系的原因,即为什么当前模块需要依赖该指定模块

3、go module相关env配置

在Go module中,可以通过go env命令查看常用环境变量。

GO111MODULE

在环境变量中,GO111MODULE变量作为Go modules的开关,其允许设置如下参数:

  • auto:项目若包含go.mod文件,则启用Go modules
  • on:启用Go modules,go命令行会使用modules,而不会去GOPATH目录下查找依赖包,推荐设置;
  • off:禁用Go modules,寻找依赖包的方式沿用旧版本通过vendor目录或者GOPATH模式来查找,不推荐设置;

如果需要对GO111MODULE环境变量的值进行变更,则可以使用如下命令:

1
`go env -w GO111MODULE=on`

GOPROXY

GOPROXY环境变量主要用于设置Go模块代理(Go module proxy),使Go在后续拉取依赖时能够脱离传统的VCS(版本控制系统)方式,直接通过镜像站点快速拉取。

GOPROXY 的默认值是:https://proxy.golang.org,direct,该站点在国内无法正常访问,因此在开启Go modules时,需要设置国内的Go模块代理。目前社区使用比较多的有两个https://goproxy.cnhttps://goproxy.io。执行命令如下:

1
`go env -w GOPROXY=https://goproxy.cn,direct`

GOPROXY允许设置多个模块代理地址,多个地址之间以英文逗号“,”分隔开,若不想使用,则可以设置为“off”,这将会禁止Go在后续操作中使用任何的Go模块代理。

direct是一个特殊指示符,用于只是Go回到模块版本的源地址去抓取,例如GitHub。当配置多个代理地址时,值代理地址列表中上一个Go模块代理地址返回404 或 410 错误时,Go 会自动尝试下一个模块代理地址,当遇到 direct时会回到源地址去抓取,当遇到EOF时中止并抛出错误。

GOSUMDB

GOSUMDB环境变量(Go checksum database)在拉取依赖版本时(无论是在源站点还是Go module proxy)保证拉取的模块版本数据未经过篡改,若发现不一致,即可能存在篡改,则会立即中止拉取。

GOSUMDB的默认值为sum.golang.org,该站点在国内无法访问,不过可以通过之前设置的GOPROXY解决,GOPROXY设置的代理goproxy.cn同样支持代理sum.golang.org

若该值设置为off,则禁止GO在后续操作中校验模块版本。

4、使用go module

通过一个案例,在开发项目时可以更好的理解和使用go module拉取和管理依赖。

在本地新建一个名为testProject的项目目录,并切换到该目录下:

1
2
$ mkdir testProject 
$ cd testProject

testProject项目目录下,初始化创建一个go.mod文件。

1
2
3
bash
$ go mod init testProject
go: creating new go.mod: module testProject

使用该命令后自动会在项目目录下创建一个go.mod文件,内容如下:

1
2
module testProject 
go 1.19
  • module testProject:表示当前项目的导入路径;
  • go 1.19:表示当前项目使用的Go版本;

go.mod文件会记录项目中使用的第三方依赖包信息,包括包名和版本,但由于目前项目并未使用到任何第三方包,因此go.mod文件暂时还没有记录任何依赖包信息。

可以在项目的目录下创建一个main.go文件,然后使用go get命令手动下载一个依赖包来测试一下:

1
$ go get -u github.com/labstack/echo go: added github.com/labstack/echo v3.3.10+incompatible go: added github.com/labstack/gommon v0.4.1 go: added github.com/mattn/go-colorable v0.1.13 go: added github.com/mattn/go-isatty v0.0.20 go: added github.com/valyala/bytebufferpool v1.0.0 go: added github.com/valyala/fasttemplate v1.2.2 go: added golang.org/x/crypto v0.16.0 go: added golang.org/x/net v0.19.0 go: added golang.org/x/sys v0.15.0 go: added golang.org/x/text v0.14.0

可以看到调用go get命令后,将依赖添加到项目中,前缀 go: added 表示成功添加了一个新的依赖包。此时go.mod中的内容:

1
module testProject go 1.19 require (     github.com/labstack/echo v3.3.10+incompatible // indirect     github.com/labstack/gommon v0.4.1 // indirect     github.com/mattn/go-colorable v0.1.13 // indirect     github.com/mattn/go-isatty v0.0.20 // indirect     github.com/valyala/bytebufferpool v1.0.0 // indirect     github.com/valyala/fasttemplate v1.2.2 // indirect     golang.org/x/crypto v0.16.0 // indirect     golang.org/x/net v0.19.0 // indirect     golang.org/x/sys v0.15.0 // indirect     golang.org/x/text v0.14.0 // indirect )

从上述go.mod文件中记录的当前项目中所有依赖包的相关信息可知,声明依赖的格式如下:

1
require module/path v1.2.3
  • require:声明依赖包的关键字;
  • module/path:依赖包的导入路径;
  • v1.2.3:依赖包的版本号,支持latest最新版本、详细版本v1.2.3、指定某次commit hash这几种格式。

在导入了指定的依赖包后,此时可以在main.go文件中使用导入的依赖包,例如:

1
2
3
4
5
6
7
8
package main 
import ( "github.com/labstack/echo" "net/http" )
func main() {
e := echo.New()
e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World!")
})
e.Logger.Fatal(e.Start(":8080")) }

使用go build生成.exe可执行文件后,执行生成的可执行文件后,在浏览器中输入http://localhost:8080/则可以看到Hello, World!打印。

通过案例可以了解到,在创建项目后,如果需要使用go module,则需要将go env中的GO111MODULE环境变量设置成on,打开go module模式。打开后,可以到项目目录下,执行go mod init初始化生成 go.mod 文件。如果需要导入指定版本的第三方依赖包,则可以使用go get命令进行下载。

执行go get 命令,在下载依赖包的同时还可以指定依赖包的版本。

  • go get -u命令会将项目中的依赖包升级到最新的次要版本或者修订版本;
  • go get -u=patch命令会将项目中的依赖包包升级到最新的修订版本;
  • go get [包名]@[版本号]命令会下载对应依赖包的指定版本或者将对应包升级到指定的版本,例如go get foo@v1.2.3

go module 安装依赖包时先拉取最新的 release tag,若无 tag 则拉取最新的 commit,且go项目会自动生成一个 go.sum 文件来记录 dependency treego module 引入了go.sum机制来对依赖包进行校验。

另外,go module 会把下载到本地的依赖包会以类似下面的形式保存在 $GOPATH/pkg/mod目录下,每个依赖包都会带有版本号进行区分,这样就允许在本地存在同一个包的多个不同版本。

1
2
3
4
5
6
7
8
9
mod 
├── cache
├── cloud.google.com
├── github.com
└──google
├── uuid@v1.1.2
├── uuid@v1.3.0
└── uuid@v1.3.1 ...

如果想清除所有本地已缓存的依赖包数据,可以执行 go clean -modcache 命令。

建立一个Go项目

我随便找了个路径建立了一个 文件夹,D:\GoAllStudyProject\FirstDemo,打开cmd输入命令go mod init FirstDemo

1
D:\GoAllStudyProject\FirstDemo>go mod init FirstDemo

此时会创建一个go.mod文件,这个时候我们可以导入Gin框架来搭建Web,
Gin 是一个用 Go (Golang) 编写的 HTTP web 框架。 它是一个类似于 martini 但拥有更好性能的 API 框架, 优于 httprouter,速度提高了近 40 倍。(以上为官网描述)
我们使用命令

1
go get -u github.com/gin-gonic/gin

此时会多出一个go.sum文件,我们在D:\GoAllStudyProject\FirstDemo 建立一个main.go文件,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package main  

import (
"fmt"
"github.com/gin-gonic/gin")

func main() {
S := gin.Default()
S.GET("/", func(c *gin.Context) {
c.JSON(200, gin.H{"msg": "服务启动成功"})
})
S.GET("/user", func(c *gin.Context) {
user := User{Name: "张三", Age: 20}
c.JSON(200, user)
})
err := S.Run(":8080")
if err != nil {
fmt.Println("服务器启动失败!")
}

}

type User struct {
Name string `json:"name"`
Age int `json:"age"`
}

然后我们运行这个main函数,访问localhost:8080,我们就能看到服务启动成功,此时我们的项目就搭建完成了,我们可以访问localhost:8080/user,此时可以看到
{
“name”: “张三”,
“age”: 20
}


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!