第十九章修改 (#832)

Co-authored-by: Joe Chen <jc@unknwon.io>
This commit is contained in:
Haigang Zhou
2022-05-19 20:00:29 +08:00
committed by GitHub
parent fa1cfcc67f
commit 800466becd
9 changed files with 82 additions and 71 deletions

View File

@@ -1,14 +1,14 @@
# 版本 5 - 分布式程序
第 5 个版本的代码 *goto_v5*19.8 节和 19.9 节讨论)见 [goto_v5](examples/chapter_19/goto_v5)。该版本仍然基于 `gob` 存储,但很容易调整为使用 json正如版本 4 演示的那样。
第 5 个版本的代码 *goto_v5*[19.8 节](19.8.md)和 [19.9 节](19.9.md) 讨论)见 [goto_v5](examples/chapter_19/goto_v5)。该版本仍然基于 `gob` 存储,但很容易调整为使用 json正如版本 4 演示的那样。
# 19.8 多服务器处理架构
目前为止 goto 以单线程运行,但即使用协程,在一台机器上运行的单一进程,也只能为一定数量的并发请求提供服务。一个缩短网址服务,相对于 `Add`(用 `Put()` 写入),通常 `Redirect` 服务(用 `Get()` 读取)要多得多。因此我们应该可以创建任意数量的只读的从slave服务器,提供服务并缓存 `Get` 方法调用的结果,将 `Put` 请求转发给主master服务器,类似如下架构:
目前为止 goto 以单线程运行,但即使用协程,在一台机器上运行的单一进程,也只能为一定数量的并发请求提供服务。一个缩短网址服务,相对于 `Add()`(用 `Put()` 写入),通常 `Redirect()` 服务(用 `Get()` 读取)要多得多。因此我们应该可以创建任意数量的只读的从 (slave) 服务器,提供服务并缓存 `Get()` 方法调用的结果,将 `Put()` 请求转发给主 (master) 服务器,类似如下架构:
![图 19.5 跨越主从计算机的分布式负载](images/19.8_fig19.5.jpg?raw=true)
对于 slave 进程,要在网络上运行 goto 应用的一个 master 节点实例它们必须能相互通信。Go 的 `rpc` 包为跨越网络发起函数调用提供了便捷的途径。这里将把 `URLStore` 变为 RPC 服务([15.9 节](15.9.md) 详细讨论了 rpc 包。slave 进程将应对 `Get` 请求以交付长 URL。当一个长 URL 要被转换为缩短版本(使用 `Put` 方法)时,它们通过 rpc 连接把任务委托给 master 进程,因此只有 master 节点会写入数据文件。
对于 slave 进程,要在网络上运行 goto 应用的一个 master 节点实例它们必须能相互通信。Go 的 `rpc` 包为跨越网络发起函数调用提供了便捷的途径。这里将把 `URLStore` 变为 RPC 服务([15.9 节](15.9.md) 详细讨论了 `rpc`。slave 进程将应对 `Get()` 请求以交付长 URL。当一个长 URL 要被转换为缩短版本(使用 `Put()` 方法)时,它们通过 rpc 连接把任务委托给 master 进程,因此只有 master 节点会写入数据文件。
截至目前 `URLStore` 上基本的 `Get()``Put()` 方法具有如下签名:
```go
@@ -16,18 +16,19 @@ func (s *URLStore) Get(key string) string
func (s *URLStore) Put(url string) string
```
而 RPC 调用仅能使用如下形式的方法(t 是 T 类型的值):
而 RPC 调用仅能使用如下形式的方法(`t``T` 类型的值):
```go
func (t T) Name(args *ArgType, reply *ReplyType) error
```
要使 `URLStore` 成为 RPC 服务,需要修改 `Put``Get` 方法使它们符合上述函数签名。以下是修改后的签名:
要使 `URLStore` 成为 RPC 服务,需要修改 `Put()``Get()` 方法使它们符合上述函数签名。以下是修改后的签名:
```go
func (s *URLStore) Get(key, url *string) error
func (s *URLStore) Put(url, key *string) error
```
`Get()` 代码变更为:
```go
func (s *URLStore) Get(key, url *string) error {
s.mu.RLock()
@@ -76,7 +77,7 @@ func (s *URLStore) Set(key, url *string) error {
s.Set(&r.Key, &r.URL)
```
还必须修改 HTTP 处理函数以适应 `URLStore` 上的更改。`Redirect` 处理函数现在返回 `URLStore` 给出错误的字符串形式:
还必须修改 HTTP 处理函数以适应 `URLStore` 上的更改。`Redirect()` 处理函数现在返回 `URLStore` 给出错误的字符串形式:
```go
func Redirect(w http.ResponseWriter, r *http.Request) {
key := r.URL.Path[1:]
@@ -89,7 +90,8 @@ func Redirect(w http.ResponseWriter, r *http.Request) {
}
```
`Add` 处理函数也以基本相同的方式修改:
`Add()` 处理函数也以基本相同的方式修改:
```go
func Add(w http.ResponseWriter, r *http.Request) {
url := r.FormValue("url")
@@ -106,12 +108,12 @@ func Add(w http.ResponseWriter, r *http.Request) {
}
```
要使应用程序更灵活,正如之前章节所为,可以添加一个命令行标志flag来决定是否在 `main()` 函数中启用 RPC 服务器:
要使应用程序更灵活,正如之前章节所为,可以添加一个命令行标志 (flag) 来决定是否在 `main()` 函数中启用 RPC 服务器:
```go
var rpcEnabled = flag.Bool("rpc", false, "enable RPC server")
```
要使 RPC 工作,还要用 `rpc` 包来注册 `URLStore`,并用 `HandleHTTP` 创建基于 HTTP 的 RPC 处理器:
要使 RPC 工作,还要用 `rpc` 包来注册 `URLStore`,并用 `HandleHTTP()` 创建基于 HTTP 的 RPC 处理器:
```go
func main() {
flag.Parse()