简介
1 特性
- 标准库就考虑性能, 但却是编译型语言
- 跨平台
- 类
C
, 强类型
- 带垃圾回收
- Go(又称 Golang)是 Google 的 Robert Griesemer,Rob Pike 及 Ken Thompson 开发的一种静态强类型、编译型语言。( 果然, 新语言就是没有历史包袱, 想怎么设计就怎么设计, )
2 用 Go 的公司
3 why go
- 最初使用的Python由于性能问题换成了Go
- C++不太适合在线Web业务早期
- 内部RPC和HTTP框架的推广
- 如果你要创建系统程序,或者基于网络的程序,Go语言是很不错的选择。
- 携程等并发模型
go 有多简单
1
2
3
4
5
6
7
|
package main import (
"net/http"
)
func main() {
http. Handle("/", http.FileServer(http.Dir(".")))
http. ListenAndServe(": 8080", nil)
}
|
OOP 与 Go
Go 语言在设计上不是一种典型的面向对象编程语言,而是一种以并发和简洁为主要目标的编程语言。尽管如此,Go 语言仍然支持面向对象编程的一些概念和特性。
Go 语言通过结构体(struct)和方法(method)来实现对面向对象编程的支持。结构体可以用于封装数据和行为,并且可以定义方法来操作结构体实例。这样就能够实现面向对象编程中的封装、继承和多态等概念。
尽管 Go 语言没有经典面向对象语言中的类(class)的概念,但可以使用结构体和方法组合来模拟类的行为。通过在结构体上定义方法,可以实现对结构体的操作和行为的封装。
除了结构体和方法,Go 语言还提供了接口(interface)的概念,它定义了一组行为规范。通过实现接口,可以实现多态的效果,使不同类型的对象可以根据接口进行统一的处理。
需要注意的是,Go 语言更强调简洁和可读性,相比于传统的面向对象编程语言,Go 语言更加注重可组合性和函数式编程的思想。因此,在使用 Go 语言进行开发时,可以灵活地选择使用面向对象编程的特性来组织代码结构,或者使用其他更适合的编程范式。
1
2
3
4
5
|
// c 风格 api
s = append(s, "d")
s = append(s, "e", "f")
// 对象式
|
学习平台
go的example
网络运行平台
短链接:https://hi-hi.cn/gitpod
gitpod 这个效果还挺好看
如何组织代码
- Go语言的代码通过包(package)组织,包类似于其它语言里的库(libraries)类推==>
STL
或者模块(modules)python
。一个包由位于单个目录下的一个或多个.go源代码文件组成,目录定义包的作用。每个源文件都以一条package
声明语句开始,这个例子里就是package main
,表示该文件属于哪个包,紧跟着一系列导入(import)(python)
的包,之后是存储在这个文件里的程序语句。
类似于python
Go语言不需要在语句或者声明的末尾添加分号,除非一行上有多条语句。实际上,编译器会主动把特定符号后的换行符转换为分号,因此换行符添加的位置会影响Go代码的正确解析(译注:比如行末是标识符、整数、浮点数、虚数、字符或字符串文字、关键字break
、continue
、fallthrough
或return
中的一个、运算符和分隔符++
、--
、)
、]
或}
中的一个)
- Go语言在代码格式上采取了很强硬的态度。
gofmt
工具把代码格式化为标准格式, ctrl + s
以后自动格式化, 妙蛙
- 包导入顺序并不重要;gofmt工具格式化时按照字母顺序对包名排序。(我看不懂, 大为震撼)
1
2
3
4
5
6
7
|
// 多个包
import{
"fmt"
"os"
}
func
|
hellow world
1
2
3
4
5
6
7
8
9
|
package main
import(
"fmt"
)
func main(){
fmt.Println("hellow world")
}
|
编译与运行
单文件
1
2
3
4
5
|
# 运行
go run helloworld.go
# 编译
go build helloworld.go
|
语法
参考资料
前言 · Go语言圣经 (studygolang.com)
- 循环没有括号
- 没有
;
- 变量类型后置
- 支持返回多个值
1
2
3
4
5
6
7
|
package main
import "fmt"
func main() {
fmt.Println("Hello, 世界")
}
|
类型与变量
- 类型不需要导入
- 可以使用
const
修饰
- 短变量声明是一种简洁的变量声明方式,用于同时声明和初始化变量。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
var b, c int = 1, 2
var e float64
// 短变量声明
f := a
// 数组
var a [5]int
b := [5]int{1, 2, 3, 4, 5}
// 多维数组
var twoD [2][3]int
|
byte
在 Go 编程语言中,byte
是一种基本数据类型,用于表示一个 8 位的无符号整数。它是 uint8 类型的别名。每个 byte 的取值范围是 0 到 255。
在实际应用中,byte
类型通常用于处理二进制数据、字节流以及字符编码等场景。例如,在读取文件或网络数据时,常常使用 byte 数组来存储和操作字节数据。同时,Go 中的字符串也是由一系列 byte 组成的,因此 byte 类型也经常用于处理字符串的各种操作。
可以通过以下方式声明和使用 byte 类型变量:
1
2
3
4
5
6
7
8
9
|
var b byte // 声明一个 byte 类型变量
b = 65 // 赋值一个字节值
fmt.Println(b) // 输出: 65
str := "Hello"
for i := 0; i < len(str); i++ {
fmt.Printf("%c ", str[i]) // 遍历字符串并打印每个字符的 byte 值
}
// 输出: 72 101 108 108 111
|
const
1
2
3
|
const s string = "constant"
const h = 500000000
const i = 3e20 / h
|
type 定义类型
25 个关键字
1
2
3
4
5
|
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
|
switch
- 默认只进入一个道路 , 与c 不同
- 也能实现自定义结构体
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
27
28
29
30
31
|
package main
import (
"fmt"
"time"
)
func main() {
a := 2
switch a {
case 1:
fmt.Println("one")
case 2:
fmt.Println("two")
case 3:
fmt.Println("three")
case 4, 5:
fmt.Println("four or five")
default:
fmt.Println("other")
}
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("It's before noon")
default:
fmt.Println("It's after noon")
}
}
|
切片— 长度可变的数组
1
2
3
4
5
6
|
// 无力吐槽了 int* arr = new int[size];
s := make([]string, 3)
// c 风格 api
s = append(s, "d")
s = append(s, "e", "f")
|
字典/ 哈希
1
2
3
4
5
6
7
8
9
10
|
// ------------map-------------
m := make(map[string]int)
m["one"] = 1
m["two"] =2
// 遍历
for i, num := range nums{
}
|
new 和 make
在 Go 语言中,make
和 new
是两个用于创建对象的关键字,但它们用途不同。
控制流 for
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
27
28
29
30
31
32
|
for j := 7; j < 9; j++ {
fmt.Println(j)
}
// 大为震撼.jpg
for {
// ...
}
// else 空行不限制
if 7%2 == 0 {
fmt.Println("7 is even")
} else {
fmt.Println("7 is odd")
}
a := 2
switch a {
case 1:
fmt.Println("one")
case 2:
fmt.Println("two")
case 3:
fmt.Println("three")
case 4, 5:
fmt.Println("four or five")
default:
fmt.Println("other")
}
|
函数
一个函数的声明由func
关键字、函数名、参数列表、返回值列表(这个例子里的main
函数参数列表和返回值都是空的)以及包含在大括号里的函数体组成。第五章进一步考察函数。
1
2
3
4
5
6
7
8
9
10
|
func add(n int){
n += 2
return n
}
func exists(m map[string]string, k string) (v string, ok bool) {
v, ok = m[k]
return v, ok
}
|
在 Go 中,我们可以使用函数字面值(function literal)或闭包(closure)来定义匿名函数。匿名函数是没有名称的函数,可以直接在代码中声明和使用。
匿名函数的定义语法如下:
匿名函数通常被赋值给一个变量,以便后续调用。示例代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package main
import "fmt"
func main() {
// 定义并调用匿名函数
func() {
fmt.Println("Hello, anonymous function!")
}()
// 将匿名函数赋值给变量,然后进行调用
greeting := func() {
fmt.Println("Hello, anonymous function assigned to a variable!")
}
greeting()
}
|
当我们执行上述代码时,输出将会是:
1
2
|
Hello, anonymous function!
Hello, anonymous function assigned to a variable!
|
通过匿名函数,我们可以灵活地在代码中定义临时的、功能独立的函数,并且可以直接调用或将其赋值给变量后再调用。
指针
1
2
3
4
5
6
7
|
func addw(n int){
n += 2;
}
func add2(n *int){
*n += 2
}
|
nil
在 Go 语言中,nil
表示一个空值或空指针。它是一种特殊的预定义常量,可以用来表示某些类型的零值或未初始化的变量。
在 Go 中,nil
可以用于多种类型的数据,如指针、切片(slice)、映射(map)、通道(channel)和函数等。当这些类型的变量没有被分配具体的值或引用时,它们的默认值就是 nil
。
使用 nil
可以判断一个指针是否为空或未初始化,以避免访问空指针而导致程序崩溃。例如,如果一个指针的值为 nil
,就表示它没有指向任何有效的内存地址。
需要注意的是,在 Go 语言中,不同类型的 nil
并不相等。例如,一个指针类型的 nil
与一个切片类型的 nil
是不相等的。因此,在比较 nil
值时,应该使用相应类型的零值或其他相应的方法来进行比较。
结构体
1
2
3
4
5
6
7
8
|
type user struct{
name string
password string
}
func main(){
a := user(name: "wang", password: "1024")
}
|
结构体方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
package main
import "fmt"
type user struct {
name string
password string
}
func (u user) checkPassword(password string) bool {
return u.password == password
}
func (u *user) resetPassword(password string) {
u.password = password
}
func main() {
a := user{name: "wang", password: "1024"}
a.resetPassword("2048")
fmt.Println(a.checkPassword("2048")) // true
}
|
大括号
在 Go 语言中,大括号({})通常用于以下几个方面:
- 块语句:大括号用于定义代码块,将多条语句组织在一起形成一个逻辑单元。例如,在函数、条件语句(if、switch)、循环语句(for)等结构中,可以使用大括号包裹多条语句来构成一个代码块。
示例:
1
2
3
4
5
6
7
8
|
go复制代码func main() {
// 代码块
var x int = 10
if x > 5 {
fmt.Println("x 大于 5")
// ...
}
}
|
- 结构体和数组的字面量初始化:在结构体和数组的初始化过程中,大括号可以用来指定初始值。
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
type Person struct {
Name string
Age int
}
func main() {
// 使用大括号初始化结构体
p := Person{
Name: "Alice",
Age: 25,
}
// 使用大括号初始化数组
arr := [3]int{1, 2, 3}
}
|
- 匿名函数体:在 Go 语言中,可以使用匿名函数(闭包)。匿名函数的函数体也需要用大括号包裹。
示例:
1
2
3
4
5
6
|
func main() {
// 定义匿名函数,并调用
func() {
fmt.Println("Hello, world!")
}()
}
|
除了上述用途外,大括号在其他情况下并没有特殊的含义。需要注意的是,在 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
27
28
29
30
31
32
33
34
35
36
|
package main
import (
"errors"
"fmt"
)
type user struct {
name string
password string
}
func findUser(users []user, name string) (v *user, err error) {
for _, u := range users {
if u.name == name {
return &u, nil
}
}
return nil, errors.New("not found")
}
func main() {
u, err := findUser([]user{{"wang", "1024"}}, "wang")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(u.name) // wang
if u, err := findUser([]user{{"wang", "1024"}}, "li"); err != nil {
fmt.Println(err) // not found
return
} else {
fmt.Println(u.name)
}
}
|
字符串
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
package main
import (
"fmt"
"strings"
)
func main() {
a := "hello"
fmt.Println(strings.Contains(a, "ll")) // true
fmt.Println(strings.Count(a, "l")) // 2
fmt.Println(strings.HasPrefix(a, "he")) // true
fmt.Println(strings.HasSuffix(a, "llo")) // true
fmt.Println(strings.Index(a, "ll")) // 2
fmt.Println(strings.Join([]string{"he", "llo"}, "-")) // he-llo
fmt.Println(strings.Repeat(a, 2)) // hellohello
fmt.Println(strings.Replace(a, "e", "E", -1)) // hEllo
fmt.Println(strings.Split("a-b-c", "-")) // [a b c]
fmt.Println(strings.ToLower(a)) // hello
fmt.Println(strings.ToUpper(a)) // HELLO
fmt.Println(len(a)) // 5
b := "你好"
fmt.Println(len(b)) // 6
}
|
字符串格式化
%v
方便的输出所有的数据
%+v
十分详细
%#v
非常详细
%.2f
保留两位小数
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
|
package main
import "fmt"
type point struct {
x, y int
}
func main() {
s := "hello"
n := 123
p := point{1, 2}
fmt.Println(s, n) // hello 123
fmt.Println(p) // {1 2}
fmt.Printf("s=%v\n", s) // s=hello
fmt.Printf("n=%v\n", n) // n=123
fmt.Printf("p=%v\n", p) // p={1 2}
fmt.Printf("p=%+v\n", p) // p={x:1 y:2}
fmt.Printf("p=%#v\n", p) // p=main.point{x:1, y:2}
f := 3.141592653
fmt.Println(f) // 3.141592653
fmt.Printf("%.2f\n", f) // 3.14
}
|
JSON 处理
- 类型首字母需要大写
- 打印
JSON
的话需要 套一层 string
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
27
28
29
30
31
32
33
34
35
36
|
package main
import (
"encoding/json"
"fmt"
)
type userInfo struct {
Name string
// 输出时, 将会是小写
Age int `json:"age"`
Hobby []string
}
func main() {
a := userInfo{Name: "wang", Age: 18, Hobby: []string{"Golang", "TypeScript"}}
buf, err := json.Marshal(a)
if err != nil {
panic(err)
}
fmt.Println(buf) // [123 34 78 97...]
fmt.Println(string(buf)) // {"Name":"wang","age":18,"Hobby":["Golang","TypeScript"]}
buf, err = json.MarshalIndent(a, "", "\t")
if err != nil {
panic(err)
}
fmt.Println(string(buf))
var b userInfo
err = json.Unmarshal(buf, &b)
if err != nil {
panic(err)
}
fmt.Printf("%#v\n", b) // main.userInfo{Name:"wang", Age:18, Hobby:[]string{"Golang", "TypeScript"}}
}
|
time
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
27
28
29
30
|
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
fmt.Println(now) // 2022-03-27 18:04:59.433297 +0800 CST m=+0.000087933
t := time.Date(2022, 3, 27, 1, 25, 36, 0, time.UTC)
t2 := time.Date(2022, 3, 27, 2, 30, 36, 0, time.UTC)
fmt.Println(t) // 2022-03-27 01:25:36 +0000 UTC
fmt.Println(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute()) // 2022 March 27 1 25
// 字符串形式
fmt.Println(t.Format("2006-01-02 15:04:05")) // 2022-03-27 01:25:36
diff := t2.Sub(t)
fmt.Println(diff) // 1h5m0s
fmt.Println(diff.Minutes(), diff.Seconds()) // 65 3900
// 解析字符串, 转化为 time 格式
t3, err := time.Parse("2006-01-02 15:04:05", "2022-03-27 01:25:36")
if err != nil {
panic(err)
}
fmt.Println(t3 == t) // true
// 获取时间戳
fmt.Println(now.Unix()) // 1648738080
}
|
数字解析
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
27
28
|
package main
import (
"fmt"
"strconv"
)
func main() {
// 字符串, 进制, 精度
// 进制传 0 自动推测
f, _ := strconv.ParseFloat("1.234", 64)
fmt.Println(f) // 1.234
n, _ := strconv.ParseInt("111", 10, 64)
fmt.Println(n) // 111
n, _ = strconv.ParseInt("0x1000", 0, 64)
fmt.Println(n) // 4096
n2, _ := strconv.Atoi("123")
fmt.Println(n2) // 123
// 十进制的转化
n2, err := strconv.Atoi("AAA")
fmt.Println(n2, err) // 0 strconv.Atoi: parsing "AAA": invalid syntax
}
|
进程信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package main
import (
"fmt"
"os"
"os/exec"
)
func main() {
// go run example/20-env/main.go a b c d
fmt.Println(os.Args) // [/var/folders/8p/n34xxfnx38dg8bv_x8l62t_m0000gn/T/go-build3406981276/b001/exe/main a b c d]
fmt.Println(os.Getenv("PATH")) // /usr/local/go/bin...
fmt.Println(os.Setenv("AA", "BB"))
buf, err := exec.Command("grep", "127.0.0.1", "/etc/hosts").CombinedOutput()
if err != nil {
panic(err)
}
fmt.Println(string(buf)) // 127.0.0.1 localhost
}
|
https://fanyi.caiyunapp.com/
关键字
defer ——推迟运行
在 Go 语言中,defer
是一个关键字,用于延迟(defer)函数的执行。当一个函数中包含 defer
语句时,被延迟的函数不会马上执行,而是等到包含它的函数执行完毕后才会被调用。
defer
语句通常用于在函数返回之前执行一些清理或收尾操作,例如关闭文件、释放资源等。无论函数是正常返回还是发生异常,被延迟的函数都会被执行。
延迟执行的函数调用会按照后进先出(LIFO)的顺序执行,即最后一个 defer
语句会最先执行。
下面是一个简单的示例代码,演示了 defer
的使用:
1
2
3
4
5
6
7
8
|
func main() {
defer fmt.Println("延迟执行的函数")
fmt.Println("普通的函数")
// 输出结果:
// 普通的函数
// 延迟执行的函数
}
|
在上述代码中,fmt.Println("延迟执行的函数")
使用了 defer
关键字,所以它会在 main
函数执行完毕之前被延迟执行,而 fmt.Println("普通的函数")
则会马上执行。因此,最终的输出结果将先打印 “普通的函数”,再打印 “延迟执行的函数”。
常用的包
组织形式
- 每个包都对应一个独立的名字空间。每个源文件都是以包的声明语句开始,用来指明包的名字。
- 包内的成员将通过类似 tempconv.CToF 的形式访问。
- 同一个包的其他源文件也是可以直接访问的,就好像所有代码都在一个文件一样。
- 要注意的是 tempconv.go 源文件导入了 fmt 包,但是 conv.go 源文件并没有,因为这个源文件中的代码并没有用到 fmt 包。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
// 每个源文件都是以包的声明语句开始,用来指明包的名字。
// Package tempconv performs Celsius and Fahrenheit conversions.
package tempconv
import "fmt"
type Celsius float64
type Fahrenheit float64
const (
AbsoluteZeroC Celsius = -273.15
FreezingC Celsius = 0
BoilingC Celsius = 100
)
func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) }
func (f Fahrenheit) String() string { return fmt.Sprintf("%g°F", f) }
|
什么叫做 基于 GOPATH 开发的
基于 GOPATH 开发指的是在 Go 语言中使用 GOPATH 环境变量设置的工作目录进行开发。在 Go 语言中,GOPATH 是一个重要的环境变量,它指定了 Go 项目的根目录。
当我们使用 Go 语言进行开发时,我们需要将项目源代码和依赖的第三方库都放置在 GOPATH 目录下的相应位置。通常,该目录结构如下:
1
2
3
4
5
6
|
GOPATH/
|-- bin/ # 可执行文件
|-- pkg/ # 编译后生成的库文件
|-- src/ # 项目源代码、第三方库等
|-- myproject/ # 项目代码
|-- github.com/xxx/yyy/ # 第三方库
|
依赖管理
go 依赖管理主要经历以下三个阶段:
- GOPATH
- GO VENDOR
- GO Module
gopath
gopath 是 go 语言支持的一个环境变量,value 是 Go 项目的工作区。
1
2
3
4
|
cd $GOPATH
|---bin 项目编译的二进制文件
|---pkg 项目编译的中间产物加速编译
|---src 项目源码
|
- 项目代码直接依赖src下的代码
go get
下载最新版本的包到src 目录下
同一个 pkg,但是 pkg 有不同版本 pkg v1 和 pkg v2, 里面包含两个方法。而 src 下只能有一个版本存在,那 AB 项目无法保证都能编译通过。
就是在 gopath 管理模式下,如果多个项目依赖同一个库,则依赖该库是同一份代码,所以不同项目不能依赖同一个库的不同版本,这很显然不能满足我们的项目依赖需求。无法实现 package 的多版本控制。
为了解决这个问题,go vendor 出现了。
Go Vendor
- 项目目录下增加 vendor 文件, 所有依赖包副本形式放在
$ProjectRoot/vendor
- 依赖寻址方式:vendor=>GOPATH
问题
go module
有了 Go module 之后,可以方便地管理项目的依赖关系,并且不需要手动下载库。使用 Go module,你可以在代码中引入需要的库,并通过指定版本或者版本范围来自动获取相应的库。当你首次引入一个新的库时,Go module 会自动下载该库及其依赖到本地的缓存中。这样,你就可以随意引入库,而无需手动下载。
依赖管理三要素
- 1.配置文件, 描述依赖go.mod
- 2.中心仓库管理依赖库 Proxy
- 3.本地工具 go get/mod
1
2
3
4
5
6
7
8
|
module example/project/ap // p依赖管理基本单元
go 1.16 // 原生库
require ( // 单元依赖
example/lib1 v1.0.2
example/lib2 v1.0.0 // indirect
example/lib3 v0.1.0-20190725025543-5a5fe074e612
example/lib4 v0.0.0-20180306012644-bacd9c7ef1dd // indirect example/lib5/v3 v3.0.2
)
|
依赖配置 - indirect 关键字
- A->B->C,A->B 属于直接依赖,A->C 属于间接依赖。
- 在 go.mod 中,对于没有直接导入该依赖模块的包,也就是非直接依赖,标识间接依赖。所以加上 indirect 后缀。
为什么需要 Proxy
直接使用版本管理仓库下载依赖,存在多个问题,
- 无法保证构建确定性:软件作者可以直接代码平台增加 / 修改 / 删除软件版本,导致下次构建使用另外版本的依赖,或者找不到依赖版本。
- 无法保证依赖可用性:依赖软件作者可以直接代码平台删除软件,导致依赖不可用。
- 增加第三方代码托管平台的压力,代码托管平台负载问题。
使用 go proxy 之后,构建时会直接从 go proxy 站点拉取依赖。类比项目中,下游无法满足我们上游的需求。
1
|
GOPROXY="https://proxy1.cn, https://proxy2.cn ,direct"
|
工具 - go get/mod
开头提到 go model 有两个本地工具,go get/mod。
指令功能 |
|
@update |
默认 |
@none |
删除依赖 |
@v1.1.2 |
下载指定tag版本,语义版本 |
@23dfdd5 |
下载特定的commit版本 |
@master |
下载分支的最新commit |
go mod
指令 |
功能 |
init |
初始化,创建go.mod文件 |
tidy |
增加需要的依赖,删除不需要的依赖 |
go module 使用方法
- 确保你的项目目录下有一个有效的
go.mod
文件。如果没有,可以通过运行 go mod init
命令来初始化一个新的模块。
- 在你的项目中,使用
import
语句导入你需要的依赖项。
- 运行
go mod tidy
命令来自动分析你的代码并下载缺少的依赖项。这个命令会根据你的代码中的导入语句自动更新 go.mod
文件,并下载相应的依赖项。
- 如果你想手动下载特定的依赖项,可以使用
go get
命令,例如 go get github.com/example/package
。
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
$ go env
GO111MODULE="on"
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/runner/.cache/go-build"
GOENV="/home/runner/.config/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/runner/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/runner/go"
GOPRIVATE=""
GOPROXY="https://goproxy.cn,direct"
GOROOT="/nix/store/4m1nfq0xhc9p1hi6dnxbcpppcgz22yf9-go-1.17.5/share/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/nix/store/4m1nfq0xhc9p1hi6dnxbcpppcgz22yf9-go-1.17.5/share/go/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.17.5"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/home/runner/app/go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build2769124553=/tmp/go-build -gno-record-gcc-switches"
# 设置
go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct
|
GO111MODULE 有三个值:off、on 和 auto (默认值)
-
GO111MODULE=off
:go 命令行将不会支持 module 功能,寻找依赖包的方式将会沿用旧版本那种通过 vendor 目录或者 GOPATH 模式来查找。
-
GO111MODULE=on
:go 命令行会使用 modules,而一点也不会去 GOPATH 目录下查找。
-
:默认值,go 命令行将会根据当前目录来决定是否启用 module 功能。这种情况下可以分为两种情形:
- 当前目录在 GOPATH/src 之外且该目录包含 go.mod 文件
- 当前文件在包含 go.mod 文件的目录下面。
嘿!要查看当前环境下是否已安装 github.com/jinzhu/gorm
包,你可以尝试以下步骤:
- 打开终端或命令提示符。
- 运行
go list -m github.com/jinzhu/gorm
命令。
- 如果你看到了类似
github.com/jinzhu/gorm
的输出,那么说明该包已经在当前环境中安装了,并且输出会显示其版本号。
- 如果没有输出或者显示
go: github.com/jinzhu/gorm: module github.com/jinzhu/gorm not found
,那么说明该包尚未在当前环境中安装。
如果你发现该包尚未安装,你可以尝试使用 go get
命令来安装它,如:go get -u github.com/jinzhu/gorm
。
go mod 使用 | 全网最详细 - 知乎 (zhihu.com)
直接在文件里 import 然后运行 go mod tidy就行吗
go build 编译的时候会根据 go.mod
里自己下载包
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
|
go mod init packagename
Go mod provides access to operations on modules.
Note that support for modules is built into all the go commands,
not just 'go mod'. For example, day-to-day adding, removing, upgrading,
and downgrading of dependencies should be done using 'go get'.
See 'go help modules' for an overview of module functionality.
Usage:
go mod <command> [arguments]
The commands are:
download download modules to local cache
edit edit go.mod from tools or scripts
graph print module requirement graph
init initialize new module in current directory
tidy add missing and remove unused modules
vendor make vendored copy of dependencies
verify verify dependencies have expected content
why explain why packages or modules are needed
Use "go help mod <command>" for more information about a command.
|
1
2
|
# 发布到 github之上, 如果别人需要
go get github.com/jacksonyoudi/gomodone
|
fmt
1
|
fmt.Println("hello world")
|
net
在 Go 语言中,net
包是一个标准库的包,提供了网络相关的功能和接口。它包含了一系列用于网络编程的函数和类型,可以用于创建和管理网络连接、实现网络通信、处理网络协议等操作。
net
包提供了对常见网络协议(如 TCP、UDP、IP)的支持,以及一些基本的网络操作,如域名解析、网络地址转换、端口扫描等。通过该包,你可以创建客户端和服务器,进行网络数据的发送和接收,处理网络连接的状态和错误等。
在使用 net
包时,你可以创建各种类型的网络连接,在这些连接上进行读写操作,还可以设置连接的属性和超时时间。此外,net
包也提供了一些高级功能,比如通过 net/http
包实现 HTTP 服务器和客户端。
总的来说,net
包是 Go 语言中用于网络编程的核心库,为开发人员提供了丰富的功能和接口,方便进行网络通信和处理。
gin
Gin 是一种轻量级的 Web 框架,用于构建基于 Go 语言的后端应用程序。它提供了快速、简单和具有高度灵活性的方式来处理 HTTP 请求和响应。Gin 框架具有良好的性能,并且易于学习和使用,适用于构建各种规模的 Web 应用程序。它支持中间件、路由、参数解析、错误处理等功能,可以帮助开发者高效地构建 RESTful API 或者其他类型的 Web 服务。Gin 框架在 Go 社区广泛流行,并得到了很多开发者的支持和贡献。
它和其他第三方 Golang 库一样。如果你是基于 GOPATH
开发的,你需要先使用 go get -u github.com/gin-gonic/gin
下载 gin,然后 import
导入即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default()
r.GET("/", func(c *gin.Context) {
c.JSON(200, gin.H{
"Blog":"www.flysnow.org",
"wechat":"flysnow_org",
})
})
r.Run(":8080")
}
|
gin.Context
是 Gin 框架中的上下文对象,它封装了请求和响应的相关信息,并提供了许多有用的方法来处理 HTTP 请求和构建 HTTP 响应。
在使用 Gin 编写路由处理函数时,通常需要接收一个 gin.Context
参数。通过这个参数,我们可以获取请求的各种信息,如请求头、URL 参数、请求体等,并使用它们来进行逻辑处理和生成响应。
下面是一些常用的 gin.Context
方法:
c.Request
:获取原始的 http.Request
对象,可以通过它访问更底层的请求和响应信息。
c.Param(name)
:获取 URL 路径中的参数值。
c.Query(key)
:获取查询字符串中指定键名的值。
c.GetHeader(key)
:获取请求头中指定键名的值。
c.ShouldBindJSON(obj)
:将请求的 JSON 数据绑定到指定的结构体对象中。
c.ShouldBindQuery(obj)
:将查询字符串的参数绑定到指定的结构体对象中。
c.JSON(code, obj)
:将指定的对象以 JSON 格式作为响应发送给客户端。
c.String(code, format string, values ...interface{})
:将格式化后的字符串作为响应发送给客户端。
c.HTML(code, name string, data interface{})
:渲染指定名称的 HTML 模板,并将其作为响应发送给客户端。
通过使用 gin.Context
,我们可以方便地处理请求和构建响应,实现更加灵活和强大的 Web 应用程序。
gin.Default () 默认使用了 logger and recovery (crash-free) 中间件
recovery 会把异常捕获到 做一个处理 比如返回一个状态码
1
2
3
4
5
6
7
|
// Default逻辑
func Default() *Engine {
debugPrintWARNINGDefault()
engine := New()
engine.Use(Logger(), Recovery())
return engine
}
|
router.GET("/someGet", getting)
表示在路由器上注册了一个 GET 请求的路由端点。当客户端发送一个 GET 请求到路径 “/someGet” 时,它会调用名为 getfun
的处理函数来响应这个请求。
1
2
|
router := gin.Default ()
router.GET ("/someGet", getfun)
|
路由
- 创建一个默认的 Gin 路由器对象(router),然后定义了两个分组(group):v1 和 v2。
- v1 分组用于处理路径以 “/v1” 开头的请求,包括三个路由端点:/login、/submit 和 /read。这些端点分别对应 loginEndpoint、submitEndpoint 和 readEndpoint 函数。
- v2 分组用于处理路径以 “/v2” 开头的请求,同样包括三个路由端点。这些端点也对应 loginEndpoint、submitEndpoint 和 readEndpoint 函数。
- 你访问的路径是 /v3 时,由于在代码中没有注册路径为 /v3 的路由端点,因此无论请求方法是什么(POST、GET 等),都会返回 404 Not Found 错误。Gin 默认情况下会返回 404 错误
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
func main() {
router := gin.Default()
// Simple group: v1
v1 := router.Group("/v1")
{
v1.POST("/login", loginEndpoint)
v1.POST("/submit", submitEndpoint)
v1.POST("/read", readEndpoint)
}
// Simple group: v2
v2 := router.Group("/v2")
{
v2.POST("/login", loginEndpoint)
v2.POST("/submit", submitEndpoint)
v2.POST("/read", readEndpoint)
}
router.Run(":8080")
}
|