go 语言学习

安装、升级、卸载等

  • 官网
  • 中文官网教程
  • Golang 速查表
  • 安装简单,按官网的说明来就好了,macOS 上就是下载个 pkg 的包双击安装即可。
  • 卸载也简单,删除那个 /usr/local/go 的目录即可。
  • 升级官网上没提,搜了一下基本就是先卸载再安装,我试了一下直接安装最新下载的也可以,会提示发现旧版本,确定后会自动卸载再安装。

国际惯例:Hello world!

名词概念

  • 程序实体:变量、常量、函数、结构体和接口等的统称。
  • 标识符:各个程序实体的名字。可以是任何 Unicode 编码可以表示的字母字符(甚至中文,但不推荐)、数字以及下划线等,但首字母不能是数字。
  • 命令源码文件:可以被直接运行,也包含程序实体。
  • 库源码文件:不可以被直接运行,仅用于存放程序实体。
  • GOPATH:Go 语言的源码文件都需要存放在环境变量 GOPATH 包含的某个工作区(目录)中的 src 目录下的某个代码包(目录)中。
  • *string 是字符串的指针类型,而不是字符串类型。
  • := 是重定义变量类型并赋值,可以改变外部变量类型和值,而 = 只能赋给相同类型的值,类型不对时编译期会报错。
  • 类似 toString() 方法的接口:
1
2
3
type Stringer interface {
String() string
}
  • Channel and go routines, 同一时间只允许一个 go routine 访问 channel,所以可以用这个机制来进行安全稳定的 concurrency 并发编程。
  • go run -race main.go Running with race detector. 使用竞跑模式运行,各 goroutine 会争抢优先级,方便发现一些隐藏的多 goroutine 运行时冲突问题。

初学者入门

极客时间的那个 《Go语言核心 36 讲》 显然不是针对初学者的,所以要先看看以下内容:

高级内容

  • Advanced Go Concurrency Patterns 介绍了 goroutine 编程的一些高级模式和技巧,如:
    • for select 循环
    • select nil channel case
    • run race detector
    • service channel, reply channels(chan chan error)

Web Framework

GraphQL

常见问题

重复定义

  • 遇到 no new variables on left side of := 报错,说明左边的变量被重复定义了,去上面的代码中找同名的变量,一定有,解决方法就是换个变量名。

通道锁死

  • fatal error: all goroutines are asleep - deadlock!
  • 一般发生在通道中没内容,而不断从通道取值时,另一种情况是向通道送值,但没放在单独的 goroutine 中。
  • 解决思路,一个是将通道送值放在 go func 即 goroutine 中,另外就是取值用 for select 后要有退出机制,在恰当的条件时 return 就可以退出,实在没有合适的条件,可以用 time.After 设置超时退出。

文件组织规则

  • 同目录的代码包声明要一致;
  • 代码包声明可以与目录名称不同;
  • 名称首字母大写的程序实体才可以被包外引用,否则只能被包内引用。
  • internal 代码包仅能被当前模块的其他代码引用,即模块级私有。
  • 同目录中只能有一个入口函数 main,否则会被(vscode)警告,算是最佳实践吧。

Go 相关项目

  • Docker 容器
  • Cobra Cli 开发命令行工具的工具,Go 语言的牛 X 工具。
  • Viper Go configuration with fangs! 管理配置文件的工具。
  • Afero A FileSystem Abstraction System for Go
  • Robotgo Golang 跨平台自动化系统,控制键盘鼠标位图和读取屏幕,窗口句柄以及全局事件监听

练习题:Web Crawler

终于完成了整个学习,最后这道题记录如下:

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package main

import (
"fmt"
"sync"
"time"
)

type Fetcher interface {
// Fetch returns the body of URL and
// a slice of URLs found on that page.
Fetch(url string) (body string, urls []string, err error)
}

const TIMEOUT = 3
const MAX_DEPTH = 4
var queue = make(chan Job)
var successes = make(chan map[string]string)
var errors = make(chan map[string]string)
var history = History{h: make(map[string]Job)}

type Job struct {
url string
depth int
}

// History is safe to use concurrently.
type History struct {
h map[string]Job
mux sync.Mutex
}

// Inc increments the history
func (c *History) Add(url string, depth int) {
c.mux.Lock()
// Lock so only one goroutine at a time can access the map c.h.
_, exist := c.h[url]
if !exist {
c.h[url] = Job{url, depth}
queue <- Job{url, depth}
}
c.mux.Unlock()
}

func fetchWeb(url string, depth int, fetcher Fetcher) {
if depth <= 0 {
return
}
body, uris, err := fetcher.Fetch(url)
if err != nil {
// fmt.Println(err)
errors <- map[string]string{url: err.Error()}
return
}
// fmt.Printf("found: %s %q\n", url, body)
successes <- map[string]string{url: body}
for _, u := range uris {
history.Add(u, depth-1)
}
}

// Crawl uses fetcher to recursively crawl
// pages starting with url, to a maximum of depth.
func Crawl(url string, depth int, fetcher Fetcher) {
quit := time.After(TIMEOUT * time.Second) // Timeout
// Fetch URLs in parallel.
// Don't fetch the same URL twice.
// This implementation doesn't do either:
go history.Add("https://golang.org/", 4)
for {
select {
case q := <-queue:
// fmt.Println(q)
go fetchWeb(q.url, q.depth, fetcher)
case s := <-successes:
for url, body := range s {
fmt.Println(url, body)
}
case e := <-errors:
for url, err := range e {
fmt.Println(url, err)
}
case <-quit:
fmt.Println("quit")
return
}
}
}

func main() {
Crawl("https://golang.org/", MAX_DEPTH, fetcher)
}

// fakeFetcher is Fetcher that returns canned results.
type fakeFetcher map[string]*fakeResult

type fakeResult struct {
body string
urls []string
}

func (f fakeFetcher) Fetch(url string) (string, []string, error) {
if res, ok := f[url]; ok {
return res.body, res.urls, nil
}
return "", nil, fmt.Errorf("not found: %s", url)
}

// fetcher is a populated fakeFetcher.
var fetcher = fakeFetcher{
"https://golang.org/": &fakeResult{
"The Go Programming Language",
[]string{
"https://golang.org/pkg/",
"https://golang.org/cmd/",
},
},
"https://golang.org/pkg/": &fakeResult{
"Packages",
[]string{
"https://golang.org/",
"https://golang.org/cmd/",
"https://golang.org/pkg/fmt/",
"https://golang.org/pkg/os/",
},
},
"https://golang.org/pkg/fmt/": &fakeResult{
"Package fmt",
[]string{
"https://golang.org/",
"https://golang.org/pkg/",
},
},
"https://golang.org/pkg/os/": &fakeResult{
"Package os",
[]string{
"https://golang.org/",
"https://golang.org/pkg/",
},
},
}
赞赏留名,相识相惜 ~