问题

前段时间发现线上有个服务接口,总是间歇性告警,有时候一天两三次,有时候一天都没有。

告警的逻辑是在一个接口中异步调用了另一个HTTP接口,这个HTTP接口调用出现超时。但是我去问了负责这个HTTP接口的同学,人家说他们的接口相应都是毫秒级别,还截图监控了,有图有真相,我还能说啥。

但是,超时是确实存在的,只是请求还可能没有到人家服务那边。

这种偶发性问题不好复现,偶尔来个告警也挺烦的,第一反应还是先解决问题,思路也简单,失败后重试。

解决方法

且不谈重试策略,先说说什么时候触发重试。

我们可以在接口请求出错抛出err的时候重试,但是这种不好控制,如果一个请求出去,十来秒都没有响应,则这个协程就要傻傻的等他报错才能重试,浪费生命啊~

所以结合上面同学给出的毫秒级响应指标,可以设定一个超时时间,如果在指定超时时间后没有返回结果,则重试(这篇重试不是重点)。

func AsyncCall() {
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(time.Millisecond*800))
defer cancel()
go func(ctx context.Context) {
// 发送HTTP请求
}() select {
case <-ctx.Done():
fmt.Println("call successfully!!!")
return
case <-time.After(time.Duration(time.Millisecond * 900)):
fmt.Println("timeout!!!")
return
}
}

  

说明

1、通过context的WithTimeout设置一个有效时间为800毫秒的context。

2、该context会在耗尽800毫秒后或者方法执行完成后结束,结束的时候会向通道ctx.Done发送信号。

3、有人可能要问,你这里已经设置了context的有效时间,为什么还要加上这个time.After呢?

这是因为该方法内的context是自己申明的,可以手动设置对应的超时时间,但是在大多数场景,这里的ctx是从上游一直传递过来的,对于上游传递过来的context还剩多少时间,我们是不知道的,所以这时候通过time.After设置一个自己预期的超时时间就很有必要了。

4、注意,这里要记得调用cancel(),不然即使提前执行完了,还要傻傻等到800毫秒后context才会被释放。

总结

上面的超时控制是搭配使用了ctx.Done和time.After。

Done通道负责监听context啥时候完事,如果在time.After设置的超时时间到了,你还没完事,那我就不等了,执行超时后的逻辑代码。

举一反三

那么,除了上面这种超时控制策略,还有其他的套路吗?

有,但是大同小异。

第一种:使用time.NewTimer

func AsyncCall() {
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(time.Millisecond * 800))
defer cancel()
timer := time.NewTimer(time.Duration(time.Millisecond * 900)) go func(ctx context.Context) {
// 发送HTTP请求
}() select {
case <-ctx.Done():
timer.Stop()
timer.Reset(time.Second)
fmt.Println("call successfully!!!")
return
case <-timer.C:
fmt.Println("timeout!!!")
return
}
}

  

这里的主要区别是将time.After换成了time.NewTimer,也是同样的思路如果接口调用提前完成,则监听到Done信号,然后关闭定时器。

否则的话,会在指定的timer即900毫秒后执行超时后的业务逻辑。

第二种:使用通道

func AsyncCall() {
ctx := context.Background()
done := make(chan struct{}, 1) go func(ctx context.Context) {
// 发送HTTP请求
done <- struct{}{}
}() select {
case <-done:
fmt.Println("call successfully!!!")
return
case <-time.After(time.Duration(800 * time.Millisecond)):
fmt.Println("timeout!!!")
return
}
}

  

1、这里主要利用通道可以在协程之间通信的特点,当调用成功后,向done通道发送信号。

2、监听Done信号,如果在time.After超时时间之前接收到,则正常返回,否则走向time.After的超时逻辑,执行超时逻辑代码。

3、这里使用的是通道和time.After组合,也可以使用通道和time.NewTimer组合。

总结

本篇主要介绍如何实现超时控制,主要有三种

1、context.WithTimeout/context.WithDeadline + time.After

2、context.WithTimeout/context.WithDeadline + time.NewTimer

3、channel + time.After/time.NewTimer

如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!如果您想持续关注我的文章,请扫描二维码,关注JackieZheng的微信公众号,我会将我的文章推送给您,并和您一起分享我日常阅读过的优质文章。

最新文章

  1. matplotlib 高级用法实例--共享x轴
  2. VS 远程发布IIS
  3. web文件操作常见安全漏洞(目录、文件名检测漏洞)
  4. TJI读书笔记12-接口
  5. Reprojection Matrix Q
  6. 如何用Apache POI操作Excel文件-----如何对一个单元格加注解?
  7. 一个由印度人编写的VC串口类
  8. C#使用指针复制字节数组
  9. CICS的database中R D中参数的含义
  10. 插入排序 --- 排序算法 --- 算法 --- java
  11. linux 内核参数图解
  12. 1088-Gnome Sequencing
  13. 解决Xcode7 iOS9苹果将原http协议改成了https协议问题
  14. Mac OS X Yosemite安装Hadoop 2.6记录
  15. Oracle sql 中的字符(串)替换与转换[转载]
  16. arcgisserver成功发布服务后,浏览服务,无地图显示
  17. 第二节. SignalR开篇以及如何指定传输协议
  18. 设计模式十: 生成器模式(Builder Pattern)
  19. java List&lt;String&gt;的初始化
  20. VirtualBox上的Ubuntu附加功能

热门文章

  1. VS自身的单元测试方法DEMO
  2. 吴裕雄--天生自然python编程:正则表达式
  3. 抛开贾跃亭!法拉第FF91能成功吗?
  4. 探索Kinect的更多可能——亲历第十九届机器人世界杯RoboCup
  5. SQL命令汇总
  6. fabric 初步实践
  7. 收费的QQ群 改变的不只是所谓的在线教育
  8. PHPExcel之蛋疼
  9. web资源预加载-生产环境实践
  10. 从头认识js-js客户端检测