安装、升级、卸载等

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

国际惯例:Hello world!

名词概念

  • 程序实体:变量、常量、函数、结构体和接口等的统称。
  • 标识符:各个程序实体的名字。可以是任何 Unicode 编码可以表示的字母字符(甚至中文,但不推荐)、数字以及下划线等,但首字母不能是数字。
  • 命令源码文件:可以被直接运行,也包含程序实体。
  • 库源码文件:不可以被直接运行,仅用于存放程序实体。
  • GOPATH:Go 语言的源码文件都需要存放在环境变量 GOPATH 包含的某个工作区(目录)中的 src 目录下的某个代码包(目录)中。
  • *string 是字符串的指针类型,而不是字符串类型。
  • := 是重定义变量类型并赋值,可以改变外部变量类型和值,而 = 只能赋给相同类型的值,类型不对时编译期会报错。
  • 类似 toString() 方法的接口:
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

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

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/",
		},
	},
}