问题

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

告警的逻辑是在一个接口中异步调用了另一个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. unittest(22)- p2p项目实战(2)-http_request

    # 2. http_request.py import requests from learn_logging_5.write_log_class import MyLog my_logger = M ...

  2. Java中的字符串比较

    在C++中,两个字符串比较的代码可以为: (string1==string2) 但是在java中,这个代码即使在两个字符串完全相同的情况下也会返回 false ,Java中必须使用 string1.e ...

  3. DOS命令编译JAVA程序

    上篇文章给大家写了怎么安装JDK配置,现在这篇文章我们就来学习下怎么在DOS命令下编译JAVA程序,以后没编译器都可以直接编译啦(嘻嘻!) 我这里就用永远的 “Hello wrold!”来演示下吧. ...

  4. 吴裕雄--天生自然KITTEN编程:画三角形

  5. MongoDB启动.mongorc.js报错解决方法

    在bin目录下输入./mongo --norc 不去加载.mongorc.js

  6. ubuntu16.04安装mysql5.6

    apt-get install software-properties-commonsudo add-apt-repository 'deb http://archive.ubuntu.com/ubu ...

  7. C++走向远洋——62(项目二1、类模板)

    */ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:text.cpp * 作者:常轩 * 微信公众号:Worldhe ...

  8. 啥是python?

    Python是一种计算机程序设计语言.是一种面向对象的动态类型语言,最初被设计用于编写自动化脚本(shell),随着版本的不断更新和语言新功能的添加,越来越多被用于独立的.大型项目的开发,可以应用于以 ...

  9. 使用 Hexo 创建项目文档网站

    当我们发布一个开源项目的时候,最重要的事情之一就是要创建项目文档.对使用项目的用户来说,文档是非常有必要的,通常我们可以使用下面这些方式来创建文档: GitHub Wiki:在 Github 上我们可 ...

  10. HTTP协议详解(深入理解)

    版权声明:本文为CSDN博主「有抱负的小狮子」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明.原文链接:https://blog.csdn.net/weixin_ ...