Golang 入门资料+笔记

learn go with tests 学习笔记(六)Selec

2020-06-23  本文已影响0人  半亩房顶

知识点

net/http/httptest

在标准库中有一个 net/http/httptest 包,它可以让你轻易建立一个 HTTP 模拟服务器(mock HTTP server)。
我们改为使用模拟测试,这样我们就可以控制可靠的服务器来测试了。

func  TestRacer(t *testing.T)  {

 slowServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request)  {

 time.Sleep(20  * time.Millisecond)

 w.WriteHeader(http.StatusOK)

 }))

 fastServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request)  {

 w.WriteHeader(http.StatusOK)

 }))

 slowURL := slowServer.URL

 fastURL := fastServer.URL

 want := fastURL

 got :=  Racer(slowURL, fastURL)

 if got != want {

 t.Errorf("got '%s', want '%s'", got, want)

 }

 slowServer.Close()

 fastServer.Close()

}

httptest.NewServer 接受一个我们传入的 匿名函数 http.HandlerFunc
http.HandlerFunc 是一个看起来类似这样的类型:type HandlerFunc func(ResponseWriter, *Request)
这些只是说它是一个需要接受一个 ResponseWriterRequest 参数的函数,这对于 HTTP 服务器来说并不奇怪。
结果呢,这里并没有什么彩蛋,这也是如何在 Go 语言写一个 真实的 HTTP 服务器的方法。唯一的区别就是我们把它封装成一个易于测试的 httptest.NewServer,它会找一个可监听的端口,然后测试完你就可以关闭它了。
我们让两个服务器中慢的那一个短暂地 time.Sleep 一段时间,当我们请求时让它比另一个慢一些。然后两个服务器都会通过 w.WriteHeader(http.StatusOK) 返回一个 OK 给调用者。

defer

在某个函数调用前加上 defer 前缀会在 包含它的函数结束时 调用它。
有时你需要清理资源,例如关闭一个文件,在我们的案例中是关闭一个服务器,使它不再监听一个端口。
你想让它在函数结束时执行(关闭服务器),但要把它放在你创建服务器语句附近,以便函数内后面的代码仍可以使用这个服务器。

进程同步

示例代码:

func Racer(a, b string) (winner string, error error) {
    select {
    case <-ping(a):
        return a, nil
    case <-ping(b):
        return b, nil
    case <-time.After(10 * time.Second):
        return "", fmt.Errorf("timed out waiting for %s and %s", a, b)
    }
}

func ping(url string) chan bool {
    ch := make(chan bool)
    go func() {
        http.Get(url)
        ch <- true
    }()
    return ch
}
(一)ping

我们定义了一个可以创建 chan bool 类型并返回它的 ping 函数。
在这个案例中,我们并不 关心 channel 中发送的类型, 我们只是想发送一个信号 来说明已经发送完了,所以返回 bool 就可以了。
同样在这个函数中,当我们完成 http.Get(url) 时启动了一个用来给 channel 发送信号的 Go 程(goroutine)。

(二)select

如果你记得并发那一章的内容,你可以通过 myVar := <-ch 来等待值发送给 channel。这是一个 阻塞 的调用,因为你需要等待值返回。
select 则允许你同时在 多个 channel 等待。第一个发送值的 channel「胜出」,case 中的代码会被执行。
我们在 select 中使用 ping 为两个 URL 设置两个 channel。无论哪个先写入其 channel 都会使 select 里的代码先被执行,这会导致那个 URL 先被返回(胜出)。
如此一来,进程同步实现起来非常简单。

引用


欢迎大家关注我的公众号


半亩房顶
上一篇下一篇

猜你喜欢

热点阅读