11-20 go语言介绍
go大版本更新
| 版本 | 更新 | 说明 |
|---|---|---|
| 1.5 | 实现自举 | 简单来说就是早期编译器是C写的,1.5后的编译器是Go写的 |
| 1.11 | 解决包依赖问题 | 项目不用再写在src里面了,引入了go module |
| 1.13 | 引入 go.mod 和 go.sum | 实现精准的可重现构建 |
| 1.16 | Go module | 成为默认的包依赖管理机制和源码构建机制。 |
go天生支持包并发,且生产效率高,其应用的执行效率,十分适合开发类似:短信网关、5G 消息网关、MQTT 网关,还有 API 网关等等
- 简单
- 显式 go不能像隐式类型转换,只能是同一种类型的数据进行混合运算
- 组合
- 并发
- 面向工程
# go执行基本
# 单 Go 源文件
go build 命令 +Go 源文件名的方式编译
运行与构建
go run xxx.go
go build xxx.go
./xxx //.\xxx.exe //windows
1
2
3
2
3
# 复杂的 Go 项目
我们需要在 Go Module 的帮助下完成项目的构建。
现在给出一个较为复杂的项目
package main
import (
"github.com/valyala/fasthttp"
"go.uber.org/zap"
)
var logger *zap.Logger
func init() {
logger, _ = zap.NewProduction()
}
func fastHTTPHandler(ctx *fasthttp.RequestCtx) {
logger.Info("hello, go module", zap.ByteString("uri", ctx.RequestURI()))
}
func main() {
fasthttp.ListenAndServe(":8081", fastHTTPHandler)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
思路:
- 直接
go build main.go,会报错,没有go.mod文件 go mod init github.com/bigwhite/hellomodule添加go.mod文件cat go.mod查看module包- 有了
go.mod文件,就可以构建了吧?再次go build main.go,缺少依赖的版本信息 go mod tidy自动添加版本信息- 再次
go build main.go就可以构建项目了,访问http://localhost:8081/验证后台信息
go module的核心是一个名为 go.mod 的文件,在这个文件中存储了这个 module 对第三方依赖的全部信息
go mod init 命令的执行结果是在当前目录下生成了一个 go.mod 文件
其实,一个 module 就是一个包的集合,这些包和 module 一起打版本、发布和分发。go.mod 所在的目录被我们称为它声明的 module 的根目录。
# 总结
go mod init `项目所在的文件夹名` // 生成go.mod文件
go mod tidy // 自动添加依赖版本信息
go build main.go //构建go项目
1
2
3
2
3
# go可执行项目布局
$tree -F exe-layout
exe-layout
├── cmd/
│ ├── app1/
│ │ └── main.go
│ └── app2/
│ └── main.go
├── go.mod
├── go.sum
├── internal/
│ ├── pkga/
│ │ └── pkg_a.go
│ └── pkgb/
│ └── pkg_b.go
├── pkg1/
│ └── pkg1.go
├── pkg2/
│ └── pkg2.go
└── vendor/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# cmd
cmd目录(有些命名为app,作用一样)
存放项目要编译构建的可执行文件对应的 main 包的源文件。
作用:
- 命令行参数解析
- 资源初始化
- 日志设施初始化
- 数据库连接初始化等工作
# 依赖管理
go.mod和go.sum
# pkgN
存放项目自身要使用、同样也是可执行文件对应 main 包所要依赖的库文件,同时这些目录下的包还可以被外部项目引用
# vendor
可选目录,以前的依赖管理包路径,现在被go module代替了
# 总结
如果有且只有一个可执行程序要构建,可如下
$tree -F -L 1 single-exe-layout
single-exe-layout
├── go.mod
├── internal/
├── main.go
├── pkg1/
├── pkg2/
└── vendor/
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# go库项目布局
$tree -F lib-layout
lib-layout
├── go.mod
├── internal/
│ ├── pkga/
│ │ └── pkg_a.go
│ └── pkgb/
│ └── pkg_b.go
├── pkg1/
│ └── pkg1.go
└── pkg2/
└── pkg2.go
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
库项目仅通过 go.mod 文件明确表述出该项目依赖的 module 或包以及版本要求就可以了
Go 库项目的初衷是为了对外部(开源或组织内部公开)暴露 API
对于仅限项目内部使用而不想暴露到外部的包,可以放在项目顶层的 internal 目录下面。
# main入口函数
main包下main函数是主函数,即入口函数
但不一定是第一个执行,还要看有没有init函数。且init()不能在main()里面显示调用
# go包初始化顺序
- 依赖包按“深度优先”的次序进行初始化;
- 每个包内按以“常量 -> 变量 -> init 函数”(main.main 函数前)的顺序进行初始化;
- 包内的多个 init 函数按出现次序进行自动调用。
# init 函数常见用途:
- 重置包级变量值;
- 实现对包级变量的复杂初始化;
- 在 init 函数中实现“注册模式”(工厂设计模式)- 空导入。
# init 函数具备的几种行为特征
init 函数十分适合做一些包级数据初始化工作以及包级数据初始状态的检查工作:
- 执行顺位排在包内其他语法元素的后面;
- 每个 init 函数在整个 Go 程序生命周期内仅会被执行一次;
- init 函数是顺序执行的,只有当一个 init 函数执行完毕后,才会去执行下一个 init 函数。
编辑 (opens new window)