我有一个websocket服务器。我为他写了一个测试,测试他优雅地关机的能力。创建了5个连接,每个连接发送5个请求。一段时间后,关机就开始了。所有25项要求都必须得到满足。如果我关闭了exit通道,那么测试就不能正常工作。
time.AfterFunc(50*time.Millisecond, func() {
close(exit)
close(done)
})如果我只调用s.shutdown函数,那么一切都没问题。
time.AfterFunc(50*time.Millisecond, func() {
require.Nil(t, s.Shutdown())
close(done)
})我的测验
func TestServer_GracefulShutdown(t *testing.T) {
done := make(chan struct{})
exit := make(chan struct{})
ctx := context.Background()
finishedRequestCount := atomic.NewInt32(0)
ln, err := net.Listen("tcp", "localhost:")
require.Nil(t, err)
handler := HandlerFunc(func(conn *websocket.Conn) {
for {
_, _, err := conn.ReadMessage()
if err != nil {
return
}
time.Sleep(100 * time.Millisecond)
finishedRequestCount.Inc()
}
})
s, err := makeServer(ctx, handler) // server create
require.Nil(t, err)
time.AfterFunc(50*time.Millisecond, func() {
close(exit)
close(done)
})
go func() {
fmt.Printf("Running...")
require.Nil(t, s.Run(ctx, exit, ln))
}()
for i := 0; i < connCount; i++ {
go func() {
err := clientRun(ln)
require.Nil(t, err)
}()
}
<-done
assert.Equal(t, int32(totalCount), finishedRequestCount.Load())
}我的跑步漏斗
func (s *Server) Run(ctx context.Context, exit <-chan struct{}, ln net.Listener) error {
errs := make(chan error, 1)
go func() {
err := s.httpServer.Run(ctx, exit, ln)
if err != nil {
errs <- err
}
}()
select {
case <-ctx.Done():
return s.Close()
case <-exit:
return s.Shutdown()
case err := <-errs:
return err
}
}我关机
func (s *Server) Shutdown() error {
err := s.httpServer.Shutdown() // we close the possibility to connect to any conn
s.wg.Wait()
return err
}发布于 2021-09-30 10:34:18
如果执行以下代码,会发生什么情况?
close(exit)
close(done)两个频道几乎同时关闭。第一个触发Shutdown函数,该函数等待优雅的关闭。但是第二个触发了对
assert.Equal(t, int32(totalCount), finishedRequestCount.Load())它是在优雅的关机仍在运行或尚未启动时触发的。
如果直接执行关机函数,它将一直阻塞直到完成,只有这样,close(done)才会启动断言。这就是为什么这样做的原因:
require.Nil(t, s.Shutdown())
close(done)您可以将close(done)移动到以下位置以使测试工作,同时使用exit通道关闭:
go func() {
fmt.Printf("Running...")
require.Nil(t, s.Run(ctx, exit, ln))
close(done)
}()这样,done将在执行Shutdown函数后关闭。
正如评论中所讨论的,我强烈建议使用上下文而不是通道来关闭。它们的复杂性在于隐藏着封闭的通道。
https://stackoverflow.com/questions/69389783
复制相似问题