重拾 Golang

什么是 Golang?

Go™ is an open source programming language that makes it easy to build simple, reliable, and efficient software.

特性

类别

  • 静态语言
  • 编译型语言

优点

  • 语言层面支持并发
  • 无依赖,直译机器码
  • 内置 runtime,支持 GC
  • 可跨平台编译
  • 支持内嵌 C
  • 丰富的标准库
  • 学习曲线低

缺点

  • 接口是枚举类型
  • import 包不支持版本
  • goroutine 一旦启动,切换将不受程序控制

环境配置

安装

 根据操作系统,在 Download 页面下载对应的安装包,进行安装

MacOS

1
2
3
# 安装完成后,iTerm 中看到可以执行 go 命令了
$ which go
/usr/local/go/bin/go

Linux

1
2
$ wget https://dl.google.com/go/go1.14.4.linux-amd64.tar.gz
$ sudo tar -C /usr/local/ -xzvf go1.14.4.linux-amd64.tar.gz

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 环境变量
$ vim ~/.bashrc
export GOROOT=/usr/local/go
export PATH=$PATH:$GOROOT/bin

# 工作目录
# bin: 存放可执行文件
# pkg: 存放编译好的库文件
# src: 存放 go 的源文件
$ mkdir -p ~/code/gopath
$ vim ~/.bashrc
export GOROOT=/usr/local/go
export GOPATH=~/code/gopath
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin

$ source ~/.bashrc

Hello world

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
# 创建项目目录
$ mkdir -p $GOPATH/src/test/hello
$ cd $GOPATH/src/test/hello

# 编写 go 源文件
$ vim hello.go
package main
import "fmt"
func main() {
fmt.Println("Hello, world!")
}

# 执行
$ go run hello.go
Hello, world!

# 编译
$ ll
total 4.0K
-rw-r--r-- 1 benedictjin wheel 75 1 15 11:20 hello.go
$ go build
$ ll
total 2.0M
-rwxr-xr-x 1 benedictjin staff 2.0M 1 15 11:20 hello*
-rw-r--r-- 1 benedictjin wheel 75 1 15 11:20 hello.go

# 运行可执行文件
$ ./hello
Hello, world!

# 清理编译的中间文件
$ go clean
$ ll
total 4.0K
-rw-r--r-- 1 benedictjin wheel 75 1 15 11:20 hello.go

下载依赖

1
2
3
4
5
6
7
$ GOROOT=/usr/local/go
$ GOPATH=~/code/gopath

# 单个依赖
$ /usr/local/go/bin/go get -t -v github.com/golang/snappy/...
# 所有依赖
$ /usr/local/go/bin/go get -t -v ./...

常用命令

go mod

命令 说明
init 在当前目录初始化新的 mod 模块
graph 打印模块依赖图
download 下载并缓存依赖包
vendor 将依赖复制到 vendor 下
edit 编辑 go.mod 文件
verify 验证依赖是否正确
why 解释为什么需要依赖
tidy 拉取缺少的模块,移除不用的模块

基本语法

常量

说明

1
const <常量名> <常量类型> = <常量值>

举例

1
2
3
4
5
6
7
const NODES int = 3

const (
NODES int = 4
SHARDS int = 64
REPLICATIONS int = 3
)

三元表达式

说明

 Golang 并不支持三元表达式,只能用 if-else 来表达对应的含义

举例

1
2
3
4
5
if expr {
n = trueYuzhouwan
} else {
n = falseYuzhouwan
}

数组

说明

1
var <变量名> [SIZE] <元素类型>

举例

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
// 声明数组变量
var yuzhouwan [10] float32
// 初始化数组
var yuzhouwan = [3]float32{0.1, 0.2, 0.30000000000000004}
// 不指定数组长度,Golang 会根据数组内的元素,来设置数组的大小
var yuzhouwan = [...]float32{0.1, 0.2, 0.30000000000000004}

// 遍历数组
var arr [3]int
var i, j int

for i = 0; i < 3; i++ {
arr[i] = i
}

for j = 0; j < 3; j++ {
fmt.Printf("%d = %d\n", j, arr[j])
}

// 打印所有元素
fmt.Printf("%v\n", arr)

// 增加新元素
var yuzhouwan [] int
for n := 0; n < 3; n++ {
yuzhouwan = append(yuzhouwan, n)
}
fmt.Printf("%v", yuzhouwan)

List

举例

1
2
3
4
5
6
7
l := list.New()
for i := 0; i < 3; i++ {
l.PushBack(i)
}
for e := l.Front(); e != nil; e = e.Next() {
fmt.Println(e.Value)
}

Map

举例

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
// 初始化
site := map[string]int{
"yuzhouwan": 0,
"blog": 1,
}

// 增加新元素
site := make(map[string]int)
site["yuzhouwan"] = 0
site["blog"] = 1

// 判断指定 key 是否存在于 map 中
if value, exists := site["yuzhouwan"]; exists {
fmt.Println("value: ", value)
} else {
fmt.Println("key not found")
}

// 遍历
m := map[string]int{"yuzhouwan": 1, "blog": 2,}
for k, v := range m {
fmt.Printf("%s=%d\n", k, v)
}

// 按照 Key 排序后遍历
m := make(map[int]string)
m[1] = "a"
m[2] = "c"
m[0] = "b"
var keys []int
for k := range m {
keys = append(keys, k)
}
sort.Ints(keys)
for _, k := range keys {
fmt.Printf("%v : %v\n", k, m[k])
}

接口

说明

1
2
3
type <接口名> interface {
<方法名称>() <方法返回值>
}

举例

1
2
3
4
type Value interface {
String() string
Set(string) error
}

自定义数据类型

说明

1
type <自定义类型> <原始类型>

举例

1
type ErrorHandling int

给自定义数据类型扩展方法

说明

 可以使得自定义数据类型作为某一个 interface 的实现类,只需要扩展 interface 中申明的方法即可

举例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
type Value interface {
String() string
Set(string) error
}

type StringsFlag []string

func (v *StringsFlag) Set(s string) error {
var err error
*v, err = str.SplitQuotedFields(s)
if *v == nil {
*v = []string{}
}
return err
}

func (v *StringsFlag) String() string {
return "<StringsFlag>"
}

初始化空的结构体

说明

 struct{} 表示一个空的结构体,不包含任何的属性和方法,相当于 Java 中的 Object。而 struct{}{} 表示对空的结构体进行初始化,相当于 Java 中的 new Object()

举例

1
struct{}{}

defer 异步

说明

 被 defer 关键字修饰的函数调用可以异步地执行。多次 defer 异步调用之间会以逆序的顺序执行,类似于压栈出栈的过程。并且,defer 修饰的函数会在外层函数返回之前进行调用(准确来说,是在外层函数设置返回值之后,在即将返回之前)。因此,defer 常用于资源的释放

举例

1
2
3
4
5
6
7
8
9
10
11
12
package main

import "fmt"

func main() {
for i := 1; i <= 3; i++ {
// noinspection GoDeferInLoop
defer fmt.Println(i)
fmt.Println(fmt.Sprintf("for %d looping...", i))
}
fmt.Println("done")
}
1
2
3
4
5
6
7
for 1 looping...
for 2 looping...
for 3 looping...
done
3
2
1

实战技巧

镜像加速

1
2
3
4
5
6
7
8
9
10
11
12
$ go env -w GO111MODULE=on

# Golang 官方
$ go env -w GOPROXY=https://goproxy.io
# 阿里云镜像
$ go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/

# 私有库不走代理
$ go env -w GOPRIVATE=yuzhouwan.com

# 检查是否生效
$ go env

多文件编译

1
2
3
$ GOROOT=/usr/local/go
$ GOPATH=/Users/benedictjin/gopath
$ /usr/local/go/bin/go build -o /Users/benedictjin/output /Users/benedictjin/yuzhouwan/main.go /Users/benedictjin/yuzhouwan/sub_process.go

常用框架

influxdb-comparisons

基础环境

1
2
3
$ wget https://dl.google.com/go/go1.14.4.linux-amd64.tar.gz
$ tar -C /usr/local -xzf go1.14.4.linux-amd64.tar.gz
$ vim ~/.bashrc
1
2
3
export GOPATH=~/code/gopath/
export GO_HOME=/usr/local/go
export PATH=$GO_HOME/bin:$PATH
1
2
$ source ~/.bashrc
$ go version
1
go version go1.14.4 linux/amd64

源码编译

1
2
3
4
5
6
7
$ mkdir -p ~/code/gopath/src/github.com/influxdata/influxdb-comparisons
$ cd ~/code/gopath/src/github.com/influxdata/influxdb-comparisons
$ yum install git -y
$ git init
$ git remote add upstream git@github.com:influxdata/influxdb-comparisons.git
$ git pull upstream master:master
$ go get -t -v github.com/influxdata/influxdb-comparisons/...

生成数据

1
2
3
$ cd ~/code/gopath/src/github.com/influxdata/influxdb-comparisons/cmd/bulk_data_gen
$ go build github.com/influxdata/influxdb-comparisons/cmd/bulk_data_gen
$ ./bulk_data_gen --scale-var=10 --format=opentsdb --timestamp-start=2020-01-01T00:00:00Z --timestamp-end=2020-01-01T00:01:00Z > /data/opentsdb_scale10_data.json

写入数据

1
2
3
$ cd ~/code/gopath/src/github.com/influxdata/influxdb-comparisons/cmd/bulk_load_opentsdb
$ go build github.com/influxdata/influxdb-comparisons/cmd/bulk_load_opentsdb
$ cat /data/opentsdb_scale10_data.json | ./bulk_load_opentsdb --urls=http://yuzhouwan.com:4242 -workers=64

踩到的坑

version go1.11.9 does not match go tool version go1.11.5

解决

1
$ brew uninstall --ignore-dependencies go
1
Uninstalling /usr/local/Cellar/go/1.11.5... (9,298 files, 404.3MB)

now.Sub(last).Milliseconds undefined (type time.Duration has no field or method Milliseconds)

解决

 将升级 Golang 到 1.13 以上即可

1
2
3
$ curl -o golang.pkg https://dl.google.com/go/go1.13.3.darwin-amd64.pkg
$ sudo open golang.pkg
$ go version

command-line-arguments undefined

解决

 不要直接右击 main.go 运行,需要同时选中相关的几个 go 程序,再右击运行

working directory is not part of a module

解决

 这里以 influxdb-comparisons 项目为例,关键步骤是 go mod init main,完整命令如下:

1
2
3
4
$ cd ~/code/gopath/src/github.com/influxdata/influxdb-comparisons/
$ go mod init main
$ go build github.com/influxdata/influxdb-comparisons/cmd/bulk_load_opentsdb
$ ./bulk_load_opentsdb
1
2
3
4
5
6
daemon URLs: [http://localhost:8086]
SysInfo:
Current GOMAXPROCS: 64
Num CPUs: 64
Trend statistics using 30 samples
Started load with 1 workers

verifying module: checksum mismatch

解决

1
2
3
4
# 如果是升级导致的,可以清理旧 mod
$ go clean -modcache
# 如果是私有代码库导致的,可以直接关闭 checksum 功能
$ go env -w GOSUMDB=off

can’t load package

描述

1
2
go: finding module for package github.com/influxdata/influxdb-comparisons/cmd/bulk_load_yuzhouwan
can't load package: package github.com/influxdata/influxdb-comparisons/cmd/bulk_load_yuzhouwan: module github.com/influxdata/influxdb-comparisons@latest found (v0.0.0-20200224230202-a75268060881), but does not contain package github.com/influxdata/influxdb-comparisons/cmd/bulk_load_yuzhouwan

解决

1
$ go env -w GO111MODULE=off

补充

  • auto
    默认判断是否启用 module 功能
    • 当前目录在 GOPATH/src 之外且该目录包含 go.mod 文件
    • 当前文件在包含 go.mod 文件的目录下面
  • off
    关闭 module 功能,寻找依赖包的方式将会沿用旧版本那种通过 vendor 目录或者 GOPATH 模式来查找
  • on
    开启 module 功能,完全不去 GOPATH 目录下查找

资料

Blog

Github

欢迎加入我们的技术群,一起交流学习

群名称 群号
人工智能(高级)
人工智能(进阶)
BigData
算法