一、Go语言背景和发展
1.软件开发的新挑战
- 多核硬件架构
- 超大规模分布式计算集群
- Web模式导致的前所未有的开发规模和更新速度
2.Go的三位创始人
- Rob Pike:Unix的早期开发者,UTF-8创始人
- Ken Thompson:Unix的创始人,C语言创始人,1983年获图灵奖
- Robert Griesemer:Google V8 JS Engine开发者,Hot Spot开发者
3.Go语言特点
- 简单:Go只有25个关键字;特别是对于一些复杂编程任务如:并发编程,内存管理,Go语言有内置的并发支持及GC
- 高效:Go是编译的静态类型语言,并且可以通过指针进行直接内存访问
- 生产力:简单清新的依赖管理,简单清新的语法,以及独特的接口类型
二、第一个Go程序
[图片上传失败...(image-8dd3db-1556979504914)]
1.应用程序入口
- 必须是main包
- package main
- 必须是main方法
- func main()
- 文件名不一定是main.go
2.退出返回值
- Go中main函数不支持任何返回值
- 通过os.Exit来返回状态
3.获取命令行参数
- main函数不支持传入参数
- 在程序中直接通过os.Args获取命令行参数
4.基本数据类型
- bool
- string
- int int8 int16 int32 int64
- uint uint8 uint16 uint32 uint64
- byte // alias for uint8
- rune
- float32 float64
- complex64 complex128
与其他主要编程的差异:
- Go语言不允许隐式类型转换
- 别名和原有类型也不能进行隐式类型转换
类型的预定义值:
- math.MaxInt64
- math.MaxFloat64
- math.MaxUint32
指针类型
- 不支持指针运算
- string是值类型,其默认的初始值为空字符串,而不是nil
5.运算符
用 == 比较数组
- 相同维数且含有相同个数元素的数组才可以比较
- 每个元素都相同的才相等
按位清零运算符
- &^
6.循环
Go语言仅支持循环关键字for
for i := 0; i <= 9; I++
7.if条件
- condition表达式结果必须为布尔值
- 支持变量赋值:
if var declaration; condition {
}
8.switch条件
- 条件表达式不限制为常量或者整数
- 单个case中,可以出现多个结果选项,使用逗号分隔
- 与C语言等规则相反,Go语言不需要用break来明确退出一个case
- 可以不设定switch之后的条件表达式,在此种情况下,整个switch结构与多个if...else...的逻辑作用等同
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("OS X.")
//break
case "linux":
fmt.Println("Linux.")
default:
fmt.Println("%s.",os)
}
switch {
case 0 <= Num && Num <= 3:
fmt.Printf("0-3")
case 4 <= Num && Num <= 6:
fmt.Printf("4-6")
}
switch I {
case 0,2:
fmt.Printf("0-2")
case 1,3:
fmt.Printf("1-3")
}
9.Map元素的访问
在访问的Key不存在时,仍会返回零值,不能通过返回nil来判断元素是否存在
if v,ok := map1["key1"]; ok{
} else {
}
map的遍历
for k,v := range map1{
}
10.Map与工厂模式
- Map的value可以是一个方法
- 与Go的Dock type接口方式一起,可以方便的实现单一方法对象的工厂模式
11.实现Set
Go的内置集合中没有Set实现,可以map[type]bool
- 元素的唯一性
- 基本操作
- 添加元素
- 判断元素是否存在
- 删除元素
- 元素个数
12.字符串
- string是数据类型,不是引用或指针类型
- string是只读的byte slice,len函数可以它所包含的byte数
- string的byte数组可以存放任何数据
13.Unicode UTF8
- Unicode是一种字符集(code point)
- UTF8是unicode的存储实现(转换为字节序列的规则)
14.常用字符串函数
- strings包(https://golang.org/pkg/strings/)
- strconv包(https://golang.org/pkg/strconv)
15.函数是一等公民
- 可以有多个返回值
- 所有参数都是值传递:slice、map、channel会有传引用的错觉
- 函数可以作为变量的值
- 函数可以作为参数和返回值
16.Go接口
- 接口为非入侵性,实现不依赖接口定义
- 所以接口的定义可以包含在接口使用者包内
17.空接口与断言
- 空接口可以表示任何类型
- 通过断言来将空接口转换为制定类型
v, ok := p.(int) //ok=true时为转换成功
18.Go接口最佳实践
- 倾向于使用小的接口定义,很多接口只包含一个方法
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int,err error)
}
- 较大的接口定义,可以由多个小接口定义组合而成
type ReadWriter interface {
Reader
Writer
}
- 只依赖于必要功能的最小接口
func StoreData(reader Reader) error {
......
}
19.编写好的错误处理
- 没有异常机制
- error类型实现了error接口
type error interface {
Error() string
}
- 可以通过errors.New来快速创建错误实例
errors.New("")
20.panic
- panic用于不可以恢复的错误
- panic退出前会执行defer指定的内容
panic vs os.Exit
- os.Exit 退出时不会调用defer指定的函数
- os.Exit 退出时不输出当前调用栈信息
recover
defer func() {
if err := recover(); err != nil {
//恢复错误
}
}()
21.package
- 基本复用模块单元
- 以首字母大写来表明可被包外代码访问
- 代码的package可以和所在的目录不一致
- 同一目录里的Go代码的package要保持一致
- 通过go get来获取远程依赖
- go get -u强制从网络更新远程依赖
- 注意代码在Github上的组织形式,以适应go get
- 直接以代码路径开始,不要有src
22.init方法
- 在main被执行前,所有依赖的package的init方法都会被执行
- 不同包的init函数按照包导入的依赖关系决定执行顺序
- 每个包可以有多个init函数
- 包的每个源文件也可以有多个init函数,这点比较特殊
23.依赖管理
Go未解决的依赖问题
- 同一环境下,不同项目使用同一包的不同版本
- 无法管理对包的特定版本的依赖
vendor路径
<p>随着Go1.5 release 版本的发布,vendor目录被添加到除了GOPATH和GOROOT之外的依赖目录查找的解决方案。在Go 1.6之前,你需要手动的设置环境变量</p>
查找依赖包路径的解决方案如下:
- 当前包下的vendor目录
- 向上级目录查找,直到找到src下的vendor目录
- 在GOPATH下面查找依赖包
- 在GOROOT目录下查找
常用的依赖管理
- godep https://github.com/tools/godep
- glide https://github.com/Masterminds/glide
- dep https://github.com/golang/dep
三、并发机制
1.Thread vs. Groutine
- 创建时默认的stack的大小
- JDK5以后Java Thread stack默认为1M
- Groutine的Stack初始化大小为2K
- 和KSE (kernel Space Entity)的对应关系
- Java Thread是1:1
- Groutine是M:N
2.Lock
package sync
Mutex
RWLock
WaiteGroup
3.CSP并发机制
CSP vs. Actor
- 和Actor的直接通讯不同,CSP模式则是通过Channel进行通讯的,更松耦合一些
- Go中channel是有容量限制并且独立于处理Groutine,而如Erlang,Actor模式中的mailbox容量是无限的,接受进程也总是被动处理消息
4.select
多渠道的选择
select {
case ret := <- retCh1:
t.Logf("result %s", ret)
case ret := <- retCh2:
t.Logf("result %s", ret)
default:
t.Error("No one returned")
}
超时控制
select {
case ret := <- retCh:
t.Logf("result %s", ret)
case <- time.After(time.Second * 1):
t.Error("time out")
}
5.channel的关闭
- 向关闭的channel发送数据,会导致panic
- v,ok <- ch;ok为bool值,true表示正常接受,false表示通道关闭
- 所有的channel接受者都会在channel关闭时,立刻从阻塞等待中返回且上述ok值为false。这个广播机制常被利用,进行向多个订阅者同时发送信号。如:退出信号
6.Context与任务取消
- 根Context:通过context.Background()创建
- 子Context:context.WithCancel(parentContext)创建
- ctx,cancel := context.WithCancel(context.Background())
- 当前Context被取消时,基于他的子context都会被取消
- 接收取消通知 <- ctx.Done()
7.单例模式(懒汉式,线程安全)
var once sync.Once
var obj *SingletonObj
func GetSingletonObj() *SingletonObj {
once.Do(func(){
fmt.Println("Create Singleton obj.")
obj = &SingletonObj{}
})
return obj
}
8、对象池
9、sync.Pool总结
- 适合于通过复用,降低复杂对象的创建和GC代价
- 协程安全,会有锁的开销
- 生命周期受GC影响,不适合于做连接池等,需自己管理生命周期的资源的池化
10、单元测试框架
Benchmark
BDD in Go
项目网站
https://github.com/smartystreets/goconvey
安装
go get -u github.com/smartystreets/goconvey/convey
启动WEB UI
$GOPATH/bin/goconvey
四、其他
1、reflect.TypeOf vs. reflect.ValueOf
- reflect.TypeOf 返回类型(reflect.Type)
- reflect.ValueOf 返回值 (reflect.Value)
- 可以从reflect.Value获得类型
- 通过kind的来判断类型
2、利用反射编写灵活的代码
- 按名字访问结构的成员
reflect.ValueOf(*e).FieldByName("Name")
- 按名字访问结构的方法
reflect.ValueOf(e).MethodByName("UpdateAge").Call([]reflect.Value{reflect.ValueOf(1)})
3、架构设计模式:Pipe-Filter模式
- 非常适合与数据处理及数据分析系统
- Filter封装数据处理的功能
- 松耦合:Filter只跟数据(格式)耦合
- Pipe用于连接Filter传递数据或者在异步处理过程中缓冲数据流(进程内同步调用,pipe演变为数据在方法调用间传递)
4、架构设计模式:Micro Kernel
特点
- 易于扩展
- 错误隔离
- 保持架构一致性
要点
- 内核包含公共流程或通用逻辑
- 将可变或可扩展部分规划为扩展点
- 抽象扩展点行为,定义接口
- 利用插件进行扩展
5、更快的JSON解析
EasyJSON采用代码生成而非反射
- 安装
go get -u github.com/mailru/easyjson/ - 使用
easyjson -all <结构定义>.go
6、Http路由规则
- URL分为两种,末尾是/:表示一个子树,后面可以跟其他子路径;末尾不是/,表示一个叶子,固定的路径(以/结尾的URL可以匹配它的任何子路径,比如/images会匹配/images/cute-cat.jpg)
- 它采用最长匹配原则,如果有多个匹配,一定采用匹配路径最长的那个进行处理
- 如果没有找到任何匹配项,会返回404错误
7、更好的Router
htttps://github.com/julienschmidt/httprouter
8、性能分析工具
准备工作
- 安装graphviz
- brew install graphviz
- 将PATH
- Mac OS:在 .bash_profile 中修改路径
- 安装go-torch
- go get github.com/uber/go-torch
- 下载并复制 flamegraph.pl (https://github.com/brendangregg/FlameGraph)至$GOPATH/bin 路径下
- 将PATH
通过文件方式输出Profile
- 灵活性高,适用于特定代码段的分析
- 通过手动调用 runtime/pprof 的API
- API相关文档 https://studygolang.com/static/pkgdoc/pkg/runtime_pprof.htm
- go tool pprof [binary] [binary.prof]
9、性能调优
常见分析指标
- Wall Time
- CPU Time
- Block Time
- Memory Time
- GC times/time spent