go-resilency 源码阅读 - deadline

golang 的 deadline/timeout(超时) 弹性模式。

创建 deadline 只需一个参数:等待的时间。

仓库给的使用例子:

go 复制代码
dl := deadline.New(1 * time.Second)

err := dl.Run(func(stopper <-chan struct{}) error {
	// do something potentially slow
	// give up when the `stopper` channel is closed (indicating a time-out)
	// 做一些可能很慢的事情
	// 当 `stopper` 通道关闭(表示超时)时放弃
	return nil
})

switch err {
case deadline.ErrTimedOut:
	// execution took too long, oops
	// 执行时间太长了
default:
	// some other error
	// 其他错误
}

首先通过 New 函数,新建一个超时器,传入的参数,是所期待的超时时长

go 复制代码
func New(timeout time.Duration) *Deadline {
	return &Deadline{
		timeout: timeout,
	}

然后调用超时器的 Run 方法即可,在运行时间超出所期待的超时时长就会退出,返回 ErrTimedOut 的错误

go 复制代码
// ErrTimedOut is the error returned from Run when the deadline expires.
var ErrTimedOut = errors.New("timed out waiting for function to finish")

// Run runs the given function, passing it a stopper channel. If the deadline passes before
// the function finishes executing, Run returns ErrTimeOut to the caller and closes the stopper
// channel so that the work function can attempt to exit gracefully. It does not (and cannot)
// simply kill the running function, so if it doesn't respect the stopper channel then it may
// keep running after the deadline passes. If the function finishes before the deadline, then
// the return value of the function is returned from Run.
// Run 运行给定函数,并传递给它一个 stopper 通道。如果在 
// 函数执行完毕之前截止时间已过,Run 会向调用者返回 ErrTimeOut,并关闭 stopper 
// 通道,以便工作函数可以优雅地退出。它不会(也不能) 
// 简单地杀死正在运行的函数,因此如果它不尊重 stopper 通道,那么它可能 
// 在截止日期过后继续运行。如果函数在截止日期前结束,那么 
// 函数的返回值将从 Run 返回。
func (d *Deadline) Run(work func(<-chan struct{}) error) error {
	result := make(chan error)
	stopper := make(chan struct{})

	go func() {
		value := work(stopper)
		select {
		case result <- value:
		case <-stopper:
		}
	}()

	select {
	case ret := <-result:
		return ret
	case <-time.After(d.timeout):
		close(stopper)
		return ErrTimedOut
	}
}

测试用例所要执行的 work 函数定义:

go 复制代码
func takesFiveMillis(stopper <-chan struct{}) error {
	time.Sleep(5 * time.Millisecond)
	return nil
}

func takesTwentyMillis(stopper <-chan struct{}) error {
	time.Sleep(20 * time.Millisecond)
	return nil
}

func returnsError(stopper <-chan struct{}) error {
	return errors.New("foo")
}

可以看出我们要执行的函数的参数都必须是 chan 类型,然后定义的字段名是 stopper,其实定义为其他字段名也是一样的

如果只是想简单地将我们执行的函数超时就不执行了,那么直接定义完相应的执行函数和新建超时器进行执行即可,比如:

go 复制代码
func takesFiveMillis(stopper <-chan struct{}) error {
	time.Sleep(5 * time.Millisecond)
	return nil
}

func takesTwentyMillis(stopper <-chan struct{}) error {
	time.Sleep(20 * time.Millisecond)
	return nil
}

func returnsError(stopper <-chan struct{}) error {
	return errors.New("foo")
}

func TestDeadline(t *testing.T) {
	dl := New(10 * time.Millisecond)

	if err := dl.Run(takesFiveMillis); err != nil {
		t.Error(err)
	}

	if err := dl.Run(takesTwentyMillis); err != ErrTimedOut {
		t.Error(err)
	}
}

但是如果你想在超时后执行一些操作,那么就可以利用我们执行函数的传入 stopper 参数,(但是这样子做,你的函数执行一定会超时,暂时不理解这个设计目的所在)比如:

go 复制代码
dl := New(10 * time.Millisecond)
done := make(chan struct{})
	err := dl.Run(func(stopper <-chan struct{}) error {
		<-stopper
		// 可以在这里执行在超时后,进行的一些处理
		close(done)
		return nil
	})
	if err != ErrTimedOut {
		t.Error(err)
	}
	<-done
相关推荐
云和数据.ChenGuang32 分钟前
用 Claude Code 重构遗留系统:从评估到落地的完整指南
java·人工智能·spring boot·后端·重构·springai
豆角焖肉1 小时前
SpringBoot 集成腾讯 AI 人脸识别
人工智能·spring boot·后端·人脸识别·腾讯 ai·base64 图片
嘎嘎风1 小时前
MiniMind 学习笔记之 21 训练细节:数据、分词器、断点续训
gpt·源码阅读
付威20231 小时前
如果你想拥有自己的 Agent,为什么值得从 rpi 开始
后端
可乐ea2 小时前
2000 个智能体把 Agent 重写成 Rust:编排流水线拆解
开发语言·后端·rust·多智能体协作·ai编程助手·智能体编排·rust重写
用户72757483196502 小时前
02-变量、常量和字符串:len("Hello, 世界") 为什么不是 9?
后端
SamDeepThinking2 小时前
CyclicBarrier的底层实现
java·后端·面试
god_cvz2 小时前
第二章:6、对话记忆与引用标注
后端·ai编程
拖孩2 小时前
全程 AI 写的小程序,刚接上会员虚拟支付,上线第二天就有人真金白银买单了|双签名、定价、iOS 的坑我全踩了一遍
前端·后端·微信小程序
我是人✓2 小时前
SpringMVC异常处理与拦截器实战揭秘
java·开发语言·后端