mirror of
https://github.com/chai2010/advanced-go-programming-book.git
synced 2025-05-24 04:22:22 +00:00
fmt code
This commit is contained in:
parent
8c25545f9d
commit
9ad62fe063
@ -11,10 +11,10 @@ Go语言中数组、字符串和切片三者是密切相关的数据结构。这
|
||||
我们先看看数组有哪些定义方式:
|
||||
|
||||
```go
|
||||
var a [3]int // 定义一个长度为3的int类型数组, 元素全部为0
|
||||
var b = [...]int{1, 2, 3} // 定义一个长度为3的int类型数组, 元素为 1, 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 a [3]int // 定义长度为3的int型数组, 元素全部为0
|
||||
var b = [...]int{1, 2, 3} // 定义长度为3的int型数组, 元素为 1, 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
|
||||
```
|
||||
|
||||
第一种方式是定义一个数组变量的最基本的方式,数组的长度明确指定,数组中的每个元素都以零值初始化。
|
||||
@ -169,7 +169,9 @@ type StringHeader struct {
|
||||
分析可以发现,“Hello, world”字符串底层数据和以下数组是完全一致的:
|
||||
|
||||
```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
|
||||
[]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码,可以避免出现多字节的字符)。
|
||||
@ -276,7 +279,7 @@ fmt.Printf("%#v\n", string([]rune{'世', '界'})) // 世界
|
||||
```go
|
||||
func forOnString(s string, forBody func(i int, r rune)) {
|
||||
for i := 0; len(s) > 0; {
|
||||
r, size := utf8.DecodeRuneInString(s)
|
||||
r, size := utf8.DecodeRuneInString(s)
|
||||
forBody(i, r)
|
||||
s = s[size:]
|
||||
i += size
|
||||
@ -360,9 +363,9 @@ func runes2string(s []int32) string {
|
||||
|
||||
```go
|
||||
type SliceHeader struct {
|
||||
Data uintptr
|
||||
Len int
|
||||
Cap int
|
||||
Data uintptr
|
||||
Len int
|
||||
Cap int
|
||||
}
|
||||
```
|
||||
|
||||
@ -555,8 +558,8 @@ func Filter(s []byte, fn func(x byte) bool) []byte {
|
||||
|
||||
```go
|
||||
func FindPhoneNumber(filename string) []byte {
|
||||
b, _ := ioutil.ReadFile(filename)
|
||||
return regexp.MustCompile("[0-9]+").Find(b)
|
||||
b, _ := ioutil.ReadFile(filename)
|
||||
return regexp.MustCompile("[0-9]+").Find(b)
|
||||
}
|
||||
```
|
||||
|
||||
@ -566,8 +569,8 @@ func FindPhoneNumber(filename string) []byte {
|
||||
|
||||
```go
|
||||
func FindPhoneNumber(filename string) []byte {
|
||||
b, _ := ioutil.ReadFile(filename)
|
||||
b = regexp.MustCompile("[0-9]+").Find(b)
|
||||
b, _ := ioutil.ReadFile(filename)
|
||||
b = regexp.MustCompile("[0-9]+").Find(b)
|
||||
return append([]byte{}, b...)
|
||||
}
|
||||
```
|
||||
|
@ -372,7 +372,7 @@ func (s UpperString) String() string {
|
||||
}
|
||||
|
||||
type fmt.Stringer interface {
|
||||
String() string
|
||||
String() string
|
||||
}
|
||||
|
||||
func main() {
|
||||
@ -384,10 +384,10 @@ Go语言中,对于基础类型(非接口类型)不支持隐式的转换,
|
||||
|
||||
```go
|
||||
var (
|
||||
a io.ReadCloser = (*os.File)(f) // 隐式转换, *os.File 类型满足了 io.ReadCloser 接口
|
||||
b io.Reader = a // 隐式转换, io.ReadCloser 满足了 io.Reader 接口
|
||||
c io.Closer = a // 隐式转换, io.ReadCloser 满足了 io.Closer 接口
|
||||
d io.Reader = c.(io.Reader) // 显式转换, io.Closer 并不显式满足 io.Reader 接口
|
||||
a io.ReadCloser = (*os.File)(f) // 隐式转换, *os.File 满足 io.ReadCloser 接口
|
||||
b io.Reader = a // 隐式转换, io.ReadCloser 满足 io.Reader 接口
|
||||
c io.Closer = a // 隐式转换, io.ReadCloser 满足 io.Closer 接口
|
||||
d io.Reader = c.(io.Reader) // 显式转换, io.Closer 不满足 io.Reader 接口
|
||||
)
|
||||
```
|
||||
|
||||
@ -395,13 +395,13 @@ var (
|
||||
|
||||
```go
|
||||
type runtime.Error interface {
|
||||
error
|
||||
error
|
||||
|
||||
// RuntimeError is a no-op function but
|
||||
// serves to distinguish types that are run time
|
||||
// errors from ordinary errors: a type is a
|
||||
// run time error if it has a RuntimeError method.
|
||||
RuntimeError()
|
||||
// RuntimeError is a no-op function but
|
||||
// serves to distinguish types that are run time
|
||||
// errors from ordinary errors: a type is a
|
||||
// run time error if it has a RuntimeError method.
|
||||
RuntimeError()
|
||||
}
|
||||
```
|
||||
|
||||
@ -485,17 +485,18 @@ func (p *grpcPlugin) GenerateImports(file *generator.FileDescriptor) {
|
||||
|
||||
```go
|
||||
type Plugin interface {
|
||||
// Name identifies the plugin.
|
||||
Name() string
|
||||
// Init is called once after data structures are built but before
|
||||
// code generation begins.
|
||||
Init(g *Generator)
|
||||
// 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.
|
||||
Generate(file *FileDescriptor)
|
||||
// GenerateImports produces the import declarations for this file.
|
||||
// It is called after Generate.
|
||||
GenerateImports(file *FileDescriptor)
|
||||
// Name identifies the plugin.
|
||||
Name() string
|
||||
// Init is called once after data structures are built but before
|
||||
// code generation begins.
|
||||
Init(g *Generator)
|
||||
// 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.
|
||||
Generate(file *FileDescriptor)
|
||||
// GenerateImports produces the import declarations for this file.
|
||||
// It is called after Generate.
|
||||
GenerateImports(file *FileDescriptor)
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -98,18 +98,18 @@ var (
|
||||
)
|
||||
|
||||
func Instance() *singleton {
|
||||
if atomic.LoadUint32(&initialized) == 1 {
|
||||
if atomic.LoadUint32(&initialized) == 1 {
|
||||
return instance
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
if instance == nil {
|
||||
if instance == nil {
|
||||
defer atomic.StoreUint32(&initialized, 1)
|
||||
instance = &singleton{}
|
||||
}
|
||||
return instance
|
||||
instance = &singleton{}
|
||||
}
|
||||
return instance
|
||||
}
|
||||
```
|
||||
|
||||
@ -145,10 +145,10 @@ var (
|
||||
)
|
||||
|
||||
func Instance() *singleton {
|
||||
once.Do(func() {
|
||||
instance = &singleton{}
|
||||
})
|
||||
return instance
|
||||
once.Do(func() {
|
||||
instance = &singleton{}
|
||||
})
|
||||
return instance
|
||||
}
|
||||
```
|
||||
|
||||
@ -162,20 +162,20 @@ config.Store(loadConfig())
|
||||
|
||||
// 启动一个后台线程, 加载更新后的配置信息
|
||||
go func() {
|
||||
for {
|
||||
time.Sleep(time.Second)
|
||||
config.Store(loadConfig())
|
||||
}
|
||||
for {
|
||||
time.Sleep(time.Second)
|
||||
config.Store(loadConfig())
|
||||
}
|
||||
}()
|
||||
|
||||
// 用于处理请求的工作者线程始终采用最新的配置信息
|
||||
for i := 0; i < 10; i++ {
|
||||
go func() {
|
||||
for r := range requests() {
|
||||
c := config.Load()
|
||||
// ...
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
for r := range requests() {
|
||||
c := config.Load()
|
||||
// ...
|
||||
}
|
||||
}()
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -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()
|
||||
if topic != nil && !topic(v) {
|
||||
return
|
||||
@ -343,14 +345,14 @@ func main() {
|
||||
var limit = make(chan int, 3)
|
||||
|
||||
func main() {
|
||||
for _, w := range work {
|
||||
go func() {
|
||||
limit <- 1
|
||||
w()
|
||||
<-limit
|
||||
}()
|
||||
}
|
||||
select{}
|
||||
for _, w := range work {
|
||||
go func() {
|
||||
limit <- 1
|
||||
w()
|
||||
<-limit
|
||||
}()
|
||||
}
|
||||
select{}
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -94,12 +94,12 @@ func CopyFile(dstName, srcName string) (written int64, err error) {
|
||||
|
||||
```go
|
||||
func ParseJSON(input string) (s *Syntax, err error) {
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
err = fmt.Errorf("JSON: internal error: %v", p)
|
||||
}
|
||||
}()
|
||||
// ...parser...
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
err = fmt.Errorf("JSON: internal error: %v", p)
|
||||
}
|
||||
}()
|
||||
// ...parser...
|
||||
}
|
||||
```
|
||||
|
||||
@ -113,7 +113,7 @@ Go语言库的实现习惯: 即使在包内部使用了`panic`,但是在导出
|
||||
|
||||
```go
|
||||
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层包装。我们可以这样遍历原始错误经历了哪些包装流程:
|
||||
|
||||
```go
|
||||
for i, e := range err.(errors.Error).Wraped() {
|
||||
fmt.Printf("wraped(%d): %v\n", i, e)
|
||||
}
|
||||
for i, e := range err.(errors.Error).Wraped() {
|
||||
fmt.Printf("wraped(%d): %v\n", i, e)
|
||||
}
|
||||
```
|
||||
|
||||
同时也可以获取每个包装错误的函数调用堆栈信息:
|
||||
@ -238,7 +238,7 @@ fmt.Println(err.(errors.Error).Code())
|
||||
```go
|
||||
f, err := os.Open("filename.ext")
|
||||
if err != nil {
|
||||
// 失败的情形, 马上返回错误
|
||||
// 失败的情形, 马上返回错误
|
||||
}
|
||||
|
||||
// 正常的处理流程
|
||||
@ -422,7 +422,7 @@ func main {
|
||||
}
|
||||
}()
|
||||
|
||||
...
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -15,14 +15,14 @@ package main
|
||||
#include <stdio.h>
|
||||
|
||||
void printint(int v) {
|
||||
printf("printint: %d\n", v);
|
||||
printf("printint: %d\n", v);
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
func main() {
|
||||
v := 42
|
||||
C.printint(C.int(v))
|
||||
v := 42
|
||||
C.printint(C.int(v))
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -268,13 +268,15 @@ panic: runtime error: cgo result has Go pointer
|
||||
|
||||
goroutine 1 [running]:
|
||||
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)
|
||||
command-line-arguments/_obj/_cgo_gotypes.go:62 +0x67
|
||||
command-line-arguments/_obj/_cgo_gotypes.go:62 +0x67
|
||||
main._Cfunc_Main()
|
||||
command-line-arguments/_obj/_cgo_gotypes.go:43 +0x41
|
||||
command-line-arguments/_obj/_cgo_gotypes.go:43 +0x41
|
||||
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
|
||||
```
|
||||
|
||||
|
@ -458,7 +458,7 @@ struct Int {
|
||||
int Twice() {
|
||||
const int* p = (int*)(this);
|
||||
return (*p) * 2;
|
||||
}
|
||||
}
|
||||
};
|
||||
int main() {
|
||||
int x = 42;
|
||||
|
@ -23,7 +23,7 @@ var Id = 9527
|
||||
```
|
||||
$ go tool compile -S pkg.go
|
||||
"".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表示数据中不包含指针数据。
|
||||
@ -117,10 +117,10 @@ var Name = "gopher"
|
||||
```
|
||||
$ go tool compile -S pkg.go
|
||||
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
|
||||
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
|
||||
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
|
||||
```
|
||||
|
||||
输出中出现了一个新的符号go.string."gopher",根据其长度和内容分析可以猜测是对应底层的"gopher"字符串数据。因为Go语言的字符串并不是值类型,Go字符串其实是一种只读的引用类型。如果多个代码中出现了相同的"gopher"只读字符串时,程序链接后可以引用的同一个符号go.string."gopher"。因此,该符号有一个SRODATA标志表示这个数据在只读内存段,dupok表示出现多个相同标识符的数据时只保留一个就可以了。
|
||||
|
@ -277,7 +277,7 @@ L_STEP_TO_END:
|
||||
|
||||
L_END:
|
||||
MOVQ $0, result+8(FP) // return 0
|
||||
RET
|
||||
RET
|
||||
```
|
||||
|
||||
在汇编版本函数中并没有定义局部变量,只有用于调用自身的临时栈空间。因为函数本身的参数和返回值有16个字节,因此栈帧的大小也为16字节。L_STEP_TO_END标号部分用于处理递归调用,是函数比较复杂的部分。L_END用于处理递归终结的部分。
|
||||
|
@ -231,16 +231,19 @@ i = 3
|
||||
(dlv) goroutine
|
||||
Thread 101686 at ./main.go:10
|
||||
Goroutine 1:
|
||||
Runtime: ./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)
|
||||
Start: /usr/local/go/src/runtime/proc.go:109 runtime.main (0x102bb90)
|
||||
Runtime: ./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)
|
||||
Start: /usr/local/go/src/runtime/proc.go:109 runtime.main (0x102bb90)
|
||||
(dlv) goroutines
|
||||
[4 goroutines]
|
||||
* 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 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)
|
||||
Goroutine 2 - 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)
|
||||
```
|
||||
|
||||
@ -290,25 +293,25 @@ TEXT ·asmSayHello(SB), $16-0
|
||||
Breakpoint 1 set at 0x105011f for main.main() ./main.go:3
|
||||
(dlv) continue
|
||||
> main.main() ./main.go:3 (hits goroutine(1):1 total:1) (PC: 0x105011f)
|
||||
1: package main
|
||||
2:
|
||||
=> 3: func main() { asmSayHello() }
|
||||
4:
|
||||
5: func asmSayHello()
|
||||
1: package main
|
||||
2:
|
||||
=>3: func main() { asmSayHello() }
|
||||
4:
|
||||
5: func asmSayHello()
|
||||
(dlv) disassemble
|
||||
TEXT main.main(SB) /path/to/pkg/main.go
|
||||
main.go:3 0x1050110 65488b0c25a0080000 mov rcx, qword ptr gs:[0x8a0]
|
||||
main.go:3 0x1050119 483b6110 cmp rsp, qword ptr [rcx+0x10]
|
||||
main.go:3 0x105011d 761a jbe 0x1050139
|
||||
=> main.go:3 0x105011f* 4883ec08 sub rsp, 0x8
|
||||
main.go:3 0x1050123 48892c24 mov qword ptr [rsp], rbp
|
||||
main.go:3 0x1050127 488d2c24 lea rbp, ptr [rsp]
|
||||
main.go:3 0x105012b e880000000 call $main.asmSayHello
|
||||
main.go:3 0x1050130 488b2c24 mov rbp, qword ptr [rsp]
|
||||
main.go:3 0x1050134 4883c408 add rsp, 0x8
|
||||
main.go:3 0x1050138 c3 ret
|
||||
main.go:3 0x1050139 e87288ffff call $runtime.morestack_noctxt
|
||||
main.go:3 0x105013e ebd0 jmp $main.main
|
||||
main.go:3 0x1050110 65488b0c25a0080000 mov rcx, qword ptr g [0x8a0]
|
||||
main.go:3 0x1050119 483b6110 cmp rsp, qword ptr [r +0x10]
|
||||
main.go:3 0x105011d 761a jbe 0x1050139
|
||||
=>main.go:3 0x105011f* 4883ec08 sub rsp, 0x8
|
||||
main.go:3 0x1050123 48892c24 mov qword ptr [rsp], rbp
|
||||
main.go:3 0x1050127 488d2c24 lea rbp, ptr [rsp]
|
||||
main.go:3 0x105012b e880000000 call $main.asmSayHello
|
||||
main.go:3 0x1050130 488b2c24 mov rbp, qword ptr [rsp]
|
||||
main.go:3 0x1050134 4883c408 add rsp, 0x8
|
||||
main.go:3 0x1050138 c3 ret
|
||||
main.go:3 0x1050139 e87288ffff call $runtime.morestack_noctxt
|
||||
main.go:3 0x105013e ebd0 jmp $main.main
|
||||
(dlv)
|
||||
```
|
||||
|
||||
|
@ -318,7 +318,8 @@ RPC的服务架设在“/jsonrpc”路径,在处理函数中基于http.Respons
|
||||
模拟一次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字符串:
|
||||
|
@ -262,7 +262,9 @@ type ServiceMethodSpec struct {
|
||||
然后我们新建一个buildServiceSpec方法用来解析每个服务的ServiceSpec元信息:
|
||||
|
||||
```go
|
||||
func (p *netrpcPlugin) buildServiceSpec(svc *descriptor.ServiceDescriptorProto) *ServiceSpec {
|
||||
func (p *netrpcPlugin) buildServiceSpec(
|
||||
svc *descriptor.ServiceDescriptorProto,
|
||||
) *ServiceSpec {
|
||||
spec := &ServiceSpec{
|
||||
ServiceName: generator.CamelCase(svc.GetName()),
|
||||
}
|
||||
|
@ -7,7 +7,10 @@
|
||||
Go语言的RPC库最简单的使用方式是通过`Client.Call`方法进行同步阻塞调用,该方法的实现如下:
|
||||
|
||||
```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
|
||||
return call.Error
|
||||
}
|
||||
@ -39,7 +42,11 @@ func doClientWork(client *rpc.Client) {
|
||||
执行异步调用的`Client.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.ServiceMethod = serviceMethod
|
||||
call.Args = args
|
||||
@ -164,7 +171,10 @@ func doClientWork(client *rpc.Client) {
|
||||
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 {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
@ -129,8 +129,12 @@ type HelloServiceServer interface {
|
||||
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)
|
||||
Hello(ctx context.Context, in *String, opts ...grpc.CallOption) (
|
||||
*String, error,
|
||||
)
|
||||
Channel(ctx context.Context, opts ...grpc.CallOption) (
|
||||
HelloService_ChannelClient, error,
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
@ -356,11 +360,15 @@ func main() {
|
||||
|
||||
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 {
|
||||
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 {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@ -378,7 +386,9 @@ func main() {
|
||||
defer conn.Close()
|
||||
|
||||
client := NewPubsubServiceClient(conn)
|
||||
stream, err := client.SubscribeTopic(context.Background(), &String{Value: "golang:"})
|
||||
stream, err := client.SubscribeTopic(
|
||||
context.Background(), &String{Value: "golang:"},
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
@ -116,7 +116,9 @@ func main() {
|
||||
RootCAs: certPool,
|
||||
})
|
||||
|
||||
conn, err := grpc.Dial("localhost:5000", grpc.WithTransportCredentials(creds))
|
||||
conn, err := grpc.Dial(
|
||||
"localhost:5000", grpc.WithTransportCredentials(creds),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@ -210,7 +212,9 @@ type Authentication struct {
|
||||
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
|
||||
}
|
||||
func (a *Authentication) RequireTransportSecurity() bool {
|
||||
@ -339,7 +343,7 @@ myServer := grpc.NewServer(
|
||||
)),
|
||||
grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(
|
||||
filter1, filter2, ...
|
||||
)),
|
||||
)),
|
||||
)
|
||||
```
|
||||
|
||||
@ -396,7 +400,9 @@ func main() {
|
||||
mux.ServeHTTP(w, r)
|
||||
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
|
||||
return
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
$ protoc -I. \
|
||||
-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
|
||||
--swagger_out=. \
|
||||
hello.proto
|
||||
-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
|
||||
--swagger_out=. \
|
||||
hello.proto
|
||||
```
|
||||
|
||||
然后会生成一个hello.swagger.json文件。这样的话就可以通过swagger-ui这个项目,在网页中提供REST接口的文档和测试等功能。
|
||||
|
@ -40,7 +40,8 @@ grpcurl中最常使用的是list命令,用于获取服务或服务方法的列
|
||||
|
||||
```shell
|
||||
$ 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反射服务,将会遇到以下错误:
|
||||
|
Loading…
x
Reference in New Issue
Block a user