写点什么

learn go with tests 学习笔记(六)进程同步

用户头像
半亩房顶
关注
发布于: 2020 年 08 月 07 日
learn go with tests 学习笔记(六)进程同步

知识点

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 先被返回(胜出)。

如此一来,进程同步实现起来非常简单。

引用



欢迎大家关注我的公众号,一起探讨技术


发布于: 2020 年 08 月 07 日阅读数: 57
用户头像

半亩房顶

关注

人生那么长,能写多少bug? 2018.11.16 加入

我希望,自己永远是自己。我希望,远离bug。

评论

发布
暂无评论
learn go with tests 学习笔记(六)进程同步