Go
最后更新时间:
Todo
1. 游标,文档流
1 | |
2. 池化技术
3. 数据库
4. google论文
the tail at scale
基本语法
itoa
https://www.jb51.net/article/257413.htm 行计数器 ### 数组
可以直接通过==比较运算符来比较两个数组 ### 切片
t:=x[m:n]的t的地址还是原x的地址
因为slice值包含指向第一个slice元素的指针,因此向函数传递slice将允许在函数内部修改底层数组的元素。换句话说,复制一个slice只是对底层的数组创建了一个新的slice别名(§2.3.2)
容量和长度 1
2fmt.Println(summer[:20]) // panic: out of range
endlessSummer := summer[:5] // extend a slice (within capacity) and the value is original slice
runes = append(runes, r) // avoid changing original slice, so we change original variable
因为我们一开始就知道names的最终大小,因此给slice分配一个合适的大小将会更有效。下面的代码创建了一个空的slice,但是slice的容量刚好可以放下map中全部的key:
names := make([]string, 0, len(ages))
for range 中赋值问题
如果要改变其中的结构,要用t[i] 1
2
3
4
5
6
7
8
9
10
11
12type T struct {
A int
B string
}
func main() {
t := []T{{1, "a"}, {2, "b"}}
fmt.Println(t)
for i, v := range t {
fmt.Printf("%p %p\n", &v, &t[i]) // 地址不同
v.A = 3
}
}rune 是一个内置类型,代表一个 Unicode
码点,也就是一个 Unicode 字符。
map
if age, ok := ages["bob"]; !ok { /* ... */ } ok
键是否真的存在于map中
struct
点操作符也可以和指向结构体的指针一起工作
如果结构体成员名字是以大写字母开头的,那么该成员就是导出的
如果要在函数内部修改结构体成员的话,用指针传入是必须的;因为在Go语言中,所有的函数参数都是值拷贝传入的,函数参数将不再是函数调用时的原始变量。slice是因为它的值就是指针。
需要注意的是Printf函数中%v参数包含的#副词,它表示用和Go语言类似的语法打印值。对于结构体类型来说,将包含每个成员的名字。
json.Marshal(movies)
data, err := json.MarshalIndent(movies, "", " ")
结构体的成员Tag可以是任意的字符串面值,但是通常是一系列用空格分隔的key:"value"键值对序列;
Year int" "json:\"released\""
Color bool "json:\"color,omitempty\""
json.Unmarshal(data, &titles)
错误处理
传播错误到父亲函数
1 | |
由于错误信息经常是以链式组合在一起的,所以错误信息中应避免大写和换行符。
在Go中,错误处理有一套独特的编码风格。检查某个子函数是否失败后,我们通常将处理失败的逻辑代码放在处理成功的代码之前。如果某个错误会导致函数返回,那么成功时的逻辑代码不应放在else语句块中,而应直接放在函数体中。Go中大部分函数的代码结构几乎相同,首先是一系列的初始检查,防止错误发生,之后是函数的实际逻辑。
defer
所以,对匿名函数采用defer机制,可以使其观察函数的返回值。
func double(x int) (result int) { defer func() { fmt.Printf("double(%d) = %d\n", x,result) }() return x + x }
被延迟执行的匿名函数可以修改函数返回给调用者的所有返回值:
包和文件
默认的包名就是包导入路径名的最后一段,因此即使两个包的导入路径不同,它们依然可能有一个相同的包名。
1
2
3
4import (
"crypto/rand"
mrand "math/rand" // alternative name mrand avoids conflict
)
指定当前工作目录 GOPATH 1
2$ export GOPATH=$HOME/gobook
$ go get gopl.io/...
pkg子目录用于保存编译后的包的目标文件,bin子目录用于保存编译后的可执行程序,例如helloworld可执行程序。
## 读整个文件 1
ioutil.ReadFile(filename)ReadFile函数返回一个字节切片(byte
slice),必须把它转换为string ## 接口
接口的动态类型和动态值
非空接口可能类型不空而值为nil,可能在一个函数内外变量为nil的定义不同
1
2
3var p *int
fmt.Println(p==nil)
f(p) //func f(p interface{}) {fmt.Println(p==nil)}
接口与方法
问题:cannot convert v (variable of type data.Up) to type
data.Vgroup: data.Up does not implement data.Vgroup (method GetVideo has
pointer receiver) 方法不能是指针
https://chenhe.me/post/pointer-and-interface-in-go#%E6%8E%A5%E5%8F%A3%E7%9A%84%E6%9C%AC%E8%B4%A8
- Book 与 *Book 是两个完全不同的类型。 -
值接收器的方法隐式地同时被声明为指针类型的方法。反之不成立。
- 接口的实现不一定是结构体,而可能是任意类型。 -
可以认为接口的值相当于接口的一个实例。把一个接口的实现赋值给接口变量,接口的值不是实现的值,是类型和实现值的指针
断言: - 一个类型断言检查它操作对象的动态类型是否和断言的类型匹配 -
一个接口类型的类型断言改变了类型的表述方式,改变了可以获取的方法集合(通常更大),但是它保留了接口值内部的动态类型和值的部分。
## json解析 结构的属性名必须大写 1
2
3
4
5
6var data struct {
Code int `json:"code"`
// not: code int
}
err := json.Unmarshal([]byte(`{ "code": 901 }`), &data)
fmt.Printf("%#v\n%v", data, err)
测试
单元测试
https://juejin.cn/post/7172037988950474759 -
以 _test.go 为后缀名, 单独通过 go test 来编译并执行 -
func TestName(t *testing.T) -
{source_filename}_test.go -
t.Error, t.Errorf, t.Fatal(+f), t.Fail, t.Log(+f) -
--cover 代码覆盖率 ### Mock
对有调库,文件输入,网络传输的代码的单元测试 打桩:函数替换
### 性能测试 
string和int转换
- strconv.Atoi(strval)
- strconv.Itoa(intval) ## 判断类型 ## 编译 main包里的所有用到的文件都要编译运行
1
2
3
4
5
6
7
8
9
10
11
12func justifyType(x interface{}) {
switch v := x.(type) {
case string:
fmt.Printf("x is a string,value is %v\n", v)
case int:
fmt.Printf("x is a int is %v\n", v)
case bool:
fmt.Printf("x is a bool is %v\n", v)
default:
fmt.Println("unsupport type!")
}
}
匿名函数引用外部变量
1 | |
Go 版本管理
gopath 此电脑/用户下载依赖的位置 go mod init
初始化此模块,用于定位此项目,包括包与包之间的引用
go mod tidy 下载所需,删除不需 go proxy
把github等上的包拉取下来,作为备份和缓存
struct{}类型当占位符
性能优化
pprof
1 | |
基本命令:
go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/XXX"
XXX改为: - profile :cpu占用,火焰图等 - heap:内存 - allocs:申请内存,可能引起频繁 GC - goroutine:申请协程过多 - mutex:锁的争用的阻塞 - block:阻塞
优化之后 -> 改动前后响应数据diff https://farmerchillax.github.io/2023/07/04/Go%E6%80%A7%E8%83%BD%E5%88%86%E6%9E%90%E5%B7%A5%E5%85%B7/
火焰图
https://www.ruanyifeng.com/blog/2017/09/flame-graph.html y轴调用栈 平顶 -> 性能问题
GC
https://zhuanlan.zhihu.com/p/334999060 三色标记法:同bfs,黑色是已经遍历,灰色是在队列中,白色是未遍历(可能不可达) 弱三色不变式:不允许“从灰色对象出发,到达白色对象的、未经访问过的路径被赋值器破坏”,允许“赋值器修改对象图,导致某一黑色对象引用白色对象“ 插入屏障:在A对象引用B对象的时候,B对象被标记为灰色。 删除屏障:被删除的对象,如果自身为灰色或者白色,那么被标记为灰色。 混合写屏障:为了消除栈的重扫过程(栈上的很容易被删除),一旦栈被扫描变为黑色,则它会继续保持黑色, 并要求将对象分配为黑色。 - GC 开始将栈上的对象全部扫描并标记为黑色; - GC 期间,任何在栈上创建的新对象,均为黑色; - 被删除的堆对象标记为灰色; - 被添加的堆对象标记为灰色; ## 逃逸分析 逃逸分析:分析这个变量需不需要放到堆上,降低效率但是保证函数ret后还在 情况有:指针逃逸,interface{}动态类型逃逸,栈空间不足,闭包
GMP模型
G:goroutine M:工作线程(OS thread),它直接对应于操作系统的线程。M负责实际执行Go代码。一个M可以执行多个Goroutine,但同一时间只能执行一个Goroutine P:执行Go代码所需的资源
DataBase

指针切片
1 | |