go入门教程- 版本 5 - 分布式程序

  1. 版本 5 - 分布式程序
  2. 19.8 多服务器处理架构
    1. 链接

版本 5 - 分布式程序

第 5 个版本的代码 goto_v5(19.8 节和 19.9 节讨论)见 goto_v5。该版本仍然基于 gob 存储,但很容易调整为使用 json,正如版本 4 演示的那样。

19.8 多服务器处理架构

目前为止 goto 以单线程运行,但即使用协程,在一台机器上运行的单一进程,也只能为一定数量的并发请求提供服务。一个缩短网址服务,相对于 Add(用 Put() 写入),通常 Redirect 服务(用 Get() 读取)要多得多。因此我们应该可以创建任意数量的只读的从(slave)服务器,提供服务并缓存 Get 方法调用的结果,将 Put 请求转发给主(master)服务器,类似如下架构:

图 19.5 跨越主从计算机的分布式负载

对于 slave 进程,要在网络上运行 goto 应用的一个 master 节点实例,它们必须能相互通信。Go 的 rpc 包为跨越网络发起函数调用提供了便捷的途径。这里将把 URLStore 变为 RPC 服务(15.9 节 详细讨论了 rpc 包)。slave 进程将应对 Get 请求以交付长 URL。当一个长 URL 要被转换为缩短版本(使用 Put 方法)时,它们通过 rpc 连接把任务委托给 master 进程,因此只有 master 节点会写入数据文件。

截至目前 URLStore 上基本的 Get()Put() 方法具有如下签名:

1
2
func (s *URLStore) Get(key string) string
func (s *URLStore) Put(url string) string

而 RPC 调用仅能使用如下形式的方法(t 是 T 类型的值):

1
func (t T) Name(args *ArgType, reply *ReplyType) error

要使 URLStore 成为 RPC 服务,需要修改 PutGet 方法使它们符合上述函数签名。以下是修改后的签名:

1
2
func (s *URLStore) Get(key, url *string) error
func (s *URLStore) Put(url, key *string) error

Get() 代码变更为:

1
2
3
4
5
6
7
8
9
func (s *URLStore) Get(key, url *string) error {
s.mu.RLock()
defer s.mu.RUnlock()
if u, ok := s.urls[*key]; ok {
*url = u
return nil
}
return errors.New("key not found")
}

现在,键和长 URL 都变成了指针,必须加上前缀 * 来取得它们的值,例如 *key 这种形式。u 是一个值,可以用 *url = u 来将其赋值给指针。

接着对 Put() 代码做同样的改动:

1
2
3
4
5
6
7
8
9
10
11
12
func (s *URLStore) Put(url, key *string) error {
for {
*key = genKey(s.Count())
if err := s.Set(key, url); err == nil {
break
}
}
if s.save != nil {
s.save <- record{*key, *url}
}
return nil
}

Put() 调用 Set(),由于后者也要做调整,keyurl 参数现在是指针类型,还必须返回 error 取代 boolean

1
2
3
4
5
6
7
8
9
func (s *URLStore) Set(key, url *string) error {
s.mu.Lock()
defer s.mu.Unlock()
if _, present := s.urls[*key]; present {
return errors.New("key already exists")
}
s.urls[*key] = *url
return nil
}

同样,当从 load() 调用 Set() 时,也必须做调整:

1
s.Set(&r.Key, &r.URL)

还必须修改 HTTP 处理函数以适应 URLStore 上的更改。Redirect 处理函数现在返回 URLStore 给出错误的字符串形式:

1
2
3
4
5
6
7
8
9
func Redirect(w http.ResponseWriter, r *http.Request) {
key := r.URL.Path[1:]
var url string
if err := store.Get(&key, &url); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
http.Redirect(w, r, url, http.StatusFound)
}

Add 处理函数也以基本相同的方式修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
func Add(w http.ResponseWriter, r *http.Request) {
url := r.FormValue("url")
if url == "" {
fmt.Fprint(w, AddForm)
return
}
var key string
if err := store.Put(&url, &key); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
fmt.Fprintf(w, "http://%s/%s", *hostname, key)
}

要使应用程序更灵活,正如之前章节所为,可以添加一个命令行标志(flag)来决定是否在 main() 函数中启用 RPC 服务器:

1
var rpcEnabled = flag.Bool("rpc", false, "enable RPC server")

要使 RPC 工作,还要用 rpc 包来注册 URLStore,并用 HandleHTTP 创建基于 HTTP 的 RPC 处理器:

1
2
3
4
5
6
7
8
9
func main() {
flag.Parse()
store = NewURLStore(*dataFile)
if *rpcEnabled { // flag has been set
rpc.RegisterName("Store", store)
rpc.HandleHTTP()
}
... (set up http like before)
}

链接


免责声明:本页面内容均来源于站内编辑发布,部分信息来源互联网,并不意味着本站赞同其观点或者证实其内容的真实性,如涉及版权等问题,请立即联系客服进行更改或删除,保证您的合法权益。转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。也可以邮件至 [email protected]

推荐阅读:

文章标题:go入门教程- 版本 5 - 分布式程序

本文作者:知识铺

发布时间:2019-10-15, 22:30:20

最后更新:2019-10-16, 21:00:40

原始链接:https://blog.zshipu.com/2019/10/15/golang/20191015/19.8/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录
×

喜欢就点赞,疼爱就打赏