1
0
mirror of https://github.com/chai2010/advanced-go-programming-book.git synced 2025-05-24 04:22:22 +00:00
This commit is contained in:
chai2010 2018-12-17 17:30:54 +08:00
parent 8c25545f9d
commit 9ad62fe063
18 changed files with 174 additions and 133 deletions

View File

@ -11,10 +11,10 @@ Go语言中数组、字符串和切片三者是密切相关的数据结构。这
我们先看看数组有哪些定义方式: 我们先看看数组有哪些定义方式:
```go ```go
var a [3]int // 定义一个长度为3的int型数组, 元素全部为0 var a [3]int // 定义长度为3的int型数组, 元素全部为0
var b = [...]int{1, 2, 3} // 定义一个长度为3的int型数组, 元素为 1, 2, 3 var b = [...]int{1, 2, 3} // 定义长度为3的int型数组, 元素为 1, 2, 3
var c = [...]int{2: 3, 1: 2} // 定义一个长度为3的int型数组, 元素为 0, 2, 3 var c = [...]int{2: 3, 1: 2} // 定义长度为3的int型数组, 元素为 0, 2, 3
var d = [...]int{1, 2, 4: 5, 6} // 定义一个长度为6的int型数组, 元素为 1, 2, 0, 0, 5, 6 var d = [...]int{1, 2, 4: 5, 6} // 定义长度为6的int型数组, 元素为 1, 2, 0, 0, 5, 6
``` ```
第一种方式是定义一个数组变量的最基本的方式,数组的长度明确指定,数组中的每个元素都以零值初始化。 第一种方式是定义一个数组变量的最基本的方式,数组的长度明确指定,数组中的每个元素都以零值初始化。
@ -169,7 +169,9 @@ type StringHeader struct {
分析可以发现“Hello, world”字符串底层数据和以下数组是完全一致的 分析可以发现“Hello, world”字符串底层数据和以下数组是完全一致的
```go ```go
var data = [...]byte{'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd'} var data = [...]byte{
'h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd',
}
``` ```
字符串虽然不是切片,但是支持切片操作,不同位置的切片底层也访问的同一块内存数据(因为字符串是只读的,相同的字符串面值常量通常是对应同一个字符串常量): 字符串虽然不是切片,但是支持切片操作,不同位置的切片底层也访问的同一块内存数据(因为字符串是只读的,相同的字符串面值常量通常是对应同一个字符串常量):
@ -202,7 +204,8 @@ fmt.Printf("%#v\n", []byte("Hello, 世界"))
输出的结果是: 输出的结果是:
```go ```go
[]byte{0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0xe4, 0xb8, 0x96, 0xe7, 0x95, 0x8c} []byte{0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0xe4, 0xb8, 0x96, 0xe7, \
0x95, 0x8c}
``` ```
分析可以发现`0xe4, 0xb8, 0x96`对应中文“世”,`0xe7, 0x95, 0x8c`对应中文“界”。我们也可以在字符串面值中直指定UTF8编码后的值源文件中全部是ASCII码可以避免出现多字节的字符 分析可以发现`0xe4, 0xb8, 0x96`对应中文“世”,`0xe7, 0x95, 0x8c`对应中文“界”。我们也可以在字符串面值中直指定UTF8编码后的值源文件中全部是ASCII码可以避免出现多字节的字符
@ -276,7 +279,7 @@ fmt.Printf("%#v\n", string([]rune{'世', '界'})) // 世界
```go ```go
func forOnString(s string, forBody func(i int, r rune)) { func forOnString(s string, forBody func(i int, r rune)) {
for i := 0; len(s) > 0; { for i := 0; len(s) > 0; {
r, size := utf8.DecodeRuneInString(s) r, size := utf8.DecodeRuneInString(s)
forBody(i, r) forBody(i, r)
s = s[size:] s = s[size:]
i += size i += size
@ -360,9 +363,9 @@ func runes2string(s []int32) string {
```go ```go
type SliceHeader struct { type SliceHeader struct {
Data uintptr Data uintptr
Len int Len int
Cap int Cap int
} }
``` ```
@ -555,8 +558,8 @@ func Filter(s []byte, fn func(x byte) bool) []byte {
```go ```go
func FindPhoneNumber(filename string) []byte { func FindPhoneNumber(filename string) []byte {
b, _ := ioutil.ReadFile(filename) b, _ := ioutil.ReadFile(filename)
return regexp.MustCompile("[0-9]+").Find(b) return regexp.MustCompile("[0-9]+").Find(b)
} }
``` ```
@ -566,8 +569,8 @@ func FindPhoneNumber(filename string) []byte {
```go ```go
func FindPhoneNumber(filename string) []byte { func FindPhoneNumber(filename string) []byte {
b, _ := ioutil.ReadFile(filename) b, _ := ioutil.ReadFile(filename)
b = regexp.MustCompile("[0-9]+").Find(b) b = regexp.MustCompile("[0-9]+").Find(b)
return append([]byte{}, b...) return append([]byte{}, b...)
} }
``` ```

View File

@ -372,7 +372,7 @@ func (s UpperString) String() string {
} }
type fmt.Stringer interface { type fmt.Stringer interface {
String() string String() string
} }
func main() { func main() {
@ -384,10 +384,10 @@ Go语言中对于基础类型非接口类型不支持隐式的转换
```go ```go
var ( var (
a io.ReadCloser = (*os.File)(f) // 隐式转换, *os.File 类型满足 io.ReadCloser 接口 a io.ReadCloser = (*os.File)(f) // 隐式转换, *os.File 满足 io.ReadCloser 接口
b io.Reader = a // 隐式转换, io.ReadCloser 满足 io.Reader 接口 b io.Reader = a // 隐式转换, io.ReadCloser 满足 io.Reader 接口
c io.Closer = a // 隐式转换, io.ReadCloser 满足 io.Closer 接口 c io.Closer = a // 隐式转换, io.ReadCloser 满足 io.Closer 接口
d io.Reader = c.(io.Reader) // 显式转换, io.Closer 显式满足 io.Reader 接口 d io.Reader = c.(io.Reader) // 显式转换, io.Closer 不满足 io.Reader 接口
) )
``` ```
@ -395,13 +395,13 @@ var (
```go ```go
type runtime.Error interface { type runtime.Error interface {
error error
// RuntimeError is a no-op function but // RuntimeError is a no-op function but
// serves to distinguish types that are run time // serves to distinguish types that are run time
// errors from ordinary errors: a type is a // errors from ordinary errors: a type is a
// run time error if it has a RuntimeError method. // run time error if it has a RuntimeError method.
RuntimeError() RuntimeError()
} }
``` ```
@ -485,17 +485,18 @@ func (p *grpcPlugin) GenerateImports(file *generator.FileDescriptor) {
```go ```go
type Plugin interface { type Plugin interface {
// Name identifies the plugin. // Name identifies the plugin.
Name() string Name() string
// Init is called once after data structures are built but before // Init is called once after data structures are built but before
// code generation begins. // code generation begins.
Init(g *Generator) Init(g *Generator)
// Generate produces the code generated by the plugin for this file, // Generate produces the code generated by the plugin for this file,
// except for the imports, by calling the generator's methods P, In, and Out. // except for the imports, by calling the generator's methods
Generate(file *FileDescriptor) // P, In, and Out.
// GenerateImports produces the import declarations for this file. Generate(file *FileDescriptor)
// It is called after Generate. // GenerateImports produces the import declarations for this file.
GenerateImports(file *FileDescriptor) // It is called after Generate.
GenerateImports(file *FileDescriptor)
} }
``` ```

View File

@ -98,18 +98,18 @@ var (
) )
func Instance() *singleton { func Instance() *singleton {
if atomic.LoadUint32(&initialized) == 1 { if atomic.LoadUint32(&initialized) == 1 {
return instance return instance
} }
mu.Lock() mu.Lock()
defer mu.Unlock() defer mu.Unlock()
if instance == nil { if instance == nil {
defer atomic.StoreUint32(&initialized, 1) defer atomic.StoreUint32(&initialized, 1)
instance = &singleton{} instance = &singleton{}
} }
return instance return instance
} }
``` ```
@ -145,10 +145,10 @@ var (
) )
func Instance() *singleton { func Instance() *singleton {
once.Do(func() { once.Do(func() {
instance = &singleton{} instance = &singleton{}
}) })
return instance return instance
} }
``` ```
@ -162,20 +162,20 @@ config.Store(loadConfig())
// 启动一个后台线程, 加载更新后的配置信息 // 启动一个后台线程, 加载更新后的配置信息
go func() { go func() {
for { for {
time.Sleep(time.Second) time.Sleep(time.Second)
config.Store(loadConfig()) config.Store(loadConfig())
} }
}() }()
// 用于处理请求的工作者线程始终采用最新的配置信息 // 用于处理请求的工作者线程始终采用最新的配置信息
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
go func() { go func() {
for r := range requests() { for r := range requests() {
c := config.Load() c := config.Load()
// ... // ...
} }
}() }()
} }
``` ```

View File

@ -267,7 +267,9 @@ func (p *Publisher) Close() {
} }
// 发送主题,可以容忍一定的超时 // 发送主题,可以容忍一定的超时
func (p *Publisher) sendTopic(sub subscriber, topic topicFunc, v interface{}, wg *sync.WaitGroup) { func (p *Publisher) sendTopic(
sub subscriber, topic topicFunc, v interface{}, wg *sync.WaitGroup,
) {
defer wg.Done() defer wg.Done()
if topic != nil && !topic(v) { if topic != nil && !topic(v) {
return return
@ -343,14 +345,14 @@ func main() {
var limit = make(chan int, 3) var limit = make(chan int, 3)
func main() { func main() {
for _, w := range work { for _, w := range work {
go func() { go func() {
limit <- 1 limit <- 1
w() w()
<-limit <-limit
}() }()
} }
select{} select{}
} }
``` ```

View File

@ -94,12 +94,12 @@ func CopyFile(dstName, srcName string) (written int64, err error) {
```go ```go
func ParseJSON(input string) (s *Syntax, err error) { func ParseJSON(input string) (s *Syntax, err error) {
defer func() { defer func() {
if p := recover(); p != nil { if p := recover(); p != nil {
err = fmt.Errorf("JSON: internal error: %v", p) err = fmt.Errorf("JSON: internal error: %v", p)
} }
}() }()
// ...parser... // ...parser...
} }
``` ```
@ -113,7 +113,7 @@ Go语言库的实现习惯: 即使在包内部使用了`panic`,但是在导出
```go ```go
if _, err := html.Parse(resp.Body); err != nil { if _, err := html.Parse(resp.Body); err != nil {
return nil, fmt.Errorf("parsing %s as HTML: %v", url,err) return nil, fmt.Errorf("parsing %s as HTML: %v", url,err)
} }
``` ```
@ -193,9 +193,9 @@ func main() {
上面的例子中错误被进行了2层包装。我们可以这样遍历原始错误经历了哪些包装流程 上面的例子中错误被进行了2层包装。我们可以这样遍历原始错误经历了哪些包装流程
```go ```go
for i, e := range err.(errors.Error).Wraped() { for i, e := range err.(errors.Error).Wraped() {
fmt.Printf("wraped(%d): %v\n", i, e) fmt.Printf("wraped(%d): %v\n", i, e)
} }
``` ```
同时也可以获取每个包装错误的函数调用堆栈信息: 同时也可以获取每个包装错误的函数调用堆栈信息:
@ -238,7 +238,7 @@ fmt.Println(err.(errors.Error).Code())
```go ```go
f, err := os.Open("filename.ext") f, err := os.Open("filename.ext")
if err != nil { if err != nil {
// 失败的情形, 马上返回错误 // 失败的情形, 马上返回错误
} }
// 正常的处理流程 // 正常的处理流程
@ -422,7 +422,7 @@ func main {
} }
}() }()
... // ...
} }
``` ```

View File

@ -15,14 +15,14 @@ package main
#include <stdio.h> #include <stdio.h>
void printint(int v) { void printint(int v) {
printf("printint: %d\n", v); printf("printint: %d\n", v);
} }
*/ */
import "C" import "C"
func main() { func main() {
v := 42 v := 42
C.printint(C.int(v)) C.printint(C.int(v))
} }
``` ```

View File

@ -268,13 +268,15 @@ panic: runtime error: cgo result has Go pointer
goroutine 1 [running]: goroutine 1 [running]:
main._cgoexpwrap_cfb3840e3af2_getGoPtr.func1(0xc420051dc0) main._cgoexpwrap_cfb3840e3af2_getGoPtr.func1(0xc420051dc0)
command-line-arguments/_obj/_cgo_gotypes.go:60 +0x3a command-line-arguments/_obj/_cgo_gotypes.go:60 +0x3a
main._cgoexpwrap_cfb3840e3af2_getGoPtr(0xc420016078) main._cgoexpwrap_cfb3840e3af2_getGoPtr(0xc420016078)
command-line-arguments/_obj/_cgo_gotypes.go:62 +0x67 command-line-arguments/_obj/_cgo_gotypes.go:62 +0x67
main._Cfunc_Main() main._Cfunc_Main()
command-line-arguments/_obj/_cgo_gotypes.go:43 +0x41 command-line-arguments/_obj/_cgo_gotypes.go:43 +0x41
main.main() main.main()
/Users/chai/go/src/github.com/chai2010/advanced-go-programming-book/examples/ch2-xx/return-go-ptr/main.go:17 +0x20 /Users/chai/go/src/github.com/chai2010 \
/advanced-go-programming-book/examples/ch2-xx \
/return-go-ptr/main.go:17 +0x20
exit status 2 exit status 2
``` ```

View File

@ -458,7 +458,7 @@ struct Int {
int Twice() { int Twice() {
const int* p = (int*)(this); const int* p = (int*)(this);
return (*p) * 2; return (*p) * 2;
} }
}; };
int main() { int main() {
int x = 42; int x = 42;

View File

@ -23,7 +23,7 @@ var Id = 9527
``` ```
$ go tool compile -S pkg.go $ go tool compile -S pkg.go
"".Id SNOPTRDATA size=8 "".Id SNOPTRDATA size=8
0x0000 37 25 00 00 00 00 00 00 '....... 0x0000 37 25 00 00 00 00 00 00 '.......
``` ```
其中`go tool compile`命令用于调用Go语言提供的底层命令工具其中`-S`参数表示输出汇编格式。输出的汇编比较简单,其中`"".Id`对应Id变量符号变量的内存大小为8个字节。变量的初始化内容为`37 25 00 00 00 00 00 00`对应十六进制格式的0x2537对应十进制为9527。SNOPTRDATA是相关的标志其中NOPTR表示数据中不包含指针数据。 其中`go tool compile`命令用于调用Go语言提供的底层命令工具其中`-S`参数表示输出汇编格式。输出的汇编比较简单,其中`"".Id`对应Id变量符号变量的内存大小为8个字节。变量的初始化内容为`37 25 00 00 00 00 00 00`对应十六进制格式的0x2537对应十进制为9527。SNOPTRDATA是相关的标志其中NOPTR表示数据中不包含指针数据。
@ -117,10 +117,10 @@ var Name = "gopher"
``` ```
$ go tool compile -S pkg.go $ go tool compile -S pkg.go
go.string."gopher" SRODATA dupok size=6 go.string."gopher" SRODATA dupok size=6
0x0000 67 6f 70 68 65 72 gopher 0x0000 67 6f 70 68 65 72 gopher
"".Name SDATA size=16 "".Name SDATA size=16
0x0000 00 00 00 00 00 00 00 00 06 00 00 00 00 00 00 00 ................ 0x0000 00 00 00 00 00 00 00 00 06 00 00 00 00 00 00 00 ................
rel 0+8 t=1 go.string."gopher"+0 rel 0+8 t=1 go.string."gopher"+0
``` ```
输出中出现了一个新的符号go.string."gopher",根据其长度和内容分析可以猜测是对应底层的"gopher"字符串数据。因为Go语言的字符串并不是值类型Go字符串其实是一种只读的引用类型。如果多个代码中出现了相同的"gopher"只读字符串时程序链接后可以引用的同一个符号go.string."gopher"。因此该符号有一个SRODATA标志表示这个数据在只读内存段dupok表示出现多个相同标识符的数据时只保留一个就可以了。 输出中出现了一个新的符号go.string."gopher",根据其长度和内容分析可以猜测是对应底层的"gopher"字符串数据。因为Go语言的字符串并不是值类型Go字符串其实是一种只读的引用类型。如果多个代码中出现了相同的"gopher"只读字符串时程序链接后可以引用的同一个符号go.string."gopher"。因此该符号有一个SRODATA标志表示这个数据在只读内存段dupok表示出现多个相同标识符的数据时只保留一个就可以了。

View File

@ -277,7 +277,7 @@ L_STEP_TO_END:
L_END: L_END:
MOVQ $0, result+8(FP) // return 0 MOVQ $0, result+8(FP) // return 0
RET RET
``` ```
在汇编版本函数中并没有定义局部变量只有用于调用自身的临时栈空间。因为函数本身的参数和返回值有16个字节因此栈帧的大小也为16字节。L_STEP_TO_END标号部分用于处理递归调用是函数比较复杂的部分。L_END用于处理递归终结的部分。 在汇编版本函数中并没有定义局部变量只有用于调用自身的临时栈空间。因为函数本身的参数和返回值有16个字节因此栈帧的大小也为16字节。L_STEP_TO_END标号部分用于处理递归调用是函数比较复杂的部分。L_END用于处理递归终结的部分。

View File

@ -231,16 +231,19 @@ i = 3
(dlv) goroutine (dlv) goroutine
Thread 101686 at ./main.go:10 Thread 101686 at ./main.go:10
Goroutine 1: Goroutine 1:
Runtime: ./main.go:10 main.main (0x10aea33) Runtime: ./main.go:10 main.main (0x10aea33)
User: ./main.go:10 main.main (0x10aea33) User: ./main.go:10 main.main (0x10aea33)
Go: /usr/local/go/src/runtime/asm_amd64.s:258 runtime.rt0_go (0x1051643) Go: /usr/local/go/src/runtime/asm_amd64.s:258 runtime.rt0_go (0x1051643)
Start: /usr/local/go/src/runtime/proc.go:109 runtime.main (0x102bb90) Start: /usr/local/go/src/runtime/proc.go:109 runtime.main (0x102bb90)
(dlv) goroutines (dlv) goroutines
[4 goroutines] [4 goroutines]
* Goroutine 1 - User: ./main.go:10 main.main (0x10aea33) (thread 101686) * Goroutine 1 - User: ./main.go:10 main.main (0x10aea33) (thread 101686)
Goroutine 2 - User: /usr/local/go/src/runtime/proc.go:292 runtime.gopark (0x102c189) Goroutine 2 - User: /usr/local/go/src/runtime/proc.go:292 \
Goroutine 3 - User: /usr/local/go/src/runtime/proc.go:292 runtime.gopark (0x102c189) runtime.gopark (0x102c189)
Goroutine 4 - User: /usr/local/go/src/runtime/proc.go:292 runtime.gopark (0x102c189) Goroutine 3 - User: /usr/local/go/src/runtime/proc.go:292 \
runtime.gopark (0x102c189)
Goroutine 4 - User: /usr/local/go/src/runtime/proc.go:292 \
runtime.gopark (0x102c189)
(dlv) (dlv)
``` ```
@ -290,25 +293,25 @@ TEXT ·asmSayHello(SB), $16-0
Breakpoint 1 set at 0x105011f for main.main() ./main.go:3 Breakpoint 1 set at 0x105011f for main.main() ./main.go:3
(dlv) continue (dlv) continue
> main.main() ./main.go:3 (hits goroutine(1):1 total:1) (PC: 0x105011f) > main.main() ./main.go:3 (hits goroutine(1):1 total:1) (PC: 0x105011f)
1: package main 1: package main
2: 2:
=> 3: func main() { asmSayHello() } =>3: func main() { asmSayHello() }
4: 4:
5: func asmSayHello() 5: func asmSayHello()
(dlv) disassemble (dlv) disassemble
TEXT main.main(SB) /path/to/pkg/main.go TEXT main.main(SB) /path/to/pkg/main.go
main.go:3 0x1050110 65488b0c25a0080000 mov rcx, qword ptr gs:[0x8a0] main.go:3 0x1050110 65488b0c25a0080000 mov rcx, qword ptr g [0x8a0]
main.go:3 0x1050119 483b6110 cmp rsp, qword ptr [rcx+0x10] main.go:3 0x1050119 483b6110 cmp rsp, qword ptr [r +0x10]
main.go:3 0x105011d 761a jbe 0x1050139 main.go:3 0x105011d 761a jbe 0x1050139
=> main.go:3 0x105011f* 4883ec08 sub rsp, 0x8 =>main.go:3 0x105011f* 4883ec08 sub rsp, 0x8
main.go:3 0x1050123 48892c24 mov qword ptr [rsp], rbp main.go:3 0x1050123 48892c24 mov qword ptr [rsp], rbp
main.go:3 0x1050127 488d2c24 lea rbp, ptr [rsp] main.go:3 0x1050127 488d2c24 lea rbp, ptr [rsp]
main.go:3 0x105012b e880000000 call $main.asmSayHello main.go:3 0x105012b e880000000 call $main.asmSayHello
main.go:3 0x1050130 488b2c24 mov rbp, qword ptr [rsp] main.go:3 0x1050130 488b2c24 mov rbp, qword ptr [rsp]
main.go:3 0x1050134 4883c408 add rsp, 0x8 main.go:3 0x1050134 4883c408 add rsp, 0x8
main.go:3 0x1050138 c3 ret main.go:3 0x1050138 c3 ret
main.go:3 0x1050139 e87288ffff call $runtime.morestack_noctxt main.go:3 0x1050139 e87288ffff call $runtime.morestack_noctxt
main.go:3 0x105013e ebd0 jmp $main.main main.go:3 0x105013e ebd0 jmp $main.main
(dlv) (dlv)
``` ```

View File

@ -318,7 +318,8 @@ RPC的服务架设在“/jsonrpc”路径在处理函数中基于http.Respons
模拟一次RPC调用的过程就是向该链接发送一个json字符串 模拟一次RPC调用的过程就是向该链接发送一个json字符串
``` ```
$ curl localhost:1234/jsonrpc -X POST --data '{"method":"HelloService.Hello","params":["hello"],"id":0}' $ curl localhost:1234/jsonrpc -X POST \
--data '{"method":"HelloService.Hello","params":["hello"],"id":0}'
``` ```
返回的结果依然是json字符串 返回的结果依然是json字符串

View File

@ -262,7 +262,9 @@ type ServiceMethodSpec struct {
然后我们新建一个buildServiceSpec方法用来解析每个服务的ServiceSpec元信息 然后我们新建一个buildServiceSpec方法用来解析每个服务的ServiceSpec元信息
```go ```go
func (p *netrpcPlugin) buildServiceSpec(svc *descriptor.ServiceDescriptorProto) *ServiceSpec { func (p *netrpcPlugin) buildServiceSpec(
svc *descriptor.ServiceDescriptorProto,
) *ServiceSpec {
spec := &ServiceSpec{ spec := &ServiceSpec{
ServiceName: generator.CamelCase(svc.GetName()), ServiceName: generator.CamelCase(svc.GetName()),
} }

View File

@ -7,7 +7,10 @@
Go语言的RPC库最简单的使用方式是通过`Client.Call`方法进行同步阻塞调用,该方法的实现如下: Go语言的RPC库最简单的使用方式是通过`Client.Call`方法进行同步阻塞调用,该方法的实现如下:
```go ```go
func (client *Client) Call(serviceMethod string, args interface{}, reply interface{}) error { func (client *Client) Call(
serviceMethod string, args interface{},
reply interface{},
) error {
call := <-client.Go(serviceMethod, args, reply, make(chan *Call, 1)).Done call := <-client.Go(serviceMethod, args, reply, make(chan *Call, 1)).Done
return call.Error return call.Error
} }
@ -39,7 +42,11 @@ func doClientWork(client *rpc.Client) {
执行异步调用的`Client.Go`方法实现如下: 执行异步调用的`Client.Go`方法实现如下:
```go ```go
func (client *Client) Go(serviceMethod string, args interface{}, reply interface{}, done chan *Call) *Call { func (client *Client) Go(
serviceMethod string, args interface{},
reply interface{},
done chan *Call,
) *Call {
call := new(Call) call := new(Call)
call.ServiceMethod = serviceMethod call.ServiceMethod = serviceMethod
call.Args = args call.Args = args
@ -164,7 +171,10 @@ func doClientWork(client *rpc.Client) {
fmt.Println("watch:", keyChanged) fmt.Println("watch:", keyChanged)
} () } ()
err := client.Call("KVStoreService.Set", [2]string{"abc", "abc-value"}, new(struct{})) err := client.Call(
"KVStoreService.Set", [2]string{"abc", "abc-value"},
new(struct{}),
)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }

View File

@ -129,8 +129,12 @@ type HelloServiceServer interface {
Channel(HelloService_ChannelServer) error Channel(HelloService_ChannelServer) error
} }
type HelloServiceClient interface { type HelloServiceClient interface {
Hello(ctx context.Context, in *String, opts ...grpc.CallOption) (*String, error) Hello(ctx context.Context, in *String, opts ...grpc.CallOption) (
Channel(ctx context.Context, opts ...grpc.CallOption) (HelloService_ChannelClient, error) *String, error,
)
Channel(ctx context.Context, opts ...grpc.CallOption) (
HelloService_ChannelClient, error,
)
} }
``` ```
@ -356,11 +360,15 @@ func main() {
client := NewPubsubServiceClient(conn) client := NewPubsubServiceClient(conn)
_, err = client.Publish(context.Background(), &String{Value: "golang: hello Go"}) _, err = client.Publish(
context.Background(), &String{Value: "golang: hello Go"},
)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
_, err = client.Publish(context.Background(), &String{Value: "docker: hello Docker"}) _, err = client.Publish(
context.Background(), &String{Value: "docker: hello Docker"},
)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -378,7 +386,9 @@ func main() {
defer conn.Close() defer conn.Close()
client := NewPubsubServiceClient(conn) client := NewPubsubServiceClient(conn)
stream, err := client.SubscribeTopic(context.Background(), &String{Value: "golang:"}) stream, err := client.SubscribeTopic(
context.Background(), &String{Value: "golang:"},
)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }

View File

@ -116,7 +116,9 @@ func main() {
RootCAs: certPool, RootCAs: certPool,
}) })
conn, err := grpc.Dial("localhost:5000", grpc.WithTransportCredentials(creds)) conn, err := grpc.Dial(
"localhost:5000", grpc.WithTransportCredentials(creds),
)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -210,7 +212,9 @@ type Authentication struct {
Password string Password string
} }
func (a *Authentication) GetRequestMetadata(context.Context, ...string) (map[string]string, error) { func (a *Authentication) GetRequestMetadata(context.Context, ...string) (
map[string]string, error,
) {
return map[string]string{"user":a.User, "password": a.Password}, nil return map[string]string{"user":a.User, "password": a.Password}, nil
} }
func (a *Authentication) RequireTransportSecurity() bool { func (a *Authentication) RequireTransportSecurity() bool {
@ -339,7 +343,7 @@ myServer := grpc.NewServer(
)), )),
grpc.StreamInterceptor(grpc_middleware.ChainStreamServer( grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(
filter1, filter2, ... filter1, filter2, ...
)), )),
) )
``` ```
@ -396,7 +400,9 @@ func main() {
mux.ServeHTTP(w, r) mux.ServeHTTP(w, r)
return return
} }
if strings.Contains(r.Header.Get("Content-Type"), "application/grpc") { if strings.Contains(
r.Header.Get("Content-Type"), "application/grpc",
) {
grpcServer.ServeHTTP(w, r) // gRPC Server grpcServer.ServeHTTP(w, r) // gRPC Server
return return
} }

View File

@ -268,9 +268,9 @@ $ curl localhost:8080/post -X POST --data '{"value":"grpc"}'
$ go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger $ go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger
$ protoc -I. \ $ protoc -I. \
-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--swagger_out=. \ --swagger_out=. \
hello.proto hello.proto
``` ```
然后会生成一个hello.swagger.json文件。这样的话就可以通过swagger-ui这个项目在网页中提供REST接口的文档和测试等功能。 然后会生成一个hello.swagger.json文件。这样的话就可以通过swagger-ui这个项目在网页中提供REST接口的文档和测试等功能。

View File

@ -40,7 +40,8 @@ grpcurl中最常使用的是list命令用于获取服务或服务方法的列
```shell ```shell
$ grpcurl localhost:1234 list $ grpcurl localhost:1234 list
Failed to dial target host "localhost:1234": tls: first record does not look like a TLS handshake Failed to dial target host "localhost:1234": tls: first record does not \
look like a TLS handshake
``` ```
如果grpc服务正常但是服务没有启动reflection反射服务将会遇到以下错误 如果grpc服务正常但是服务没有启动reflection反射服务将会遇到以下错误