Go(协程超时控制的实现)

本文主要介绍了Go 协程超时控制的实现,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

Go,协程超时控制的实现,久久派带你了解更多相关信息。

目录
  • Go 协程超时控制
  • Select 超时控制
    • go timer 计时器
    • go context

Go 协程超时控制

  • Select 阻塞方式
  • Context 方式

先说个场景:

假设业务中 A 服务需要调用 服务B,要求设置 5s 超时,那么如何优雅实现?

Select 超时控制

考虑是否可以用 select + time.After 方式进行实现

这里主要利用的是通道在携程之间通信的特点,当程序调用成功后,会向通道中发送信号。没调用成功前,通道会阻塞。

select { case res := <-c2:  fmt.Println(res) case <-time.After(time.Second * 3):  fmt.Println(\"timeout 2\") }

当 c2 通道中有数据时,并且超时时间没有达到 3s,走 case res := <-c2 这个业务逻辑,当超时时间达到 3s , 走的 case <-time.After(time.Second * 3) 这个业务逻辑, 这样就可以实现超时 3s 的控制。

res:= <-c2 是因为channel 可以实现阻塞,那么 time.After 为啥可以阻塞呢?

看 After 源码。sleep.go 可以看到其实也是 channel

func After(d Duration) <-chan Time { return NewTimer(d).C}

完整代码示例:

package timeoutimport ( \"fmt\" \"testing\" \"time\")func TestSelectTimeOut(t *testing.T) { // 在这个例子中, 假设我们执行了一个外部调用, 2秒之后将结果写入c1 c1 := make(chan string, 1) go func() {  time.Sleep(time.Second * 2)  c1 <- \"result 1\" }() // 这里使用select来实现超时, `res := <-c1`等待通道结果, // `<- Time.After`则在等待1秒后返回一个值, 因为select首先 // 执行那些不再阻塞的case, 所以这里会执行超时程序, 如果 // `res := <-c1`超过1秒没有执行的话 select { case res := <-c1:  fmt.Println(res) case <-time.After(time.Second * 1):  fmt.Println(\"timeout 1\") } // 如果我们将超时时间设为3秒, 这个时候`res := <-c2`将在 // 超时case之前执行, 从而能够输出写入通道c2的值 c2 := make(chan string, 1) go func() {  time.Sleep(time.Second * 2)  c2 <- \"result 2\" }() select { case res := <-c2:  fmt.Println(res) case <-time.After(time.Second * 3):  fmt.Println(\"timeout 2\") }}

运行结果:

=== RUN TestSelectTimeOut
timeout 1
result 2
— PASS: TestSelectTimeOut (3.00s)
PASS

go timer 计时器

这个是 timer 类似的计时器实现,通用也是通过通道来发送数据。

package mainimport \"time\"import \"fmt\"func main() {  // Ticker使用和Timer相似的机制, 同样是使用一个通道来发送数据。  // 这里我们使用range函数来遍历通道数据, 这些数据每隔500毫秒被  // 发送一次, 这样我们就可以接收到  ticker := time.NewTicker(time.Millisecond * 500)  go func() {    for t := range ticker.C {    fmt.Println(\"Tick at\", t)    }  }()  // Ticker和Timer一样可以被停止。 一旦Ticker停止后, 通道将不再  // 接收数据, 这里我们将在1500毫秒之后停止  time.Sleep(time.Millisecond * 1500)  ticker.Stop()  fmt.Println(\"Ticker stopped\")}

go context

context 监听是否有 IO 操作,开始从当前连接中读取网络请求,每当读取到一个请求则会将该cancelCtx传入,用以传递取消信号,可发送取消信号,取消所有进行中的网络请求。

  go func(ctx context.Context, info *Info) {   timeLimit := 120   timeoutCtx, cancel := context.WithTimeout(ctx, time.Duration(timeLimit)*time.Millisecond)   defer func() {    cancel()    wg.Done()   }()   resp := DoHttp(timeoutCtx, info.req)  }(ctx, info)

关键看业务代码: resp := DoHttp(timeoutCtx, info.req) 业务代码中包含 http 调用 NewRequestWithContext

req, err := http.NewRequestWithContext(ctx, \"POST\", url, strings.NewReader(paramString))

上面的代码,设置了过期时间,当DoHttp(timeoutCtx, info.req) 处理时间超过超时时间时,会自动截止,并且打印 context deadline exceeded。

看个代码:

package mainimport ( \"context\" \"fmt\" \"testing\" \"time\")func TestTimerContext(t *testing.T) { now := time.Now() later, _ := time.ParseDuration(\"10s\") ctx, cancel := context.WithDeadline(context.Background(), now.Add(later)) defer cancel() go Monitor(ctx) time.Sleep(20 * time.Second)}func Monitor(ctx context.Context) { select { case <-ctx.Done():  fmt.Println(ctx.Err()) case <-time.After(20 * time.Second):  fmt.Println(\"stop monitor\") }}

运行结果:

=== RUN TestTimerContext
context deadline exceeded
— PASS: TestTimerContext (20.00s)
PASS

Context 接口有如下:

type Context interface {    Deadline() (deadline time.Time, ok bool)    Done() <-chan struct{}    Err() error    Value(key interface{}) interface{}}

  • Deadline — 返回 context.Context 被取消的时间,也就是完成工作的截止日期;
  • Done — 返回一个 Channel,这个 Channel 会在当前工作完成或者上下文被取消之后关闭,多次调用 Done 方法会返回同一个 Channel;
  • Err — 返回 context.Context 结束的原因,它只会在 Done 返回的 Channel 被关闭时才会返回非空的值;
    • 如果 context.Context 被取消,会返回 Canceled 错误;
    • 如果 context.Context 超时,会返回 DeadlineExceeded 错误;
  • Value — 从 context.Context 中获取键对应的值,对于同一个上下文来说,多次调用 Value 并传入相同的 Key 会返回相同的结果,该方法可以用来传递请求特定的数据;

到此这篇关于Go 协程超时控制的实现的文章就介绍到这了,更多相关Go 协程超时控制内容请搜索趣讯吧以前的文章或继续浏览下面的相关文章希望大家以后多多支持趣讯吧!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,请发送邮件至 55@qq.com 举报,一经查实,本站将立刻删除。转转请注明出处:https://www.szhjjp.com/n/11852.html

(0)
nan
上一篇 2021-08-11
下一篇 2021-08-11

相关推荐

  • 糖果什么牌子好吃(糖果排行榜10强推荐)

    糖果是一种非常常见的小零食,但随着老百姓生活水平的提高,大家对于糖果的要求也越来越高,传统的糖果已经吸引不了大家的味蕾了,为了迎合大家的需求,所以现在出现了各种各样的糖果,这些糖果更受现代人的欢迎,下面小编就给大家介绍几款好吃的糖果吧!Top1:kd

    2021-10-02
    0
  • 世界上知名的中国品牌(中国的品牌有哪些)

    本内容来源于@什么值得买APP,观点仅代表作者本人|作者:值行改革春风吹满地,国货品牌真争气~越来越发现,国内的一些品牌物美价廉,而且覆盖面也很广,每个领域你都能找到不输国外大牌的国货品牌!比如回力、人本、海澜之家、波司登、特步、鸿星尔克等。今天就…

    2022-01-17 随笔
    0
  • 标致48怎么样(211款标致48口碑)

    东风标致408是荣获了车质网“投诉榜第一名”的车型,一时标致408竟有些老鼠过街,人人喊打的尴尬,以至于标致408的销量已经逐步走低,连续几个月销量降到千台以内,2020年1月份仅售出534台。不过依然有朋友问网:看了好多车,觉得标致408

    2021-12-16 用户投稿
    0
  • 提取公积金需要的证件(公积金取出来需要什么手续)

    今天我们来聊聊关于无房职工租房时住房公积金如何提取提取住房公积金,需要本人公积金账户绑定一张银行卡,这个可以在手机支付宝上直接操作绑定,免去在提取时的一些操作,具体的操作流程是怎样的呢?需要准备的资料有1.个人身份证件;2.房屋租赁

    2021-12-13
    0
  • 法国巴黎老佛爷百货营业时间(巴黎老佛爷百货恢复营业)

    当地时间11月28日,法国启动第二轮“解封”,巴黎老佛爷百货公司、春天百货均恢复营业。为应对第二波新冠肺炎疫情,法国10月30日进入第二次全国”封城”,巴黎老佛爷百货停业近一个月。考虑到疫情闭店以及1月31日开始实施的法国百货公司疫情封锁

    2021-12-07
    0
  • 怎么解散微信群聊(如何一键解散微信群)

    怎么解散微信群聊(如何一键解散微信群)。久久派带你了解更多相关信息。随着智能手机的出现,人们的社交方式也发生了变化,逐渐地从线下、PC端转到了线上,其中微信最为火爆,而该软件根据用户的需求,还研发出了“社群”功能,但很

    2022-01-17
    0

发表回复

登录后才能评论