diff --git a/README.md b/README.md index 5c31ada..4428e88 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ![](cover.png) -- 作者:柴树杉 (chai2010, chaishushan@gmail.com) +- 作者:柴树杉 (chai2010, chaishushan@gmail.com), 曹春晖 (cch123, https://github.com/cch123) - 网址:https://github.com/chai2010/advanced-go-programming-book ## 在线阅读 diff --git a/SUMMARY.md b/SUMMARY.md index c94b1e5..877d943 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -34,12 +34,9 @@ * [第四章 RPC和Protobuf](ch4-rpc/readme.md) * [4.1. RPC入门](ch4-rpc/ch4-01-rpc-intro.md) * [4.2. Protobuf](ch4-rpc/ch4-02-pb-intro.md) - * [4.3. 玩转RPC(TODO)](ch4-rpc/ch4-03-netrpc-hack.md) - * [4.4. GRPC入门(TODO)](ch4-rpc/ch4-04-grpc.md) - * [4.5. GRPC进阶(TODO)](ch4-rpc/ch4-05-grpc-hack.md) - * [4.6. Protobuf扩展语法和插件(TODO)](ch4-rpc/ch4-06-pb-option.md) - * [4.7. 其它RPC系统(TODO)](ch4-rpc/ch4-07-other-rpc.md) - * [4.8. 补充说明(TODO)](ch4-rpc/ch4-08-faq.md) + * [4.3. 玩转RPC](ch4-rpc/ch4-03-netrpc-hack.md) + * [4.4. GRPC入门](ch4-rpc/ch4-04-grpc.md) + * [4.5. 补充说明](ch4-rpc/ch4-05-faq.md) * [第五章 Go和Web](ch5-web/readme.md) * [5.1. Web开发简介](ch5-web/ch5-01-introduction.md) * [5.2. Router请求路由](ch5-web/ch5-02-router.md) diff --git a/appendix/appendix-c-author.md b/appendix/appendix-c-author.md index 76daed6..9eb5306 100644 --- a/appendix/appendix-c-author.md +++ b/appendix/appendix-c-author.md @@ -1,4 +1,4 @@ # 附录C:作者简介 - [柴树杉(网络ID:chai2010)](https://github.com/chai2010) 国内第一批Go语言爱好者,创建了最早的QQ讨论组和golang-china邮件列表,组织 [Go语言官方文档](https://github.com/golang-china) 和 [《Go语言圣经》](https://github.com/golang-china/gopl-zh) 的翻译工作,Go语言代码的贡献者。目前在[青云QingCloud](https://www.qingcloud.com/)从事开源的多云应用管理平台[OpenPitrix](https://github.com/openpitrix/openpitrix)开发工作。 -- [cch123](https://github.com/cch123): TODO +- [曹春晖(网络ID:cch123)](https://github.com/cch123) 在 web 领域工作多年,开源爱好者。对大型网站系统的架构和相关工具的实现很感兴趣,并且有一些研究成果。目前在滴滴平台技术部工作。 diff --git a/ch1-basic/ch1-03-array-string-and-slice.md b/ch1-basic/ch1-03-array-string-and-slice.md index 1742dfe..cbf5760 100644 --- a/ch1-basic/ch1-03-array-string-and-slice.md +++ b/ch1-basic/ch1-03-array-string-and-slice.md @@ -177,7 +177,7 @@ s1 := "hello, world"[:5] s2 := "hello, world"[7:] ``` -和数组一样,内置的`len`和`cap`函数返回相同的结果,都对应字符串的长度。也可以通过`reflect.StringHeader`结构访问字符串的长度(这里只是为了演示字符串的结构,并不是推荐的做法): +字符串和数组类似,内置的`len`函数返回字符串的长度。也可以通过`reflect.StringHeader`结构访问字符串的长度(这里只是为了演示字符串的结构,并不是推荐的做法): ```go fmt.Println("len(s):", (*reflect.StringHeader)(unsafe.Pointer(&s)).Len) // 12 diff --git a/ch1-basic/ch1-05-mem.md b/ch1-basic/ch1-05-mem.md index 8f55148..3356c03 100644 --- a/ch1-basic/ch1-05-mem.md +++ b/ch1-basic/ch1-05-mem.md @@ -1,394 +1,394 @@ -# 1.5. 面向并发的内存模型 - -在早期,CPU都是以单核的形式顺序执行机器指令。Go语言的祖先C语言正是这种顺序编程语言的代表。顺序编程语言中的顺序是指:所有的指令都是以串行的方式执行,在相同的时刻有且仅有一个CPU在顺序执行程序的指令。 - -随着处理器技术的发展,单核时代以提升处理器频率来提高运行效率的方式遇到了瓶颈,目前各种主流的CPU频率基本被锁定在了3GHZ附近。单核CPU的发展的停滞,给多核CPU的发展带来了机遇。相应地,编程语言也开始逐步向并行化的方向发展。Go语言正是在多核和网络化的时代背景下诞生的原生支持并发的编程语言。 - -常见的并行编程有多种模型,主要有多线程、消息传递等。从理论上来看,多线程和基于消息的并发编程是等价的。由于多线程并发模型可以自然对应到多核的处理器,主流的操作系统因此也都提供了系统级的多线程支持,同时从概念上讲多线程似乎也更直观,因此多线程编程模型逐步被吸纳到主流的编程语言特性或语言扩展库中。而主流编程语言对基于消息的并发编程模型支持则相比较少,Erlang语言是支持基于消息传递并发编程模型的代表者,它的并发体之间不共享内存。Go语言是基于消息并发模型的集大成者,它将基于CSP模型的并发编程内置到了语言中,通过一个go关键字就可以轻易地启动一个Goroutine,与Erlang不同的是Go语言的Goroutine之间是共享内存的。 - -## Goroutine和系统线程 - -Goroutine是Go语言特有的并发体,是一种轻量级的线程,由go关键字启动。在真实的Go语言的实现中,goroutine和系统线程也不是等价的。尽管两者的区别实际上只是一个量的区别,但正是这个量变引发了Go语言并发编程质的飞跃。 - -首先,每个系统级线程都会有一个固定大小的栈(一般默认可能是2MB),这个栈主要用来保存函数递归调用时参数和局部变量。固定了栈的大小这导致了两个问题:一是对于很多只需要很小的栈空间的线程来说是一个巨大的浪费,二是对于少数需要巨大栈空间的线程来说又面临栈溢出的风险。针对这两个问题的解决方案是:要么降低固定的栈大小,提升空间的利用率;要么增大栈的深度以允许更深的函数递归调用,但这两者是没法同时兼得的。相反,一个Goroutine会以一个很小的栈启动(可能是2KB或4KB),当遇到深度递归导致当前栈空间不足时,Goroutine会根据需要动态地伸缩栈的大小(主流实现中栈的最大值可达到1GB)。因为启动的代价很小,所以我们可以轻易地启动成千上万个Goroutine。 - -Go的运行时还包含了其自己的调度器,这个调度器使用了一些技术手段,可以在n个操作系统线程上多工调度m个Goroutine。Go调度器的工作和内核的调度是相似的,但是这个调度器只关注单独的Go程序中的Goroutine。Goroutine采用的是半抢占式的协作调度,只有在当前Goroutine发生阻塞时才会导致调度;同时发生在用户态,调度器会根据具体函数只保存必要的寄存器,切换的代价要比系统线程低得多。运行时有一个`runtime.GOMAXPROCS`变量,用于控制当前运行正常非阻塞Goroutine的系统线程数目。 - -在Go语言中启动一个Goroutine不仅和调用函数一样简单,而且Goroutine之间调度代价也很低,这些因素极大地促进了并发编程的流行和发展。 - -## 原子操作 - -所谓的原子操作就是并发编程中“最小的且不可并行化”的操作。通常,有多个并发体对一个共享资源的操作是原子操作的话,同一时刻最多只能有一个并发体对该资源进行操作。从线程角度看,在当前线程修改共享资源期间,其它的线程是不能访问该资源的。原子操作对于多线程并发编程模型来说,不会发生有别于单线程的意外情况,共享资源的完整性可以得到保证。 - -一般情况下,原子操作都是通过“互斥”访问来保证访问的,通常由特殊的CPU指令提供保护。当然,如果仅仅是想模拟下粗粒度的原子操作,我们可以借助于`sync.Mutex`来实现: - -```go -import ( - "sync" -) - -var total struct { - sync.Mutex - value int -} - -func worker(wg *sync.WaitGroup) { - defer wg.Done() - - for i := 0; i <= 100; i++ { - total.Lock() - total.value += i - total.Unlock() - } -} - -func main() { - var wg sync.WaitGroup - wg.Add(2) - go worker(&wg) - go worker(&wg) - wg.Wait() - - fmt.Println(total.value) -} -``` - -在`worker`的循环中,为了保证`total.value += i`的原子性,我们通过`sync.Mutex`加锁和解锁来保证该语句在同一时刻只被一个线程访问。对于多线程模型的程序而言,进出临界区前后进行加锁和解锁都是必须的。如果没有锁的保护,`total`的最终值将由于多线程之间的竞争而可能会不正确。 - -用互斥锁来保护一个数值型的共享资源,麻烦且效率低下。标准库的`sync/atomic`包对原子操作提供了丰富的支持。我们可以重新实现上面的例子: - -```go -import ( - "sync" - "sync/atomic" -) - -var total uint64 - -func worker(wg *sync.WaitGroup) { - defer wg.Done() - - var i uint64 - for i = 0; i <= 100; i++ { - atomic.AddUint64(&total, i) - } -} - -func main() { - var wg sync.WaitGroup - wg.Add(2) - - go worker(&wg) - go worker(&wg) - wg.Wait() -} -``` - -`atomic.AddUint64`函数调用保证了`total`的读取、更新和保存是一个原子操作,因此在多线程中访问也是安全的。 - -原子操作配合互斥锁可以实现非常高效的单件模式。互斥锁的代价比普通整数的原子读写高很多,在性能敏感的地方可以增加一个数字型的标志位,通过原子检测标志位状态降低互斥锁的使用次数来提高性能。 - -```go -type singleton struct {} - -var ( - instance *singleton - initialized uint32 - mu sync.Mutex -) - -func Instance() *singleton { - if atomic.LoadUint32(&initialized) == 1 { - return instance - } - - mu.Lock() - defer mu.Unlock() - - if instance == nil { - defer atomic.StoreUint32(&initialized, 1) - instance = &singleton{} - } - return instance -} -``` - -我们可以将通用的代码提取出来,就成了标准库中`sync.Once`的实现: - -```go -type Once struct { - m Mutex - done uint32 -} - -func (o *Once) Do(f func()) { - if atomic.LoadUint32(&o.done) == 1 { - return - } - - o.m.Lock() - defer o.m.Unlock() - - if o.done == 0 { - defer atomic.StoreUint32(&o.done, 1) - f() - } -} -``` - -基于`sync.Once`重新实现单件模式: - -```go -var ( - instance *singleton - once sync.Once -) - -func Instance() *singleton { - once.Do(func() { - instance = &singleton{} - }) - return instance -} -``` - -`sync/atomic`包对基本的数值类型及复杂对象的读写都提供了原子操作的支持。`atomic.Value`原子对象提供了`Load`和`Store`两个原子方法,分别用于加载和保存数据,返回值和参数都是`interface{}`类型,因此可以用于任意的自定义复杂类型。 - -```go -var config atomic.Value // 保存当前配置信息 - -// 初始化配置信息 -config.Store(loadConfig()) - -// 启动一个后台线程, 加载更新后的配置信息 -go func() { - for { - time.Sleep(time.Second) - config.Store(loadConfig()) - } -}() - -// 用于处理请求的工作者线程始终采用最新的配置信息 -for i := 0; i < 10; i++ { - go func() { - for r := range requests() { - c := config.Load() - // ... - } - }() -} -``` - -这是一个简化的生产者、消费者模型:后台线程生成最新的配置信息;前台多个工作者线程获取最新的配置信息。所有线程共享配置信息资源。 - -## 顺序一致性内存模型 - -如果只是想简单地在线程之间进行数据同步的话,原子操作已经为编程人员提供了一些同步保障。不过这种保障有一个前提:顺序一致性的内存模型。要了解顺序一致性,我们先看看一个简单的例子: - -```go -var a string -var done bool - -func setup() { - a = "hello, world" - done = true -} - -func main() { - go setup() - for !done {} - print(a) -} -``` - -我们创建了`setup`线程,用于对字符串`a`的初始化工作,初始化完成之后设置`done`标志为`true`。`main`函数所在的主线程中,通过`for !done {}`检测`done`变为`true`时,认为字符串初始化工作完成,然后进行字符串的打印工作。 - -但是Go语言并不保证在`main`函数中观测到的对`done`的写入操作发生在对字符串`a`的写入的操作之后,因此程序很可能打印一个空字符串。更糟糕的是,因为两个线程之间没有同步事件,`setup`线程对`done`的写入操作甚至无法被`main`线程看到,`main`函数有可能陷入死循环中。 - -在Go语言中,同一个Goroutine线程内部,顺序一致性内存模型是得到保证的。但是不同的Goroutine之间,并不满足顺序一致性内存模型,需要通过明确定义的同步事件来作为同步的参考。如果两个事件不可排序,那么就说这两个事件是并发的。为了最大化并行,Go语言的编译器和处理器在不影响上述规定的前提下可能会对执行语句重新排序(CPU也会对一些指令进行乱序执行)。 - -因此,如果在一个Goroutine中顺序执行`a = 1; b = 2;`两个语句,虽然在当前的Goroutine中可以认为`a = 1;`语句先于`b = 2;`语句执行,但是在另一个Goroutine中`b = 2;`语句可能会先于`a = 1;`语句执行,甚至在另一个Goroutine中无法看到它们的变化(可能始终在寄存器中)。也就是说在另一个Goroutine看来, `a = 1; b = 2;`两个语句的执行顺序是不确定的。如果一个并发程序无法确定事件的偏序关系,那么程序的运行结果往往会有不确定的结果。比如下面这个程序: - - -```go -func main() { - go println("你好, 世界") -} -``` - -根据Go语言规范,`main`函数退出时程序结束,不会等待任何后台线程。因为Goroutine的执行和`main`函数的返回事件是并发的,谁都有可能先发生,所以什么时候打印,能否打印都是未知的。 - -用前面的原子操作并不能解决问题,因为我们无法确定两个原子操作之间的顺序。解决问题的办法就是通过同步原语来给两个事件明确排序: - -```go -func main() { - done := make(chan int) - - go func(){ - println("你好, 世界") - done <- 1 - }() - - <-done -} -``` - -当`<-done`执行时,必然要求`done <- 1`也已经执行。根据同一个Gorouine依然满足顺序一致性规则,我们可以判断当`done <- 1`执行时,`println("你好, 世界")`语句必然已经执行完成了。因此,现在的程序确保可以正常打印结果。 - -当然,通过`sync.Mutex`互斥量也是可以实现同步的: - -```go -func main() { - var mu sync.Mutex - - mu.Lock() - go func(){ - println("你好, 世界") - mu.Unock() - }() - - mu.Lock() -} -``` - -可以确定后台线程的`mu.Unock()`必然在`println("你好, 世界")`完成后发生(同一个线程满足顺序一致性),`main`函数的第二个`mu.Lock()`必然在后台线程的`mu.Unock()`之后发生(`sync.Mutex`保证),此时后台线程的打印工作已经顺利完成了。 - -## 初始化顺序 - -前面函数章节中我们已经简单介绍过程序的初始化顺序,这是属于Go语言面向并发的内存模型的基础规范。 - -Go程序的初始化和执行总是从`main.main`函数开始的。但是如果`main`包里导入了其它的包,则会按照顺序将它们包含进`main`包里(这里的导入顺序依赖具体实现,一般可能是以文件名或包路径名的字符串顺序导入)。如果某个包被多次导入的话,在执行的时候只会导入一次。当一个包被导入时,如果它还导入了其它的包,则先将其它的包包含进来,然后创建和初始化这个包的常量和变量。然后就是调用包里的`init`函数,如果一个包有多个`init`函数的话,实现可能是以文件名的顺序调用,同一个文件内的多个`init`则是以出现的顺序依次调用(`init`不是普通函数,可以定义有多个,所以不能被其它函数调用)。最终,在`main`包的所有包常量、包变量被创建和初始化,并且`init`函数被执行后,才会进入`main.main`函数,程序开始正常执行。下图是Go程序函数启动顺序的示意图: - -![](../images/ch1-04-init.png) - -要注意的是,在`main.main`函数执行之前所有代码都运行在同一个goroutine中,也是运行在程序的主系统线程中。如果某个`init`函数内部用go关键字启动了新的goroutine的话,新的goroutine只有在进入`main.main`函数之后才可能被执行到。 - -因为所有的`init`函数和`main`函数都是在主线程完成,它们也是满足顺序一致性模型的。 - -## Goroutine的创建 - -`go`语句会在当前Goroutine对应函数返回前创建新的Goroutine. 例如: - - -```go -var a string - -func f() { - print(a) -} - -func hello() { - a = "hello, world" - go f() -} -``` - -执行`go f()`语句创建Goroutine和`hello`函数是在同一个Goroutine中执行, 根据语句的书写顺序可以确定Goroutine的创建发生在`hello`函数返回之前, 但是新创建Goroutine对应的`f()`的执行事件和`hello`函数返回的事件则是不可排序的,也就是并发的。调用`hello`可能会在将来的某一时刻打印`"hello, world"`,也很可能是在`hello`函数执行完成后才打印。 - -## 基于Channel的通信 - -Channel通信是在Goroutine之间进行同步的主要方法。在无缓存的Channel上的每一次发送操作都有与其对应的接收操作相配对,发送和接收操作通常发生在不同的Goroutine上(在同一个Goroutine上执行2个操作很容易导致死锁)。**无缓存的Channel上的发送操作总在对应的接收操作完成前发生.** - - -```go -var done = make(chan bool) -var msg string - -func aGoroutine() { - msg = "你好, 世界" - done <- true -} - -func main() { - go aGoroutine() - <-done - println(msg) -} -``` - -可保证打印出“hello, world”。该程序首先对`msg`进行写入,然后在`done`管道上发送同步信号,随后从`done`接收对应的同步信号,最后执行`println`函数。 - -若在关闭信道后继续从中接收数据,接收者就会收到该信道返回的零值。因此在这个例子中,用`close(c)`关闭管道代替`done <- false`依然能保证该程序产生相同的行为。 - -```go -var done = make(chan bool) -var msg string - -func aGoroutine() { - msg = "你好, 世界" - close(done) -} - -func main() { - go aGoroutine() - <-done - println(msg) -} -``` - -**对于从无缓冲信道进行的接收,发生在对该信道进行的发送完成之前。** - -基于上面这个规则可知,交换两个Goroutine中的接收和发送操作也是可以的(但是很危险): - -```go -var done = make(chan bool) -var msg string - -func aGoroutine() { - msg = "hello, world" - <-done -} -func main() { - go aGoroutine() - done <- true - println(msg) -} -``` - -也可保证打印出“hello, world”。因为`main`线程中`done <- true`发送完成前,后台线程`<-done`接收已经开始,这保证`msg = "hello, world"`被执行了,所以之后`println(msg)`的msg已经被赋值过了。简而言之,后台线程首先对`msg`进行写入,然后从`done`中接收信号,随后`main`线程向`done`发送对应的信号,最后执行`println`函数完成。但是,若该信道为带缓冲的(例如,`done = make(chan bool, 1)`),`main`线程的`done <- true`接收操作将不会被后台线程的`<-done`接收操作阻塞,该程序将无法保证打印出“hello, world”。 - -对于带缓冲的Channel,**对于Channel的第`K`个接收完成操作发生在第`K+C`个发送操作完成之前,其中`C`是Channel的缓存大小。** 如果将`C`设置为0自然就对应无缓存的Channel,也即使第K个接收完成在第K个发送完成之前。因为无缓存的Channel只能同步发1个,也就简化为前面无缓存Channel的规则:**对于从无缓冲信道进行的接收,发生在对该信道进行的发送完成之前。** - -我们可以根据控制Channel的缓存大小来控制并发执行的Goroutine的最大数目, 例如: - -```go -var limit = make(chan int, 3) - -func main() { - for _, w := range work { - go func() { - limit <- 1 - w() - <-limit - }() - } - select{} -} -``` - -最后一句`select{}`是一个空的管道选择语句,该语句会导致`main`线程阻塞,从而避免程序过早退出。还有`for{}`、`<-make(chan int)`等诸多方法可以达到类似的效果。因为`main`线程被阻塞了,如果需要程序正常退出的话可以通过调用`os.Exit(0)`实现。 - -## 不靠谱的同步 - -前面我们已经分析过,下面代码无法保证正常打印结果。实际的运行效果也是大概率不能正常输出结果。 - -```go -func main() { - go println("你好, 世界") -} -``` - -刚接触Go语言的话,可能希望通过加入一个随机的休眠时间来保证正常的输出: - -```go -func main() { - go println("hello, world") - time.Sleep(time.Second) -} -``` - -因为主线程休眠了1秒钟,因此这个程序大概率是可以正常输出结果的。因此,很多人会觉得这个程序已经没有问题了。但是这个程序是不稳健的,依然有失败的可能性。我们先假设程序是可以稳定输出结果的。因为Go线程的启动是非阻塞的,`main`线程显式休眠了1秒钟退出导致程序结束,我们可以近似地认为程序总共执行了1秒多时间。现在假设`println`函数内部实现休眠的时间大于`main`线程休眠的时间的话,就会导致矛盾:后台线程既然先于`main`线程完成打印,那么执行时间肯定是小于`main`线程执行时间的。当然这是不可能的。 - -严谨的并发程序的正确性不应该是依赖于CPU的执行速度和休眠时间等不靠谱的因素的。严谨的并发也应该是可以静态推导出结果的:根据线程内顺序一致性,结合Channel或`sync`同步事件的可排序性来推导,最终完成各个线程各段代码的偏序关系排序。如果两个事件无法根据此规则来排序,那么它们就是并发的,也就是执行先后顺序不可靠的。 - -解决同步问题的思路是相同的:使用显式的同步。 +# 1.5. 面向并发的内存模型 + +在早期,CPU都是以单核的形式顺序执行机器指令。Go语言的祖先C语言正是这种顺序编程语言的代表。顺序编程语言中的顺序是指:所有的指令都是以串行的方式执行,在相同的时刻有且仅有一个CPU在顺序执行程序的指令。 + +随着处理器技术的发展,单核时代以提升处理器频率来提高运行效率的方式遇到了瓶颈,目前各种主流的CPU频率基本被锁定在了3GHZ附近。单核CPU的发展的停滞,给多核CPU的发展带来了机遇。相应地,编程语言也开始逐步向并行化的方向发展。Go语言正是在多核和网络化的时代背景下诞生的原生支持并发的编程语言。 + +常见的并行编程有多种模型,主要有多线程、消息传递等。从理论上来看,多线程和基于消息的并发编程是等价的。由于多线程并发模型可以自然对应到多核的处理器,主流的操作系统因此也都提供了系统级的多线程支持,同时从概念上讲多线程似乎也更直观,因此多线程编程模型逐步被吸纳到主流的编程语言特性或语言扩展库中。而主流编程语言对基于消息的并发编程模型支持则相比较少,Erlang语言是支持基于消息传递并发编程模型的代表者,它的并发体之间不共享内存。Go语言是基于消息并发模型的集大成者,它将基于CSP模型的并发编程内置到了语言中,通过一个go关键字就可以轻易地启动一个Goroutine,与Erlang不同的是Go语言的Goroutine之间是共享内存的。 + +## Goroutine和系统线程 + +Goroutine是Go语言特有的并发体,是一种轻量级的线程,由go关键字启动。在真实的Go语言的实现中,goroutine和系统线程也不是等价的。尽管两者的区别实际上只是一个量的区别,但正是这个量变引发了Go语言并发编程质的飞跃。 + +首先,每个系统级线程都会有一个固定大小的栈(一般默认可能是2MB),这个栈主要用来保存函数递归调用时参数和局部变量。固定了栈的大小这导致了两个问题:一是对于很多只需要很小的栈空间的线程来说是一个巨大的浪费,二是对于少数需要巨大栈空间的线程来说又面临栈溢出的风险。针对这两个问题的解决方案是:要么降低固定的栈大小,提升空间的利用率;要么增大栈的深度以允许更深的函数递归调用,但这两者是没法同时兼得的。相反,一个Goroutine会以一个很小的栈启动(可能是2KB或4KB),当遇到深度递归导致当前栈空间不足时,Goroutine会根据需要动态地伸缩栈的大小(主流实现中栈的最大值可达到1GB)。因为启动的代价很小,所以我们可以轻易地启动成千上万个Goroutine。 + +Go的运行时还包含了其自己的调度器,这个调度器使用了一些技术手段,可以在n个操作系统线程上多工调度m个Goroutine。Go调度器的工作和内核的调度是相似的,但是这个调度器只关注单独的Go程序中的Goroutine。Goroutine采用的是半抢占式的协作调度,只有在当前Goroutine发生阻塞时才会导致调度;同时发生在用户态,调度器会根据具体函数只保存必要的寄存器,切换的代价要比系统线程低得多。运行时有一个`runtime.GOMAXPROCS`变量,用于控制当前运行正常非阻塞Goroutine的系统线程数目。 + +在Go语言中启动一个Goroutine不仅和调用函数一样简单,而且Goroutine之间调度代价也很低,这些因素极大地促进了并发编程的流行和发展。 + +## 原子操作 + +所谓的原子操作就是并发编程中“最小的且不可并行化”的操作。通常,有多个并发体对一个共享资源的操作是原子操作的话,同一时刻最多只能有一个并发体对该资源进行操作。从线程角度看,在当前线程修改共享资源期间,其它的线程是不能访问该资源的。原子操作对于多线程并发编程模型来说,不会发生有别于单线程的意外情况,共享资源的完整性可以得到保证。 + +一般情况下,原子操作都是通过“互斥”访问来保证访问的,通常由特殊的CPU指令提供保护。当然,如果仅仅是想模拟下粗粒度的原子操作,我们可以借助于`sync.Mutex`来实现: + +```go +import ( + "sync" +) + +var total struct { + sync.Mutex + value int +} + +func worker(wg *sync.WaitGroup) { + defer wg.Done() + + for i := 0; i <= 100; i++ { + total.Lock() + total.value += i + total.Unlock() + } +} + +func main() { + var wg sync.WaitGroup + wg.Add(2) + go worker(&wg) + go worker(&wg) + wg.Wait() + + fmt.Println(total.value) +} +``` + +在`worker`的循环中,为了保证`total.value += i`的原子性,我们通过`sync.Mutex`加锁和解锁来保证该语句在同一时刻只被一个线程访问。对于多线程模型的程序而言,进出临界区前后进行加锁和解锁都是必须的。如果没有锁的保护,`total`的最终值将由于多线程之间的竞争而可能会不正确。 + +用互斥锁来保护一个数值型的共享资源,麻烦且效率低下。标准库的`sync/atomic`包对原子操作提供了丰富的支持。我们可以重新实现上面的例子: + +```go +import ( + "sync" + "sync/atomic" +) + +var total uint64 + +func worker(wg *sync.WaitGroup) { + defer wg.Done() + + var i uint64 + for i = 0; i <= 100; i++ { + atomic.AddUint64(&total, i) + } +} + +func main() { + var wg sync.WaitGroup + wg.Add(2) + + go worker(&wg) + go worker(&wg) + wg.Wait() +} +``` + +`atomic.AddUint64`函数调用保证了`total`的读取、更新和保存是一个原子操作,因此在多线程中访问也是安全的。 + +原子操作配合互斥锁可以实现非常高效的单件模式。互斥锁的代价比普通整数的原子读写高很多,在性能敏感的地方可以增加一个数字型的标志位,通过原子检测标志位状态降低互斥锁的使用次数来提高性能。 + +```go +type singleton struct {} + +var ( + instance *singleton + initialized uint32 + mu sync.Mutex +) + +func Instance() *singleton { + if atomic.LoadUint32(&initialized) == 1 { + return instance + } + + mu.Lock() + defer mu.Unlock() + + if instance == nil { + defer atomic.StoreUint32(&initialized, 1) + instance = &singleton{} + } + return instance +} +``` + +我们可以将通用的代码提取出来,就成了标准库中`sync.Once`的实现: + +```go +type Once struct { + m Mutex + done uint32 +} + +func (o *Once) Do(f func()) { + if atomic.LoadUint32(&o.done) == 1 { + return + } + + o.m.Lock() + defer o.m.Unlock() + + if o.done == 0 { + defer atomic.StoreUint32(&o.done, 1) + f() + } +} +``` + +基于`sync.Once`重新实现单件模式: + +```go +var ( + instance *singleton + once sync.Once +) + +func Instance() *singleton { + once.Do(func() { + instance = &singleton{} + }) + return instance +} +``` + +`sync/atomic`包对基本的数值类型及复杂对象的读写都提供了原子操作的支持。`atomic.Value`原子对象提供了`Load`和`Store`两个原子方法,分别用于加载和保存数据,返回值和参数都是`interface{}`类型,因此可以用于任意的自定义复杂类型。 + +```go +var config atomic.Value // 保存当前配置信息 + +// 初始化配置信息 +config.Store(loadConfig()) + +// 启动一个后台线程, 加载更新后的配置信息 +go func() { + for { + time.Sleep(time.Second) + config.Store(loadConfig()) + } +}() + +// 用于处理请求的工作者线程始终采用最新的配置信息 +for i := 0; i < 10; i++ { + go func() { + for r := range requests() { + c := config.Load() + // ... + } + }() +} +``` + +这是一个简化的生产者、消费者模型:后台线程生成最新的配置信息;前台多个工作者线程获取最新的配置信息。所有线程共享配置信息资源。 + +## 顺序一致性内存模型 + +如果只是想简单地在线程之间进行数据同步的话,原子操作已经为编程人员提供了一些同步保障。不过这种保障有一个前提:顺序一致性的内存模型。要了解顺序一致性,我们先看看一个简单的例子: + +```go +var a string +var done bool + +func setup() { + a = "hello, world" + done = true +} + +func main() { + go setup() + for !done {} + print(a) +} +``` + +我们创建了`setup`线程,用于对字符串`a`的初始化工作,初始化完成之后设置`done`标志为`true`。`main`函数所在的主线程中,通过`for !done {}`检测`done`变为`true`时,认为字符串初始化工作完成,然后进行字符串的打印工作。 + +但是Go语言并不保证在`main`函数中观测到的对`done`的写入操作发生在对字符串`a`的写入的操作之后,因此程序很可能打印一个空字符串。更糟糕的是,因为两个线程之间没有同步事件,`setup`线程对`done`的写入操作甚至无法被`main`线程看到,`main`函数有可能陷入死循环中。 + +在Go语言中,同一个Goroutine线程内部,顺序一致性内存模型是得到保证的。但是不同的Goroutine之间,并不满足顺序一致性内存模型,需要通过明确定义的同步事件来作为同步的参考。如果两个事件不可排序,那么就说这两个事件是并发的。为了最大化并行,Go语言的编译器和处理器在不影响上述规定的前提下可能会对执行语句重新排序(CPU也会对一些指令进行乱序执行)。 + +因此,如果在一个Goroutine中顺序执行`a = 1; b = 2;`两个语句,虽然在当前的Goroutine中可以认为`a = 1;`语句先于`b = 2;`语句执行,但是在另一个Goroutine中`b = 2;`语句可能会先于`a = 1;`语句执行,甚至在另一个Goroutine中无法看到它们的变化(可能始终在寄存器中)。也就是说在另一个Goroutine看来, `a = 1; b = 2;`两个语句的执行顺序是不确定的。如果一个并发程序无法确定事件的偏序关系,那么程序的运行结果往往会有不确定的结果。比如下面这个程序: + + +```go +func main() { + go println("你好, 世界") +} +``` + +根据Go语言规范,`main`函数退出时程序结束,不会等待任何后台线程。因为Goroutine的执行和`main`函数的返回事件是并发的,谁都有可能先发生,所以什么时候打印,能否打印都是未知的。 + +用前面的原子操作并不能解决问题,因为我们无法确定两个原子操作之间的顺序。解决问题的办法就是通过同步原语来给两个事件明确排序: + +```go +func main() { + done := make(chan int) + + go func(){ + println("你好, 世界") + done <- 1 + }() + + <-done +} +``` + +当`<-done`执行时,必然要求`done <- 1`也已经执行。根据同一个Gorouine依然满足顺序一致性规则,我们可以判断当`done <- 1`执行时,`println("你好, 世界")`语句必然已经执行完成了。因此,现在的程序确保可以正常打印结果。 + +当然,通过`sync.Mutex`互斥量也是可以实现同步的: + +```go +func main() { + var mu sync.Mutex + + mu.Lock() + go func(){ + println("你好, 世界") + mu.Unlock() + }() + + mu.Lock() +} +``` + +可以确定后台线程的`mu.Unlock()`必然在`println("你好, 世界")`完成后发生(同一个线程满足顺序一致性),`main`函数的第二个`mu.Lock()`必然在后台线程的`mu.Unlock()`之后发生(`sync.Mutex`保证),此时后台线程的打印工作已经顺利完成了。 + +## 初始化顺序 + +前面函数章节中我们已经简单介绍过程序的初始化顺序,这是属于Go语言面向并发的内存模型的基础规范。 + +Go程序的初始化和执行总是从`main.main`函数开始的。但是如果`main`包里导入了其它的包,则会按照顺序将它们包含进`main`包里(这里的导入顺序依赖具体实现,一般可能是以文件名或包路径名的字符串顺序导入)。如果某个包被多次导入的话,在执行的时候只会导入一次。当一个包被导入时,如果它还导入了其它的包,则先将其它的包包含进来,然后创建和初始化这个包的常量和变量。然后就是调用包里的`init`函数,如果一个包有多个`init`函数的话,实现可能是以文件名的顺序调用,同一个文件内的多个`init`则是以出现的顺序依次调用(`init`不是普通函数,可以定义有多个,所以不能被其它函数调用)。最终,在`main`包的所有包常量、包变量被创建和初始化,并且`init`函数被执行后,才会进入`main.main`函数,程序开始正常执行。下图是Go程序函数启动顺序的示意图: + +![](../images/ch1-04-init.png) + +要注意的是,在`main.main`函数执行之前所有代码都运行在同一个goroutine中,也是运行在程序的主系统线程中。如果某个`init`函数内部用go关键字启动了新的goroutine的话,新的goroutine只有在进入`main.main`函数之后才可能被执行到。 + +因为所有的`init`函数和`main`函数都是在主线程完成,它们也是满足顺序一致性模型的。 + +## Goroutine的创建 + +`go`语句会在当前Goroutine对应函数返回前创建新的Goroutine. 例如: + + +```go +var a string + +func f() { + print(a) +} + +func hello() { + a = "hello, world" + go f() +} +``` + +执行`go f()`语句创建Goroutine和`hello`函数是在同一个Goroutine中执行, 根据语句的书写顺序可以确定Goroutine的创建发生在`hello`函数返回之前, 但是新创建Goroutine对应的`f()`的执行事件和`hello`函数返回的事件则是不可排序的,也就是并发的。调用`hello`可能会在将来的某一时刻打印`"hello, world"`,也很可能是在`hello`函数执行完成后才打印。 + +## 基于Channel的通信 + +Channel通信是在Goroutine之间进行同步的主要方法。在无缓存的Channel上的每一次发送操作都有与其对应的接收操作相配对,发送和接收操作通常发生在不同的Goroutine上(在同一个Goroutine上执行2个操作很容易导致死锁)。**无缓存的Channel上的发送操作总在对应的接收操作完成前发生.** + + +```go +var done = make(chan bool) +var msg string + +func aGoroutine() { + msg = "你好, 世界" + done <- true +} + +func main() { + go aGoroutine() + <-done + println(msg) +} +``` + +可保证打印出“hello, world”。该程序首先对`msg`进行写入,然后在`done`管道上发送同步信号,随后从`done`接收对应的同步信号,最后执行`println`函数。 + +若在关闭信道后继续从中接收数据,接收者就会收到该信道返回的零值。因此在这个例子中,用`close(c)`关闭管道代替`done <- false`依然能保证该程序产生相同的行为。 + +```go +var done = make(chan bool) +var msg string + +func aGoroutine() { + msg = "你好, 世界" + close(done) +} + +func main() { + go aGoroutine() + <-done + println(msg) +} +``` + +**对于从无缓冲信道进行的接收,发生在对该信道进行的发送完成之前。** + +基于上面这个规则可知,交换两个Goroutine中的接收和发送操作也是可以的(但是很危险): + +```go +var done = make(chan bool) +var msg string + +func aGoroutine() { + msg = "hello, world" + <-done +} +func main() { + go aGoroutine() + done <- true + println(msg) +} +``` + +也可保证打印出“hello, world”。因为`main`线程中`done <- true`发送完成前,后台线程`<-done`接收已经开始,这保证`msg = "hello, world"`被执行了,所以之后`println(msg)`的msg已经被赋值过了。简而言之,后台线程首先对`msg`进行写入,然后从`done`中接收信号,随后`main`线程向`done`发送对应的信号,最后执行`println`函数完成。但是,若该信道为带缓冲的(例如,`done = make(chan bool, 1)`),`main`线程的`done <- true`接收操作将不会被后台线程的`<-done`接收操作阻塞,该程序将无法保证打印出“hello, world”。 + +对于带缓冲的Channel,**对于Channel的第`K`个接收完成操作发生在第`K+C`个发送操作完成之前,其中`C`是Channel的缓存大小。** 如果将`C`设置为0自然就对应无缓存的Channel,也即使第K个接收完成在第K个发送完成之前。因为无缓存的Channel只能同步发1个,也就简化为前面无缓存Channel的规则:**对于从无缓冲信道进行的接收,发生在对该信道进行的发送完成之前。** + +我们可以根据控制Channel的缓存大小来控制并发执行的Goroutine的最大数目, 例如: + +```go +var limit = make(chan int, 3) + +func main() { + for _, w := range work { + go func() { + limit <- 1 + w() + <-limit + }() + } + select{} +} +``` + +最后一句`select{}`是一个空的管道选择语句,该语句会导致`main`线程阻塞,从而避免程序过早退出。还有`for{}`、`<-make(chan int)`等诸多方法可以达到类似的效果。因为`main`线程被阻塞了,如果需要程序正常退出的话可以通过调用`os.Exit(0)`实现。 + +## 不靠谱的同步 + +前面我们已经分析过,下面代码无法保证正常打印结果。实际的运行效果也是大概率不能正常输出结果。 + +```go +func main() { + go println("你好, 世界") +} +``` + +刚接触Go语言的话,可能希望通过加入一个随机的休眠时间来保证正常的输出: + +```go +func main() { + go println("hello, world") + time.Sleep(time.Second) +} +``` + +因为主线程休眠了1秒钟,因此这个程序大概率是可以正常输出结果的。因此,很多人会觉得这个程序已经没有问题了。但是这个程序是不稳健的,依然有失败的可能性。我们先假设程序是可以稳定输出结果的。因为Go线程的启动是非阻塞的,`main`线程显式休眠了1秒钟退出导致程序结束,我们可以近似地认为程序总共执行了1秒多时间。现在假设`println`函数内部实现休眠的时间大于`main`线程休眠的时间的话,就会导致矛盾:后台线程既然先于`main`线程完成打印,那么执行时间肯定是小于`main`线程执行时间的。当然这是不可能的。 + +严谨的并发程序的正确性不应该是依赖于CPU的执行速度和休眠时间等不靠谱的因素的。严谨的并发也应该是可以静态推导出结果的:根据线程内顺序一致性,结合Channel或`sync`同步事件的可排序性来推导,最终完成各个线程各段代码的偏序关系排序。如果两个事件无法根据此规则来排序,那么它们就是并发的,也就是执行先后顺序不可靠的。 + +解决同步问题的思路是相同的:使用显式的同步。 diff --git a/ch1-basic/ch1-07-error-and-panic.md b/ch1-basic/ch1-07-error-and-panic.md index 80ea6ff..060ee3d 100644 --- a/ch1-basic/ch1-07-error-and-panic.md +++ b/ch1-basic/ch1-07-error-and-panic.md @@ -66,7 +66,7 @@ func CopyFile(dstName, srcName string) (written int64, err error) { } ``` -上面的代码虽然能够工作,但是隐藏一个bug。如果第一个`os.Open`调用失败,那么会在没有释放`src`文件资源的情况下返回。虽然我们可以通过在第二个返回语句前添加`src.Close()`调用来修复这个BUG;但是当代码变得复杂时,类似的问题将很难被发现和修复。我们可以通过`defer`语句来确保每个被正常打开的文件都能被正常关闭: +上面的代码虽然能够工作,但是隐藏一个bug。如果第一个`os.Open`调用成功,但是第二个`os.Create`调用失败,那么会在没有释放`src`文件资源的情况下返回。虽然我们可以通过在第二个返回语句前添加`src.Close()`调用来修复这个BUG;但是当代码变得复杂时,类似的问题将很难被发现和修复。我们可以通过`defer`语句来确保每个被正常打开的文件都能被正常关闭: ```go func CopyFile(dstName, srcName string) (written int64, err error) { diff --git a/ch3-asm/ch3-01-basic.md b/ch3-asm/ch3-01-basic.md index 064971d..22b5389 100644 --- a/ch3-asm/ch3-01-basic.md +++ b/ch3-asm/ch3-01-basic.md @@ -152,7 +152,7 @@ DATA ·Name+8(SB)/8,$6 因为在Go汇编语言中,go.string."gopher"不是一个合法的符号,我们无法手工创建(这是给编译器保留的部分特权,因为手工创建类似符号可能打破编译器输出代码的某些规则)。因此我们新创建了一个·NameData符号表示底层的字符串数据。 -然后定义·Name符号为两个16字节,其中前8个字节用·NameData符号对应的地址初始化,后8个字节为常量6表示字符串长度。 +然后定义·Name符号内存大小为16字节,其中前8个字节用·NameData符号对应的地址初始化,后8个字节为常量6表示字符串长度。 通过以下代码测试输出Name变量: @@ -172,7 +172,7 @@ func main() { pkgpath.NameData: missing Go //type information for global symbol: size 8 ``` -提示汇编中定义的NameData符号没有类型信息。其实Go汇编语言中定义的数据并没有所谓的类型,每个符号只不过是对应一个内存而且。出现这种错误的原因是,Go语言的垃圾回收器在扫描NameData变量的时候,无法知晓该变量内部是否包含指针。因此,真正错误的原因并不是NameData没有类型,二是NameData变量没有标注是否会含有指针信息。 +提示汇编中定义的NameData符号没有类型信息。其实Go汇编语言中定义的数据并没有所谓的类型,每个符号只不过是对应一块内存而已。出现这种错误的原因是,Go语言的垃圾回收器在扫描NameData变量的时候,无法知晓该变量内部是否包含指针。因此,真正错误的原因并不是NameData没有类型,而是NameData变量没有标注是否会含有指针信息。 通过给NameData变量增加一个标志,表示其中不会包含指针数据可以修复该错误: @@ -193,9 +193,9 @@ var NameData [8]byte var Name string ``` -我们将NameData声明为长度为8的字节数组。因为编译器可以通过类型分析出该变量不会包含指针,因此汇编代码中可以NOPTR标志信息。 +我们将NameData声明为长度为8的字节数组。编译器可以通过类型分析出该变量不会包含指针,因此汇编代码中可以省略NOPTR标志。 -在这个实现中,Name字符串底层其实引用的是NameData内存对应的“gopher”字符串数据。因此,如果NameData发生变化的化,Name字符串的数据也会跟着变化的。 +在这个实现中,Name字符串底层其实引用的是NameData内存对应的“gopher”字符串数据。因此,如果NameData发生变化,Name字符串的数据也会跟着变化。 ```go func main() { @@ -218,7 +218,7 @@ DATA ·Name+8(SB)/8,$6 DATA ·Name+16(SB)/8,$"gopher" ``` -在新的结构中,Name符号对应的内存从16字节变为24字节,多出的8个字节用户存放底层的“gopher”字符串。·Name符号前16个字节依然对应reflect.StringHeader结构体:Data部分对应`$·Name+16(SB)`,表示数据的地址为Name符号往后偏移16个字节的位置;Len部分依然对应6个字节的长度。 +在新的结构中,Name符号对应的内存从16字节变为24字节,多出的8个字节存放底层的“gopher”字符串。·Name符号前16个字节依然对应reflect.StringHeader结构体:Data部分对应`$·Name+16(SB)`,表示数据的地址为Name符号往后偏移16个字节的位置;Len部分依然对应6个字节的长度。 ## 定义main函数 @@ -245,7 +245,7 @@ TEXT ·main(SB), $16-0 CALL runtime·printnl(SB) RET ``` -`TEXT ·main(SB), $16-0`用于定义`main`函数,其中`$16-0`表示`main`函数的帧大小是16个字节(对应string头的大小,用于给`runtime·printstring`函数传递参数),`0`表示`main`函数没有参数和返回值。`main`函数内部通过调用运行时内部的`runtime·printstring(SB)`函数来打印字符串。然后调用runtime·printnl打印换行符号。 +`TEXT ·main(SB), $16-0`用于定义`main`函数,其中`$16-0`表示`main`函数的帧大小是16个字节(对应string头部结构体的大小,用于给`runtime·printstring`函数传递参数),`0`表示`main`函数没有参数和返回值。`main`函数内部通过调用运行时内部的`runtime·printstring(SB)`函数来打印字符串。然后调用`runtime·printnl`打印换行符号。 Go语言函数在函数调用时,完全通过栈传递调用参数和返回值。先通过MOVQ指令,将helloworld对应的字符串头部结构体的16个字节复制到栈指针SP对应的16字节的空间,然后通过CALL指令调用对应函数。最后使用RET指令表示当前函数返回。 @@ -254,7 +254,7 @@ Go语言函数在函数调用时,完全通过栈传递调用参数和返回值 Go语言函数或方法符号在编译为目标文件后,目标文件中的每个符号均包含对应包的绝对导入路径。因此目标文件的符号可能非常复杂,比如“path/to/pkg.(*SomeType).SomeMethod”或“go.string."abc"”。目标文件的符号名中不仅仅包含普通的字母,还可能包含诸多特殊字符。而Go语言的汇编器是从plan9移植过来的二把刀,并不能处理这些特殊的字符,导致了用Go汇编语言手工实现Go诸多特性时遇到种种限制。 -Go汇编语言同样遵循Go语言少即是多的哲学,它只保留了最基本的特性:定义变量和全局函数。同时为了简化Go汇编器的词法扫描程序的实现,特别引入了Unicode中的中点`·`和大写的除法`/`,对应的Unicode码点为`U+00B7`和`U+2215`。汇编器编译后,中点`·`会被替换为ASCII中的点“.”,大写点除法会被替换为ASCII码中的除法“/”,比如`math/rand·Int`会被替换为`math/rand.Int`。这样可以将点和浮点数中的小数点、大写的除法和表达式中的除法符号分开,可以简化汇编程序此法分析部分的实现。 +Go汇编语言同样遵循Go语言少即是多的哲学,它只保留了最基本的特性:定义变量和全局函数。同时为了简化Go汇编器的词法扫描程序的实现,特别引入了Unicode中的中点`·`和大写的除法`/`,对应的Unicode码点为`U+00B7`和`U+2215`。汇编器编译后,中点`·`会被替换为ASCII中的点“.”,大写的除法会被替换为ASCII码中的除法“/”,比如`math/rand·Int`会被替换为`math/rand.Int`。这样可以将中点和浮点数中的小数点、大写的除法和表达式中的除法符号分开,可以简化汇编程序词法分析部分的实现。 即使暂时抛开Go汇编语言设计取舍的问题,中点`·`和除法`/`两个字符的如何输入就是一个挑战。这两个字符在 https://golang.org/doc/asm 文档中均有描述,因此直接从该页面复制是最简单可靠的方式。 diff --git a/ch3-asm/ch3-07-goroutine-id.md b/ch3-asm/ch3-07-goroutine-id.md index 70eaac5..1de517d 100644 --- a/ch3-asm/ch3-07-goroutine-id.md +++ b/ch3-asm/ch3-07-goroutine-id.md @@ -4,7 +4,7 @@ ## 故意设计没有goid -根据官方的相关资料显示,Go语言刻意没有提供goid的原因是为了避免被滥用。因为大部分用户在轻松拿到goid之后,在之后的编程中会不自觉地编写出强依赖goid的代码。强依赖goid将导致这些代码不好移植,同时也会导致并发模型复杂化。同时,Go语言中可能同时存在海量的Goroutine,但是每个Goroutine合适被销毁并不好实时监控,这也会导致依赖goid的资源无法很好地自动回收(需要手工回收)。如果你是Go汇编语言用户,完全可以忽略这些借口。 +根据官方的相关资料显示,Go语言刻意没有提供goid的原因是为了避免被滥用。因为大部分用户在轻松拿到goid之后,在之后的编程中会不自觉地编写出强依赖goid的代码。强依赖goid将导致这些代码不好移植,同时也会导致并发模型复杂化。同时,Go语言中可能同时存在海量的Goroutine,但是每个Goroutine何时被销毁并不好实时监控,这也会导致依赖goid的资源无法很好地自动回收(需要手工回收)。如果你是Go汇编语言用户,完全可以忽略这些借口。 ## 纯Go方式获取goid @@ -191,7 +191,7 @@ TEXT ·getg(SB), NOSPLIT, $32-16 RET ``` -其中AX寄存器对应g指针,BX寄存器对应g结构体的类型。然后通过runtime·convT2E函数将类型转为接口。因为我们使用的不是是g结构体指针类型,因此返回的接口表示的g结构体值类型。理论上我们也可以构造g指针类型的接口,但是因为Go汇编语言的限制,我们无法`type·*runtime·g`标识符。 +其中AX寄存器对应g指针,BX寄存器对应g结构体的类型。然后通过runtime·convT2E函数将类型转为接口。因为我们使用的不是g结构体指针类型,因此返回的接口表示的g结构体值类型。理论上我们也可以构造g指针类型的接口,但是因为Go汇编语言的限制,我们无法`type·*runtime·g`标识符。 基于g返回的接口,就可以容易获取goid了: @@ -264,7 +264,7 @@ TEXT ·getg(SB), NOSPLIT, $32-16 RET ``` -其中NO_LOCAL_POINTERS表示函数没有局部指针变量。同时对返回的接口进行零值初始化,初始化完成后通过GO_RESULTS_INITIALIZED告知GC。这样可以在保证栈分裂是,GC能够正确处理返回值和局部变量中的指针。 +其中NO_LOCAL_POINTERS表示函数没有局部指针变量。同时对返回的接口进行零值初始化,初始化完成后通过GO_RESULTS_INITIALIZED告知GC。这样可以在保证栈分裂时,GC能够正确处理返回值和局部变量中的指针。 ## goid的应用: 局部存储 @@ -316,7 +316,7 @@ func Delete(key interface{}) { } ``` -最后我们再提供一个Clean函数,用于是否Goroutine对应的map资源: +最后我们再提供一个Clean函数,用于释放Goroutine对应的map资源: ```go func Clean() { @@ -350,5 +350,5 @@ func main() { } ``` -通过Goroutine局部存储,不同层次函数之间可以共享存储资源。同时未来避免资源泄露,需要再Goroutine的根函数中,通过defer语句调用gls.Clean()函数释放资源。 +通过Goroutine局部存储,不同层次函数之间可以共享存储资源。同时为了避免资源泄漏,需要在Goroutine的根函数中,通过defer语句调用gls.Clean()函数释放资源。 diff --git a/ch3-asm/ch3-08-faq.md b/ch3-asm/ch3-08-faq.md index 3036cb2..9555120 100644 --- a/ch3-asm/ch3-08-faq.md +++ b/ch3-asm/ch3-08-faq.md @@ -1,3 +1,3 @@ # 3.8. 补充说明 -得益于Go语言的设计,Go汇编语言的优势也非常明显:跨操作系统、不同CPU之间的用法也非常相似、支持C语言预处理器、支持模块。同时Go汇编语言也存在很多不足:它不是一个独立的语言,底层需要依赖Go语言甚至操作系统;很多高级特性很难通过手工汇编完成。虽然Go语言官方尽量保持Go汇编语言简单,但是汇编语言是一个比较大的话题,大到足以写一本Go汇编语言的教程。本章的目的是让大家对Go汇编语言简单入门,在看到底层汇编代码的时候不会一头雾水,在某些遇到性能或禁制的场合能够通过Go汇编突破限制。这只是一个开始,后续版本会继续完善。 +得益于Go语言的设计,Go汇编语言的优势也非常明显:跨操作系统、不同CPU之间的用法也非常相似、支持C语言预处理器、支持模块。同时Go汇编语言也存在很多不足:它不是一个独立的语言,底层需要依赖Go语言甚至操作系统;很多高级特性很难通过手工汇编完成。虽然Go语言官方尽量保持Go汇编语言简单,但是汇编语言是一个比较大的话题,大到足以写一本Go汇编语言的教程。本章的目的是让大家对Go汇编语言简单入门,在看到底层汇编代码的时候不会一头雾水,在某些遇到性能受限制的场合能够通过Go汇编突破限制。这只是一个开始,后续版本会继续完善。 diff --git a/ch4-rpc/ch4-01-rpc-intro.md b/ch4-rpc/ch4-01-rpc-intro.md index 4ce242e..10432ba 100644 --- a/ch4-rpc/ch4-01-rpc-intro.md +++ b/ch4-rpc/ch4-01-rpc-intro.md @@ -82,7 +82,7 @@ func RegisterHelloService(svc HelloServiceInterface) error { } ``` -我们将RPC服务的接口规范分为三个部分:首选是服务的名字,然后是服务要实现的详细方法列表,最后是注册该类型服务的函数。为了避免名字冲突,我们在RPC服务的名字中增加了包路径前缀(这个是RPC服务抽象的包路径,并非完全等价Go语言的包路径)。RegisterHelloService注册服务时,编译器会要求传入的对象满足HelloServiceInterface接口。 +我们将RPC服务的接口规范分为三个部分:首先是服务的名字,然后是服务要实现的详细方法列表,最后是注册该类型服务的函数。为了避免名字冲突,我们在RPC服务的名字中增加了包路径前缀(这个是RPC服务抽象的包路径,并非完全等价Go语言的包路径)。RegisterHelloService注册服务时,编译器会要求传入的对象满足HelloServiceInterface接口。 在定义了RPC服务接口规范之后,客户端就可以根据规范编写RPC调用的代码了: @@ -101,7 +101,7 @@ func main() { } ``` -其中唯一的变化是client.Call的第一个参数用`HelloServiceName+".Hello"`代理了"HelloService.Hello"。然后通过client.Call函数调用RPC方法依然比较繁琐,同时参数的类型依然无法得到编译器提供的安全保障。 +其中唯一的变化是client.Call的第一个参数用`HelloServiceName+".Hello"代替了"HelloService.Hello"。然而通过client.Call函数调用RPC方法依然比较繁琐,同时参数的类型依然无法得到编译器提供的安全保障。 为了简化客户端用户调用RPC函数,我们在可以在接口规范部分增加对客户端的简单包装: @@ -125,7 +125,7 @@ func (p *HelloServiceClient) Hello(request string, reply *string) error { } ``` -我们在接口规范中针对客户端新增加了HelloServiceClient类型,改类型也必须满足HelloServiceInterface接口,这样客户端用户就可以直接通过接口对应的方法调用RPC函数。同时提供了一个DialHelloService方法,直接拨号HelloService服务。 +我们在接口规范中针对客户端新增加了HelloServiceClient类型,该类型也必须满足HelloServiceInterface接口,这样客户端用户就可以直接通过接口对应的方法调用RPC函数。同时提供了一个DialHelloService方法,直接拨号HelloService服务。 基于新的客户端接口,我们可以简化客户端用户的代码: @@ -175,14 +175,14 @@ func main() { } ``` -在新的RPC服务端实现中,我们用RegisterHelloService函数来注册函数,这样不仅可以避免服务名称的工作,同时也保证了传入的服务对象满足了RPC接口定义的定义。最后我们支持多个TCP链接,然后为每个TCP链接建立RPC服务。 +在新的RPC服务端实现中,我们用RegisterHelloService函数来注册函数,这样不仅可以避免命名服务名称的工作,同时也保证了传入的服务对象满足了RPC接口的定义。最后我们支持多个TCP链接,然后为每个TCP链接建立RPC服务。 ## 跨语言的RPC 标准库的RPC默认采用Go语言特有的gob规范编码,因此从其它语言调用Go语言实现的RPC服务将比较困难。在互联网的微服务时代,每个RPC以及服务的使用者都可能采用不同的编程语言,因此跨语言是互联网时代RPC的一个首要条件。得益于RPC的框架设计,Go语言的RPC其实也是很容易实现跨语言支持的。 -Go语言的RPC框架有两个比较有特色的设计:一个是RPC数据打包时可以通过插件实现自定义的编码和解码;另一个是RPC建立在抽象的io.ReadWriteCloser接口之上的,我们可以将RPC架设在不同的通讯协议之上。我们这里将尝试通过官方自带的net/rpc/jsonrpc扩展实现一个跨语言的PPC。 +Go语言的RPC框架有两个比较有特色的设计:一个是RPC数据打包时可以通过插件实现自定义的编码和解码;另一个是RPC建立在抽象的io.ReadWriteCloser接口之上的,我们可以将RPC架设在不同的通讯协议之上。这里我们将尝试通过官方自带的net/rpc/jsonrpc扩展实现一个跨语言的PPC。 首先是基于json实现RPC服务: @@ -237,9 +237,9 @@ func main() { {"method":"HelloService.Hello","params":["hello"],"id":0} ``` -这是一个json编码的数据,其中method部分对应要调用的rpc服务和方法组合成的名字,params部分的第一个元素为参数部分,id是由调用端维护的一个唯一的调用编号。 +这是一个json编码的数据,其中method部分对应要调用的rpc服务和方法组合成的名字,params部分的第一个元素为参数,id是由调用端维护的一个唯一的调用编号。 -请求的json数据对应在内部对应两个结构体:客户端是clientRequest,服务端是serverRequest。clientRequest和serverRequest结构体的内容基本是一致的: +请求的json数据对象在内部对应两个结构体:客户端是clientRequest,服务端是serverRequest。clientRequest和serverRequest结构体的内容基本是一致的: ```go type clientRequest struct { @@ -255,7 +255,7 @@ type serverRequest struct { } ``` -在获取到RPC调用对应的json数据后,我们可以通过直接向假设了RPC服务的TCP服务器发送json数据模拟RPC方法调用: +在获取到RPC调用对应的json数据后,我们可以通过直接向架设了RPC服务的TCP服务器发送json数据模拟RPC方法调用: ``` $ echo -e '{"method":"HelloService.Hello","params":["hello"],"id":1}' | nc localhost 1234 @@ -285,13 +285,13 @@ type serverResponse struct { } ``` -因此无论是采用任何语言,只要遵循同样的json结构,以同样的流程就可以和Go语言编写的RPC服务进行通信。这样我们就实现了跨语言的RPC。 +因此无论采用何种语言,只要遵循同样的json结构,以同样的流程就可以和Go语言编写的RPC服务进行通信。这样我们就实现了跨语言的RPC。 ## Http上的RPC Go语言内在的RPC框架已经支持在Http协议上提供RPC服务。但是框架的http服务同样采用了内置的gob协议,并且没有提供采用其它协议的接口,因此从其它语言依然无法访问的。在前面的例子中,我们已经实现了在纯的TCP协议之上运行jsonrpc服务,并且可以通过nc命令行工具成功实现了RPC方法调用。现在我们尝试在http协议上提供jsonrpc服务。 -心的RPC服务其实是一个类似REST规范的接口,采用请求和相应处理流程: +新的RPC服务其实是一个类似REST规范的接口,接收请求和采用相应处理流程: ```go func main() { @@ -315,7 +315,7 @@ func main() { RPC的服务假设在“/jsonrpc”路径,在处理函数中基于http.ResponseWriter和http.Request类型的参数构造一个io.ReadWriteCloser类型的conn通道。然后基于conn构建针对服务端的json编码解码器。最后通过rpc.ServeRequest处理一次RPC方法调用。 -模拟一次RPC调用的过程就是向该链接发生一个json字符串: +模拟一次RPC调用的过程就是向该链接发送一个json字符串: ``` $ curl localhost:1234/jsonrpc -X POST --data '{"method":"HelloService.Hello","params":["hello"],"id":0}' diff --git a/ch4-rpc/ch4-02-pb-intro.md b/ch4-rpc/ch4-02-pb-intro.md index 4993d1c..c815b67 100644 --- a/ch4-rpc/ch4-02-pb-intro.md +++ b/ch4-rpc/ch4-02-pb-intro.md @@ -1,10 +1,10 @@ # 4.2. Protobuf -Protobuf是Protocol Buffers的简称,它是Google公司开发的一种数据描述语言,并于2008年对外开源。Protobuf刚开源时的定位类似于XML、JSON等数据描述语言,通过附带工具生成等代码提实现将结构化数据序列化的功能。但是我们更关注的是Protobuf作为接口规范的描述语言,可以作为设计安全的跨语言PRC接口的基础工具。 +Protobuf是Protocol Buffers的简称,它是Google公司开发的一种数据描述语言,并于2008年对外开源。Protobuf刚开源时的定位类似于XML、JSON等数据描述语言,通过附带工具生成代码并实现将结构化数据序列化的功能。但是我们更关注的是Protobuf作为接口规范的描述语言,可以作为设计安全的跨语言PRC接口的基础工具。 ## Protobuf入门 -对于没有用过Protobuf读者,建议先从官网了解下基本用法。这里我们尝试如何将Protobuf和RPC结合在一起使用,通过Protobuf来最终保证RPC的接口规范和完全。Protobuf中最基本的数据单元是message,是类似Go语言中结构体的存在。在message中可以嵌套message或其它的基础数据类型的成员。 +对于没有用过Protobuf读者,建议先从官网了解下基本用法。这里我们尝试如何将Protobuf和RPC结合在一起使用,通过Protobuf来最终保证RPC的接口规范和安全。Protobuf中最基本的数据单元是message,是类似Go语言中结构体的存在。在message中可以嵌套message或其它的基础数据类型的成员。 首先创建hello.proto文件,其中包装HelloService服务中用到的字符串类型: @@ -18,11 +18,11 @@ message String { } ``` -开头的syntax语句表示采用Protobuf第三版本的语法。第三版的Protobuf对语言进行的提炼简化,所有成员均采用类似Go语言中的零值初始化(不在支持自定义默认值),同时消息成员也不再支持required特性。然后package指令指明当前是main包(这样可以和Go的包明保持一致),当然用户也可以针对不同的语言定制对应的包路径和名称。最后message关键字定义一个新的String类型,在最终生成的Go语言代码中对应一个String结构体。String类型中只有一个字符串类型的value成员,该成员的Protobuf编码时的成员编号为1。 +开头的syntax语句表示采用Protobuf第三版本的语法。第三版的Protobuf对语言进行了提炼简化,所有成员均采用类似Go语言中的零值初始化(不再支持自定义默认值),同时消息成员也不再支持required特性。然后package指令指明当前是main包(这样可以和Go的包明保持一致),当然用户也可以针对不同的语言定制对应的包路径和名称。最后message关键字定义一个新的String类型,在最终生成的Go语言代码中对应一个String结构体。String类型中只有一个字符串类型的value成员,该成员的Protobuf编码时的成员编号为1。 -在XML或JSON成数据描述语言中,一遍通过成员的名字来绑定对应的数据。但是Protobuf编码却是通过成员的唯一编号来绑定对应的数据,因此Protobuf编码后数据的体积会比较小,但是也非常不便于人类查阅。我们目前并不关注Protobuf的编码技术,最终生成的Go结构体可以自由采用JSON或gob等编码格式,因此大家可以暂时忽略Protobuf的成员编号部分。 +在XML或JSON等数据描述语言中,一般通过成员的名字来绑定对应的数据。但是Protobuf编码却是通过成员的唯一编号来绑定对应的数据,因此Protobuf编码后数据的体积会比较小,但是也非常不便于人类查阅。我们目前并不关注Protobuf的编码技术,最终生成的Go结构体可以自由采用JSON或gob等编码格式,因此大家可以暂时忽略Protobuf的成员编码部分。 -Protobuf核心的工具集是C++语言开发的,在官方的protoc编译器中并不支持Go语言。要想基于上面的hello.proto文件生成相应的Go代码,需要安装相应的工具。首先是安装官方的protoc工具,可以从 https://github.com/google/protobuf/releases 下载。然后是安装针对Go语言的代码生成插件,可以通过`go get github.com/golang/protobuf/protoc-gen-go`命令按安装。 +Protobuf核心的工具集是C++语言开发的,在官方的protoc编译器中并不支持Go语言。要想基于上面的hello.proto文件生成相应的Go代码,需要安装相应的工具。首先是安装官方的protoc工具,可以从 https://github.com/google/protobuf/releases 下载。然后是安装针对Go语言的代码生成插件,可以通过`go get github.com/golang/protobuf/protoc-gen-go`命令安装。 然后通过以下命令生成相应的Go代码: @@ -30,7 +30,7 @@ Protobuf核心的工具集是C++语言开发的,在官方的protoc编译器中 $ protoc --go_out=. hello.proto ``` -其中`go_out`参数告知protoc编译器取加载对应的protoc-gen-go工具,然后通过该工具生成代码,生成代码放到当前目录。最后是一系列要处理的protobuf文件的列表。 +其中`go_out`参数告知protoc编译器去加载对应的protoc-gen-go工具,然后通过该工具生成代码,生成代码放到当前目录。最后是一系列要处理的protobuf文件的列表。 这里只生成了一个hello.pb.go文件,其中String结构体内容如下: @@ -57,7 +57,7 @@ func (m *String) GetValue() string { } ``` -生成的结构体中有一些以`XXX_`为前缀名字的成员,目前可以忽略这些成员。同时String类型还自动生成了一组方法,其中ProtoMessage方法表示这是一个实现了proto.Message接口的方法。此外Protobuf还为每个成员生成了一个Get方法,Get方法不仅可以处理空指针类型,而且可以和Protobuf第三版的方法保持一致(第二版的自定义默认值特性依赖这类方法)。 +生成的结构体中有一些以`XXX_`为名字前缀的成员,目前可以忽略这些成员。同时String类型还自动生成了一组方法,其中ProtoMessage方法表示这是一个实现了proto.Message接口的方法。此外Protobuf还为每个成员生成了一个Get方法,Get方法不仅可以处理空指针类型,而且可以和Protobuf第二版的方法保持一致(第二版的自定义默认值特性依赖这类方法)。 基于新的String类型,我们可以重新实现HelloService: @@ -74,7 +74,7 @@ func (p *HelloService) Hello(request *String, reply *String) error { 至此,我们初步实现了Protobuf和RPC组合工作。在启动RPC服务时,我们依然可以选择默认的gob或手工指定json编码,甚至可以重新基于protobuf编码实现一个插件。虽然做了这么多工作,但是似乎并没有看到什么收益! -回顾第一章中更安全的PRC接口部分的内容,当时我们花费了极大的力气去给RPC服务增加安全的保障。最终得到的更安全的PRC接口的代码本书就非常繁琐比利于手工维护,同时全部安全相关的代码只适用于Go语言环境!既然使用了Protobuf定义的输入和输出参数,那么RPC服务接口是否也可以通过Protobuf定义呢?其实用Protobuf定义语言无关的PRC服务接口才是它真正的价值所在! +回顾第一章中更安全的PRC接口部分的内容,当时我们花费了极大的力气去给RPC服务增加安全的保障。最终得到的更安全的PRC接口的代码本书就非常繁琐的使用手工维护,同时全部安全相关的代码只适用于Go语言环境!既然使用了Protobuf定义的输入和输出参数,那么RPC服务接口是否也可以通过Protobuf定义呢?其实用Protobuf定义语言无关的PRC服务接口才是它真正的价值所在! 下面更新hello.proto文件,通过Protobuf来定义HelloService服务: @@ -92,7 +92,7 @@ service HelloService { $ protoc --go_out=plugins=grpc:. hello.proto ``` -在生成的代码中多了一些类似HelloServiceServer、HelloServiceClient的新类型。这些类似是为grpc服务的,并不符合我们的RPC要求。 +在生成的代码中多了一些类似HelloServiceServer、HelloServiceClient的新类型。这些类型是为grpc服务的,并不符合我们的RPC要求。 grpc插件为我们提供了改进思路,下面我们将探索如何为我们的RPC生成安全的代码。 @@ -233,7 +233,7 @@ func main() { $ protoc --go-netrpc_out=plugins=netrpc:. hello.proto ``` -其中`--go-netrpc_out`参数高中protoc编译器加载名为protoc-gen-go-netrpc的插件,插件中的`plugins=netrpc`指示启用内部名为netrpc的netrpcPlugin插件。 +其中`--go-netrpc_out`参数告知protoc编译器加载名为protoc-gen-go-netrpc的插件,插件中的`plugins=netrpc`指示启用内部名为netrpc的netrpcPlugin插件。 在新生成的hello.pb.go文件中将包含增加的注释代码。至此,手工定制的Protobuf代码生成插件终于可以工作了。 diff --git a/ch4-rpc/ch4-03-netrpc-hack.md b/ch4-rpc/ch4-03-netrpc-hack.md index c394f09..85c3ca9 100644 --- a/ch4-rpc/ch4-03-netrpc-hack.md +++ b/ch4-rpc/ch4-03-netrpc-hack.md @@ -1,24 +1,151 @@ # 4.3. 玩转RPC -TODO +在不同的场景中RPC有着不同的需求,因此开源的社区就诞生了各种RPC框架。本节我们将尝试Go内置RPC框架在一些比较特殊场景的用法。 - + clientChan := make(chan *rpc.Client) + + go func() { + for { + conn, err := listener.Accept() + if err != nil { + log.Fatal("Accept error:", err) + } + + clientChan <- rpc.NewClient(conn) + } + }() + + doClientWork(clientChan) +} +``` + +当每个链接建立后,基于网络链接构造RPC客户端对象并发送到clientChan管道。 + +客户端执行RPC调用的操作在doClientWork函数完成: + +```go +func doClientWork(clientChan <-chan *rpc.Client) { + client := <-clientChan + defer client.Close() + + var reply string + err = client.Call("HelloService.Hello", "hello", &reply) + if err != nil { + log.Fatal(err) + } + + fmt.Println(reply) +} +``` + +首先从管道去取一个RPC客户端对象,并且通过defer语句指定在函数退出前关闭客户端。然后是执行正常的RPC调用。 + + +## 上下文信息 + +首先是上下文信息,基于上下文我们可以针对不同客户端提供定制化的RPC服务。我们可以通过为每个信道提供独立的RPC服务来实现对上下文特性的支持。 + +首先改造HelloService,里面增加了对应链接的conn成员: + +```go +type HelloService struct { + conn net.Conn +} +``` + +然后为每个信道启动独立的RPC服务: + +```go +func main() { + listener, err := net.Listen("tcp", ":1234") + if err != nil { + log.Fatal("ListenTCP error:", err) + } + + for { + conn, err := listener.Accept() + if err != nil { + log.Fatal("Accept error:", err) + } + + go func() { + defer conn.Close() + + p := rpc.NewServer() + p.Register(&HelloService{conn: conn}) + p.ServeConn(conn) + } () + } +} +``` + +Hello方法中就可以根据conn成员识别不同信道的RPC调用: + +```go +func (p *HelloService) Hello(request string, reply *string) error { + *reply = "hello:" + request + ", from" + p.conn.RemoteAddr().String() + return nil +} +``` + +基于上下文信息,我们可以方便地为RPC服务增加简单的登陆状态的验证: + +```go +type HelloService struct { + conn net.Conn + isLogin bool +} + +func (p *HelloService) Login(request string, reply *string) error { + if request != "user:password" { + return fmt.Errorf("auth failed") + } + log.Println("login ok") + p.isLogin = true + return nil +} + +func (p *HelloService) Hello(request string, reply *string) error { + if !p.isLogin { + return fmt.Errorf("please login") + } + *reply = "hello:" + request + ", from" + p.conn.RemoteAddr().String() + return nil +} +``` + +这样可以要求在客户端链接RPC服务时,首先要执行登陆操作,登陆成功后才能正常执行其他的服务。 diff --git a/ch4-rpc/ch4-04-grpc.md b/ch4-rpc/ch4-04-grpc.md index 0872864..3c35e8e 100644 --- a/ch4-rpc/ch4-04-grpc.md +++ b/ch4-rpc/ch4-04-grpc.md @@ -1,8 +1,223 @@ # 4.4. GRPC入门 +GRPC是Google公司基于Protobuf开发的跨语言的开源RPC框架。GRPC基于HTTP/2协议设计,可以基于一个HTTP/2链接提供多个服务,对于移动设备更加友好。本节将讲述GRPC的简单用法。 + +## GRPC入门 + +如果从Protobuf的角度看,GRPC只不过是一个针对service接口生成代码的生成器。我们在本章的第二节中手工实现了一个简单的Protobuf代码生成器插件,只不过当时生成的代码是适配标准库的RPC框架的。 + +创建hello.proto文件,定义HelloService接口: + +```proto +syntax = "proto3"; + +package main; + +message String { + string value = 1; +} + +service HelloService { + rpc Hello (String) returns (String); +} +``` + +使用protoc-gen-go内置的grpc插件生成GRPC代码: + +``` +$ protoc --go_out=plugins=grpc:. hello.proto +``` + +GRPC插件会为服务端和客户端生成不同的接口: + +```go +type HelloServiceServer interface { + Hello(context.Context, *String) (*String, error) +} + +type HelloServiceClient interface { + Hello(ctx context.Context, in *String, opts ...grpc.CallOption) (*String, error) +} +``` + +GRPC通过context.Context参数,为每个方法调用提供了上下文支持。客户端在调用方法的时候,可以通过可选的grpc.CallOption类型的参数提供额外的上下文信息。 + +基于服务端的HelloServiceServer接口可以重新实现HelloService服务: + +```go +type HelloServiceImpl struct{} + +func (p *HelloServiceImpl) Hello(ctx context.Context, args *String) (*String, error) { + reply := &String{Value: "hello:" + args.GetValue()} + return reply, nil +} +``` + +GRPC服务的启动流程和标准库的RPC服务启动流程类似: + +```go +func main() { + grpcServer := grpc.NewServer() + RegisterHelloServiceServer(grpcServer, &HelloServiceImpl{}) + + lis, err := net.Listen("tcp", ":1234") + if err != nil { + log.Fatal(err) + } + grpcServer.Serve(lis) +} +``` + +首先是通过`grpc.NewServer()`构造一个GRPC服务对象,然后通过GRPC插件生成的RegisterHelloServiceServer函数注册我们实现的HelloServiceImpl服务。然后通过`grpcServer.Serve(lis)`在一个监听端口上提供GRPC服务。 + +然后就可以通过客户端链接GRPC服务了: + +```go +func main() { + conn, err := grpc.Dial("localhost:1234", grpc.WithInsecure()) + if err != nil { + log.Fatal(err) + } + defer conn.Close() + + client := NewHelloServiceClient(conn) + reply, err := client.Hello(context.Background(), &String{Value: "hello"}) + if err != nil { + log.Fatal(err) + } + fmt.Println(reply.GetValue()) +} +``` + +其中grpc.Dial负责和GRPC服务建立链接,然后NewHelloServiceClient函数基于已经建立的链接构造HelloServiceClient对象。返回的client其实是一个HelloServiceClient接口对象,通过接口定义的方法就可以调用服务端对应的GRPC服务提供的方法。 + +GRPC和标准库的RPC框架还有一个区别,GRPC生成的接口并不支持异步调用。 + +## GRPC流 + +RPC是远程函数调用,因此每次调用的函数参数和返回值不能太大,否则将严重影响每次调用的性能。因此传统的RPC方法调用对于上传和下载较大数据量场景并不适合。同时传统RPC模式也不适用于对时间不确定的订阅和发布模式。为此,GRPC框架分别提供了服务器端和客户端的流特性。 + +服务端或客户端的单向流是双向流的特例,我们在HelloService增加一个支持双向流的Channel方法: + +```proto +service HelloService { + rpc Hello (String) returns (String); + + rpc Channel (stream String) returns (stream String); +} +``` + +关键字stream指定启用流特性,参数部分是接收客户端参数的流,返回值是返回给客户端的流。 + +重新生成代码可以看到接口中新增加的Channel方法的定义: + +```go +type HelloServiceServer interface { + Hello(context.Context, *String) (*String, error) + Channel(HelloService_ChannelServer) error +} +type HelloServiceClient interface { + Hello(ctx context.Context, in *String, opts ...grpc.CallOption) (*String, error) + Channel(ctx context.Context, opts ...grpc.CallOption) (HelloService_ChannelClient, error) +} +``` + +在服务端的Channel方法参数是一个新的HelloService_ChannelServer类型的参数,可以用于和客户端双向通信。客户端的Channel方法返回一个HelloService_ChannelClient类型的返回值,可以用于和服务端进行双向通信。 + +HelloService_ChannelServer和HelloService_ChannelClient均为接口类型: + +```go +type HelloService_ChannelServer interface { + Send(*String) error + Recv() (*String, error) + grpc.ServerStream +} + +type HelloService_ChannelClient interface { + Send(*String) error + Recv() (*String, error) + grpc.ClientStream +} +``` + +可以发现服务端和客户端的流辅助接口均定义了Send和Recv方法用于流数据的双向通信。 + +现在我们可以实现流服务: + +```go +func (p *HelloServiceImpl) Channel(stream HelloService_ChannelServer) error { + for { + args, err := stream.Recv() + if err != nil { + if err == io.EOF { + return nil + } + return err + } + + reply := &String{Value: "hello:" + args.GetValue()} + + err = stream.Send(reply) + if err != nil { + return err + } + } +} +``` + +服务端在循环中接收客户端发来的数据,如果遇到io.EOF表示客户端流被关闭,如果函数退出表示服务端流关闭。然后生成返回的数据通过流发送给客户端。需要注意的是,发送和接收的操作并不需要一一对应,用户可以根据真实场景进行组织代码。 + +客户端需要先调用Channel方法获取返回的流对象: + +```go +stream, err := client.Channel(context.Background()) +if err != nil { + log.Fatal(err) +} +``` + +在客户端我们将发送和接收操作放到两个独立的Goroutine。首先是向服务端发送数据: + +```go +go func() { + for { + if err := stream.Send(&String{Value: "hi"}); err != nil { + log.Fatal(err) + } + time.Sleep(time.Second) + } +}() +``` + +然后在循环中接收服务端返回的数据: + +```go +for { + reply, err := stream.Recv() + if err != nil { + if err == io.EOF { + break + } + log.Fatal(err) + } + fmt.Println(reply.GetValue()) +} +``` + +这样就完成了完整的流接收和发送支持。 + + + diff --git a/ch5-web/ch5-10-service-discovery.md b/ch5-web/ch5-10-service-discovery.md index 808e3e6..1692eb8 100644 --- a/ch5-web/ch5-10-service-discovery.md +++ b/ch5-web/ch5-10-service-discovery.md @@ -127,7 +127,7 @@ for _, endpoint := range endpointList { ```shell ls /platform/order-system/create-order-service-http -[] +['10.1.23.1:1023', '10.11.23.1:1023'] ``` 当与 zk 断开连接时,注册在该节点下的临时节点也会消失,即实现了服务节点故障时的被动摘除。 @@ -136,16 +136,51 @@ ls /platform/order-system/create-order-service-http ## 基于 zk 的完整服务发现流程 -节点故障断开 zk 连接时,zk 会负责将该消息通知所有监听方。 - 用代码来实现一下上面的几个逻辑。 ### 临时节点注册 ```go +package main + +import ( + "fmt" + "time" + + "github.com/samuel/go-zookeeper/zk" +) + +func main() { + c, _, err := zk.Connect([]string{"127.0.0.1"}, time.Second) + if err != nil { + panic(err) + } + + res, err := c.Create("/platform/order-system/create-order-service-http/10.1.13.3:1043", []byte("1"), + zk.FlagEphemeral, zk.WorldACL(zk.PermAll)) + if err != nil { + panic(err) + } + println(res) + time.Sleep(time.Second * 50) +} ``` -### 服务节点获取 +在 sleep 的时候我们在 cli 中查看写入的临时节点数据: + +```shell +ls /platform/order-system/create-order-service-http +['10.1.13.3:1043'] +``` + +在程序结束之后,很快这条数据也消失了: + +```shell +ls /platform/order-system/create-order-service-http +[] +``` + +### watch 数据变化 ### 消息通知 diff --git a/ch6-cloud/ch6-10-delay-job.md b/ch6-cloud/ch6-10-delay-job.md index 70d5cd9..9b32207 100644 --- a/ch6-cloud/ch6-10-delay-job.md +++ b/ch6-cloud/ch6-10-delay-job.md @@ -94,3 +94,28 @@ Go 自身的 timer 就是用时间堆来实现的,不过并没有使用二叉 ### 时间轮 ![timewheel](../images/ch6-timewheel.png) + +用时间轮来实现 timer 时,我们需要定义每一个格子的“刻度”,可以将时间轮想像成一个时钟,中心有秒针顺时针转动。每次转动到一个刻度时,我们就需要去查看该刻度挂载的 tasklist 是否有已经到期的任务。 + +从结构上来讲,时间轮和哈希表很相似,如果我们把哈希算法定义为:触发时间%时间轮元素大小。那么这就是一个简单的哈希表。在哈希冲突时,采用链表挂载哈希冲突的定时器。 + +除了这种单层时间轮,业界也有一些时间轮采用多层实现,这里就不再赘述了。 + +## 任务分发 + +有了基本的 timer 实现方案,如果我们开发的是单机系统,那么就可以撸起袖子开干了,不过本章我们讨论的是分布式,距离“分布式”还稍微有一些距离。 + +我们还需要把这些“定时”或是“延时”(本质也是定时)任务分发出去。下面是一种思路: + +![task-dist](../images/ch6-task-sched.png) + +每一个实例每隔一小时,会去数据库里把下一个小时需要处理的定时任务捞出来,捞取的时候只要取那些 task_id % shard_count = shard_id 的那些 task 即可。 + +当这些定时任务被触发之后需要通知用户侧,有两种思路: + +1. 将任务被触发的信息封装为一条 event 消息,发往消息队列,由用户侧对消息队列进行监听。 +2. 对用户预先配置的回调函数进行调用。 + +两种方案各有优缺点,如果采用 1,那么如果消息队列出故障会导致整个系统不可用,当然,现在的消息队列一般也会有自身的高可用方案,大多数时候我们不用担心这个问题。其次一般业务流程中间走消息队列的话会导致延时增加,定时任务若必须在触发后的几十毫秒到几百毫秒内完成,那么采用消息队列就会有一定的风险。如果采用 2,会加重定时任务系统的负担。我们知道,单机的 timer 执行时最害怕的就是回调函数执行时间长,这样会阻塞后续的任务执行。在分布式场景下,这种忧虑依然是适用的。一个不负责任的业务回调可能就会直接拖垮整个定时任务系统。所以我们还要考虑在回调的基础上增加经过测试的超时时间设置,并且对由用户填入的超时时间做慎重的审核。 + +## rebalance 和幂等考量 diff --git a/examples/ch4-02-proto/proto-v1/Makefile b/examples/ch4-02-proto/proto-v1/Makefile deleted file mode 100644 index e69de29..0000000 diff --git a/examples/ch4-02-proto/proto-v1/main.go b/examples/ch4-02-proto/proto-v1/main.go deleted file mode 100644 index b25334d..0000000 --- a/examples/ch4-02-proto/proto-v1/main.go +++ /dev/null @@ -1,12 +0,0 @@ -package main - -type HelloService struct{} - -func (p *HelloService) Hello(request String, reply *String) error { - reply.Value = "hello:" + request.GetValue() - return nil -} - -func main() { - -} diff --git a/examples/ch4-03-rpc-hack/rpc-context/main.go b/examples/ch4-03-rpc-hack/rpc-context/main.go deleted file mode 100644 index d8d44f3..0000000 --- a/examples/ch4-03-rpc-hack/rpc-context/main.go +++ /dev/null @@ -1,5 +0,0 @@ -package main - -func main() { - // todo -} diff --git a/examples/ch4-03-rpc-hack/rpc-reverse/main.go b/examples/ch4-03-rpc-hack/rpc-reverse/main.go deleted file mode 100644 index d8d44f3..0000000 --- a/examples/ch4-03-rpc-hack/rpc-reverse/main.go +++ /dev/null @@ -1,5 +0,0 @@ -package main - -func main() { - // todo -} diff --git a/images/ch6-task-sched.png b/images/ch6-task-sched.png new file mode 100644 index 0000000..948123a Binary files /dev/null and b/images/ch6-task-sched.png differ diff --git a/preface.md b/preface.md index f689a2c..ec908e3 100644 --- a/preface.md +++ b/preface.md @@ -4,7 +4,7 @@ ![](cover.png) -- 作者:柴树杉 (chai2010, chaishushan@gmail.com) +- 作者:柴树杉 (chai2010, chaishushan@gmail.com), 曹春晖 (cch123, https://github.com/cch123) - 网址:https://github.com/chai2010/advanced-go-programming-book ## 在线阅读 diff --git a/examples/ch1-01/hello/hello.go b/vendor/gobook.examples/ch1-01/hello/hello.go similarity index 100% rename from examples/ch1-01/hello/hello.go rename to vendor/gobook.examples/ch1-01/hello/hello.go diff --git a/examples/ch1-02/hello-alef/hello.alef b/vendor/gobook.examples/ch1-02/hello-alef/hello.alef similarity index 100% rename from examples/ch1-02/hello-alef/hello.alef rename to vendor/gobook.examples/ch1-02/hello-alef/hello.alef diff --git a/examples/ch1-02/hello-b/main.b b/vendor/gobook.examples/ch1-02/hello-b/main.b similarity index 100% rename from examples/ch1-02/hello-b/main.b rename to vendor/gobook.examples/ch1-02/hello-b/main.b diff --git a/examples/ch1-02/hello-c-01/hello-c-01.c b/vendor/gobook.examples/ch1-02/hello-c-01/hello-c-01.c similarity index 100% rename from examples/ch1-02/hello-c-01/hello-c-01.c rename to vendor/gobook.examples/ch1-02/hello-c-01/hello-c-01.c diff --git a/examples/ch1-02/hello-c-02/hello-c-02.c b/vendor/gobook.examples/ch1-02/hello-c-02/hello-c-02.c similarity index 100% rename from examples/ch1-02/hello-c-02/hello-c-02.c rename to vendor/gobook.examples/ch1-02/hello-c-02/hello-c-02.c diff --git a/examples/ch1-02/hello-c-03/hello-c-03.c b/vendor/gobook.examples/ch1-02/hello-c-03/hello-c-03.c similarity index 100% rename from examples/ch1-02/hello-c-03/hello-c-03.c rename to vendor/gobook.examples/ch1-02/hello-c-03/hello-c-03.c diff --git a/examples/ch1-02/hello-c-04/hello-c-04.c b/vendor/gobook.examples/ch1-02/hello-c-04/hello-c-04.c similarity index 100% rename from examples/ch1-02/hello-c-04/hello-c-04.c rename to vendor/gobook.examples/ch1-02/hello-c-04/hello-c-04.c diff --git a/examples/ch1-02/hello-go-200806/hello.go.txt b/vendor/gobook.examples/ch1-02/hello-go-200806/hello.go.txt similarity index 100% rename from examples/ch1-02/hello-go-200806/hello.go.txt rename to vendor/gobook.examples/ch1-02/hello-go-200806/hello.go.txt diff --git a/examples/ch1-02/hello-go-20080627/hello.go.txt b/vendor/gobook.examples/ch1-02/hello-go-20080627/hello.go.txt similarity index 100% rename from examples/ch1-02/hello-go-20080627/hello.go.txt rename to vendor/gobook.examples/ch1-02/hello-go-20080627/hello.go.txt diff --git a/examples/ch1-02/hello-go-20080811/hello.go.txt b/vendor/gobook.examples/ch1-02/hello-go-20080811/hello.go.txt similarity index 100% rename from examples/ch1-02/hello-go-20080811/hello.go.txt rename to vendor/gobook.examples/ch1-02/hello-go-20080811/hello.go.txt diff --git a/examples/ch1-02/hello-go-20081024/hello.go.txt b/vendor/gobook.examples/ch1-02/hello-go-20081024/hello.go.txt similarity index 100% rename from examples/ch1-02/hello-go-20081024/hello.go.txt rename to vendor/gobook.examples/ch1-02/hello-go-20081024/hello.go.txt diff --git a/examples/ch1-02/hello-go-20090915/hello.go.txt b/vendor/gobook.examples/ch1-02/hello-go-20090915/hello.go.txt similarity index 100% rename from examples/ch1-02/hello-go-20090915/hello.go.txt rename to vendor/gobook.examples/ch1-02/hello-go-20090915/hello.go.txt diff --git a/examples/ch1-02/hello-go-20091211/hello.go b/vendor/gobook.examples/ch1-02/hello-go-20091211/hello.go similarity index 100% rename from examples/ch1-02/hello-go-20091211/hello.go rename to vendor/gobook.examples/ch1-02/hello-go-20091211/hello.go diff --git a/examples/ch1-02/hello-go-asm/hello.go b/vendor/gobook.examples/ch1-02/hello-go-asm/hello.go similarity index 100% rename from examples/ch1-02/hello-go-asm/hello.go rename to vendor/gobook.examples/ch1-02/hello-go-asm/hello.go diff --git a/examples/ch1-02/hello-go-asm/hello_amd64.s b/vendor/gobook.examples/ch1-02/hello-go-asm/hello_amd64.s similarity index 100% rename from examples/ch1-02/hello-go-asm/hello_amd64.s rename to vendor/gobook.examples/ch1-02/hello-go-asm/hello_amd64.s diff --git a/examples/ch1-02/hello-go-cgo/hello.go b/vendor/gobook.examples/ch1-02/hello-go-cgo/hello.go similarity index 100% rename from examples/ch1-02/hello-go-cgo/hello.go rename to vendor/gobook.examples/ch1-02/hello-go-cgo/hello.go diff --git a/examples/ch1-02/hello-go-swig/hello.cc b/vendor/gobook.examples/ch1-02/hello-go-swig/hello.cc similarity index 100% rename from examples/ch1-02/hello-go-swig/hello.cc rename to vendor/gobook.examples/ch1-02/hello-go-swig/hello.cc diff --git a/examples/ch1-02/hello-go-swig/hello.go b/vendor/gobook.examples/ch1-02/hello-go-swig/hello.go similarity index 100% rename from examples/ch1-02/hello-go-swig/hello.go rename to vendor/gobook.examples/ch1-02/hello-go-swig/hello.go diff --git a/examples/ch1-02/hello-go-swig/hello.swigcxx b/vendor/gobook.examples/ch1-02/hello-go-swig/hello.swigcxx similarity index 100% rename from examples/ch1-02/hello-go-swig/hello.swigcxx rename to vendor/gobook.examples/ch1-02/hello-go-swig/hello.swigcxx diff --git a/examples/ch1-02/hello-go-v2/hello.go b/vendor/gobook.examples/ch1-02/hello-go-v2/hello.go similarity index 100% rename from examples/ch1-02/hello-go-v2/hello.go rename to vendor/gobook.examples/ch1-02/hello-go-v2/hello.go diff --git a/examples/ch1-02/hello-limbo/hello.limbo b/vendor/gobook.examples/ch1-02/hello-limbo/hello.limbo similarity index 100% rename from examples/ch1-02/hello-limbo/hello.limbo rename to vendor/gobook.examples/ch1-02/hello-limbo/hello.limbo diff --git a/examples/ch1-02/hello-newsqueak/hello.newsqueak b/vendor/gobook.examples/ch1-02/hello-newsqueak/hello.newsqueak similarity index 100% rename from examples/ch1-02/hello-newsqueak/hello.newsqueak rename to vendor/gobook.examples/ch1-02/hello-newsqueak/hello.newsqueak diff --git a/examples/ch1-02/prime-newsqueak/prime.newsqueak b/vendor/gobook.examples/ch1-02/prime-newsqueak/prime.newsqueak similarity index 100% rename from examples/ch1-02/prime-newsqueak/prime.newsqueak rename to vendor/gobook.examples/ch1-02/prime-newsqueak/prime.newsqueak diff --git a/examples/ch2-01-quick-guide/hello-01/main.go b/vendor/gobook.examples/ch2-01-quick-guide/hello-01/main.go similarity index 100% rename from examples/ch2-01-quick-guide/hello-01/main.go rename to vendor/gobook.examples/ch2-01-quick-guide/hello-01/main.go diff --git a/examples/ch2-01-quick-guide/hello-02/main.go b/vendor/gobook.examples/ch2-01-quick-guide/hello-02/main.go similarity index 100% rename from examples/ch2-01-quick-guide/hello-02/main.go rename to vendor/gobook.examples/ch2-01-quick-guide/hello-02/main.go diff --git a/examples/ch2-01-quick-guide/hello-03/hello.c b/vendor/gobook.examples/ch2-01-quick-guide/hello-03/hello.c similarity index 100% rename from examples/ch2-01-quick-guide/hello-03/hello.c rename to vendor/gobook.examples/ch2-01-quick-guide/hello-03/hello.c diff --git a/examples/ch2-01-quick-guide/hello-03/main.go b/vendor/gobook.examples/ch2-01-quick-guide/hello-03/main.go similarity index 100% rename from examples/ch2-01-quick-guide/hello-03/main.go rename to vendor/gobook.examples/ch2-01-quick-guide/hello-03/main.go diff --git a/examples/ch2-01-quick-guide/hello-04/main.go b/vendor/gobook.examples/ch2-01-quick-guide/hello-04/main.go similarity index 100% rename from examples/ch2-01-quick-guide/hello-04/main.go rename to vendor/gobook.examples/ch2-01-quick-guide/hello-04/main.go diff --git a/examples/ch2-01-quick-guide/hello-05/main.go b/vendor/gobook.examples/ch2-01-quick-guide/hello-05/main.go similarity index 100% rename from examples/ch2-01-quick-guide/hello-05/main.go rename to vendor/gobook.examples/ch2-01-quick-guide/hello-05/main.go diff --git a/examples/ch2-01-quick-guide/hello-06/main.go b/vendor/gobook.examples/ch2-01-quick-guide/hello-06/main.go similarity index 100% rename from examples/ch2-01-quick-guide/hello-06/main.go rename to vendor/gobook.examples/ch2-01-quick-guide/hello-06/main.go diff --git a/examples/ch2-04-func/return-go-ptr/main.go b/vendor/gobook.examples/ch2-04-func/return-go-ptr/main.go similarity index 100% rename from examples/ch2-04-func/return-go-ptr/main.go rename to vendor/gobook.examples/ch2-04-func/return-go-ptr/main.go diff --git a/examples/ch2-05-internal/01-cgo-gen-files/Makefile b/vendor/gobook.examples/ch2-05-internal/01-cgo-gen-files/Makefile similarity index 100% rename from examples/ch2-05-internal/01-cgo-gen-files/Makefile rename to vendor/gobook.examples/ch2-05-internal/01-cgo-gen-files/Makefile diff --git a/examples/ch2-05-internal/01-cgo-gen-files/_obj/_cgo_export.c b/vendor/gobook.examples/ch2-05-internal/01-cgo-gen-files/_obj/_cgo_export.c similarity index 100% rename from examples/ch2-05-internal/01-cgo-gen-files/_obj/_cgo_export.c rename to vendor/gobook.examples/ch2-05-internal/01-cgo-gen-files/_obj/_cgo_export.c diff --git a/examples/ch2-05-internal/01-cgo-gen-files/_obj/_cgo_export.h b/vendor/gobook.examples/ch2-05-internal/01-cgo-gen-files/_obj/_cgo_export.h similarity index 100% rename from examples/ch2-05-internal/01-cgo-gen-files/_obj/_cgo_export.h rename to vendor/gobook.examples/ch2-05-internal/01-cgo-gen-files/_obj/_cgo_export.h diff --git a/examples/ch2-05-internal/01-cgo-gen-files/_obj/_cgo_flags b/vendor/gobook.examples/ch2-05-internal/01-cgo-gen-files/_obj/_cgo_flags similarity index 100% rename from examples/ch2-05-internal/01-cgo-gen-files/_obj/_cgo_flags rename to vendor/gobook.examples/ch2-05-internal/01-cgo-gen-files/_obj/_cgo_flags diff --git a/examples/ch2-05-internal/01-cgo-gen-files/_obj/_cgo_gotypes.go b/vendor/gobook.examples/ch2-05-internal/01-cgo-gen-files/_obj/_cgo_gotypes.go similarity index 100% rename from examples/ch2-05-internal/01-cgo-gen-files/_obj/_cgo_gotypes.go rename to vendor/gobook.examples/ch2-05-internal/01-cgo-gen-files/_obj/_cgo_gotypes.go diff --git a/examples/ch2-05-internal/01-cgo-gen-files/_obj/_cgo_main.c b/vendor/gobook.examples/ch2-05-internal/01-cgo-gen-files/_obj/_cgo_main.c similarity index 100% rename from examples/ch2-05-internal/01-cgo-gen-files/_obj/_cgo_main.c rename to vendor/gobook.examples/ch2-05-internal/01-cgo-gen-files/_obj/_cgo_main.c diff --git a/examples/ch2-05-internal/01-cgo-gen-files/_obj/hello.cgo1.go b/vendor/gobook.examples/ch2-05-internal/01-cgo-gen-files/_obj/hello.cgo1.go similarity index 100% rename from examples/ch2-05-internal/01-cgo-gen-files/_obj/hello.cgo1.go rename to vendor/gobook.examples/ch2-05-internal/01-cgo-gen-files/_obj/hello.cgo1.go diff --git a/examples/ch2-05-internal/01-cgo-gen-files/_obj/hello.cgo2.c b/vendor/gobook.examples/ch2-05-internal/01-cgo-gen-files/_obj/hello.cgo2.c similarity index 100% rename from examples/ch2-05-internal/01-cgo-gen-files/_obj/hello.cgo2.c rename to vendor/gobook.examples/ch2-05-internal/01-cgo-gen-files/_obj/hello.cgo2.c diff --git a/examples/ch2-05-internal/01-cgo-gen-files/_obj/main.cgo1.go b/vendor/gobook.examples/ch2-05-internal/01-cgo-gen-files/_obj/main.cgo1.go similarity index 100% rename from examples/ch2-05-internal/01-cgo-gen-files/_obj/main.cgo1.go rename to vendor/gobook.examples/ch2-05-internal/01-cgo-gen-files/_obj/main.cgo1.go diff --git a/examples/ch2-05-internal/01-cgo-gen-files/_obj/main.cgo2.c b/vendor/gobook.examples/ch2-05-internal/01-cgo-gen-files/_obj/main.cgo2.c similarity index 100% rename from examples/ch2-05-internal/01-cgo-gen-files/_obj/main.cgo2.c rename to vendor/gobook.examples/ch2-05-internal/01-cgo-gen-files/_obj/main.cgo2.c diff --git a/examples/ch2-05-internal/01-cgo-gen-files/hello.go b/vendor/gobook.examples/ch2-05-internal/01-cgo-gen-files/hello.go similarity index 100% rename from examples/ch2-05-internal/01-cgo-gen-files/hello.go rename to vendor/gobook.examples/ch2-05-internal/01-cgo-gen-files/hello.go diff --git a/examples/ch2-05-internal/01-cgo-gen-files/main.go b/vendor/gobook.examples/ch2-05-internal/01-cgo-gen-files/main.go similarity index 100% rename from examples/ch2-05-internal/01-cgo-gen-files/main.go rename to vendor/gobook.examples/ch2-05-internal/01-cgo-gen-files/main.go diff --git a/examples/ch2-05-internal/01-cgo-gen-files/nocgo_1.go b/vendor/gobook.examples/ch2-05-internal/01-cgo-gen-files/nocgo_1.go similarity index 100% rename from examples/ch2-05-internal/01-cgo-gen-files/nocgo_1.go rename to vendor/gobook.examples/ch2-05-internal/01-cgo-gen-files/nocgo_1.go diff --git a/examples/ch2-05-internal/01-cgo-gen-files/nocgo_x.go b/vendor/gobook.examples/ch2-05-internal/01-cgo-gen-files/nocgo_x.go similarity index 100% rename from examples/ch2-05-internal/01-cgo-gen-files/nocgo_x.go rename to vendor/gobook.examples/ch2-05-internal/01-cgo-gen-files/nocgo_x.go diff --git a/examples/ch2-05-internal/02-go-call-c-func/Makefile b/vendor/gobook.examples/ch2-05-internal/02-go-call-c-func/Makefile similarity index 100% rename from examples/ch2-05-internal/02-go-call-c-func/Makefile rename to vendor/gobook.examples/ch2-05-internal/02-go-call-c-func/Makefile diff --git a/examples/ch2-05-internal/02-go-call-c-func/_obj/_cgo_export.c b/vendor/gobook.examples/ch2-05-internal/02-go-call-c-func/_obj/_cgo_export.c similarity index 100% rename from examples/ch2-05-internal/02-go-call-c-func/_obj/_cgo_export.c rename to vendor/gobook.examples/ch2-05-internal/02-go-call-c-func/_obj/_cgo_export.c diff --git a/examples/ch2-05-internal/02-go-call-c-func/_obj/_cgo_export.h b/vendor/gobook.examples/ch2-05-internal/02-go-call-c-func/_obj/_cgo_export.h similarity index 100% rename from examples/ch2-05-internal/02-go-call-c-func/_obj/_cgo_export.h rename to vendor/gobook.examples/ch2-05-internal/02-go-call-c-func/_obj/_cgo_export.h diff --git a/examples/ch2-05-internal/02-go-call-c-func/_obj/_cgo_flags b/vendor/gobook.examples/ch2-05-internal/02-go-call-c-func/_obj/_cgo_flags similarity index 100% rename from examples/ch2-05-internal/02-go-call-c-func/_obj/_cgo_flags rename to vendor/gobook.examples/ch2-05-internal/02-go-call-c-func/_obj/_cgo_flags diff --git a/examples/ch2-05-internal/02-go-call-c-func/_obj/_cgo_gotypes.go b/vendor/gobook.examples/ch2-05-internal/02-go-call-c-func/_obj/_cgo_gotypes.go similarity index 100% rename from examples/ch2-05-internal/02-go-call-c-func/_obj/_cgo_gotypes.go rename to vendor/gobook.examples/ch2-05-internal/02-go-call-c-func/_obj/_cgo_gotypes.go diff --git a/examples/ch2-05-internal/02-go-call-c-func/_obj/_cgo_main.c b/vendor/gobook.examples/ch2-05-internal/02-go-call-c-func/_obj/_cgo_main.c similarity index 100% rename from examples/ch2-05-internal/02-go-call-c-func/_obj/_cgo_main.c rename to vendor/gobook.examples/ch2-05-internal/02-go-call-c-func/_obj/_cgo_main.c diff --git a/examples/ch2-05-internal/02-go-call-c-func/_obj/main.cgo1.go b/vendor/gobook.examples/ch2-05-internal/02-go-call-c-func/_obj/main.cgo1.go similarity index 100% rename from examples/ch2-05-internal/02-go-call-c-func/_obj/main.cgo1.go rename to vendor/gobook.examples/ch2-05-internal/02-go-call-c-func/_obj/main.cgo1.go diff --git a/examples/ch2-05-internal/02-go-call-c-func/_obj/main.cgo2.c b/vendor/gobook.examples/ch2-05-internal/02-go-call-c-func/_obj/main.cgo2.c similarity index 100% rename from examples/ch2-05-internal/02-go-call-c-func/_obj/main.cgo2.c rename to vendor/gobook.examples/ch2-05-internal/02-go-call-c-func/_obj/main.cgo2.c diff --git a/examples/ch2-05-internal/02-go-call-c-func/main.go b/vendor/gobook.examples/ch2-05-internal/02-go-call-c-func/main.go similarity index 100% rename from examples/ch2-05-internal/02-go-call-c-func/main.go rename to vendor/gobook.examples/ch2-05-internal/02-go-call-c-func/main.go diff --git a/examples/ch2-05-internal/03-c-call-go-func/Makefile b/vendor/gobook.examples/ch2-05-internal/03-c-call-go-func/Makefile similarity index 100% rename from examples/ch2-05-internal/03-c-call-go-func/Makefile rename to vendor/gobook.examples/ch2-05-internal/03-c-call-go-func/Makefile diff --git a/examples/ch2-05-internal/03-c-call-go-func/_obj/_cgo_export.c b/vendor/gobook.examples/ch2-05-internal/03-c-call-go-func/_obj/_cgo_export.c similarity index 100% rename from examples/ch2-05-internal/03-c-call-go-func/_obj/_cgo_export.c rename to vendor/gobook.examples/ch2-05-internal/03-c-call-go-func/_obj/_cgo_export.c diff --git a/examples/ch2-05-internal/03-c-call-go-func/_obj/_cgo_export.h b/vendor/gobook.examples/ch2-05-internal/03-c-call-go-func/_obj/_cgo_export.h similarity index 100% rename from examples/ch2-05-internal/03-c-call-go-func/_obj/_cgo_export.h rename to vendor/gobook.examples/ch2-05-internal/03-c-call-go-func/_obj/_cgo_export.h diff --git a/examples/ch2-05-internal/03-c-call-go-func/_obj/_cgo_flags b/vendor/gobook.examples/ch2-05-internal/03-c-call-go-func/_obj/_cgo_flags similarity index 100% rename from examples/ch2-05-internal/03-c-call-go-func/_obj/_cgo_flags rename to vendor/gobook.examples/ch2-05-internal/03-c-call-go-func/_obj/_cgo_flags diff --git a/examples/ch2-05-internal/03-c-call-go-func/_obj/_cgo_gotypes.go b/vendor/gobook.examples/ch2-05-internal/03-c-call-go-func/_obj/_cgo_gotypes.go similarity index 100% rename from examples/ch2-05-internal/03-c-call-go-func/_obj/_cgo_gotypes.go rename to vendor/gobook.examples/ch2-05-internal/03-c-call-go-func/_obj/_cgo_gotypes.go diff --git a/examples/ch2-05-internal/03-c-call-go-func/_obj/_cgo_main.c b/vendor/gobook.examples/ch2-05-internal/03-c-call-go-func/_obj/_cgo_main.c similarity index 100% rename from examples/ch2-05-internal/03-c-call-go-func/_obj/_cgo_main.c rename to vendor/gobook.examples/ch2-05-internal/03-c-call-go-func/_obj/_cgo_main.c diff --git a/examples/ch2-05-internal/03-c-call-go-func/_obj/sum.cgo1.go b/vendor/gobook.examples/ch2-05-internal/03-c-call-go-func/_obj/sum.cgo1.go similarity index 100% rename from examples/ch2-05-internal/03-c-call-go-func/_obj/sum.cgo1.go rename to vendor/gobook.examples/ch2-05-internal/03-c-call-go-func/_obj/sum.cgo1.go diff --git a/examples/ch2-05-internal/03-c-call-go-func/_obj/sum.cgo2.c b/vendor/gobook.examples/ch2-05-internal/03-c-call-go-func/_obj/sum.cgo2.c similarity index 100% rename from examples/ch2-05-internal/03-c-call-go-func/_obj/sum.cgo2.c rename to vendor/gobook.examples/ch2-05-internal/03-c-call-go-func/_obj/sum.cgo2.c diff --git a/examples/ch2-05-internal/03-c-call-go-func/main.c b/vendor/gobook.examples/ch2-05-internal/03-c-call-go-func/main.c similarity index 100% rename from examples/ch2-05-internal/03-c-call-go-func/main.c rename to vendor/gobook.examples/ch2-05-internal/03-c-call-go-func/main.c diff --git a/examples/ch2-05-internal/03-c-call-go-func/sum.go b/vendor/gobook.examples/ch2-05-internal/03-c-call-go-func/sum.go similarity index 100% rename from examples/ch2-05-internal/03-c-call-go-func/sum.go rename to vendor/gobook.examples/ch2-05-internal/03-c-call-go-func/sum.go diff --git a/examples/ch2-05-internal/03-c-call-go-func/sum.h b/vendor/gobook.examples/ch2-05-internal/03-c-call-go-func/sum.h similarity index 100% rename from examples/ch2-05-internal/03-c-call-go-func/sum.h rename to vendor/gobook.examples/ch2-05-internal/03-c-call-go-func/sum.h diff --git a/examples/ch2-06-qsort/01-qsort-v1/Makefile b/vendor/gobook.examples/ch2-06-qsort/01-qsort-v1/Makefile similarity index 100% rename from examples/ch2-06-qsort/01-qsort-v1/Makefile rename to vendor/gobook.examples/ch2-06-qsort/01-qsort-v1/Makefile diff --git a/examples/ch2-06-qsort/01-qsort-v1/main.c b/vendor/gobook.examples/ch2-06-qsort/01-qsort-v1/main.c similarity index 100% rename from examples/ch2-06-qsort/01-qsort-v1/main.c rename to vendor/gobook.examples/ch2-06-qsort/01-qsort-v1/main.c diff --git a/examples/ch2-06-qsort/02-qsort-v2/main.go b/vendor/gobook.examples/ch2-06-qsort/02-qsort-v2/main.go similarity index 100% rename from examples/ch2-06-qsort/02-qsort-v2/main.go rename to vendor/gobook.examples/ch2-06-qsort/02-qsort-v2/main.go diff --git a/examples/ch2-06-qsort/02-qsort-v2/qsort.go b/vendor/gobook.examples/ch2-06-qsort/02-qsort-v2/qsort.go similarity index 100% rename from examples/ch2-06-qsort/02-qsort-v2/qsort.go rename to vendor/gobook.examples/ch2-06-qsort/02-qsort-v2/qsort.go diff --git a/examples/ch2-06-qsort/02-qsort-v2/qsort_test.go b/vendor/gobook.examples/ch2-06-qsort/02-qsort-v2/qsort_test.go similarity index 100% rename from examples/ch2-06-qsort/02-qsort-v2/qsort_test.go rename to vendor/gobook.examples/ch2-06-qsort/02-qsort-v2/qsort_test.go diff --git a/examples/ch2-06-qsort/02-qsort-v2/test_helper.go b/vendor/gobook.examples/ch2-06-qsort/02-qsort-v2/test_helper.go similarity index 100% rename from examples/ch2-06-qsort/02-qsort-v2/test_helper.go rename to vendor/gobook.examples/ch2-06-qsort/02-qsort-v2/test_helper.go diff --git a/examples/ch2-06-qsort/03-qsort-v3/main.go b/vendor/gobook.examples/ch2-06-qsort/03-qsort-v3/main.go similarity index 100% rename from examples/ch2-06-qsort/03-qsort-v3/main.go rename to vendor/gobook.examples/ch2-06-qsort/03-qsort-v3/main.go diff --git a/examples/ch2-06-qsort/03-qsort-v3/sort.go b/vendor/gobook.examples/ch2-06-qsort/03-qsort-v3/sort.go similarity index 100% rename from examples/ch2-06-qsort/03-qsort-v3/sort.go rename to vendor/gobook.examples/ch2-06-qsort/03-qsort-v3/sort.go diff --git a/examples/ch2-06-qsort/03-qsort-v3/sort_test.go b/vendor/gobook.examples/ch2-06-qsort/03-qsort-v3/sort_test.go similarity index 100% rename from examples/ch2-06-qsort/03-qsort-v3/sort_test.go rename to vendor/gobook.examples/ch2-06-qsort/03-qsort-v3/sort_test.go diff --git a/examples/ch2-06-qsort/04-qsort-v4/main.go b/vendor/gobook.examples/ch2-06-qsort/04-qsort-v4/main.go similarity index 100% rename from examples/ch2-06-qsort/04-qsort-v4/main.go rename to vendor/gobook.examples/ch2-06-qsort/04-qsort-v4/main.go diff --git a/examples/ch2-06-qsort/04-qsort-v4/sort.go b/vendor/gobook.examples/ch2-06-qsort/04-qsort-v4/sort.go similarity index 100% rename from examples/ch2-06-qsort/04-qsort-v4/sort.go rename to vendor/gobook.examples/ch2-06-qsort/04-qsort-v4/sort.go diff --git a/examples/ch2-06-qsort/04-qsort-v4/sort_test.go b/vendor/gobook.examples/ch2-06-qsort/04-qsort-v4/sort_test.go similarity index 100% rename from examples/ch2-06-qsort/04-qsort-v4/sort_test.go rename to vendor/gobook.examples/ch2-06-qsort/04-qsort-v4/sort_test.go diff --git a/examples/ch2-08-class/class-cc2go/main.go b/vendor/gobook.examples/ch2-08-class/class-cc2go/main.go similarity index 100% rename from examples/ch2-08-class/class-cc2go/main.go rename to vendor/gobook.examples/ch2-08-class/class-cc2go/main.go diff --git a/examples/ch2-08-class/class-cc2go/my_buffer.cc b/vendor/gobook.examples/ch2-08-class/class-cc2go/my_buffer.cc similarity index 100% rename from examples/ch2-08-class/class-cc2go/my_buffer.cc rename to vendor/gobook.examples/ch2-08-class/class-cc2go/my_buffer.cc diff --git a/examples/ch2-08-class/class-cc2go/my_buffer.go b/vendor/gobook.examples/ch2-08-class/class-cc2go/my_buffer.go similarity index 100% rename from examples/ch2-08-class/class-cc2go/my_buffer.go rename to vendor/gobook.examples/ch2-08-class/class-cc2go/my_buffer.go diff --git a/examples/ch2-08-class/class-cc2go/my_buffer.h b/vendor/gobook.examples/ch2-08-class/class-cc2go/my_buffer.h similarity index 100% rename from examples/ch2-08-class/class-cc2go/my_buffer.h rename to vendor/gobook.examples/ch2-08-class/class-cc2go/my_buffer.h diff --git a/examples/ch2-08-class/class-cc2go/my_buffer_capi.cc b/vendor/gobook.examples/ch2-08-class/class-cc2go/my_buffer_capi.cc similarity index 100% rename from examples/ch2-08-class/class-cc2go/my_buffer_capi.cc rename to vendor/gobook.examples/ch2-08-class/class-cc2go/my_buffer_capi.cc diff --git a/examples/ch2-08-class/class-cc2go/my_buffer_capi.go b/vendor/gobook.examples/ch2-08-class/class-cc2go/my_buffer_capi.go similarity index 100% rename from examples/ch2-08-class/class-cc2go/my_buffer_capi.go rename to vendor/gobook.examples/ch2-08-class/class-cc2go/my_buffer_capi.go diff --git a/examples/ch2-08-class/class-cc2go/my_buffer_capi.h b/vendor/gobook.examples/ch2-08-class/class-cc2go/my_buffer_capi.h similarity index 100% rename from examples/ch2-08-class/class-cc2go/my_buffer_capi.h rename to vendor/gobook.examples/ch2-08-class/class-cc2go/my_buffer_capi.h diff --git a/examples/ch2-08-class/class-go2cc/goobj.go b/vendor/gobook.examples/ch2-08-class/class-go2cc/goobj.go similarity index 100% rename from examples/ch2-08-class/class-go2cc/goobj.go rename to vendor/gobook.examples/ch2-08-class/class-go2cc/goobj.go diff --git a/examples/ch2-08-class/class-go2cc/main.cc b/vendor/gobook.examples/ch2-08-class/class-go2cc/main.cc similarity index 100% rename from examples/ch2-08-class/class-go2cc/main.cc rename to vendor/gobook.examples/ch2-08-class/class-go2cc/main.cc diff --git a/examples/ch2-08-class/class-go2cc/main.go b/vendor/gobook.examples/ch2-08-class/class-go2cc/main.go similarity index 100% rename from examples/ch2-08-class/class-go2cc/main.go rename to vendor/gobook.examples/ch2-08-class/class-go2cc/main.go diff --git a/examples/ch2-08-class/class-go2cc/persion.go b/vendor/gobook.examples/ch2-08-class/class-go2cc/persion.go similarity index 100% rename from examples/ch2-08-class/class-go2cc/persion.go rename to vendor/gobook.examples/ch2-08-class/class-go2cc/persion.go diff --git a/examples/ch2-08-class/class-go2cc/person.cc b/vendor/gobook.examples/ch2-08-class/class-go2cc/person.cc similarity index 100% rename from examples/ch2-08-class/class-go2cc/person.cc rename to vendor/gobook.examples/ch2-08-class/class-go2cc/person.cc diff --git a/examples/ch2-08-class/class-go2cc/person.h b/vendor/gobook.examples/ch2-08-class/class-go2cc/person.h similarity index 100% rename from examples/ch2-08-class/class-go2cc/person.h rename to vendor/gobook.examples/ch2-08-class/class-go2cc/person.h diff --git a/examples/ch2-08-class/class-go2cc/person_capi.go b/vendor/gobook.examples/ch2-08-class/class-go2cc/person_capi.go similarity index 100% rename from examples/ch2-08-class/class-go2cc/person_capi.go rename to vendor/gobook.examples/ch2-08-class/class-go2cc/person_capi.go diff --git a/examples/ch2-08-class/class-go2cc/person_capi.h b/vendor/gobook.examples/ch2-08-class/class-go2cc/person_capi.h similarity index 100% rename from examples/ch2-08-class/class-go2cc/person_capi.h rename to vendor/gobook.examples/ch2-08-class/class-go2cc/person_capi.h diff --git a/examples/ch2-09-so-and-lib/incorrect-dll-api/Makefile b/vendor/gobook.examples/ch2-09-so-and-lib/incorrect-dll-api/Makefile similarity index 100% rename from examples/ch2-09-so-and-lib/incorrect-dll-api/Makefile rename to vendor/gobook.examples/ch2-09-so-and-lib/incorrect-dll-api/Makefile diff --git a/examples/ch2-09-so-and-lib/incorrect-dll-api/main.go b/vendor/gobook.examples/ch2-09-so-and-lib/incorrect-dll-api/main.go similarity index 100% rename from examples/ch2-09-so-and-lib/incorrect-dll-api/main.go rename to vendor/gobook.examples/ch2-09-so-and-lib/incorrect-dll-api/main.go diff --git a/examples/ch2-09-so-and-lib/incorrect-dll-api/mystring/Makefile b/vendor/gobook.examples/ch2-09-so-and-lib/incorrect-dll-api/mystring/Makefile similarity index 100% rename from examples/ch2-09-so-and-lib/incorrect-dll-api/mystring/Makefile rename to vendor/gobook.examples/ch2-09-so-and-lib/incorrect-dll-api/mystring/Makefile diff --git a/examples/ch2-09-so-and-lib/incorrect-dll-api/mystring/mystring.c b/vendor/gobook.examples/ch2-09-so-and-lib/incorrect-dll-api/mystring/mystring.c similarity index 100% rename from examples/ch2-09-so-and-lib/incorrect-dll-api/mystring/mystring.c rename to vendor/gobook.examples/ch2-09-so-and-lib/incorrect-dll-api/mystring/mystring.c diff --git a/examples/ch2-09-so-and-lib/incorrect-dll-api/mystring/mystring.h b/vendor/gobook.examples/ch2-09-so-and-lib/incorrect-dll-api/mystring/mystring.h similarity index 100% rename from examples/ch2-09-so-and-lib/incorrect-dll-api/mystring/mystring.h rename to vendor/gobook.examples/ch2-09-so-and-lib/incorrect-dll-api/mystring/mystring.h diff --git a/examples/ch2-09-so-and-lib/make-clib-dll/Makefile b/vendor/gobook.examples/ch2-09-so-and-lib/make-clib-dll/Makefile similarity index 100% rename from examples/ch2-09-so-and-lib/make-clib-dll/Makefile rename to vendor/gobook.examples/ch2-09-so-and-lib/make-clib-dll/Makefile diff --git a/examples/ch2-09-so-and-lib/make-clib-dll/_test_main.c b/vendor/gobook.examples/ch2-09-so-and-lib/make-clib-dll/_test_main.c similarity index 100% rename from examples/ch2-09-so-and-lib/make-clib-dll/_test_main.c rename to vendor/gobook.examples/ch2-09-so-and-lib/make-clib-dll/_test_main.c diff --git a/examples/ch2-09-so-and-lib/make-clib-dll/main.go b/vendor/gobook.examples/ch2-09-so-and-lib/make-clib-dll/main.go similarity index 100% rename from examples/ch2-09-so-and-lib/make-clib-dll/main.go rename to vendor/gobook.examples/ch2-09-so-and-lib/make-clib-dll/main.go diff --git a/examples/ch2-09-so-and-lib/make-clib-dll/number-win64.def b/vendor/gobook.examples/ch2-09-so-and-lib/make-clib-dll/number-win64.def similarity index 100% rename from examples/ch2-09-so-and-lib/make-clib-dll/number-win64.def rename to vendor/gobook.examples/ch2-09-so-and-lib/make-clib-dll/number-win64.def diff --git a/examples/ch2-09-so-and-lib/make-clib-dll/number.h b/vendor/gobook.examples/ch2-09-so-and-lib/make-clib-dll/number.h similarity index 100% rename from examples/ch2-09-so-and-lib/make-clib-dll/number.h rename to vendor/gobook.examples/ch2-09-so-and-lib/make-clib-dll/number.h diff --git a/examples/ch2-09-so-and-lib/make-clib-from-multi-pkg/Makefile b/vendor/gobook.examples/ch2-09-so-and-lib/make-clib-from-multi-pkg/Makefile similarity index 100% rename from examples/ch2-09-so-and-lib/make-clib-from-multi-pkg/Makefile rename to vendor/gobook.examples/ch2-09-so-and-lib/make-clib-from-multi-pkg/Makefile diff --git a/examples/ch2-09-so-and-lib/make-clib-from-multi-pkg/_test_main.c b/vendor/gobook.examples/ch2-09-so-and-lib/make-clib-from-multi-pkg/_test_main.c similarity index 100% rename from examples/ch2-09-so-and-lib/make-clib-from-multi-pkg/_test_main.c rename to vendor/gobook.examples/ch2-09-so-and-lib/make-clib-from-multi-pkg/_test_main.c diff --git a/examples/ch2-09-so-and-lib/make-clib-from-multi-pkg/main.go b/vendor/gobook.examples/ch2-09-so-and-lib/make-clib-from-multi-pkg/main.go similarity index 100% rename from examples/ch2-09-so-and-lib/make-clib-from-multi-pkg/main.go rename to vendor/gobook.examples/ch2-09-so-and-lib/make-clib-from-multi-pkg/main.go diff --git a/examples/ch2-09-so-and-lib/make-clib-from-multi-pkg/main.h b/vendor/gobook.examples/ch2-09-so-and-lib/make-clib-from-multi-pkg/main.h similarity index 100% rename from examples/ch2-09-so-and-lib/make-clib-from-multi-pkg/main.h rename to vendor/gobook.examples/ch2-09-so-and-lib/make-clib-from-multi-pkg/main.h diff --git a/examples/ch2-09-so-and-lib/make-clib-from-multi-pkg/number/number.go b/vendor/gobook.examples/ch2-09-so-and-lib/make-clib-from-multi-pkg/number/number.go similarity index 100% rename from examples/ch2-09-so-and-lib/make-clib-from-multi-pkg/number/number.go rename to vendor/gobook.examples/ch2-09-so-and-lib/make-clib-from-multi-pkg/number/number.go diff --git a/examples/ch2-09-so-and-lib/make-clib-from-multi-pkg/number/number.h b/vendor/gobook.examples/ch2-09-so-and-lib/make-clib-from-multi-pkg/number/number.h similarity index 100% rename from examples/ch2-09-so-and-lib/make-clib-from-multi-pkg/number/number.h rename to vendor/gobook.examples/ch2-09-so-and-lib/make-clib-from-multi-pkg/number/number.h diff --git a/examples/ch2-09-so-and-lib/make-clib-shared/Makefile b/vendor/gobook.examples/ch2-09-so-and-lib/make-clib-shared/Makefile similarity index 100% rename from examples/ch2-09-so-and-lib/make-clib-shared/Makefile rename to vendor/gobook.examples/ch2-09-so-and-lib/make-clib-shared/Makefile diff --git a/examples/ch2-09-so-and-lib/make-clib-shared/_test_main.c b/vendor/gobook.examples/ch2-09-so-and-lib/make-clib-shared/_test_main.c similarity index 100% rename from examples/ch2-09-so-and-lib/make-clib-shared/_test_main.c rename to vendor/gobook.examples/ch2-09-so-and-lib/make-clib-shared/_test_main.c diff --git a/examples/ch2-09-so-and-lib/make-clib-shared/main.go b/vendor/gobook.examples/ch2-09-so-and-lib/make-clib-shared/main.go similarity index 100% rename from examples/ch2-09-so-and-lib/make-clib-shared/main.go rename to vendor/gobook.examples/ch2-09-so-and-lib/make-clib-shared/main.go diff --git a/examples/ch2-09-so-and-lib/make-clib-shared/number.h b/vendor/gobook.examples/ch2-09-so-and-lib/make-clib-shared/number.h similarity index 100% rename from examples/ch2-09-so-and-lib/make-clib-shared/number.h rename to vendor/gobook.examples/ch2-09-so-and-lib/make-clib-shared/number.h diff --git a/examples/ch2-09-so-and-lib/make-clib-static/Makefile b/vendor/gobook.examples/ch2-09-so-and-lib/make-clib-static/Makefile similarity index 100% rename from examples/ch2-09-so-and-lib/make-clib-static/Makefile rename to vendor/gobook.examples/ch2-09-so-and-lib/make-clib-static/Makefile diff --git a/examples/ch2-09-so-and-lib/make-clib-static/_test_main.c b/vendor/gobook.examples/ch2-09-so-and-lib/make-clib-static/_test_main.c similarity index 100% rename from examples/ch2-09-so-and-lib/make-clib-static/_test_main.c rename to vendor/gobook.examples/ch2-09-so-and-lib/make-clib-static/_test_main.c diff --git a/examples/ch2-09-so-and-lib/make-clib-static/main.go b/vendor/gobook.examples/ch2-09-so-and-lib/make-clib-static/main.go similarity index 100% rename from examples/ch2-09-so-and-lib/make-clib-static/main.go rename to vendor/gobook.examples/ch2-09-so-and-lib/make-clib-static/main.go diff --git a/examples/ch2-09-so-and-lib/make-clib-static/number.h b/vendor/gobook.examples/ch2-09-so-and-lib/make-clib-static/number.h similarity index 100% rename from examples/ch2-09-so-and-lib/make-clib-static/number.h rename to vendor/gobook.examples/ch2-09-so-and-lib/make-clib-static/number.h diff --git a/examples/ch2-09-so-and-lib/plugin/Makefile b/vendor/gobook.examples/ch2-09-so-and-lib/plugin/Makefile similarity index 100% rename from examples/ch2-09-so-and-lib/plugin/Makefile rename to vendor/gobook.examples/ch2-09-so-and-lib/plugin/Makefile diff --git a/examples/ch2-09-so-and-lib/plugin/main.go b/vendor/gobook.examples/ch2-09-so-and-lib/plugin/main.go similarity index 100% rename from examples/ch2-09-so-and-lib/plugin/main.go rename to vendor/gobook.examples/ch2-09-so-and-lib/plugin/main.go diff --git a/examples/ch2-09-so-and-lib/plugin/plugin.go b/vendor/gobook.examples/ch2-09-so-and-lib/plugin/plugin.go similarity index 100% rename from examples/ch2-09-so-and-lib/plugin/plugin.go rename to vendor/gobook.examples/ch2-09-so-and-lib/plugin/plugin.go diff --git a/examples/ch2-09-so-and-lib/use-clib-shared/Makefile b/vendor/gobook.examples/ch2-09-so-and-lib/use-clib-shared/Makefile similarity index 100% rename from examples/ch2-09-so-and-lib/use-clib-shared/Makefile rename to vendor/gobook.examples/ch2-09-so-and-lib/use-clib-shared/Makefile diff --git a/examples/ch2-09-so-and-lib/use-clib-shared/main.go b/vendor/gobook.examples/ch2-09-so-and-lib/use-clib-shared/main.go similarity index 100% rename from examples/ch2-09-so-and-lib/use-clib-shared/main.go rename to vendor/gobook.examples/ch2-09-so-and-lib/use-clib-shared/main.go diff --git a/examples/ch2-09-so-and-lib/use-clib-shared/number/Makefile b/vendor/gobook.examples/ch2-09-so-and-lib/use-clib-shared/number/Makefile similarity index 100% rename from examples/ch2-09-so-and-lib/use-clib-shared/number/Makefile rename to vendor/gobook.examples/ch2-09-so-and-lib/use-clib-shared/number/Makefile diff --git a/examples/ch2-09-so-and-lib/use-clib-shared/number/number.c b/vendor/gobook.examples/ch2-09-so-and-lib/use-clib-shared/number/number.c similarity index 100% rename from examples/ch2-09-so-and-lib/use-clib-shared/number/number.c rename to vendor/gobook.examples/ch2-09-so-and-lib/use-clib-shared/number/number.c diff --git a/examples/ch2-09-so-and-lib/use-clib-shared/number/number.h b/vendor/gobook.examples/ch2-09-so-and-lib/use-clib-shared/number/number.h similarity index 100% rename from examples/ch2-09-so-and-lib/use-clib-shared/number/number.h rename to vendor/gobook.examples/ch2-09-so-and-lib/use-clib-shared/number/number.h diff --git a/examples/ch2-09-so-and-lib/use-clib-static-v1/Makefile b/vendor/gobook.examples/ch2-09-so-and-lib/use-clib-static-v1/Makefile similarity index 100% rename from examples/ch2-09-so-and-lib/use-clib-static-v1/Makefile rename to vendor/gobook.examples/ch2-09-so-and-lib/use-clib-static-v1/Makefile diff --git a/examples/ch2-09-so-and-lib/use-clib-static-v1/main.go b/vendor/gobook.examples/ch2-09-so-and-lib/use-clib-static-v1/main.go similarity index 100% rename from examples/ch2-09-so-and-lib/use-clib-static-v1/main.go rename to vendor/gobook.examples/ch2-09-so-and-lib/use-clib-static-v1/main.go diff --git a/examples/ch2-09-so-and-lib/use-clib-static-v1/number/Makefile b/vendor/gobook.examples/ch2-09-so-and-lib/use-clib-static-v1/number/Makefile similarity index 100% rename from examples/ch2-09-so-and-lib/use-clib-static-v1/number/Makefile rename to vendor/gobook.examples/ch2-09-so-and-lib/use-clib-static-v1/number/Makefile diff --git a/examples/ch2-09-so-and-lib/use-clib-static-v1/number/number.c b/vendor/gobook.examples/ch2-09-so-and-lib/use-clib-static-v1/number/number.c similarity index 100% rename from examples/ch2-09-so-and-lib/use-clib-static-v1/number/number.c rename to vendor/gobook.examples/ch2-09-so-and-lib/use-clib-static-v1/number/number.c diff --git a/examples/ch2-09-so-and-lib/use-clib-static-v1/number/number.h b/vendor/gobook.examples/ch2-09-so-and-lib/use-clib-static-v1/number/number.h similarity index 100% rename from examples/ch2-09-so-and-lib/use-clib-static-v1/number/number.h rename to vendor/gobook.examples/ch2-09-so-and-lib/use-clib-static-v1/number/number.h diff --git a/examples/ch2-09-so-and-lib/use-clib-static-v2/Makefile b/vendor/gobook.examples/ch2-09-so-and-lib/use-clib-static-v2/Makefile similarity index 100% rename from examples/ch2-09-so-and-lib/use-clib-static-v2/Makefile rename to vendor/gobook.examples/ch2-09-so-and-lib/use-clib-static-v2/Makefile diff --git a/examples/ch2-09-so-and-lib/use-clib-static-v2/main.go b/vendor/gobook.examples/ch2-09-so-and-lib/use-clib-static-v2/main.go similarity index 100% rename from examples/ch2-09-so-and-lib/use-clib-static-v2/main.go rename to vendor/gobook.examples/ch2-09-so-and-lib/use-clib-static-v2/main.go diff --git a/examples/ch2-09-so-and-lib/use-clib-static-v2/number/Makefile b/vendor/gobook.examples/ch2-09-so-and-lib/use-clib-static-v2/number/Makefile similarity index 100% rename from examples/ch2-09-so-and-lib/use-clib-static-v2/number/Makefile rename to vendor/gobook.examples/ch2-09-so-and-lib/use-clib-static-v2/number/Makefile diff --git a/examples/ch2-09-so-and-lib/use-clib-static-v2/number/number.c b/vendor/gobook.examples/ch2-09-so-and-lib/use-clib-static-v2/number/number.c similarity index 100% rename from examples/ch2-09-so-and-lib/use-clib-static-v2/number/number.c rename to vendor/gobook.examples/ch2-09-so-and-lib/use-clib-static-v2/number/number.c diff --git a/examples/ch2-09-so-and-lib/use-clib-static-v2/number/number.h b/vendor/gobook.examples/ch2-09-so-and-lib/use-clib-static-v2/number/number.h similarity index 100% rename from examples/ch2-09-so-and-lib/use-clib-static-v2/number/number.h rename to vendor/gobook.examples/ch2-09-so-and-lib/use-clib-static-v2/number/number.h diff --git a/examples/ch2-09-so-and-lib/use-clib-static-v2/z_link_number_c.c b/vendor/gobook.examples/ch2-09-so-and-lib/use-clib-static-v2/z_link_number_c.c similarity index 100% rename from examples/ch2-09-so-and-lib/use-clib-static-v2/z_link_number_c.c rename to vendor/gobook.examples/ch2-09-so-and-lib/use-clib-static-v2/z_link_number_c.c diff --git a/examples/ch2-10-py/hello-py/Makefile b/vendor/gobook.examples/ch2-10-py/hello-py/Makefile similarity index 100% rename from examples/ch2-10-py/hello-py/Makefile rename to vendor/gobook.examples/ch2-10-py/hello-py/Makefile diff --git a/examples/ch2-10-py/hello-py/gopkg.h b/vendor/gobook.examples/ch2-10-py/hello-py/gopkg.h similarity index 100% rename from examples/ch2-10-py/hello-py/gopkg.h rename to vendor/gobook.examples/ch2-10-py/hello-py/gopkg.h diff --git a/examples/ch2-10-py/hello-py/main.go b/vendor/gobook.examples/ch2-10-py/hello-py/main.go similarity index 100% rename from examples/ch2-10-py/hello-py/main.go rename to vendor/gobook.examples/ch2-10-py/hello-py/main.go diff --git a/examples/ch2-10-py/hello-py/py3-config.go b/vendor/gobook.examples/ch2-10-py/hello-py/py3-config.go similarity index 100% rename from examples/ch2-10-py/hello-py/py3-config.go rename to vendor/gobook.examples/ch2-10-py/hello-py/py3-config.go diff --git a/examples/ch2-10-py/hello-so/Makefile b/vendor/gobook.examples/ch2-10-py/hello-so/Makefile similarity index 100% rename from examples/ch2-10-py/hello-so/Makefile rename to vendor/gobook.examples/ch2-10-py/hello-so/Makefile diff --git a/examples/ch2-10-py/hello-so/_test_so.c b/vendor/gobook.examples/ch2-10-py/hello-so/_test_so.c similarity index 100% rename from examples/ch2-10-py/hello-so/_test_so.c rename to vendor/gobook.examples/ch2-10-py/hello-so/_test_so.c diff --git a/examples/ch2-10-py/hello-so/hello.py b/vendor/gobook.examples/ch2-10-py/hello-so/hello.py similarity index 100% rename from examples/ch2-10-py/hello-so/hello.py rename to vendor/gobook.examples/ch2-10-py/hello-so/hello.py diff --git a/examples/ch2-10-py/hello-so/main.go b/vendor/gobook.examples/ch2-10-py/hello-so/main.go similarity index 100% rename from examples/ch2-10-py/hello-so/main.go rename to vendor/gobook.examples/ch2-10-py/hello-so/main.go diff --git a/examples/ch2-10-py/hello-so/say-hello.h b/vendor/gobook.examples/ch2-10-py/hello-so/say-hello.h similarity index 100% rename from examples/ch2-10-py/hello-so/say-hello.h rename to vendor/gobook.examples/ch2-10-py/hello-so/say-hello.h diff --git a/examples/ch2-xx-08/hello-swig-v1/Makefile b/vendor/gobook.examples/ch2-xx-08/hello-swig-v1/Makefile similarity index 100% rename from examples/ch2-xx-08/hello-swig-v1/Makefile rename to vendor/gobook.examples/ch2-xx-08/hello-swig-v1/Makefile diff --git a/examples/ch2-xx-08/hello-swig-v1/hello.cc b/vendor/gobook.examples/ch2-xx-08/hello-swig-v1/hello.cc similarity index 100% rename from examples/ch2-xx-08/hello-swig-v1/hello.cc rename to vendor/gobook.examples/ch2-xx-08/hello-swig-v1/hello.cc diff --git a/examples/ch2-xx-08/hello-swig-v1/hello.swigcxx b/vendor/gobook.examples/ch2-xx-08/hello-swig-v1/hello.swigcxx similarity index 100% rename from examples/ch2-xx-08/hello-swig-v1/hello.swigcxx rename to vendor/gobook.examples/ch2-xx-08/hello-swig-v1/hello.swigcxx diff --git a/examples/ch2-xx-08/hello-swig-v1/hello_test.go b/vendor/gobook.examples/ch2-xx-08/hello-swig-v1/hello_test.go similarity index 100% rename from examples/ch2-xx-08/hello-swig-v1/hello_test.go rename to vendor/gobook.examples/ch2-xx-08/hello-swig-v1/hello_test.go diff --git a/examples/ch2-xx-08/hello-swig-v1/runme.go b/vendor/gobook.examples/ch2-xx-08/hello-swig-v1/runme.go similarity index 100% rename from examples/ch2-xx-08/hello-swig-v1/runme.go rename to vendor/gobook.examples/ch2-xx-08/hello-swig-v1/runme.go diff --git a/examples/ch2-xx-08/hello-swig-v2/Makefile b/vendor/gobook.examples/ch2-xx-08/hello-swig-v2/Makefile similarity index 100% rename from examples/ch2-xx-08/hello-swig-v2/Makefile rename to vendor/gobook.examples/ch2-xx-08/hello-swig-v2/Makefile diff --git a/examples/ch2-xx-08/hello-swig-v2/hello.cc b/vendor/gobook.examples/ch2-xx-08/hello-swig-v2/hello.cc similarity index 100% rename from examples/ch2-xx-08/hello-swig-v2/hello.cc rename to vendor/gobook.examples/ch2-xx-08/hello-swig-v2/hello.cc diff --git a/examples/ch2-xx-08/hello-swig-v2/hello.go b/vendor/gobook.examples/ch2-xx-08/hello-swig-v2/hello.go similarity index 100% rename from examples/ch2-xx-08/hello-swig-v2/hello.go rename to vendor/gobook.examples/ch2-xx-08/hello-swig-v2/hello.go diff --git a/examples/ch2-xx-08/hello-swig-v2/hello.i b/vendor/gobook.examples/ch2-xx-08/hello-swig-v2/hello.i similarity index 100% rename from examples/ch2-xx-08/hello-swig-v2/hello.i rename to vendor/gobook.examples/ch2-xx-08/hello-swig-v2/hello.i diff --git a/examples/ch2-xx-08/hello-swig-v2/runme.go b/vendor/gobook.examples/ch2-xx-08/hello-swig-v2/runme.go similarity index 100% rename from examples/ch2-xx-08/hello-swig-v2/runme.go rename to vendor/gobook.examples/ch2-xx-08/hello-swig-v2/runme.go diff --git a/examples/ch2-xx-08/hello-swig-v2/swig_wrap.cc b/vendor/gobook.examples/ch2-xx-08/hello-swig-v2/swig_wrap.cc similarity index 100% rename from examples/ch2-xx-08/hello-swig-v2/swig_wrap.cc rename to vendor/gobook.examples/ch2-xx-08/hello-swig-v2/swig_wrap.cc diff --git a/examples/ch2-xx/hello/.gitignore b/vendor/gobook.examples/ch2-xx/hello/.gitignore similarity index 100% rename from examples/ch2-xx/hello/.gitignore rename to vendor/gobook.examples/ch2-xx/hello/.gitignore diff --git a/examples/ch2-xx/hello/Makefile b/vendor/gobook.examples/ch2-xx/hello/Makefile similarity index 100% rename from examples/ch2-xx/hello/Makefile rename to vendor/gobook.examples/ch2-xx/hello/Makefile diff --git a/examples/ch2-xx/hello/_obj/_cgo_export.c b/vendor/gobook.examples/ch2-xx/hello/_obj/_cgo_export.c similarity index 100% rename from examples/ch2-xx/hello/_obj/_cgo_export.c rename to vendor/gobook.examples/ch2-xx/hello/_obj/_cgo_export.c diff --git a/examples/ch2-xx/hello/_obj/_cgo_export.h b/vendor/gobook.examples/ch2-xx/hello/_obj/_cgo_export.h similarity index 100% rename from examples/ch2-xx/hello/_obj/_cgo_export.h rename to vendor/gobook.examples/ch2-xx/hello/_obj/_cgo_export.h diff --git a/examples/ch2-xx/hello/_obj/_cgo_flags b/vendor/gobook.examples/ch2-xx/hello/_obj/_cgo_flags similarity index 100% rename from examples/ch2-xx/hello/_obj/_cgo_flags rename to vendor/gobook.examples/ch2-xx/hello/_obj/_cgo_flags diff --git a/examples/ch2-xx/hello/_obj/_cgo_gotypes.go b/vendor/gobook.examples/ch2-xx/hello/_obj/_cgo_gotypes.go similarity index 100% rename from examples/ch2-xx/hello/_obj/_cgo_gotypes.go rename to vendor/gobook.examples/ch2-xx/hello/_obj/_cgo_gotypes.go diff --git a/examples/ch2-xx/hello/_obj/_cgo_main.c b/vendor/gobook.examples/ch2-xx/hello/_obj/_cgo_main.c similarity index 100% rename from examples/ch2-xx/hello/_obj/_cgo_main.c rename to vendor/gobook.examples/ch2-xx/hello/_obj/_cgo_main.c diff --git a/examples/ch2-xx/hello/_obj/hello.cgo1.go b/vendor/gobook.examples/ch2-xx/hello/_obj/hello.cgo1.go similarity index 100% rename from examples/ch2-xx/hello/_obj/hello.cgo1.go rename to vendor/gobook.examples/ch2-xx/hello/_obj/hello.cgo1.go diff --git a/examples/ch2-xx/hello/_obj/hello.cgo2.c b/vendor/gobook.examples/ch2-xx/hello/_obj/hello.cgo2.c similarity index 100% rename from examples/ch2-xx/hello/_obj/hello.cgo2.c rename to vendor/gobook.examples/ch2-xx/hello/_obj/hello.cgo2.c diff --git a/examples/ch2-xx/hello/hello.go b/vendor/gobook.examples/ch2-xx/hello/hello.go similarity index 100% rename from examples/ch2-xx/hello/hello.go rename to vendor/gobook.examples/ch2-xx/hello/hello.go diff --git a/examples/ch3-01-quick-guide/id-01/pkg.go b/vendor/gobook.examples/ch3-01-quick-guide/id-01/pkg.go similarity index 100% rename from examples/ch3-01-quick-guide/id-01/pkg.go rename to vendor/gobook.examples/ch3-01-quick-guide/id-01/pkg.go diff --git a/examples/ch3-01-quick-guide/id-01/runme.go b/vendor/gobook.examples/ch3-01-quick-guide/id-01/runme.go similarity index 100% rename from examples/ch3-01-quick-guide/id-01/runme.go rename to vendor/gobook.examples/ch3-01-quick-guide/id-01/runme.go diff --git a/examples/ch3-01-quick-guide/id-02/pkg.go b/vendor/gobook.examples/ch3-01-quick-guide/id-02/pkg.go similarity index 100% rename from examples/ch3-01-quick-guide/id-02/pkg.go rename to vendor/gobook.examples/ch3-01-quick-guide/id-02/pkg.go diff --git a/examples/ch3-01-quick-guide/id-02/pkg_amd64.s b/vendor/gobook.examples/ch3-01-quick-guide/id-02/pkg_amd64.s similarity index 100% rename from examples/ch3-01-quick-guide/id-02/pkg_amd64.s rename to vendor/gobook.examples/ch3-01-quick-guide/id-02/pkg_amd64.s diff --git a/examples/ch3-01-quick-guide/id-02/runme.go b/vendor/gobook.examples/ch3-01-quick-guide/id-02/runme.go similarity index 100% rename from examples/ch3-01-quick-guide/id-02/runme.go rename to vendor/gobook.examples/ch3-01-quick-guide/id-02/runme.go diff --git a/examples/ch3-01-quick-guide/main-01/Makefile b/vendor/gobook.examples/ch3-01-quick-guide/main-01/Makefile similarity index 100% rename from examples/ch3-01-quick-guide/main-01/Makefile rename to vendor/gobook.examples/ch3-01-quick-guide/main-01/Makefile diff --git a/examples/ch3-01-quick-guide/main-01/main.go b/vendor/gobook.examples/ch3-01-quick-guide/main-01/main.go similarity index 100% rename from examples/ch3-01-quick-guide/main-01/main.go rename to vendor/gobook.examples/ch3-01-quick-guide/main-01/main.go diff --git a/examples/ch3-01-quick-guide/main-01/main_amd64.s b/vendor/gobook.examples/ch3-01-quick-guide/main-01/main_amd64.s similarity index 100% rename from examples/ch3-01-quick-guide/main-01/main_amd64.s rename to vendor/gobook.examples/ch3-01-quick-guide/main-01/main_amd64.s diff --git a/examples/ch3-01-quick-guide/str-01/pkg.go b/vendor/gobook.examples/ch3-01-quick-guide/str-01/pkg.go similarity index 100% rename from examples/ch3-01-quick-guide/str-01/pkg.go rename to vendor/gobook.examples/ch3-01-quick-guide/str-01/pkg.go diff --git a/examples/ch3-01-quick-guide/str-02/pkg.go b/vendor/gobook.examples/ch3-01-quick-guide/str-02/pkg.go similarity index 100% rename from examples/ch3-01-quick-guide/str-02/pkg.go rename to vendor/gobook.examples/ch3-01-quick-guide/str-02/pkg.go diff --git a/examples/ch3-01-quick-guide/str-02/pkg_amd64.s b/vendor/gobook.examples/ch3-01-quick-guide/str-02/pkg_amd64.s similarity index 100% rename from examples/ch3-01-quick-guide/str-02/pkg_amd64.s rename to vendor/gobook.examples/ch3-01-quick-guide/str-02/pkg_amd64.s diff --git a/examples/ch3-01-quick-guide/str-02/runme.go b/vendor/gobook.examples/ch3-01-quick-guide/str-02/runme.go similarity index 100% rename from examples/ch3-01-quick-guide/str-02/runme.go rename to vendor/gobook.examples/ch3-01-quick-guide/str-02/runme.go diff --git a/examples/ch3-01-quick-guide/str-03/pkg.go b/vendor/gobook.examples/ch3-01-quick-guide/str-03/pkg.go similarity index 100% rename from examples/ch3-01-quick-guide/str-03/pkg.go rename to vendor/gobook.examples/ch3-01-quick-guide/str-03/pkg.go diff --git a/examples/ch3-01-quick-guide/str-03/pkg_amd64.s b/vendor/gobook.examples/ch3-01-quick-guide/str-03/pkg_amd64.s similarity index 100% rename from examples/ch3-01-quick-guide/str-03/pkg_amd64.s rename to vendor/gobook.examples/ch3-01-quick-guide/str-03/pkg_amd64.s diff --git a/examples/ch3-01-quick-guide/str-03/runme.go b/vendor/gobook.examples/ch3-01-quick-guide/str-03/runme.go similarity index 100% rename from examples/ch3-01-quick-guide/str-03/runme.go rename to vendor/gobook.examples/ch3-01-quick-guide/str-03/runme.go diff --git a/examples/ch3-xx/add/add.go b/vendor/gobook.examples/ch3-xx/add/add.go similarity index 100% rename from examples/ch3-xx/add/add.go rename to vendor/gobook.examples/ch3-xx/add/add.go diff --git a/examples/ch3-xx/add/add_asm.go b/vendor/gobook.examples/ch3-xx/add/add_asm.go similarity index 100% rename from examples/ch3-xx/add/add_asm.go rename to vendor/gobook.examples/ch3-xx/add/add_asm.go diff --git a/examples/ch3-xx/add/add_asm_amd64.s b/vendor/gobook.examples/ch3-xx/add/add_asm_amd64.s similarity index 100% rename from examples/ch3-xx/add/add_asm_amd64.s rename to vendor/gobook.examples/ch3-xx/add/add_asm_amd64.s diff --git a/examples/ch3-xx/add/add_asm_generic.go b/vendor/gobook.examples/ch3-xx/add/add_asm_generic.go similarity index 100% rename from examples/ch3-xx/add/add_asm_generic.go rename to vendor/gobook.examples/ch3-xx/add/add_asm_generic.go diff --git a/examples/ch3-xx/add/add_test.go b/vendor/gobook.examples/ch3-xx/add/add_test.go similarity index 100% rename from examples/ch3-xx/add/add_test.go rename to vendor/gobook.examples/ch3-xx/add/add_test.go diff --git a/examples/ch3-xx/add/runme.go b/vendor/gobook.examples/ch3-xx/add/runme.go similarity index 100% rename from examples/ch3-xx/add/runme.go rename to vendor/gobook.examples/ch3-xx/add/runme.go diff --git a/examples/ch3-xx/binary_search/binary_search.go b/vendor/gobook.examples/ch3-xx/binary_search/binary_search.go similarity index 100% rename from examples/ch3-xx/binary_search/binary_search.go rename to vendor/gobook.examples/ch3-xx/binary_search/binary_search.go diff --git a/examples/ch3-xx/binary_search/binary_search_amd64.s b/vendor/gobook.examples/ch3-xx/binary_search/binary_search_amd64.s similarity index 100% rename from examples/ch3-xx/binary_search/binary_search_amd64.s rename to vendor/gobook.examples/ch3-xx/binary_search/binary_search_amd64.s diff --git a/examples/ch3-xx/binary_search/binary_search_test.go b/vendor/gobook.examples/ch3-xx/binary_search/binary_search_test.go similarity index 100% rename from examples/ch3-xx/binary_search/binary_search_test.go rename to vendor/gobook.examples/ch3-xx/binary_search/binary_search_test.go diff --git a/examples/ch3-xx/globalvar/asm_amd64.s b/vendor/gobook.examples/ch3-xx/globalvar/asm_amd64.s similarity index 100% rename from examples/ch3-xx/globalvar/asm_amd64.s rename to vendor/gobook.examples/ch3-xx/globalvar/asm_amd64.s diff --git a/examples/ch3-xx/globalvar/globalvar.go b/vendor/gobook.examples/ch3-xx/globalvar/globalvar.go similarity index 100% rename from examples/ch3-xx/globalvar/globalvar.go rename to vendor/gobook.examples/ch3-xx/globalvar/globalvar.go diff --git a/examples/ch3-xx/globalvar/runme.go b/vendor/gobook.examples/ch3-xx/globalvar/runme.go similarity index 100% rename from examples/ch3-xx/globalvar/runme.go rename to vendor/gobook.examples/ch3-xx/globalvar/runme.go diff --git a/examples/ch3-xx/hello/hello.go b/vendor/gobook.examples/ch3-xx/hello/hello.go similarity index 100% rename from examples/ch3-xx/hello/hello.go rename to vendor/gobook.examples/ch3-xx/hello/hello.go diff --git a/examples/ch3-xx/hello/hello_amd64.s b/vendor/gobook.examples/ch3-xx/hello/hello_amd64.s similarity index 100% rename from examples/ch3-xx/hello/hello_amd64.s rename to vendor/gobook.examples/ch3-xx/hello/hello_amd64.s diff --git a/examples/ch3-xx/hello/runme.go b/vendor/gobook.examples/ch3-xx/hello/runme.go similarity index 100% rename from examples/ch3-xx/hello/runme.go rename to vendor/gobook.examples/ch3-xx/hello/runme.go diff --git a/examples/ch3-xx/ifelse/ifelse.go b/vendor/gobook.examples/ch3-xx/ifelse/ifelse.go similarity index 100% rename from examples/ch3-xx/ifelse/ifelse.go rename to vendor/gobook.examples/ch3-xx/ifelse/ifelse.go diff --git a/examples/ch3-xx/ifelse/ifelse_ams_amd64.s b/vendor/gobook.examples/ch3-xx/ifelse/ifelse_ams_amd64.s similarity index 100% rename from examples/ch3-xx/ifelse/ifelse_ams_amd64.s rename to vendor/gobook.examples/ch3-xx/ifelse/ifelse_ams_amd64.s diff --git a/examples/ch3-xx/ifelse/ifelse_test.go b/vendor/gobook.examples/ch3-xx/ifelse/ifelse_test.go similarity index 100% rename from examples/ch3-xx/ifelse/ifelse_test.go rename to vendor/gobook.examples/ch3-xx/ifelse/ifelse_test.go diff --git a/examples/ch3-xx/ifelse/runme.go b/vendor/gobook.examples/ch3-xx/ifelse/runme.go similarity index 100% rename from examples/ch3-xx/ifelse/runme.go rename to vendor/gobook.examples/ch3-xx/ifelse/runme.go diff --git a/examples/ch3-xx/instr/bench_test.go b/vendor/gobook.examples/ch3-xx/instr/bench_test.go similarity index 100% rename from examples/ch3-xx/instr/bench_test.go rename to vendor/gobook.examples/ch3-xx/instr/bench_test.go diff --git a/examples/ch3-xx/instr/instr.go b/vendor/gobook.examples/ch3-xx/instr/instr.go similarity index 100% rename from examples/ch3-xx/instr/instr.go rename to vendor/gobook.examples/ch3-xx/instr/instr.go diff --git a/examples/ch3-xx/instr/instr_amd64.s b/vendor/gobook.examples/ch3-xx/instr/instr_amd64.s similarity index 100% rename from examples/ch3-xx/instr/instr_amd64.s rename to vendor/gobook.examples/ch3-xx/instr/instr_amd64.s diff --git a/examples/ch3-xx/loop/loop.go b/vendor/gobook.examples/ch3-xx/loop/loop.go similarity index 100% rename from examples/ch3-xx/loop/loop.go rename to vendor/gobook.examples/ch3-xx/loop/loop.go diff --git a/examples/ch3-xx/loop/loop_asm_amd64.s b/vendor/gobook.examples/ch3-xx/loop/loop_asm_amd64.s similarity index 100% rename from examples/ch3-xx/loop/loop_asm_amd64.s rename to vendor/gobook.examples/ch3-xx/loop/loop_asm_amd64.s diff --git a/examples/ch3-xx/loop/loop_test.go b/vendor/gobook.examples/ch3-xx/loop/loop_test.go similarity index 100% rename from examples/ch3-xx/loop/loop_test.go rename to vendor/gobook.examples/ch3-xx/loop/loop_test.go diff --git a/examples/ch3-xx/loop/runme.go b/vendor/gobook.examples/ch3-xx/loop/runme.go similarity index 100% rename from examples/ch3-xx/loop/runme.go rename to vendor/gobook.examples/ch3-xx/loop/runme.go diff --git a/examples/ch3-xx/min/min.go b/vendor/gobook.examples/ch3-xx/min/min.go similarity index 100% rename from examples/ch3-xx/min/min.go rename to vendor/gobook.examples/ch3-xx/min/min.go diff --git a/examples/ch3-xx/min/min_asm_amd64.s b/vendor/gobook.examples/ch3-xx/min/min_asm_amd64.s similarity index 100% rename from examples/ch3-xx/min/min_asm_amd64.s rename to vendor/gobook.examples/ch3-xx/min/min_asm_amd64.s diff --git a/examples/ch3-xx/min/min_test.go b/vendor/gobook.examples/ch3-xx/min/min_test.go similarity index 100% rename from examples/ch3-xx/min/min_test.go rename to vendor/gobook.examples/ch3-xx/min/min_test.go diff --git a/examples/ch3-xx/min/runme.go b/vendor/gobook.examples/ch3-xx/min/runme.go similarity index 100% rename from examples/ch3-xx/min/runme.go rename to vendor/gobook.examples/ch3-xx/min/runme.go diff --git a/examples/ch3-xx/slice/runme.go b/vendor/gobook.examples/ch3-xx/slice/runme.go similarity index 100% rename from examples/ch3-xx/slice/runme.go rename to vendor/gobook.examples/ch3-xx/slice/runme.go diff --git a/examples/ch3-xx/slice/slice.go b/vendor/gobook.examples/ch3-xx/slice/slice.go similarity index 100% rename from examples/ch3-xx/slice/slice.go rename to vendor/gobook.examples/ch3-xx/slice/slice.go diff --git a/examples/ch3-xx/slice/slice_asm_amd64.s b/vendor/gobook.examples/ch3-xx/slice/slice_asm_amd64.s similarity index 100% rename from examples/ch3-xx/slice/slice_asm_amd64.s rename to vendor/gobook.examples/ch3-xx/slice/slice_asm_amd64.s diff --git a/examples/ch3-xx/slice/slice_test.go b/vendor/gobook.examples/ch3-xx/slice/slice_test.go similarity index 100% rename from examples/ch3-xx/slice/slice_test.go rename to vendor/gobook.examples/ch3-xx/slice/slice_test.go diff --git a/examples/ch3-xx/stackmap/stackmap.go b/vendor/gobook.examples/ch3-xx/stackmap/stackmap.go similarity index 100% rename from examples/ch3-xx/stackmap/stackmap.go rename to vendor/gobook.examples/ch3-xx/stackmap/stackmap.go diff --git a/examples/ch3-xx/stackmap/stackmap_amd64.s b/vendor/gobook.examples/ch3-xx/stackmap/stackmap_amd64.s similarity index 100% rename from examples/ch3-xx/stackmap/stackmap_amd64.s rename to vendor/gobook.examples/ch3-xx/stackmap/stackmap_amd64.s diff --git a/examples/ch3-xx/stackmap/stackmap_test.go b/vendor/gobook.examples/ch3-xx/stackmap/stackmap_test.go similarity index 100% rename from examples/ch3-xx/stackmap/stackmap_test.go rename to vendor/gobook.examples/ch3-xx/stackmap/stackmap_test.go diff --git a/examples/ch3-xx/sum/sum.go b/vendor/gobook.examples/ch3-xx/sum/sum.go similarity index 100% rename from examples/ch3-xx/sum/sum.go rename to vendor/gobook.examples/ch3-xx/sum/sum.go diff --git a/examples/ch3-xx/sum/sum_amd64.s b/vendor/gobook.examples/ch3-xx/sum/sum_amd64.s similarity index 100% rename from examples/ch3-xx/sum/sum_amd64.s rename to vendor/gobook.examples/ch3-xx/sum/sum_amd64.s diff --git a/examples/ch3-xx/sum/sum_test.go b/vendor/gobook.examples/ch3-xx/sum/sum_test.go similarity index 100% rename from examples/ch3-xx/sum/sum_test.go rename to vendor/gobook.examples/ch3-xx/sum/sum_test.go diff --git a/examples/ch3-xx/vector/sum_amd64.s b/vendor/gobook.examples/ch3-xx/vector/sum_amd64.s similarity index 100% rename from examples/ch3-xx/vector/sum_amd64.s rename to vendor/gobook.examples/ch3-xx/vector/sum_amd64.s diff --git a/examples/ch3-xx/vector/vector.go b/vendor/gobook.examples/ch3-xx/vector/vector.go similarity index 100% rename from examples/ch3-xx/vector/vector.go rename to vendor/gobook.examples/ch3-xx/vector/vector.go diff --git a/examples/ch3-xx/vector/vector_amd64.s b/vendor/gobook.examples/ch3-xx/vector/vector_amd64.s similarity index 100% rename from examples/ch3-xx/vector/vector_amd64.s rename to vendor/gobook.examples/ch3-xx/vector/vector_amd64.s diff --git a/examples/ch3-xx/vector/vector_test.go b/vendor/gobook.examples/ch3-xx/vector/vector_test.go similarity index 100% rename from examples/ch3-xx/vector/vector_test.go rename to vendor/gobook.examples/ch3-xx/vector/vector_test.go diff --git a/examples/ch4-01-rpc-inro/hello-client-v1/main.go b/vendor/gobook.examples/ch4-01-rpc-intro/hello-client-v1/main.go similarity index 100% rename from examples/ch4-01-rpc-inro/hello-client-v1/main.go rename to vendor/gobook.examples/ch4-01-rpc-intro/hello-client-v1/main.go diff --git a/examples/ch4-01-rpc-inro/hello-server-v1/main.go b/vendor/gobook.examples/ch4-01-rpc-intro/hello-server-v1/main.go similarity index 100% rename from examples/ch4-01-rpc-inro/hello-server-v1/main.go rename to vendor/gobook.examples/ch4-01-rpc-intro/hello-server-v1/main.go diff --git a/examples/ch4-01-rpc-inro/hello-service-v2/api/hello.go b/vendor/gobook.examples/ch4-01-rpc-intro/hello-service-v2/api/hello.go similarity index 100% rename from examples/ch4-01-rpc-inro/hello-service-v2/api/hello.go rename to vendor/gobook.examples/ch4-01-rpc-intro/hello-service-v2/api/hello.go diff --git a/examples/ch4-01-rpc-inro/hello-service-v2/client/main.go b/vendor/gobook.examples/ch4-01-rpc-intro/hello-service-v2/client/main.go similarity index 100% rename from examples/ch4-01-rpc-inro/hello-service-v2/client/main.go rename to vendor/gobook.examples/ch4-01-rpc-intro/hello-service-v2/client/main.go diff --git a/examples/ch4-01-rpc-inro/hello-service-v2/server/main.go b/vendor/gobook.examples/ch4-01-rpc-intro/hello-service-v2/server/main.go similarity index 100% rename from examples/ch4-01-rpc-inro/hello-service-v2/server/main.go rename to vendor/gobook.examples/ch4-01-rpc-intro/hello-service-v2/server/main.go diff --git a/examples/ch4-01-rpc-inro/hello-service-v3/client/main.go b/vendor/gobook.examples/ch4-01-rpc-intro/hello-service-v3/client/main.go similarity index 100% rename from examples/ch4-01-rpc-inro/hello-service-v3/client/main.go rename to vendor/gobook.examples/ch4-01-rpc-intro/hello-service-v3/client/main.go diff --git a/examples/ch4-01-rpc-inro/hello-service-v3/server-on-http/main.go b/vendor/gobook.examples/ch4-01-rpc-intro/hello-service-v3/server-on-http/main.go similarity index 100% rename from examples/ch4-01-rpc-inro/hello-service-v3/server-on-http/main.go rename to vendor/gobook.examples/ch4-01-rpc-intro/hello-service-v3/server-on-http/main.go diff --git a/examples/ch4-01-rpc-inro/hello-service-v3/server/main.go b/vendor/gobook.examples/ch4-01-rpc-intro/hello-service-v3/server/main.go similarity index 100% rename from examples/ch4-01-rpc-inro/hello-service-v3/server/main.go rename to vendor/gobook.examples/ch4-01-rpc-intro/hello-service-v3/server/main.go diff --git a/vendor/gobook.examples/ch4-02-proto/hello-server/main.go b/vendor/gobook.examples/ch4-02-proto/hello-server/main.go new file mode 100644 index 0000000..abfeb0d --- /dev/null +++ b/vendor/gobook.examples/ch4-02-proto/hello-server/main.go @@ -0,0 +1,34 @@ +package main + +import ( + "log" + "net" + "net/rpc" + + pb "gobook.examples/ch4-02-proto/hello.pb" +) + +type HelloService struct{} + +func (p *HelloService) Hello(request pb.String, reply *pb.String) error { + reply.Value = "hello:" + request.GetValue() + return nil +} + +func main() { + rpc.Register(new(HelloService)) + + listener, err := net.Listen("tcp", ":1234") + if err != nil { + log.Fatal("ListenTCP error:", err) + } + + for { + conn, err := listener.Accept() + if err != nil { + log.Fatal("Accept error:", err) + } + + go rpc.ServeConn(conn) + } +} diff --git a/vendor/gobook.examples/ch4-02-proto/hello.pb/Makefile b/vendor/gobook.examples/ch4-02-proto/hello.pb/Makefile new file mode 100644 index 0000000..fd4ecff --- /dev/null +++ b/vendor/gobook.examples/ch4-02-proto/hello.pb/Makefile @@ -0,0 +1,6 @@ +generate: + protoc --go_out=. hello.proto + +clean: + -rm *.pb.go + diff --git a/examples/ch4-02-proto/proto-v1/hello.pb.go b/vendor/gobook.examples/ch4-02-proto/hello.pb/hello.pb.go similarity index 54% rename from examples/ch4-02-proto/proto-v1/hello.pb.go rename to vendor/gobook.examples/ch4-02-proto/hello.pb/hello.pb.go index 73d46ea..6b41f4f 100644 --- a/examples/ch4-02-proto/proto-v1/hello.pb.go +++ b/vendor/gobook.examples/ch4-02-proto/hello.pb/hello.pb.go @@ -1,14 +1,12 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // source: hello.proto -package main +package hello import proto "github.com/golang/protobuf/proto" import fmt "fmt" import math "math" -import "net/rpc" - // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf @@ -21,7 +19,7 @@ var _ = math.Inf const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package type String struct { - Value string `protobuf:"bytes,1,opt,name=value" json:"value,omitempty"` + Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -31,7 +29,7 @@ func (m *String) Reset() { *m = String{} } func (m *String) String() string { return proto.CompactTextString(m) } func (*String) ProtoMessage() {} func (*String) Descriptor() ([]byte, []int) { - return fileDescriptor_hello_5dd9d59ecabc789f, []int{0} + return fileDescriptor_hello_d230db15bc3b8c9b, []int{0} } func (m *String) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_String.Unmarshal(m, b) @@ -59,47 +57,18 @@ func (m *String) GetValue() string { } func init() { - proto.RegisterType((*String)(nil), "main.String") + proto.RegisterType((*String)(nil), "hello.String") } -type HelloServiceInterface interface { - Hello(in *String, out *String) error -} +func init() { proto.RegisterFile("hello.proto", fileDescriptor_hello_d230db15bc3b8c9b) } -func RegisterHelloService(srv *rpc.Server, x HelloService) error { - if err := srv.RegisterName("HelloService", x); err != nil { - return err - } - return nil -} - -type HelloServiceClient struct { - *rpc.Client -} - -var _ HelloServiceInterface = (*HelloServiceClient)(nil) - -func DialHelloService(network, address string) (*HelloServiceClient, error) { - c, err := rpc.Dial(network, address) - if err != nil { - return nil, err - } - return &HelloServiceClient{Client: c}, nil -} - -func (p *HelloServiceClient) Hello(in *String, out *String) error { - return p.Client.Call("HelloService.Hello", in, out) -} - -func init() { proto.RegisterFile("hello.proto", fileDescriptor_hello_5dd9d59ecabc789f) } - -var fileDescriptor_hello_5dd9d59ecabc789f = []byte{ - // 107 bytes of a gzipped FileDescriptorProto +var fileDescriptor_hello_d230db15bc3b8c9b = []byte{ + // 103 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xce, 0x48, 0xcd, 0xc9, - 0xc9, 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0xc9, 0x4d, 0xcc, 0xcc, 0x53, 0x92, 0xe3, - 0x62, 0x0b, 0x2e, 0x29, 0xca, 0xcc, 0x4b, 0x17, 0x12, 0xe1, 0x62, 0x2d, 0x4b, 0xcc, 0x29, 0x4d, - 0x95, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0c, 0x82, 0x70, 0x8c, 0x8c, 0xb9, 0x78, 0x3c, 0x40, 0x9a, - 0x82, 0x53, 0x8b, 0xca, 0x32, 0x93, 0x53, 0x85, 0x94, 0xb9, 0x58, 0xc1, 0x7c, 0x21, 0x1e, 0x3d, - 0x90, 0x7e, 0x3d, 0x88, 0x66, 0x29, 0x14, 0x5e, 0x12, 0x1b, 0xd8, 0x06, 0x63, 0x40, 0x00, 0x00, - 0x00, 0xff, 0xff, 0xa1, 0x2a, 0xa2, 0x5a, 0x70, 0x00, 0x00, 0x00, + 0xc9, 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x05, 0x73, 0x94, 0xe4, 0xb8, 0xd8, 0x82, + 0x4b, 0x8a, 0x32, 0xf3, 0xd2, 0x85, 0x44, 0xb8, 0x58, 0xcb, 0x12, 0x73, 0x4a, 0x53, 0x25, 0x18, + 0x15, 0x18, 0x35, 0x38, 0x83, 0x20, 0x1c, 0x23, 0x53, 0x2e, 0x1e, 0x0f, 0x90, 0xc2, 0xe0, 0xd4, + 0xa2, 0xb2, 0xcc, 0xe4, 0x54, 0x21, 0x55, 0x2e, 0x56, 0x30, 0x5f, 0x88, 0x57, 0x0f, 0x62, 0x1a, + 0x44, 0xb7, 0x14, 0x2a, 0x37, 0x89, 0x0d, 0x6c, 0x89, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0x15, + 0xe8, 0xb1, 0xcc, 0x73, 0x00, 0x00, 0x00, } diff --git a/examples/ch4-02-proto/proto-v1/hello.proto b/vendor/gobook.examples/ch4-02-proto/hello.pb/hello.proto similarity index 89% rename from examples/ch4-02-proto/proto-v1/hello.proto rename to vendor/gobook.examples/ch4-02-proto/hello.pb/hello.proto index d5542f9..e9aeb89 100644 --- a/examples/ch4-02-proto/proto-v1/hello.proto +++ b/vendor/gobook.examples/ch4-02-proto/hello.pb/hello.proto @@ -1,6 +1,6 @@ syntax = "proto3"; -package main; +package hello; message String { string value = 1; diff --git a/examples/ch4-02-proto/protoc-gen-go-netrpc/main.go b/vendor/gobook.examples/ch4-02-proto/protoc-gen-go-netrpc/main.go similarity index 100% rename from examples/ch4-02-proto/protoc-gen-go-netrpc/main.go rename to vendor/gobook.examples/ch4-02-proto/protoc-gen-go-netrpc/main.go diff --git a/examples/ch4-02-proto/protoc-gen-go-netrpc/netprpc.go b/vendor/gobook.examples/ch4-02-proto/protoc-gen-go-netrpc/netprpc.go similarity index 100% rename from examples/ch4-02-proto/protoc-gen-go-netrpc/netprpc.go rename to vendor/gobook.examples/ch4-02-proto/protoc-gen-go-netrpc/netprpc.go diff --git a/vendor/gobook.examples/ch4-03-rpc-hack/rpc-auth/client/main.go b/vendor/gobook.examples/ch4-03-rpc-hack/rpc-auth/client/main.go new file mode 100644 index 0000000..07209b7 --- /dev/null +++ b/vendor/gobook.examples/ch4-03-rpc-hack/rpc-auth/client/main.go @@ -0,0 +1,37 @@ +package main + +import ( + "fmt" + "log" + "net/rpc" +) + +func main() { + client, err := rpc.Dial("tcp", "localhost:1234") + if err != nil { + log.Fatal("dialing:", err) + } + + var reply string + + err = client.Call("HelloService.Login", "abc", &reply) + if err != nil { + log.Println(err) + } else { + log.Println("login ok") + } + + err = client.Call("HelloService.Login", "user:password", &reply) + if err != nil { + log.Println(err) + } else { + log.Println("login ok") + } + + err = client.Call("HelloService.Hello", "hello", &reply) + if err != nil { + log.Fatal(err) + } + + fmt.Println(reply) +} diff --git a/examples/ch4-03-rpc-hack/rpc-auth/main.go b/vendor/gobook.examples/ch4-03-rpc-hack/rpc-auth/main.go similarity index 100% rename from examples/ch4-03-rpc-hack/rpc-auth/main.go rename to vendor/gobook.examples/ch4-03-rpc-hack/rpc-auth/main.go diff --git a/vendor/gobook.examples/ch4-03-rpc-hack/rpc-auth/server/main.go b/vendor/gobook.examples/ch4-03-rpc-hack/rpc-auth/server/main.go new file mode 100644 index 0000000..281b1cf --- /dev/null +++ b/vendor/gobook.examples/ch4-03-rpc-hack/rpc-auth/server/main.go @@ -0,0 +1,53 @@ +package main + +import ( + "fmt" + "log" + "net" + "net/rpc" +) + +type HelloService struct { + conn net.Conn + isLogin bool +} + +func ServeHelloService(conn net.Conn) { + p := rpc.NewServer() + p.Register(&HelloService{conn: conn}) + p.ServeConn(conn) +} + +func (p *HelloService) Login(request string, reply *string) error { + if request != "user:password" { + log.Println("login failed") + return fmt.Errorf("auth failed") + } + log.Println("login ok") + p.isLogin = true + return nil +} + +func (p *HelloService) Hello(request string, reply *string) error { + if !p.isLogin { + return fmt.Errorf("please login") + } + *reply = "hello:" + request + ", from" + p.conn.RemoteAddr().String() + return nil +} + +func main() { + listener, err := net.Listen("tcp", ":1234") + if err != nil { + log.Fatal("ListenTCP error:", err) + } + + for { + conn, err := listener.Accept() + if err != nil { + log.Fatal("Accept error:", err) + } + + go ServeHelloService(conn) + } +} diff --git a/vendor/gobook.examples/ch4-03-rpc-hack/rpc-context/client/main.go b/vendor/gobook.examples/ch4-03-rpc-hack/rpc-context/client/main.go new file mode 100644 index 0000000..7e655ec --- /dev/null +++ b/vendor/gobook.examples/ch4-03-rpc-hack/rpc-context/client/main.go @@ -0,0 +1,22 @@ +package main + +import ( + "fmt" + "log" + "net/rpc" +) + +func main() { + client, err := rpc.Dial("tcp", "localhost:1234") + if err != nil { + log.Fatal("dialing:", err) + } + + var reply string + err = client.Call("HelloService.Hello", "hello", &reply) + if err != nil { + log.Fatal(err) + } + + fmt.Println(reply) +} diff --git a/vendor/gobook.examples/ch4-03-rpc-hack/rpc-context/server/main.go b/vendor/gobook.examples/ch4-03-rpc-hack/rpc-context/server/main.go new file mode 100644 index 0000000..fc7b2f0 --- /dev/null +++ b/vendor/gobook.examples/ch4-03-rpc-hack/rpc-context/server/main.go @@ -0,0 +1,38 @@ +package main + +import ( + "log" + "net" + "net/rpc" +) + +type HelloService struct { + conn net.Conn +} + +func ServeHelloService(conn net.Conn) { + p := rpc.NewServer() + p.Register(&HelloService{conn: conn}) + p.ServeConn(conn) +} + +func (p *HelloService) Hello(request string, reply *string) error { + *reply = "hello:" + request + ", from" + p.conn.RemoteAddr().String() + return nil +} + +func main() { + listener, err := net.Listen("tcp", ":1234") + if err != nil { + log.Fatal("ListenTCP error:", err) + } + + for { + conn, err := listener.Accept() + if err != nil { + log.Fatal("Accept error:", err) + } + + go ServeHelloService(conn) + } +} diff --git a/vendor/gobook.examples/ch4-03-rpc-hack/rpc-reverse/client/main.go b/vendor/gobook.examples/ch4-03-rpc-hack/rpc-reverse/client/main.go new file mode 100644 index 0000000..c8cc94a --- /dev/null +++ b/vendor/gobook.examples/ch4-03-rpc-hack/rpc-reverse/client/main.go @@ -0,0 +1,39 @@ +package main + +import ( + "fmt" + "log" + "net" + "net/rpc" +) + +func main() { + listener, err := net.Listen("tcp", ":1234") + if err != nil { + log.Fatal("ListenTCP error:", err) + } + + clientChan := make(chan *rpc.Client) + + go func() { + for { + conn, err := listener.Accept() + if err != nil { + log.Fatal("Accept error:", err) + } + + clientChan <- rpc.NewClient(conn) + } + }() + + client := <-clientChan + defer client.Close() + + var reply string + err = client.Call("HelloService.Hello", "hello", &reply) + if err != nil { + log.Fatal(err) + } + + fmt.Println(reply) +} diff --git a/vendor/gobook.examples/ch4-03-rpc-hack/rpc-reverse/server/main.go b/vendor/gobook.examples/ch4-03-rpc-hack/rpc-reverse/server/main.go new file mode 100644 index 0000000..b8c95b7 --- /dev/null +++ b/vendor/gobook.examples/ch4-03-rpc-hack/rpc-reverse/server/main.go @@ -0,0 +1,29 @@ +package main + +import ( + "net" + "net/rpc" + "time" +) + +type HelloService struct{} + +func (p *HelloService) Hello(request string, reply *string) error { + *reply = "hello:" + request + return nil +} + +func main() { + rpc.Register(new(HelloService)) + + for { + conn, _ := net.Dial("tcp", "localhost:1234") + if conn == nil { + time.Sleep(time.Second) + continue + } + + rpc.ServeConn(conn) + conn.Close() + } +} diff --git a/examples/ch4-04-grpc/auth/dummy.txt b/vendor/gobook.examples/ch4-04-grpc/auth/dummy.txt similarity index 100% rename from examples/ch4-04-grpc/auth/dummy.txt rename to vendor/gobook.examples/ch4-04-grpc/auth/dummy.txt diff --git a/vendor/gobook.examples/ch4-04-grpc/basic/client/Makefile b/vendor/gobook.examples/ch4-04-grpc/basic/client/Makefile new file mode 100644 index 0000000..f67153b --- /dev/null +++ b/vendor/gobook.examples/ch4-04-grpc/basic/client/Makefile @@ -0,0 +1,2 @@ +default: + protoc --go_out=plugins=grpc:. hello.proto diff --git a/vendor/gobook.examples/ch4-04-grpc/basic/client/hello.pb.go b/vendor/gobook.examples/ch4-04-grpc/basic/client/hello.pb.go new file mode 100644 index 0000000..5918273 --- /dev/null +++ b/vendor/gobook.examples/ch4-04-grpc/basic/client/hello.pb.go @@ -0,0 +1,218 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: hello.proto + +package main + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +import ( + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type String struct { + Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *String) Reset() { *m = String{} } +func (m *String) String() string { return proto.CompactTextString(m) } +func (*String) ProtoMessage() {} +func (*String) Descriptor() ([]byte, []int) { + return fileDescriptor_hello_8909b24da4a57d21, []int{0} +} +func (m *String) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_String.Unmarshal(m, b) +} +func (m *String) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_String.Marshal(b, m, deterministic) +} +func (dst *String) XXX_Merge(src proto.Message) { + xxx_messageInfo_String.Merge(dst, src) +} +func (m *String) XXX_Size() int { + return xxx_messageInfo_String.Size(m) +} +func (m *String) XXX_DiscardUnknown() { + xxx_messageInfo_String.DiscardUnknown(m) +} + +var xxx_messageInfo_String proto.InternalMessageInfo + +func (m *String) GetValue() string { + if m != nil { + return m.Value + } + return "" +} + +func init() { + proto.RegisterType((*String)(nil), "main.String") +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// HelloServiceClient is the client API for HelloService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type HelloServiceClient interface { + Hello(ctx context.Context, in *String, opts ...grpc.CallOption) (*String, error) + Channel(ctx context.Context, opts ...grpc.CallOption) (HelloService_ChannelClient, error) +} + +type helloServiceClient struct { + cc *grpc.ClientConn +} + +func NewHelloServiceClient(cc *grpc.ClientConn) HelloServiceClient { + return &helloServiceClient{cc} +} + +func (c *helloServiceClient) Hello(ctx context.Context, in *String, opts ...grpc.CallOption) (*String, error) { + out := new(String) + err := c.cc.Invoke(ctx, "/main.HelloService/Hello", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *helloServiceClient) Channel(ctx context.Context, opts ...grpc.CallOption) (HelloService_ChannelClient, error) { + stream, err := c.cc.NewStream(ctx, &_HelloService_serviceDesc.Streams[0], "/main.HelloService/Channel", opts...) + if err != nil { + return nil, err + } + x := &helloServiceChannelClient{stream} + return x, nil +} + +type HelloService_ChannelClient interface { + Send(*String) error + Recv() (*String, error) + grpc.ClientStream +} + +type helloServiceChannelClient struct { + grpc.ClientStream +} + +func (x *helloServiceChannelClient) Send(m *String) error { + return x.ClientStream.SendMsg(m) +} + +func (x *helloServiceChannelClient) Recv() (*String, error) { + m := new(String) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +// HelloServiceServer is the server API for HelloService service. +type HelloServiceServer interface { + Hello(context.Context, *String) (*String, error) + Channel(HelloService_ChannelServer) error +} + +func RegisterHelloServiceServer(s *grpc.Server, srv HelloServiceServer) { + s.RegisterService(&_HelloService_serviceDesc, srv) +} + +func _HelloService_Hello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(String) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HelloServiceServer).Hello(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/main.HelloService/Hello", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HelloServiceServer).Hello(ctx, req.(*String)) + } + return interceptor(ctx, in, info, handler) +} + +func _HelloService_Channel_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(HelloServiceServer).Channel(&helloServiceChannelServer{stream}) +} + +type HelloService_ChannelServer interface { + Send(*String) error + Recv() (*String, error) + grpc.ServerStream +} + +type helloServiceChannelServer struct { + grpc.ServerStream +} + +func (x *helloServiceChannelServer) Send(m *String) error { + return x.ServerStream.SendMsg(m) +} + +func (x *helloServiceChannelServer) Recv() (*String, error) { + m := new(String) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +var _HelloService_serviceDesc = grpc.ServiceDesc{ + ServiceName: "main.HelloService", + HandlerType: (*HelloServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Hello", + Handler: _HelloService_Hello_Handler, + }, + }, + Streams: []grpc.StreamDesc{ + { + StreamName: "Channel", + Handler: _HelloService_Channel_Handler, + ServerStreams: true, + ClientStreams: true, + }, + }, + Metadata: "hello.proto", +} + +func init() { proto.RegisterFile("hello.proto", fileDescriptor_hello_8909b24da4a57d21) } + +var fileDescriptor_hello_8909b24da4a57d21 = []byte{ + // 124 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xce, 0x48, 0xcd, 0xc9, + 0xc9, 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0xc9, 0x4d, 0xcc, 0xcc, 0x53, 0x92, 0xe3, + 0x62, 0x0b, 0x2e, 0x29, 0xca, 0xcc, 0x4b, 0x17, 0x12, 0xe1, 0x62, 0x2d, 0x4b, 0xcc, 0x29, 0x4d, + 0x95, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0c, 0x82, 0x70, 0x8c, 0xe2, 0xb8, 0x78, 0x3c, 0x40, 0x9a, + 0x82, 0x53, 0x8b, 0xca, 0x32, 0x93, 0x53, 0x85, 0x94, 0xb9, 0x58, 0xc1, 0x7c, 0x21, 0x1e, 0x3d, + 0x90, 0x7e, 0x3d, 0x88, 0x66, 0x29, 0x14, 0x9e, 0x90, 0x26, 0x17, 0xbb, 0x73, 0x46, 0x62, 0x5e, + 0x5e, 0x6a, 0x0e, 0x3e, 0x65, 0x1a, 0x8c, 0x06, 0x8c, 0x49, 0x6c, 0x60, 0xc7, 0x18, 0x03, 0x02, + 0x00, 0x00, 0xff, 0xff, 0x96, 0x1d, 0x8c, 0x0a, 0x9b, 0x00, 0x00, 0x00, +} diff --git a/vendor/gobook.examples/ch4-04-grpc/basic/client/hello.proto b/vendor/gobook.examples/ch4-04-grpc/basic/client/hello.proto new file mode 100644 index 0000000..8f30ac6 --- /dev/null +++ b/vendor/gobook.examples/ch4-04-grpc/basic/client/hello.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; + +package main; + +message String { + string value = 1; +} + +service HelloService { + rpc Hello (String) returns (String); + + rpc Channel (stream String) returns (stream String); +} diff --git a/vendor/gobook.examples/ch4-04-grpc/basic/client/main.go b/vendor/gobook.examples/ch4-04-grpc/basic/client/main.go new file mode 100644 index 0000000..0d67b31 --- /dev/null +++ b/vendor/gobook.examples/ch4-04-grpc/basic/client/main.go @@ -0,0 +1,100 @@ +package main + +import ( + "context" + "fmt" + "io" + "log" + "net" + "time" + + "google.golang.org/grpc" +) + +type HelloServiceImpl struct{} + +func (p *HelloServiceImpl) Hello(ctx context.Context, args *String) (*String, error) { + reply := &String{Value: "hello:" + args.GetValue()} + return reply, nil +} + +func (p *HelloServiceImpl) Channel(stream HelloService_ChannelServer) error { + for { + log.Println(111) + args, err := stream.Recv() + if err != nil { + if err == io.EOF { + return nil + } + return err + } + + reply := &String{Value: "hello:" + args.GetValue()} + + err = stream.Send(reply) + if err != nil { + return err + } + } +} + +func main() { + go startGrpcServer() + doClientWork() +} + +func startGrpcServer() { + grpcServer := grpc.NewServer() + RegisterHelloServiceServer(grpcServer, &HelloServiceImpl{}) + + lis, err := net.Listen("tcp", ":1234") + if err != nil { + log.Fatal(err) + } + + grpcServer.Serve(lis) +} + +func doClientWork() { + conn, err := grpc.Dial("localhost:1234", grpc.WithInsecure()) + if err != nil { + log.Fatal(err) + } + defer conn.Close() + + client := NewHelloServiceClient(conn) + reply, err := client.Hello(context.Background(), &String{Value: "hello"}) + if err != nil { + log.Fatal(err) + } + fmt.Println(reply.GetValue()) + + stream, err := client.Channel(context.Background()) + if err != nil { + log.Fatal(err) + } + + go func() { + for { + if err := stream.Send(&String{Value: "hi"}); err != nil { + log.Fatal(err) + } + + time.Sleep(time.Second) + } + }() + + for { + log.Println(222) + + reply, err := stream.Recv() + if err != nil { + if err == io.EOF { + break + } + log.Fatal(err) + } + + fmt.Println(reply.GetValue()) + } +} diff --git a/examples/ch4-04-grpc/basic/dummy.txt b/vendor/gobook.examples/ch4-04-grpc/basic/dummy.txt similarity index 100% rename from examples/ch4-04-grpc/basic/dummy.txt rename to vendor/gobook.examples/ch4-04-grpc/basic/dummy.txt diff --git a/examples/ch4-04-grpc/http-handle/dummy.txt b/vendor/gobook.examples/ch4-04-grpc/http-handle/dummy.txt similarity index 100% rename from examples/ch4-04-grpc/http-handle/dummy.txt rename to vendor/gobook.examples/ch4-04-grpc/http-handle/dummy.txt diff --git a/examples/ch4-04-grpc/reflect/dummy.txt b/vendor/gobook.examples/ch4-04-grpc/reflect/dummy.txt similarity index 100% rename from examples/ch4-04-grpc/reflect/dummy.txt rename to vendor/gobook.examples/ch4-04-grpc/reflect/dummy.txt diff --git a/examples/ch4-04-grpc/stream/dummy.txt b/vendor/gobook.examples/ch4-04-grpc/stream/dummy.txt similarity index 100% rename from examples/ch4-04-grpc/stream/dummy.txt rename to vendor/gobook.examples/ch4-04-grpc/stream/dummy.txt diff --git a/examples/ch4-05-grpc-hack/panic-and-log/dummy.txt b/vendor/gobook.examples/ch4-05-grpc-hack/panic-and-log/dummy.txt similarity index 100% rename from examples/ch4-05-grpc-hack/panic-and-log/dummy.txt rename to vendor/gobook.examples/ch4-05-grpc-hack/panic-and-log/dummy.txt diff --git a/examples/ch4-05-grpc-hack/rest-and-swagger/dummy.txt b/vendor/gobook.examples/ch4-05-grpc-hack/rest-and-swagger/dummy.txt similarity index 100% rename from examples/ch4-05-grpc-hack/rest-and-swagger/dummy.txt rename to vendor/gobook.examples/ch4-05-grpc-hack/rest-and-swagger/dummy.txt diff --git a/examples/ch4-05-grpc-hack/validate/dummy.txt b/vendor/gobook.examples/ch4-05-grpc-hack/validate/dummy.txt similarity index 100% rename from examples/ch4-05-grpc-hack/validate/dummy.txt rename to vendor/gobook.examples/ch4-05-grpc-hack/validate/dummy.txt diff --git a/examples/ch4-06-pb/http-router/dummy.txt b/vendor/gobook.examples/ch4-06-pb/http-router/dummy.txt similarity index 100% rename from examples/ch4-06-pb/http-router/dummy.txt rename to vendor/gobook.examples/ch4-06-pb/http-router/dummy.txt diff --git a/examples/ch4-06-pb/pb-option/dummy.txt b/vendor/gobook.examples/ch4-06-pb/pb-option/dummy.txt similarity index 100% rename from examples/ch4-06-pb/pb-option/dummy.txt rename to vendor/gobook.examples/ch4-06-pb/pb-option/dummy.txt diff --git a/examples/ch4-06-pb/pb-web-frameswork/dummy.txt b/vendor/gobook.examples/ch4-06-pb/pb-web-frameswork/dummy.txt similarity index 100% rename from examples/ch4-06-pb/pb-web-frameswork/dummy.txt rename to vendor/gobook.examples/ch4-06-pb/pb-web-frameswork/dummy.txt diff --git a/examples/ch4-06-pb/plugin-framework/dummy.txt b/vendor/gobook.examples/ch4-06-pb/plugin-framework/dummy.txt similarity index 100% rename from examples/ch4-06-pb/plugin-framework/dummy.txt rename to vendor/gobook.examples/ch4-06-pb/plugin-framework/dummy.txt diff --git a/examples/ch4-07-other-rpc/msgpack/dummy.txt b/vendor/gobook.examples/ch4-07-other-rpc/msgpack/dummy.txt similarity index 100% rename from examples/ch4-07-other-rpc/msgpack/dummy.txt rename to vendor/gobook.examples/ch4-07-other-rpc/msgpack/dummy.txt diff --git a/examples/ch4-07-other-rpc/protorpc/dummy.txt b/vendor/gobook.examples/ch4-07-other-rpc/protorpc/dummy.txt similarity index 100% rename from examples/ch4-07-other-rpc/protorpc/dummy.txt rename to vendor/gobook.examples/ch4-07-other-rpc/protorpc/dummy.txt diff --git a/examples/ch4-07-other-rpc/rpcx/dummy.txt b/vendor/gobook.examples/ch4-07-other-rpc/rpcx/dummy.txt similarity index 100% rename from examples/ch4-07-other-rpc/rpcx/dummy.txt rename to vendor/gobook.examples/ch4-07-other-rpc/rpcx/dummy.txt diff --git a/examples/ch4-07-other-rpc/thrift/dummy.txt b/vendor/gobook.examples/ch4-07-other-rpc/thrift/dummy.txt similarity index 100% rename from examples/ch4-07-other-rpc/thrift/dummy.txt rename to vendor/gobook.examples/ch4-07-other-rpc/thrift/dummy.txt