Skip to content

Commit

Permalink
修改排版 ch4/*
Browse files Browse the repository at this point in the history
  • Loading branch information
iGmainC committed Jan 24, 2022
1 parent c8b48f1 commit a60f1e8
Show file tree
Hide file tree
Showing 10 changed files with 294 additions and 296 deletions.
88 changes: 44 additions & 44 deletions ch4-rpc/ch4-01-rpc-intro.md

Large diffs are not rendered by default.

88 changes: 44 additions & 44 deletions ch4-rpc/ch4-02-pb-intro.md

Large diffs are not rendered by default.

70 changes: 35 additions & 35 deletions ch4-rpc/ch4-03-netrpc-hack.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# 4.3 玩转RPC
# 4.3 玩转 RPC

在不同的场景中RPC有着不同的需求,因此开源的社区就诞生了各种RPC框架。本节我们将尝试Go内置RPC框架在一些比较特殊场景的用法
在不同的场景中 RPC 有着不同的需求,因此开源的社区就诞生了各种 RPC 框架。本节我们将尝试 Go 内置 RPC 框架在一些比较特殊场景的用法

## 4.3.1 客户端RPC的实现原理
## 4.3.1 客户端 RPC 的实现原理

Go语言的RPC库最简单的使用方式是通过`Client.Call`方法进行同步阻塞调用,该方法的实现如下:
Go 语言的 RPC 库最简单的使用方式是通过 `Client.Call` 方法进行同步阻塞调用,该方法的实现如下:

```go
func (client *Client) Call(
Expand All @@ -16,9 +16,9 @@ func (client *Client) Call(
}
```

首先通过`Client.Go`方法进行一次异步调用,返回一个表示这次调用的`Call`结构体。然后等待`Call`结构体的Done管道返回调用结果
首先通过 `Client.Go` 方法进行一次异步调用,返回一个表示这次调用的 `Call` 结构体。然后等待 `Call` 结构体的 Done 管道返回调用结果

我们也可以通过`Client.Go`方法异步调用前面的HelloService服务
我们也可以通过 `Client.Go` 方法异步调用前面的 HelloService 服务

```go
func doClientWork(client *rpc.Client) {
Expand All @@ -37,9 +37,9 @@ func doClientWork(client *rpc.Client) {
}
```

在异步调用命令发出后,一般会执行其他的任务,因此异步调用的输入参数和返回值可以通过返回的Call变量进行获取
在异步调用命令发出后,一般会执行其他的任务,因此异步调用的输入参数和返回值可以通过返回的 Call 变量进行获取

执行异步调用的`Client.Go`方法实现如下:
执行异步调用的 `Client.Go` 方法实现如下:

```go
func (client *Client) Go(
Expand All @@ -58,9 +58,9 @@ func (client *Client) Go(
}
```

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

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

```go
func (call *Call) done() {
Expand All @@ -74,13 +74,13 @@ func (call *Call) done() {
}
```

`Call.done`方法的实现可以得知`call.Done`管道会将处理后的call返回
`Call.done` 方法的实现可以得知 `call.Done` 管道会将处理后的 call 返回

## 4.3.2 基于RPC实现Watch功能
## 4.3.2 基于 RPC 实现 Watch 功能

在很多系统中都提供了Watch监视功能的接口,当系统满足某种条件时Watch方法返回监控的结果。在这里我们可以尝试通过RPC框架实现一个基本的Watch功能。如前文所描述,因为`client.send`是线程安全的,我们也可以通过在不同的Goroutine中同时并发阻塞调用RPC方法。通过在一个独立的Goroutine中调用Watch函数进行监控
在很多系统中都提供了 Watch 监视功能的接口,当系统满足某种条件时 Watch 方法返回监控的结果。在这里我们可以尝试通过 RPC 框架实现一个基本的 Watch 功能。如前文所描述,因为 `client.send` 是线程安全的,我们也可以通过在不同的 Goroutine 中同时并发阻塞调用 RPC 方法。通过在一个独立的 Goroutine 中调用 Watch 函数进行监控

为了便于演示,我们计划通过RPC构造一个简单的内存KV数据库。首先定义服务如下:
为了便于演示,我们计划通过 RPC 构造一个简单的内存 KV 数据库。首先定义服务如下:

```go
type KVStoreService struct {
Expand All @@ -97,9 +97,9 @@ func NewKVStoreService() *KVStoreService {
}
```

其中`m`成员是一个map类型,用于存储KV数据`filter`成员对应每个Watch调用时定义的过滤器函数列表。而`mu`成员为互斥锁,用于在多个Goroutine访问或修改时对其它成员提供保护
其中 `m` 成员是一个 map 类型,用于存储 KV 数据`filter` 成员对应每个 Watch 调用时定义的过滤器函数列表。而 `mu` 成员为互斥锁,用于在多个 Goroutine 访问或修改时对其它成员提供保护

然后就是Get和Set方法
然后就是 Get 和 Set 方法

```go
func (p *KVStoreService) Get(key string, value *string) error {
Expand Down Expand Up @@ -131,9 +131,9 @@ func (p *KVStoreService) Set(kv [2]string, reply *struct{}) error {
}
```

在Set方法中,输入参数是key和value组成的数组,用一个匿名的空结构体表示忽略了输出参数。当修改某个key对应的值时会调用每一个过滤器函数
在 Set 方法中,输入参数是 key 和 value 组成的数组,用一个匿名的空结构体表示忽略了输出参数。当修改某个 key 对应的值时会调用每一个过滤器函数

而过滤器列表在Watch方法中提供
而过滤器列表在 Watch 方法中提供

```go
func (p *KVStoreService) Watch(timeoutSecond int, keyChanged *string) error {
Expand All @@ -156,9 +156,9 @@ func (p *KVStoreService) Watch(timeoutSecond int, keyChanged *string) error {
}
```

Watch方法的输入参数是超时的秒数。当有key变化时将key作为返回值返回。如果超过时间后依然没有key被修改,则返回超时的错误。Watch的实现中,用唯一的id表示每个Watch调用,然后根据id将自身对应的过滤器函数注册到`p.filter`列表。
Watch 方法的输入参数是超时的秒数。当有 key 变化时将 key 作为返回值返回。如果超过时间后依然没有 key 被修改,则返回超时的错误。Watch 的实现中,用唯一的 id 表示每个 Watch 调用,然后根据 id 将自身对应的过滤器函数注册到 `p.filter` 列表。

KVStoreService服务的注册和启动过程我们不再赘述。下面我们看看如何从客户端使用Watch方法
KVStoreService 服务的注册和启动过程我们不再赘述。下面我们看看如何从客户端使用 Watch 方法

```go
func doClientWork(client *rpc.Client) {
Expand All @@ -183,13 +183,13 @@ func doClientWork(client *rpc.Client) {
}
```

首先启动一个独立的Goroutine监控key的变化。同步的watch调用会阻塞,直到有key发生变化或者超时。然后在通过Set方法修改KV值时,服务器会将变化的key通过Watch方法返回。这样我们就可以实现对某些状态的监控。
首先启动一个独立的 Goroutine 监控 key 的变化。同步的 watch 调用会阻塞,直到有 key 发生变化或者超时。然后在通过 Set 方法修改 KV 值时,服务器会将变化的 key 通过 Watch 方法返回。这样我们就可以实现对某些状态的监控。

## 4.3.3 反向RPC
## 4.3.3 反向 RPC

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

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

```go
func main() {
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,9 +236,9 @@ func main() {
}
```

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

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

```go
func doClientWork(clientChan <-chan *rpc.Client) {
Expand All @@ -255,22 +255,22 @@ func doClientWork(clientChan <-chan *rpc.Client) {
}
```

首先从管道去取一个RPC客户端对象,并且通过defer语句指定在函数退出前关闭客户端。然后是执行正常的RPC调用
首先从管道去取一个 RPC 客户端对象,并且通过 defer 语句指定在函数退出前关闭客户端。然后是执行正常的 RPC 调用


## 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 All @@ -305,7 +305,7 @@ func (p *HelloService) Hello(request string, reply *string) error {
}
```

基于上下文信息,我们可以方便地为RPC服务增加简单的登陆状态的验证
基于上下文信息,我们可以方便地为 RPC 服务增加简单的登陆状态的验证

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

这样可以要求在客户端链接RPC服务时,首先要执行登陆操作,登陆成功后才能正常执行其他的服务。
这样可以要求在客户端链接 RPC 服务时,首先要执行登陆操作,登陆成功后才能正常执行其他的服务。
Loading

0 comments on commit a60f1e8

Please sign in to comment.