问题

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

告警的逻辑是在一个接口中异步调用了另一个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的微信公众号,我会将我的文章推送给您,并和您一起分享我日常阅读过的优质文章。

Golang三种方式实现超时退出的更多相关文章

  1. Java设置session超时(失效)的三种方式

    1. 在web容器中设置(此处以tomcat为例) 在tomcat-6.0\conf\web.xml中设置,以下是tomcat 6.0中的默认配置: <!-- ================= ...

  2. session处理超时的三种方式

    1.      在web容器中设置(此处以tomcat为例) 在tomcat-5.0.28\conf\web.xml中设置,以下是tomcat 5.0中的默认配置: <!-- ========= ...

  3. 设置session超时的三种方式

    设置session超时的三种方式 1. 在容器中设置:如在tomcat-7\conf\web.xml中设置 Tomcat默认session超时时间为30分钟,可以根据需要修改,负数或0为不限制sess ...

  4. 【Golang 接口自动化07】struct转map的三种方式

    背景 我们在前面介绍过怎么使用net/http发送json或者map数据,那么它能不能直接发送结构体数据呢?我们今天一起来学习结构体struct转map的三种方法,为后续做铺垫. struct转map ...

  5. Spark部署三种方式介绍:YARN模式、Standalone模式、HA模式

    参考自:Spark部署三种方式介绍:YARN模式.Standalone模式.HA模式http://www.aboutyun.com/forum.php?mod=viewthread&tid=7 ...

  6. Linux就这个范儿 第15章 七种武器 linux 同步IO: sync、fsync与fdatasync Linux中的内存大页面huge page/large page David Cutler Linux读写内存数据的三种方式

    Linux就这个范儿 第15章 七种武器  linux 同步IO: sync.fsync与fdatasync   Linux中的内存大页面huge page/large page  David Cut ...

  7. 三种方式上传文件-Java

    前言:负责,因为该项目他(jetty嵌入式开始SpringMvc)实现文件上传的必要性,并拥有java文件上传这一块还没有被曝光.并 Http 更多晦涩协议.因此,这种渐进的方式来学习和实践上载文件的 ...

  8. 【Java EE 学习 52】【Spring学习第四天】【Spring与JDBC】【JdbcTemplate创建的三种方式】【Spring事务管理】【事务中使用dbutils则回滚失败!!!??】

    一.JDBC编程特点 静态代码+动态变量=JDBC编程. 静态代码:比如所有的数据库连接池 都实现了DataSource接口,都实现了Connection接口. 动态变量:用户名.密码.连接的数据库. ...

  9. 加载gif动画的三种方式

    GifView.h/** * 调用结束就开始播放动画,如果需要用户指定何时播放的话,只需要把timer的开始放到合适的位置.通过对CFDictonaryRaf 也就是gifProperties的改变, ...

随机推荐

  1. Nginx 原理和架构

    Nginx 是一个免费的,开源的,高性能的 HTTP 服务器和反向代理,以及 IMAP / POP3 代理服务器.Nginx 以其高性能,稳定性,丰富的功能,简单的配置和低资源消耗而闻名Nginx 里 ...

  2. Python中为什么可以通过bin(n & 0xffffffff)来获得负数的补码?

    一开始我以为这不是个大问题,因为本来整型数在内存中就是以补码的形式存在的,输出自然也是按照补码输出的,例如C语言中 printf("%X\n",-3); //输出 //FFFFFF ...

  3. cesium入门示例-探测效果

    动画实现方式通过多个canvas实现,参考的https://www.yueyanshaosun.cn/ysCesium/views/5_geometricObj2_entityCanvas.html ...

  4. drf中的jwt使用与手动签发效验

    jwt认证 1)session存储token,需要数据库参与,耗服务器资源.低效 2)缓存存token,需要缓存参与,高效,不易集群 3)客户端存token,服务器存签发与交易token的算法,高效, ...

  5. python基础实现简单的shell sed 替换功能

    #coding:utf-8 from pygame.draw import lines import sys,os old_file = sys.argv[1] #接受外部设备上的参数 new_fil ...

  6. ASP制作建议留言板

    <html>  <head>  <meta http-equiv="Content-Type" content="text/html;cha ...

  7. Samtec 5G探索之路

    序言:时代在发展,2020年5G作为元年.5G全程第五代移动通信技术(英语:5th generation mobile networks或5th generation wireless systems ...

  8. ajax+lazyload时lazyload失效问题及解决

    最近写公司的项目的时候遇到一个关于图片加载的问题,所做的页面是一个商城的商品列表页,其中需要显示商品图片,名称等信息,因为商品列表可能会很长,所以其中图片需要滑到可以显示的区域再进行加载. 首先我的图 ...

  9. Echarts轻松入门,内附踩坑秘籍

    首先介绍一下我们的主角ECharts ECharts,一个纯 Javascript 的图表库,可以流畅的运行在 PC 和移动设备上,兼容当前绝大部分浏览器(IE8/9/10/11,Chrome,Fir ...

  10. safari坑之 video

    博客地址: https://www.seyana.life/post/19 本来是打算给博客左上角的gif做个优化, 把gif换成webm,以video的形式自动播放,能从180k降到50k, 现在浏 ...