go的ctx超时控制

正确写法

对于要控制超时的函数,要异步调用它,才能做到超时控制

go 复制代码
func TestCtxCancal(t *testing.T) {
	tag := "TestCtxCancal"
	ctx := context.Background()
	// kafka关闭可能耗时较长,设置强制退出时间,设置5s超时
	ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
	defer cancel()
	stop := make(chan struct{}, 1)
	go func() {
		// mock kafka close 耗时很长,耗时15s
		time.Sleep(time.Second * 15)
		logger.Infow(ctx, tag, "msg", "kafka has closed")
		stop <- struct{}{}
	}()

	hasCancelTimeout := false
	select {
	case <-ctx.Done():
		logger.Error(context.TODO(), "sercer.main", "kafka Shutdown timed out, might not have stopped gracefully")
		hasCancelTimeout = true
	case <-stop:
		logger.Info(context.TODO(), "main", "kafka consume has stopped")
		hasCancelTimeout = false
	}

    //校验代码进入了select的超时case 
	assert.True(t, hasCancelTimeout)
}

错误写法

同步调用要控制超时的函数,就会导致必须等到函数执行完后,才走到select的代码,超时控制失去作用

go 复制代码
func TestCtxCancalFail(t *testing.T) {
	tag := "TestCtxCancal"
	ctx := context.Background()
	// kafka关闭可能耗时较长,设置强制退出时间
	timeout := 5 * time.Second
	ctx, cancel := context.WithTimeout(ctx, timeout)
	defer cancel()
	stop := make(chan struct{}, 1)

	start := time.Now()
	// 如果同步调用close,那么监听超时代码就失去意义
	{
		// mock kafka close 耗时很长
		time.Sleep(time.Second * 15)
		logger.Infow(ctx, tag, "msg", "kafka has closed")
		stop <- struct{}{}
	}

	select {
	case <-ctx.Done():
		logger.Error(context.TODO(), "sercer.main", "kafka Shutdown timed out, might not have stopped gracefully")
	case <-stop:
		logger.Info(context.TODO(), "main", "kafka consume has stopped")
	}

	elapsed := time.Since(start)
	// 证明同步调用要控制超时的函数,cancel的超时控制就失去意义了
	// 10s已经远大于 timeout := 5 * time.Second
	assert.True(t, elapsed > 10*time.Second)
}

总结

go的ctx超时控制常用于服务的优雅退出,在释放资源时,加一个超时控制,避免服务迟迟未能退出。在使用context.WithTimeout时,要注意正确异步调用要控制超时的函数

相关推荐
ZealSinger1 天前
Go的DefaultClient超时为何总不生效
go·超时·context·net/http·defaultclient
我叫黑大帅1 天前
Go日志库工程选型与逃逸分析评测报告
后端·面试·go
福兮说2 天前
Go 优雅退出:Shutdown 之后,后台 goroutine 还在跑(四个实测的坑)
后端·go
jeffwang2 天前
不装分词插件,用 PostgreSQL 做中文商品搜索:bigram + 向量 + RRF,以及「单字搜不到」这个坑
搜索引擎·postgresql·go
云浪2 天前
通过 Goroutine 搞懂 Go 并发与常见的 9 个坑
后端·go
福兮说2 天前
singleflight 防缓存击穿,这四个坑它不会替你挡
go
我的div丢了肿么办2 天前
自定义类型和类型别名以及实例化结构体的5种方式
后端·go
小满zs2 天前
Go语言第十三章(互斥锁,读写锁)
后端·go
福兮说2 天前
errgroup 的六个坑:Wait 之后 ctx 已取消、SetLimit 嵌套死锁,以及另外四个
后端·go
福兮说2 天前
Go 解析邮件的三个坑:GBK 标题、QP 正文,以及 NextPart 偷偷帮你做的事
后端·go