go 语言学习
安装、升级、卸载等
- 官网
- 中文官网教程
- 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
- iris 号称最快的 web 框架,因为基于fasthttp性能非常的好,需要考虑的是如果选它做web框架,你的代码将难以迁移到别的框架上,因为它实现了和标准库net/http不一样的接口。 看了一下文档,相当全面。
- Hugo The world’s fastest framework for building websites 静态网站生成器
- 零基础使用Hugo和GitHub Pages创建自己的博客 非常专业的博客
- Basic Usage 基础使用方法介绍
- Host on GitHub 官方文档
- Echo 最流行的框架,功能、文档齐全
- Revel 全栈框架,但据说不太合 golang 的语法习惯。 借鉴的java和scala语言的 play框架 的很多想法。
- Martini 据说是最易用的 web 框架,是一个强大为了编写模块化Web应用而生的GO语言框架。
- Negroni 不是一个框架,它是为了方便使用 net/http 而设计的一个库而已。
- Beego 国人开发的一个使用 Go 的思维来帮助您构建并开发 Go 应用程序的开源框架
- Golang Web FrameWork 比较
- Go语言的Web框架
- 谁是最快的Go Web框架
- 使用Golang(Margini)+Mongodb打造你的第一个站点
GraphQL
- GraphQL 用例:使用 Golang 和 PostgreSQL 构建一个博客引擎 API
- GraphQL Starter Kit for GoLang A GraphQL server example written in GoLang integrating with Cassandra and MongoDB.
- graphql-go/graphql An implementation of GraphQL for Go / Golang
常见问题
重复定义
- 遇到
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/",
},
},
}