Skip to content

Commit

Permalink
Fix typo in ch4-rpc
Browse files Browse the repository at this point in the history
链接 -> 连接
  • Loading branch information
mstmdev committed Jul 20, 2022
1 parent 752253b commit cd6897e
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 28 deletions.
10 changes: 5 additions & 5 deletions ch4-rpc/ch4-01-rpc-intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func main() {
}
```

其中 rpc.Register 函数调用会将对象类型中所有满足 RPC 规则的对象方法注册为 RPC 函数,所有注册的方法会放在 “HelloService” 服务空间之下。然后我们建立一个唯一的 TCP 链接,并且通过 rpc.ServeConn 函数在该 TCP 链接上为对方提供 RPC 服务。
其中 rpc.Register 函数调用会将对象类型中所有满足 RPC 规则的对象方法注册为 RPC 函数,所有注册的方法会放在 “HelloService” 服务空间之下。然后我们建立一个唯一的 TCP 连接,并且通过 rpc.ServeConn 函数在该 TCP 连接上为对方提供 RPC 服务。

下面是客户端请求 HelloService 服务的代码:

Expand All @@ -60,7 +60,7 @@ func main() {
}
```

首先是通过 rpc.Dial 拨号 RPC 服务,然后通过 client.Call 调用具体的 RPC 方法。在调用 client.Call 时,第一个参数是用点号链接的 RPC 服务名字和方法名字,第二和第三个参数分别我们定义 RPC 方法的两个参数。
首先是通过 rpc.Dial 拨号 RPC 服务,然后通过 client.Call 调用具体的 RPC 方法。在调用 client.Call 时,第一个参数是用点号连接的 RPC 服务名字和方法名字,第二和第三个参数分别我们定义 RPC 方法的两个参数。

由这个例子可以看出 RPC 的使用其实非常简单。

Expand Down Expand Up @@ -175,7 +175,7 @@ func main() {
}
```

在新的 RPC 服务端实现中,我们用 RegisterHelloService 函数来注册函数,这样不仅可以避免命名服务名称的工作,同时也保证了传入的服务对象满足了 RPC 接口的定义。最后我们新的服务改为支持多个 TCP 链接,然后为每个 TCP 链接提供 RPC 服务。
在新的 RPC 服务端实现中,我们用 RegisterHelloService 函数来注册函数,这样不仅可以避免命名服务名称的工作,同时也保证了传入的服务对象满足了 RPC 接口的定义。最后我们新的服务改为支持多个 TCP 连接,然后为每个 TCP 连接提供 RPC 服务。


## 4.1.3 跨语言的 RPC
Expand Down Expand Up @@ -229,7 +229,7 @@ func main() {
}
```

先手工调用 net.Dial 函数建立 TCP 链接,然后基于该链接建立针对客户端的 json 编解码器。
先手工调用 net.Dial 函数建立 TCP 连接,然后基于该连接建立针对客户端的 json 编解码器。

在确保客户端可以正常调用 RPC 服务的方法之后,我们用一个普通的 TCP 服务代替 Go 语言版本的 RPC 服务,这样可以查看客户端调用时发送的数据格式。比如通过 nc 命令 `nc -l 1234` 在同样的端口启动一个 TCP 服务。然后再次执行一次 RPC 调用将会发现 nc 输出了以下的信息:

Expand Down Expand Up @@ -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 \
Expand Down
20 changes: 10 additions & 10 deletions ch4-rpc/ch4-03-netrpc-hack.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func (client *Client) Go(
}
```

首先是构造一个表示当前调用的 call 变量,然后通过 `client.send` 将 call 的完整参数发送到 RPC 框架。`client.send` 方法调用是线程安全的,因此可以从多个 Goroutine 同时向同一个 RPC 链接发送调用指令
首先是构造一个表示当前调用的 call 变量,然后通过 `client.send` 将 call 的完整参数发送到 RPC 框架。`client.send` 方法调用是线程安全的,因此可以从多个 Goroutine 同时向同一个 RPC 连接发送调用指令

当调用完成或者发生错误时,将调用 `call.done` 方法通知完成:

Expand Down Expand Up @@ -187,7 +187,7 @@ func doClientWork(client *rpc.Client) {

## 4.3.3 反向 RPC

通常的 RPC 是基于 C/S 结构,RPC 的服务端对应网络的服务器,RPC 的客户端也对应网络客户端。但是对于一些特殊场景,比如在公司内网提供一个 RPC 服务,但是在外网无法链接到内网的服务器。这种时候我们可以参考类似反向代理的技术,首先从内网主动链接到外网的 TCP 服务器,然后基于 TCP 链接向外网提供 RPC 服务。
通常的 RPC 是基于 C/S 结构,RPC 的服务端对应网络的服务器,RPC 的客户端也对应网络客户端。但是对于一些特殊场景,比如在公司内网提供一个 RPC 服务,但是在外网无法连接到内网的服务器。这种时候我们可以参考类似反向代理的技术,首先从内网主动连接到外网的 TCP 服务器,然后基于 TCP 连接向外网提供 RPC 服务。

以下是启动反向 RPC 服务的代码:

Expand All @@ -208,9 +208,9 @@ func main() {
}
```

反向 RPC 的内网服务将不再主动提供 TCP 监听服务,而是首先主动链接到对方的 TCP 服务器。然后基于每个建立的 TCP 链接向对方提供 RPC 服务。
反向 RPC 的内网服务将不再主动提供 TCP 监听服务,而是首先主动连接到对方的 TCP 服务器。然后基于每个建立的 TCP 连接向对方提供 RPC 服务。

而 RPC 客户端则需要在一个公共的地址提供一个 TCP 服务,用于接受 RPC 服务器的链接请求
而 RPC 客户端则需要在一个公共的地址提供一个 TCP 服务,用于接受 RPC 服务器的连接请求

```go
func main() {
Expand All @@ -236,7 +236,7 @@ func main() {
}
```

当每个链接建立后,基于网络链接构造 RPC 客户端对象并发送到 clientChan 管道。
当每个连接建立后,基于网络连接构造 RPC 客户端对象并发送到 clientChan 管道。

客户端执行 RPC 调用的操作在 doClientWork 函数完成:

Expand All @@ -260,17 +260,17 @@ func doClientWork(clientChan <-chan *rpc.Client) {

## 4.3.4 上下文信息

基于上下文我们可以针对不同客户端提供定制化的 RPC 服务。我们可以通过为每个链接提供独立的 RPC 服务来实现对上下文特性的支持。
基于上下文我们可以针对不同客户端提供定制化的 RPC 服务。我们可以通过为每个连接提供独立的 RPC 服务来实现对上下文特性的支持。

首先改造 HelloService,里面增加了对应链接的 conn 成员:
首先改造 HelloService,里面增加了对应连接的 conn 成员:

```go
type HelloService struct {
conn net.Conn
}
```

然后为每个链接启动独立的 RPC 服务:
然后为每个连接启动独立的 RPC 服务:

```go
func main() {
Expand All @@ -296,7 +296,7 @@ func main() {
}
```

Hello 方法中就可以根据 conn 成员识别不同链接的 RPC 调用:
Hello 方法中就可以根据 conn 成员识别不同连接的 RPC 调用:

```go
func (p *HelloService) Hello(request string, reply *string) error {
Expand Down Expand Up @@ -331,4 +331,4 @@ func (p *HelloService) Hello(request string, reply *string) error {
}
```

这样可以要求在客户端链接 RPC 服务时,首先要执行登陆操作,登陆成功后才能正常执行其他的服务。
这样可以要求在客户端连接 RPC 服务时,首先要执行登陆操作,登陆成功后才能正常执行其他的服务。
8 changes: 4 additions & 4 deletions ch4-rpc/ch4-04-grpc.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# 4.4 gRPC 入门

gRPC 是 Google 公司基于 Protobuf 开发的跨语言的开源 RPC 框架。gRPC 基于 HTTP/2 协议设计,可以基于一个 HTTP/2 链接提供多个服务,对于移动设备更加友好。本节将讲述 gRPC 的简单用法。
gRPC 是 Google 公司基于 Protobuf 开发的跨语言的开源 RPC 框架。gRPC 基于 HTTP/2 协议设计,可以基于一个 HTTP/2 连接提供多个服务,对于移动设备更加友好。本节将讲述 gRPC 的简单用法。

## 4.4.1 gRPC 技术栈

Expand Down Expand Up @@ -82,7 +82,7 @@ func main() {

首先是通过 `grpc.NewServer()` 构造一个 gRPC 服务对象,然后通过 gRPC 插件生成的 RegisterHelloServiceServer 函数注册我们实现的 HelloServiceImpl 服务。然后通过 `grpcServer.Serve(lis)` 在一个监听端口上提供 gRPC 服务。

然后就可以通过客户端链接 gRPC 服务了:
然后就可以通过客户端连接 gRPC 服务了:

```go
func main() {
Expand All @@ -101,9 +101,9 @@ func main() {
}
```

其中 grpc.Dial 负责和 gRPC 服务建立链接,然后 NewHelloServiceClient 函数基于已经建立的链接构造 HelloServiceClient 对象。返回的 client 其实是一个 HelloServiceClient 接口对象,通过接口定义的方法就可以调用服务端对应的 gRPC 服务提供的方法。
其中 grpc.Dial 负责和 gRPC 服务建立连接,然后 NewHelloServiceClient 函数基于已经建立的连接构造 HelloServiceClient 对象。返回的 client 其实是一个 HelloServiceClient 接口对象,通过接口定义的方法就可以调用服务端对应的 gRPC 服务提供的方法。

gRPC 和标准库的 RPC 框架有一个区别,gRPC 生成的接口并不支持异步调用。不过我们可以在多个 Goroutine 之间安全地共享 gRPC 底层的 HTTP/2 链接,因此可以通过在另一个 Goroutine 阻塞调用的方式模拟异步调用。
gRPC 和标准库的 RPC 框架有一个区别,gRPC 生成的接口并不支持异步调用。不过我们可以在多个 Goroutine 之间安全地共享 gRPC 底层的 HTTP/2 连接,因此可以通过在另一个 Goroutine 阻塞调用的方式模拟异步调用。

## 4.4.3 gRPC 流

Expand Down
14 changes: 7 additions & 7 deletions ch4-rpc/ch4-05-grpc-hack.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

## 4.5.1 证书认证

gRPC 建立在 HTTP/2 协议之上,对 TLS 提供了很好的支持。我们前面章节中 gRPC 的服务都没有提供证书支持,因此客户端在链接服务器中通过 `grpc.WithInsecure()` 选项跳过了对服务器证书的验证。没有启用证书的 gRPC 服务在和客户端进行的是明文通讯,信息面临被任何第三方监听的风险。为了保障 gRPC 通信不被第三方监听篡改或伪造,我们可以对服务器启动 TLS 加密特性。
gRPC 建立在 HTTP/2 协议之上,对 TLS 提供了很好的支持。我们前面章节中 gRPC 的服务都没有提供证书支持,因此客户端在连接服务器中通过 `grpc.WithInsecure()` 选项跳过了对服务器证书的验证。没有启用证书的 gRPC 服务在和客户端进行的是明文通讯,信息面临被任何第三方监听的风险。为了保障 gRPC 通信不被第三方监听篡改或伪造,我们可以对服务器启动 TLS 加密特性。

可以用以下命令为服务器和客户端分别生成私钥和证书:

Expand Down Expand Up @@ -64,7 +64,7 @@ func main() {

其中 credentials.NewClientTLSFromFile 是构造客户端用的证书对象,第一个参数是服务器的证书文件,第二个参数是签发证书的服务器的名字。然后通过 grpc.WithTransportCredentials(creds) 将证书对象转为参数选项传人 grpc.Dial 函数。

以上这种方式,需要提前将服务器的证书告知客户端,这样客户端在链接服务器时才能进行对服务器证书认证。在复杂的网络环境中,服务器证书的传输本身也是一个非常危险的问题。如果在中间某个环节,服务器证书被监听或替换那么对服务器的认证也将不再可靠。
以上这种方式,需要提前将服务器的证书告知客户端,这样客户端在连接服务器时才能进行对服务器证书认证。在复杂的网络环境中,服务器证书的传输本身也是一个非常危险的问题。如果在中间某个环节,服务器证书被监听或替换那么对服务器的认证也将不再可靠。

为了避免证书的传递过程中被篡改,可以通过一个安全可靠的根证书分别对服务器和客户端的证书进行签名。这样客户端或服务器在收到对方的证书后可以通过根证书进行验证证书的有效性。

Expand Down Expand Up @@ -128,7 +128,7 @@ func main() {
}
```

在新的客户端代码中,我们不再直接依赖服务器端证书文件。在 credentials.NewTLS 函数调用中,客户端通过引入一个 CA 根证书和服务器的名字来实现对服务器进行验证。客户端在链接服务器时会首先请求服务器的证书,然后使用 CA 根证书对收到的服务器端证书进行验证。
在新的客户端代码中,我们不再直接依赖服务器端证书文件。在 credentials.NewTLS 函数调用中,客户端通过引入一个 CA 根证书和服务器的名字来实现对服务器进行验证。客户端在连接服务器时会首先请求服务器的证书,然后使用 CA 根证书对收到的服务器端证书进行验证。

如果客户端的证书也采用 CA 根证书签名的话,服务器端也可以对客户端进行证书认证。我们用 CA 根证书对客户端证书签名:

Expand Down Expand Up @@ -178,7 +178,7 @@ func main() {

## 4.5.2 Token 认证

前面讲述的基于证书的认证是针对每个 gRPC 链接的认证。gRPC 还为每个 gRPC 方法调用提供了认证支持,这样就基于用户 Token 对不同的方法访问进行权限管理。
前面讲述的基于证书的认证是针对每个 gRPC 连接的认证。gRPC 还为每个 gRPC 方法调用提供了认证支持,这样就基于用户 Token 对不同的方法访问进行权限管理。

要实现对每个 gRPC 方法进行认证,需要实现 grpc.PerRPCCredentials 接口:

Expand All @@ -202,7 +202,7 @@ type PerRPCCredentials interface {
}
```

在 GetRequestMetadata 方法中返回认证需要的必要信息。RequireTransportSecurity 方法表示是否要求底层使用安全链接。在真实的环境中建议必须要求底层启用安全的链接,否则认证信息有泄露和被篡改的风险。
在 GetRequestMetadata 方法中返回认证需要的必要信息。RequireTransportSecurity 方法表示是否要求底层使用安全连接。在真实的环境中建议必须要求底层启用安全的连接,否则认证信息有泄露和被篡改的风险。

我们可以创建一个 Authentication 类型,用于实现用户名和密码的认证:

Expand All @@ -222,7 +222,7 @@ func (a *Authentication) RequireTransportSecurity() bool {
}
```

在 GetRequestMetadata 方法中,我们返回地认证信息包装 user 和 password 两个信息。为了演示代码简单,RequireTransportSecurity 方法表示不要求底层使用安全链接
在 GetRequestMetadata 方法中,我们返回地认证信息包装 user 和 password 两个信息。为了演示代码简单,RequireTransportSecurity 方法表示不要求底层使用安全连接

然后在每次请求 gRPC 服务时就可以将 Token 信息作为参数选项传人:

Expand All @@ -243,7 +243,7 @@ func main() {
}
```

通过 grpc.WithPerRPCCredentials 函数将 Authentication 对象转为 grpc.Dial 参数。因为这里没有启用安全链接,需要传人 grpc.WithInsecure() 表示忽略证书认证。
通过 grpc.WithPerRPCCredentials 函数将 Authentication 对象转为 grpc.Dial 参数。因为这里没有启用安全连接,需要传人 grpc.WithInsecure() 表示忽略证书认证。

然后在 gRPC 服务端的每个方法中通过 Authentication 类型的 Auth 方法进行身份认证:

Expand Down
4 changes: 2 additions & 2 deletions ch4-rpc/ch4-08-grpcurl.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ $ go get github.com/fullstorydev/grpcurl
$ go install github.com/fullstorydev/grpcurl/cmd/grpcurl
```

grpcurl 中最常使用的是 list 命令,用于获取服务或服务方法的列表。比如 `grpcurl localhost:1234 list` 命令将获取本地 1234 端口上的 grpc 服务的列表。在使用 grpcurl 时,需要通过 `-cert``-key` 参数设置公钥和私钥文件,链接启用了 tls 协议的服务。对于没有没用 tls 协议的 grpc 服务,通过 `-plaintext` 参数忽略 tls 证书的验证过程。如果是 Unix Socket 协议,则需要指定 `-unix` 参数。
grpcurl 中最常使用的是 list 命令,用于获取服务或服务方法的列表。比如 `grpcurl localhost:1234 list` 命令将获取本地 1234 端口上的 grpc 服务的列表。在使用 grpcurl 时,需要通过 `-cert``-key` 参数设置公钥和私钥文件,连接启用了 tls 协议的服务。对于没有没用 tls 协议的 grpc 服务,通过 `-plaintext` 参数忽略 tls 证书的验证过程。如果是 Unix Socket 协议,则需要指定 `-unix` 参数。

如果没有配置好公钥和私钥文件,也没有忽略证书的验证过程,那么将会遇到类似以下的错误:

Expand Down Expand Up @@ -179,7 +179,7 @@ $ grpcurl -plaintext -d '{"value":"gopher"}' \

如果 `-d` 参数是 `@` 则表示从标准输入读取 json 输入参数,这一般用于比较输入复杂的 json 数据,也可以用于测试流方法。

下面命令是链接 Channel 流方法,通过从标准输入读取输入流参数:
下面命令是连接 Channel 流方法,通过从标准输入读取输入流参数:

```shell
$ grpcurl -plaintext -d @ localhost:1234 HelloService.HelloService/Channel
Expand Down

0 comments on commit cd6897e

Please sign in to comment.