了解常量和遍历【const var 关键词】
:=
初始化以及赋值
// 对变量 num 初始化,并赋值为 12
// 之后想修改值不能用 := ,要用 =
num := 12;
for 循环关键字的使用【源码: ScoteAI-book/ch01/1.4/loop.go】
指针的使用 【源码: ScottAI-book/ch01/1.2/pointer/main.go】
net/http 的使用 【源码: ScottAI-book/ch01/1.1/helloserver/main.go】
垃圾回收机制——三色标记法
包及作用域
// 调用其他包的变量
package1.num1
package2.num1
基本数据类型
复合数据类型
引用数据类型
接口数据类型
格式化说明符
注意:
Go语言中的**指针(pointer)**也是一种引用类型,但它在语义上更接近于基本类型。指针可以用于间接引用和修改变量的内存地址,但与切片、映射和通道等引用类型有所不同。
小提示: 引用类型引用传递,复合类型值传递!
类型 | 长度 | 默认值 | 说明 |
---|---|---|---|
bool | 1 | false | |
byte | 1 | 0 | uint8 |
rune | 4 | 0 | uint32 |
int、uint | 4或8 | 0 | 32或64 |
int8、uint8 | 1 | 0 | |
uint16、uint16 | 2 | 0 | |
int32、uint32 | 4 | 0 | |
int64、uint64 | 8 | 0 | |
float32 | 4 | 0.0 | |
float64 | 8 | 0.0 | |
complex64 | 8 | ||
complex128 | 16 | ||
uSintptr | 4或8 | 指针类型 |
注意: Go 语言中没有对象和类的概念,封装思想都是通过复合类型来实现,比如结构体
数组
// 初始化数组
var a [3]int
var b [3]int = [3]int{1,2,3}
c := [...]int{1,2,3,4}
// 新语法(记一下)index : value
d := [...]int{4,4:1,1:2} // 等同于 [4 2 0 0 1]
切片 (slice):相当于 python 切片
创建切片:使用 make
s := make([]int,10)
如果要增加元素,建议采取 append
方法
如果要复制,采取 copy
方法【必须是切片,数组复制:a[:]
】
多维切片可以通过嵌套切片来创建
// 创建一个二维切片
matrix := [][]int{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
}
在删除之前,将要删除元素置为 nil
,否则垃圾回收已删除元素,从而切片容量不会发生变化
map:相当于python中字典,或者Java中Map
创建 map ,使用 make
m := make(map[string] int)
使用其中元素
m := map[string] int {
"k1": 11,
"k2": 22
}
fmt.Println("k1: ", m["k1"])
delete
函数delete(m, "k1")
结构体(struct)
type Person struct {
Name string
Gender,Age int
}
同类型可以写在一行,并用 ,
号隔开
初始化结构体
// 初始值 nil
var p *Person;
// 初始化结构体
var pp = new(Person)
pp.Name = "张三"
// 初始化并赋值
var p1 = Person{Name: "张三", Gender: 1, Age: 12}
封装性:属性名首字母大写(public),属性名首字母小写(private)
继承性:嵌套结构体(不可以是它自身,但可以有指针指向它自己)
// 父类 Person , 子类 Employee Student
type Person struct {
Name string
Gender,Age int
}
type Employee struct {
p Person
Salary int
}
type Student struct {
Person
School string
}
// 赋值操作
e := Employee{p:Person{"Scott",1,30},Salary:1000}
var s Student
s.Name = "Billy" //相当于 s.Person.Name = "Billy"
s.Gender = 1 //相当于 s.Person.Gender = 1
s.Age = 6 //相当于 s.Person.Age = 6
s.School = "xxx 大学"
JSON(encoding/json、encoding/xml、encoding/asnl)
字符串操作常用包:
[]bytes
类型后进行处理对于参数传值
make & new 函数对比说明
函数
定义
闭包(保留外部函数的变量)
作用域
返回值(可多个)
变长参数(…)
defer关键字:用于释放资源,按照后进先出规则(LIFO)
f,err := os.Open("filename")
if err != nil {
fmt.Println(err)
}
// 关闭资源
defer f.Close()
方法
定义
// 定义1个结构体
type Rectangle struct {
w,h float64
}
// 定义方法
func (r Rectangle) area() float64 {
return r.w * r.h
}
接口
定义
type ShapeDesc interface {
Area() float64
Perimeter() float64
}
使用
// 前提:结构体需要重写 Area() 和 Perimeter 方法
var s1,s2 ShapeDesc
// 类型断言: x.(T),其中 x 相当于变量,T 相当于类型(此处是: type circle struct)
_,ok := s1.(circle)
接口只能声明方法,没有实现
实现接口,必须实现接口内的所有方法(方法名、形参、返回值完全一致)
接口声明方法不可重名
接口可嵌套
反射(reflect包)【源码: ScottAI-book/ch04/4.4/main.go】
总结
高性能编程 = 协程(goroutine)+ 通道 (channel)
Go 语言将基于CSP(Communicating Sequential Process)模型的并发编程内置到语言中,即协程之间可以共享内存。
goroutine 协程: 一种轻量级的线程,使用 Go 语言关键字启动。
goroutine 和 系统线程是不一样的
所有的 Go 语言都是通过 goroutine 运行的(包括 main 函数)
核心概念:进程、线程、goroutine
如何运行一个协程?go 关键字
func hello() {
fmt.Println("Hello World!")
}
func main() {
// 使用关键字启动协程
go hello()
// 加上延时
// 主线程结束会关闭所有协程,从而导致不输出 Hello World
time.Sleep(1*time.Second)
}
sync.WaitGroup 去除休眠方式等待协程结束
通道(channel)协程间的通信
初始值 nil
开启通道关键字 chan
关闭通道:close 函数
// 初始化
c1 := make(chan int)
// 将通道带入协程中
go writeChan(c1, 666)
// 接收通道数据
a := <-c1
// 协程中将值写入通道
func writeChan(c chan int, x int) {
// 写入通道该过程是阻塞的,必须有协程接收数据
c <- x
// 关闭通道
close(c)
}
通道方向:单向 & 双向(默认)
// 默认通道——双向
func one(c chan int,x int) {
// 向通道c写入数据x
c <- x
}
// out 通道只写, in 通道只读
// 箭头流向: 指向chan是写,指向变量是读
func two(out chan<- int, in<-chan int) {
// 读取 in 通道,赋值给 v
for v:= range in {
// 将数据 v 写入 out 通道
out <- v
}
}
缓存通道【源码: ScottAI-book/ch05/5.2/buffer/main.go】
在创建通道是可以指定队列最大长度
// 指定队列长度: 3
c := make(chan int 3)
尾部插入元素,头部获取元素
队列空,接收数据的协程阻塞,等待另一个协程向该通道发送数据
切换通道 select (可以理解为 switch case)
select 监听通道通信,有通信发生触发相应代码块
基本结构
select {
case <- ch1:
fmt.Println("从通道1读取数据")
case ch2 <- 1:
fmt.Println("向通道2写入数据")
default:
fmt.Println("前面都不满足的情况")
}
只能选择其中1个,都满足的情况会从中抽取1个
如果没有写 default,在没有向通道写入数据之前会阻塞
select 超时问题解决【源码: ScottAI-book/ch05/5.2/timeout1/main.go】
1. 当某个协程向通道写入数据,没有协程接收时,将会死锁。【超时】
2. 这时可以通过 select + time.After 去解决【检查】
3. 如果可以通过随机数值代替具体数值
// 随机种子
rand.Seed(time.Now().UnixNano)
// 随机数
no := rand.Intn(6)
// 随机秒
no *= 1000
du := time.Duration(int32(no))*time.Millisecond
管道(pipeline)【源码: ScottAI-book/ch05/5.3/main.go】
使用管道好处(3点):
小结
前面总结:数据类型、函数、方法、接口、反射、协程、通道、管道。
编译快原因:
包(package)
对于导入的包必须使用(IDE自动管理,无需人工操作)
如果是包名冲突,必须起别名【当前文件有效】
import (
crand "crypto/rand"
"math/rand"
)
可以同python导入全部(import * from xxx)一样,可以简写成 .
import . "fmt"
// 使用时, 无需 fmt.Println()
Println("Hello World")
空导入,只需要其中的 init 函数,即只编译导入文件但不使用其中函数
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
)
包名的别名一般用复数形式,如 bytes、strings等
Go 工具(Go Tool): 下载、查询、构建、格式化、测试、安装代码包
运行 go help
查看命令
GOPATH
环境变量【重要】指定工作区间根目录,有3个子目录
src
存放源文件pkg
存放编译后的包bin
存放可执行文件GOROOT
环境变量,默认采取Go语言安装目录
GOOS
和 GOARCH
指定目标操作系统,指定目标处理器(如arm、amd64),交叉编译时会遇到
运行 go env
查看各个环境变量及对应的值【太多,掌握 GOPATH 即可】
GO 命令
go get
从互联网下载包
// 下载mysql 驱动包
go get -u github.com/go-sql-driver/mysql
go-get 包含了安装(go install)和编译(go build)两个步骤
go build
编译指定源文件,多个源文件用空格隔开
go install
编译源文件
go list
查看包信息,查看完整信息:go list -json fmt
go doc
打印输出文档信息
go doc fmt.Println
godoc
生成体系化的 Web 页面
go run
运行 go 文件
go test
测试,一般以 *_test.go 命名【方便 go build 不编译这些文件】,如 one_test.go 文件
// one_test.go
package one
// 测试文件必须引入该包
import "testing"
// 参数写 T
func TestFun1(t *testing.T) {
// 写测试代码...
// t.Error("错误....")
}
基准测试(Benchmark)
var final int
func benchmarkFun1(b *testing.B) {
var end int
for i := 0; i < b.N; i++ {
end = fun()
}
final = end
}
代码优化
go tool pprof -help
了解相关用法小结
基本类型、复合类型、函数、方法、接口、反射、协程、通道、管道,和包的管理,bug 定位,性能分析,对 *_test.go 文件的测试,使用 godoc 命令生成文档。基本上 Go 语言基础部分学完了。下一步进阶:竞态与并发、sync 包、context 包、工作池、Go Web 编程、net/http 包、Web 框架(如基于httprouter的gin框架、MVC框架Beego)、Web底层服务(TCPSocket、UDPSocket、WebSocket)、中间介、数据库访问(database/sql 接口)
微服务框架(对比):
框架 | 团队 | 开源时间 | 概述 | 优势 | 缺点 |
---|---|---|---|---|---|
go-micro | 国外大佬Asim团队 | 2015年 | 是最早,最经典的Go微服务框架之一 | 轻量级框架,入门简单,文档清晰 | 版本兼容性差,社区活跃度一般 |
go-zero | 国内大佬万俊峰团队 | 2020 | 提供了微服务框架需要具备的通用能力 | 社区生态非常好,无论是文档更新还是技术群都很活跃 | 相比于go-micro比较重,同时也只带一部分的强约束,学习门槛比go-micro略高 |
go-kit | 国外大佬 | 2015 | Go-kit将自己描述为微服务的标准库。像Go一样,go-kit为您提供可用于构建应用程序的单独包。 | 极度轻量级框架 | 社区建设一般 |
tars-go | 腾讯开源 | 2018 | tarsgo是tars这个大的C++重量级微服务框架下的go语言服务框架 | 优势在于很多能力不用从头开始做起,直接依托母体tars | 缺点是独立性较差,要选用这个tarsgo的前提,就是要先选用tars这个C++的框架 |
dubbo-go | 阿里开源 | 2019 | dubbogo是dubbo这个Java重量级微服务框架下的go语言服务框架 | 和腾讯开源项目类似 | 和腾讯开源项目类似 |
go-kratos | B站开源 | 2019 | 轻量级的微服务框架,框架定位于解决微服务的核心诉求。 | 暂无,后续补充 | 暂无,后续补充 |
jupiter | 斗鱼开源 | 2020 | 面向服务治理的Golang微服务框架 | 暂无,后续补充 | 暂无,后续补充 |
go 的基本等级:
初级
初级呢,只要求掌握Golang的基本语法,懂几个流行的框架和库,能更删改查去做业务就行。一般我会问50%的golang知识点,一般集中在slice、map这块的;30%的数据库知识点,主要考察数据库的索引,事务的隔离,sql语句的优化之类的,也很基础;20%数据结构知识点,数据结构是编程的基础,这个怎么都逃不掉。一句话,能干活就行。
中级
中级呢,好歹也要知道一些底层的东西,或者是源码层的东西,什么goroutine的实现原理,什么内存逃逸,还有微服务相关的东西,另外docker和k8s也是必须要问的,主要考察知识的深度和广度,因为可能是小组的组长,要有一定的技术视野。
高级
高级呢,偏于项目管理和技术选型,golang应该也没有多少问的,但是也要问一两个golang设计哲学性的问题,比如对泛型怎么看之类的,主要还是对于系统架构和项目的管理的理解,顺便聊聊他之前做过的项目怎么管理,用了哪些技术选型,考量是什么,为什么最后选择了这个放弃了那个,然后碰到过什么坑,是怎么解决的。