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
2
3
4
5
6
7
# Goland
30天无限试用插件法
配置http仓库
# 第三方插件库
# version: GoLand 2021.1之后的不支持此插件
https://plugins.zhile.io
搜索"IDE Eval Reset"
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
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.
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
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)
}
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
2Boolean 错误,检查一直为 true、false 或者冗余的表达式
fmt.Println(i != 0 || i != 1)
1Range 循环,比如如下代码主协程会先退出,go routine无法被执行
words := []string{"foo", "bar", "baz"} for _, word := range words { go func() { fmt.Println(word). }() }
1
2
3Unreachable的代码,如 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
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
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
# Go 语法
基本数据类型
bool
string
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
byte
rune
float32 float64
complex64 complex128
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])
}
2
3
4
5
6
7
函数: 一等公民
返回值的类型写在最后面
没有默认参数、可选参数
- 可以有多个返回值
- 所有参数都是值传递: slice,map,channel 会有传引⽤用的错觉
- 函数可以作为变量量的值
- 函数可以作为参数和返回值
指针
指针不能运算
参数传递
值传递?引用传递?
Go语言只有值传递一种方式
数组
相同类型且长度固定连续内存片段
以编号访问每个元素
定义方法
var identifier [len]type
示例
var array1 [5]int
array2 := [3]int{1, 2, 3}
array3 := [...]int{1, 2, 3}
var grid [4][5]bool
2
3
4
数量写在类型前面
使用“...”让编译器帮我们数
# i:[x], v:values;
for i, v := range array3 {
fmt.Println(i, v)
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)
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
- 元素的唯⼀一性
- 基本操作
添加元素
判断元素是否存在
删除元素
元素个数
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)
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
- string 是数据类型,不是引用或指针类型
- string 是只读的、不可变的 byte slice,len 函数可以它所包含的 byte 数
- string 的 byte 数组可以存放任何数据
Unicode UTF8
- Unicode 是⼀一种字符集(code point)
- 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
)
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 框架、负载均衡等分布式技术,具备一定的系统架构设计能力;