Go

# Go

# 入门篇

# 配好环境:选择一种最适合你的Go安装方法

# 配置IDE

# vim

# vim-go (opens new window)
# Vim 8 packages
mkdir -p ~/.vim/pack/plugins/start/
cd ~/.vim/pack/plugins/start/
git clone https://github.com/fatih/vim-go.git
cd vim-go/doc
vi vim-go.txt
:GoInstallBinaries
1
2
3
4
5
6
7

# Goland

30天无限试用插件法

配置http仓库
# 第三方插件库
# version: GoLand 2021.1之后的不支持此插件
https://plugins.zhile.io

搜索"IDE Eval Reset"
1
2
3
4
5
6

公网许可服务器

存在失效的可能性

https://flm.nighthawkcodingsociety.com


https://learnku.com/articles/67432
IDEA.VMOptions
-javaagent:/Users/shizc/Downloads/patch/ja-netfilter/ja-netfilter.jar
1
2
3
4
5
6

# 初窥门径:一个Go程序的结构是怎样的?

# 基础篇

# COMMAND

# usage

Go is a tool for managing Go source code.

Usage:

	go <command> [arguments]

The commands are:

	bug         start a bug report
	build       compile packages and dependencies												编译包和依赖
	clean       remove object files and cached files
	doc         show documentation for package or symbol
	env         print Go environment information
	fix         update packages to use new APIs
	fmt         gofmt (reformat) package sources												格式化
	generate    generate Go files by processing source
	get         add dependencies to current module and install them			添加依赖到本地模块
	install     compile and install packages and dependencies
	list        list packages or modules
	mod         module maintenance
	run         compile and run Go program															编译并运行 Go 程序
	test        test packages
	tool        run specified go tool
	version     print Go version																				打印 Go 环境的版本信息
	vet         report likely mistakes in packages

Use "go help <command>" for more information about a command.

Additional help topics:

	buildconstraint build constraints
	buildmode       build modes
	c               calling between Go and C
	cache           build and test caching
	environment     environment variables
	filetype        file types
	go.mod          the go.mod file
	gopath          GOPATH environment variable
	gopath-get      legacy GOPATH go get
	goproxy         module proxy protocol
	importpath      import path syntax
	modules         modules, module versions, and more
	module-get      module-aware go get
	module-auth     module authentication using go.sum
	packages        package lists and patterns
	private         configuration for downloading non-public code
	testflag        testing flags
	testfunc        testing functions
	vcs             controlling version control with GOVCS

Use "go help <topic>" for more information about that topic.
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

#

go mod init github.com/bigwhite/module-mode
go mod tidy
go build xxx.go

# 指定平台并编译包和依赖
GOOS=linux go build xxx.go

# 格式化Go程序代码
go fmt xxx.go
1
2
3
4
5
6
7
8
9

# go build

  • Go 语言不支持动态链接,因此编译时会将所有依赖编译进同一个二进制文件。

  • 指定输出目录。

    • go build –o bin/mybinary
      
      1
  • 常用环境变量设置编译操作系统和 CPU 架构。

    • GOOS=linux GOARCH=amd64 go build
      
      1
  • 全支持列表。

    • cat $GOROOT/src/go/build/syslist.go
      
      1

# go test

Go 语言原生自带测试

import "testing"

func TestIncrease(t *testing.T) { t.Log("Start testing") increase(1, 2)

}
1
2
3
4
5

go test ./... -v 运行测试 go test 命令扫描所有*_test.go为结尾的文件,惯例是将测试代码与正式代码放在同目录, 如 foo.go 的测试代码一般写在 foo_test.go

# go vet

代码静态检查,发现可能的 bug 或者可疑的构造

  • Print-format 错误,检查类型不匹配的print

    str := “hello world!”
    fmt.Printf("%d\n", str)
    
    1
    2
  • Boolean 错误,检查一直为 true、false 或者冗余的表达式

    fmt.Println(i != 0 || i != 1)
    
    1
  • Range 循环,比如如下代码主协程会先退出,go routine无法被执行

    words := []string{"foo", "bar", "baz"} for _, word := range words {
    go func() { fmt.Println(word).
    }() }
    
    1
    2
    3
  • Unreachable的代码,如 return 之后的代码

  • 其他错误,比如变量自赋值,error 检查滞后等

    res, err := http.Get("https://www.spreadsheetdb.io/")
    defer res.Body.Close()
    if err != nil {
      log.Fatal(err)
    }
    
    1
    2
    3
    4
    5

# Go语法

# 基本程序结构

退回返回值

  • Go 中 main 函数不支持任何返回值

  • 通过 os.Exit 来返回状态

package main

import (
	"os"
)

func main() {
  
	os.Exit(0x00)
  
}

---
进程 已完成,退出代码为 0
1
2
3
4
5
6
7
8
9
10
11
12
13
14

获取命令行参数

  • main 函数不支持传入参数
  • 在程序中直接通过 os.Args 获取命令行参数

编写测试程序

  • 源码文件以 _test 结尾:xxx_test.go
  • 测试方法名以 Test 开头:func TestXXX(t *testing.T) {……}

运算符

  • 算数运算符

    • Go语言没有前置的 ++, --,例如:++a,--a
    • Go语言有后置的**++**, --,例如:a++,a--
  • 比较运算符

    • == 比较数组
      • 相同维数且含有相同个数元素的数据才可以比较
      • 每个元素都相同的才相等
  • 位运算符

    • &^ 按位置清 0

    • 若右操作数为1 ,那么无论左操作数为 0 还是 1 ,其结果都为 0;若右操作数为 0 ,则原来左操作数是什么其结果还是什么。

    • 1 &^ 0 -- 1
      1 &^ 1 -- 0
      0 &^ 1 -- 0
      0 &^ 0 -- 0
      
      1
      2
      3
      4

命名规则

  • Go 源文件总是用全小写字母形式的短小单词命名,并且以.go 扩展名结尾。
  • 下划线这种分隔符,在 Go 源文件命名中有特殊作用

$mkdir ~/goprojects // 创建一个可以集合所有专栏项目的根文件夹
$cd ~/goprojects
$mkdir helloworld // 创建存储helloworld示例的文件夹
$cd helloworld

# macOS linux
$go build main.go
$./main
hello, world

# windows
>go build main.go
>.\main.exe
hello, world
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# Go 基础

软件开发的新挑战

  • 多核硬件架构
  • 超大规模分布式计算集群
  • Web模式导致的前所未有的开发规模和更新速度

# Go 的创始人

  • Rob Pike Unix的早期开发者,UTF-8创始人
  • Ken Thompson Unix的创始人,C语言创始人,1983年获图灵奖
  • Robert Griesemer Google V8 JS Engine,Hot Spot开发者

# GOPROXY


1

# Go 语法

基本数据类型

bool
string
int		int8 	int16 	int32 	int64
uint	uint8 uint16 	uint32 	uint64	uintptr
byte
rune
float32		float64
complex64	complex128
1
2
3
4
5
6
7
8

类型转换

  • Go语言不允许隐式类型转换
  • 别名和原有类型也不允许隐式类型转换

类型的预定义值

  • math.MaxInt64
  • math.MaxFloat64
  • math.MaxUint32

指针类型

  • 不支持指针运算
  • string 是值类型,其默认的初始化值是空字符串,而不是 nil

if 条件

if条件是不需要括号的

if的条件里可以赋值

if的条件里赋值的变量的作用域就在这个if语句里

switch

switch会自动break,除非使用fallthrough

switch后可以没有表达式

循环

for的条件里不需要括号

for的条件里可以省略初始条件、结束条件、递增表达式

n := 0
for n < 5 {
}

for i := range array1 {
		fmt.Println(array1[i])
	}
1
2
3
4
5
6
7

函数: 一等公民

返回值的类型写在最后面

没有默认参数、可选参数

  1. 可以有多个返回值
  2. 所有参数都是值传递: slice,map,channel 会有传引⽤用的错觉
  3. 函数可以作为变量量的值
  4. 函数可以作为参数和返回值

指针

指针不能运算

参数传递

值传递?引用传递?

Go语言只有值传递一种方式

数组

相同类型且长度固定连续内存片段

以编号访问每个元素

定义方法

  var identifier [len]type
1

示例

  var array1 [5]int
	array2 := [3]int{1, 2, 3}
	array3 := [...]int{1, 2, 3}
	var grid [4][5]bool
1
2
3
4

数量写在类型前面

使用“...”让编译器帮我们数

# i:[x], v:values;
for i, v := range array3 {
		fmt.Println(i, v)
1
2
3

为什么要使用range?

  • 意义明确,美观
  • C++没有类似的能力
  • Java/Python:只能for each value,不能同时获取i, v

数组是值类型

  • [10]int和[20]int 是不同的类型
  • 调用func f(arr [10]int)会拷贝数组
  • 在Go语言中一般不直接使用数组

Slice(切片)

前闭后开

Slice本身没有数据,是对底层array的一个view

Slice的扩展

Slice可以向后扩展,不可以向前扩展

s[i] 不可以超越len(s) , 向后扩展不可以超越底层数组cap(s)

添加元素时,如果超越cap(s), 系统会重新分配更大的底层数组

由于值传递的关系,必须接收append的返回值

arr := [...]int{1, 2, 3}
	s := arr[1:3]


// expand
	arr := [...]int{1, 2, 3, 4, 5, 6, 7, 8}
	s := arr[2:6]
	s1 := s[3:5] 															//s[3], s[4]
	fmt.Println(s)
	fmt.Println(s1)
  fmt.Printf("s=%v, len(s)=%d, cap(s)=%d\n", s, len(s), cap(s))


// append
	s2 := append(s1, 2, 3, 4)
	fmt.Println(s2)


// 创建一个 len(s)=16 的slice
	s := make([]int, 16)
// 创建一个 len(s)=16, cap(s)=32 的slice
	s := make([]int, 16, 32)
  fmt.Printf("s=%v, len(s)=%d, cap(s)=%d\n", s, len(s), cap(s))


// 删除s中第三个元素,后面的元素前移
	s = append(s[:3], s[4:]...)
	fmt.Printf("s=%v, len(s)=%d, cap(s)=%d\n", s, len(s), cap(s))

// 删除s中第一个元素
	s := [...]int{3, 4, 5, 6}
	head := s[0]
	s1 := s[1:]
	fmt.Println(head, s1)

// 删除s中最后一个元素
	s := [...]int{3, 4, 5, 6}
	tail := s[len(s)-1]
	s2 := s[:len(s)-1]
	fmt.Println(tail, s2)
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

Map

  • // 创建
    map[K]V, map[K1]map[K2]V
    
    map := 
    
    1
    2
    3
    4
  • 使用 range 遍历 key ,或者遍历 key, value对

  • 不保证遍历顺序,如需顺序,需手动对key排序

  • 使用len获得元素个数

Map的key

  • map使用哈希表,必须可以比较相等
  • 除了 slice, map, function 的内建类型都可以作为key
  • Struct类型不包括上述字段,也可作为key

Map与工厂模式

  • Map 的 value 可以是⼀一个⽅方法
  • 与 Go 的 Dock type 接⼝方式一起,可以⽅便的实现单一方法对象的⼯厂模式

实现 Set Go 的内置集合中没有 Set 实现, 可以 map[type]bool

  1. 元素的唯⼀一性
  2. 基本操作
  • 添加元素

  • 判断元素是否存在

  • 删除元素

  • 元素个数

	m := map[string]string{
		"name": "dc",
		"site": "imooc",
		"num":  "9537",
	}

	m1 := make(map[string]int)				// m1 == empty map
	var m2 map[string]int							// m2 ==nil
																		// 在Go语言中,nil是可以参与运算的,nil是可以与empty map进行混用的
	fmt.Println(m, m1, m2)

// 遍历(traversal)
	for k, v := range m {
		fmt.Println(k, v)
	}

// 获取map中name的值
// 当获取map中不存在的元素时,依然可以取得值,值为空字符串。
	name := m["name"]
	fmt.Println(name)

//判断存在与否
	name, ok := m["name"]
	fmt.Println(name, ok)
=== >> >> >>
	if name, ok := m["name"]; ok {
		fmt.Println(name)
	} else {
		fmt.Println("key does not exist")
	}


// 删除元素
	site, ok := m["site"]
	fmt.Println(site, ok)
	delete(m, "site")
	site, ok = m["site"]
	fmt.Println(site, ok)



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

字符串与字符编码

rune

  • 使用utf8.RuneCountInString获得字符数量
  • 使用len获得字节长度
  • 使用[]byte获得字节

其他字符串操作

  • Fields, Split, Join
  • Contains, Index
  • ToLower, ToUpper
  • Trim, TrimRight, TrimLeft
  1. string 是数据类型,不是引用或指针类型
  2. string 是只读的、不可变的 byte slice,len 函数可以它所包含的 byte 数
  3. string 的 byte 数组可以存放任何数据

Unicode UTF8

  1. Unicode 是⼀一种字符集(code point)
  2. UTF8 是 unicode 的存储实现 (转换为字节序列列的规则)

结构体与方法

面向对象

  • Go语言仅支持封装,不支持继承和多态
  • Go语言没有class,只有struct

结构的定义

结构的创建

  • 不论地址还是结构本身,一律使用 . 来访问成员

封装

  • 名字一般使用CameICase
  • 首字母大写:public
  • 首字母小写:private

  • 每个目录一个包

  • main包包含可执行入口

  • 为定义结构的方法必须放在同一个包内

  • 可以是不同的文件

如何扩充系统类型或别人的类型?

  • 定义别名
  • 还用组合

内存是一个连续的数据集合,每一个内存存储区域都有唯一的地址标识,称为内存地址。

内存地址编号是一个无符号(表示大于0)十六进制整型数据表示的。

计算机能够识别的数据格式:二进制、八进制、十进制、十六进制。

可以为内存指定区域起别名,称为变量名。

微服务定义

围绕业务功能构建的,服务关于单一业务,服务间采用轻量级的通信机制,可以全自动独立部署,可以使用不同的编程语言和数据存储技术。微服务架构通过业务拆分实现服务组件化,通过组件组合快速开发系统,业务单一的服务组件又可以独立部署,使得整个系统变得清晰灵活:

  • 原子服务
  • 独立进程
  • 隔离部署
  • 去中心化服务治理

缺点:

  • 基础设施的建设、复杂度高

变量赋值

与其他主要编程语言的差异:

  • 赋值可以自动类型推断
  • 在一个赋值语句中可以对多个变量进行同时赋值

交换两个变量的值:

  • 通过一个中间变量
  • 直接交换

常量定义

快速设置连续值

const (
  Monday = iota + 1
  Tuesday
  Wednesday
  Thursday
  Friday
  Saturday
  Sunday
)

const (
  Open = iota << 1
  Close
  Pending
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

context

上下文:串通整个请求流程,我都能够访问这一个共有的流程。

# 职场生涯

中级 Go 工程师任职要求

  • 熟练掌握 Go 语言及 Echo、Gin、Beego 等常见的开发框架,能够进行 Go 语言相关逻辑的深层优化;

  • 熟练掌握面向网络的编程,掌握 TCP/IP 协议,对 Socket/WebSocket 通信和 HTTP/HTTPS 协议有深刻理解;

  • 掌握 Linux 系统及原理,有 Shell 脚本编写能力,有较强的 Linux 下 TroubleShooting 能力;

  • 熟悉常用开源系统和中间件 RabbitMQ、RocketMQ、Kafka 等,熟悉容器技术 Docker,容器编排如 Kubernetes 的相关技术;

  • 熟练掌握 Redis 等 NoSQL 技术,精通 MySQL 的开发设计和调优;

  • 熟悉 RPC 框架、负载均衡等分布式技术,具备一定的系统架构设计能力;