Golang

关注公众号 jb51net

关闭
首页 > 脚本专栏 > Golang > Go Resiliency库中timeout

Go Resiliency库中timeout实现原理及源码解析

作者:soft2967

Go-Resiliency库中的timeout是一种基于协程的超时机制,通过创建协程来执行任务并设置超时时间,若任务执行时间超时则中止协程并返回错误,需要详细了解可以参考下文

1.go-resiliency简介

​ 今天看到项目里用到了go-resiliency这个库,库整体比较简单,代码量不大。主要实现go中几种常见的模式:

后面分析下这几种模式的实现

- circuit-breaker 熔断器
- semaphore       信号量
- timeout         函数超时
- batching        批处理
- retriable       可重复

2.timeout模式

先看看模式的test用例

import (
	"errors"
	"testing"
	"time"
)
func takesFiveSecond(stopper <-chan struct{}) error {
	time.Sleep(5 * time.Second)
	return nil
}
func takesTwentySecond(stopper <-chan struct{}) error {
	time.Sleep(20 * time.Second)
	return nil
}
func TestDeadline(t *testing.T) {
	dl := New(10 * time.Second)
  //执行takesFiveSecond
  if err := dl.Run(takesFiveSecond); err != nil {
		t.Error(err)
	}
  //执行takesTwentySecond
  if err := dl.Run(takesTwentySecond); err == ErrTimedOut {
		t.Error(err)
	}
}

3.源码实现如下

type Deadline struct {
	timeout time.Duration
}
func New(timeout time.Duration) *Deadline {
	return &Deadline{
		timeout: timeout,
	}
}

Deadline对象只有一个timeout成员变量

Run核心函数:

//1. 可以看到Run函数有一个入参是一个函数,函数的原型为func (<-chan struct{}))error 也就是说我们传入work变量就需要定义一个这个的签名函数。
//2. Run函数返回error,这个返回实际是入参work函数返回的。
//3.为什么work函数变量,要有一个chan了? 这个主要为了能让work函数里来控制,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监听2个chan,一个读取结果,一个为超时定时器。
  //如果在timeout时间内未读取到执行结果,就触发time.After返回超时
	select {
	case ret := <-result:
		return ret
	case <-time.After(d.timeout):
		close(stopper)
		return ErrTimedOut
	}
}

Run函数定义:Run(work func(<-chan struct{}) error) error :

4.扩展一下

go语言里超时控制还有其他常用方式吗

对就是context.WithTimeout,让我们使用context.WithTimeout来重新实现上面的对象,只需要修改一个地方

import (
	"context"
	"errors"
	"time"
)
var ErrTimedOut = errors.New("timed out waiting for function to finish")
type ContextTimeOut struct {
	timeout time.Duration
}
// New constructs a new Deadline with the given timeout.
func New(timeout time.Duration) *ContextTimeOut {
	return &ContextTimeOut{
		timeout: timeout,
	}
}
func (d *ContextTimeOut) 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:
		}
	}()
	ctx, _ := context.WithTimeout(context.Background(), d.timeout)
	select {
	case ret := <-result:
		return ret
	case <-ctx.Done():
		close(stopper)
		return ErrTimedOut
	}
}

到此这篇关于Go Resiliency库中timeout实现原理及源码解析的文章就介绍到这了,更多相关Go Resiliency库中timeout内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文