tadoquery设置超时时间_go timeout、context基本原理,如何防止超时耗尽资源

        在上一篇文章中,我们探讨过设置超时时间过小,没有错误,服务端返回“empty reply”的错误(参见go http包超时时间设置过小会发生什么),本次我们将会探讨go http包的超时问题,如何避免因超时导致资源耗尽而引发崩溃,最后我们还会讨论下go 并发设计之context。         先来看一段代码,在代码中我们设置WriteTimeout为1s,但请求处理需要3s,
package mainimport (  "fmt"  "io"  "net/http"  "time")func slowHandler(w http.ResponseWriter, req *http.Request) {  io.WriteString(w, "begin slowHandler\n")  sleepSeconds := 3  //模拟慢处理请求 耗时 3s  for i := 0; i < sleepSeconds; i++ {    fmt.Println("i:", i, ", now:", time.Now())    time.Sleep(1 * time.Second)  }  io.WriteString(w, "end slowHandler\n")  fmt.Println("finish slow execution!")}func main() {  srv := http.Server{    Addr:         ":8888",    WriteTimeout: 1 * time.Second,//设置超时为1s    Handler:      http.HandlerFunc(slowHandler),  }  if err := srv.ListenAndServe(); err != nil {    fmt.Printf("Server failed: %s\n", err)  }}
运行代码后IDE输出 e226cc8c75f88bbb718f1837eb868071.png curl请求返回“Empty reply" 6aad589277df8548cf0ad954feea1953.png从上面的例子中我们可以发现设置1s超时, 1s后我们写不了响应 了,但是handler请求还在处理,并没有停止,而且即使写不了响应后,我们还需要等待3s(慢请求执行3s)才能拿到响应结果,那我们需要如何优化,才能立即拿到超时结果,且能终止慢请求的处理呢?一、基本原理介绍1、基本概念        在net/http包中,通过创建一个http.Server结构体,该结构体包括4个超时参数,如下图所示,这4个参数很好理解,这里就不再解释。 2007c855f52a51406fb992cd07bdac00.png我们之前讲过处理请求时,每次都会调用readRequest,从下图我们可以看出我们设置的超时会被用做设置c.rwc的dealline参数,而c.rwc是什么呢? 它是*net.TCPConn类型,这体现了我们的http请求实际上是基于tcp的,意味着我们设置的超时参数表现为tcp连接的deadline,而不是http超时。 ce62edca4d96d1847e1251913ace4871.png简单理解deadline机制就是 设置一个绝对值,超过该值后,特定的行为都会被限制,比如一开始的例子中,因无法写入导致无法获取超时后的响应。那我们如何处理超时呢?2、处理超时        我们还是先贴代码
package mainimport (  "fmt"  "io"  "net/http"  "time")func slowHandler(w http.ResponseWriter, req *http.Request) {  io.WriteString(w, "begin slowHandler\n")  sleepSeconds := 10  //模拟慢处理请求 耗时 3s  for i := 0; i < sleepSeconds; i++ {    fmt.Println("i:", i, ", now:", time.Now())    time.Sleep(1 * time.Second)  }  io.WriteString(w, "end slowHandler\n")  fmt.Println("finish slow execution!")}func main() {  srv := http.Server{    Addr: ":8888",    //该值需要大于超时时间,不然无法写入响应    WriteTimeout: 5 * time.Second,    // http.HandlerFunc(slowHandler),使用TimeoutHandler进行封装    //方法在go中是一等公民,设置1s超时    Handler:      http.TimeoutHandler(http.HandlerFunc(slowHandler), 1*time.Second, "request Timeout!\n"),  }  if err := srv.ListenAndServe(); err != nil {    fmt.Printf("Server failed: %s\n", err)  }}
代码运行后发现超时输出了"request Timeout" 4ae9d7d4ba0e6a215d47105c987afb83.png但是请求处理并未结束,虽然超时了,但是仍然占着资源。那我们怎么才能在超时后,请求结束,释放资源呢?这就依赖于我们的上下文context了。 7e891c2e8e9d07e40ce8559968929a16.png二、go context设计原理        在介绍之前我们先来了解下go上下文的设计原理,本小节篇幅较长,大家可选择跳过。1、基本介绍         go中的上下文可用来同步信号,设置截止日期,传递请求相关值的结构体,context是go语言中独有的设计, context.Context接口中定义了如下4个方法。 2734e41c469a030c67a0a58b7990b4ea.png
  • Deadline,返回context.Context被取消的截止时间,即工作截止时间
  • Done,返回一个struct的chan,这个chan会在工作完成,或者上下文取消后关闭;多次调用返回的值相同
  • Err,返回context.Context结束的原因,只有在Done返回的chan被关闭时才会返回非空值
  • Value, 从context.Context中获取对应键的值,多次调用返回的结果相同
        在go中,处理一次请求可能会创建多个 goroutine,一般 HTTP/RPC 请求的处理器会启动新的 groutine 访问数据库和其他服务。context.Context 都会从最顶层的 goroutine 一层一层传递到最下层。context.Context 可以在上层 goroutine 执行出现错误时,将信号及时同步给下层,如下图所示 74e975dbdf01cad9c0694556c7c4db45.png 2、默认上下文         context包中常用的上下文还包括backgroud和todo,分别通过调用context.Backgroud和context.TODO来获取,从截图中可以看出它们是在context包中定义好的私有变量。并且可以看到,它们都是new(emptyCtx)进行初始化的,是指向私有结构体emptyCtx的指针。 b0d00e0f14dc8da1115328fc7905525b.png 从下面的截图中可以看到emptyCtx是一个int类型,通过返回nil来实现的context.Context接口,并没有什么特殊的功能,从代码上来看context.TODO和context.Background似乎没有太大的差别。 50fce45e26ac151e0a3a5df3fd3ef012.png在多数情况下,如果当前函数没有上下文作为入参,我们都会使用 context.Background 作为起始的上下文向下传递,这点可以参见goredis。 3、关于信号取消         从下图中我们可以看到timeoutHandler在处理请求时会调用context.WithTimeout, 37d63773991c4f3b29b7cbdf513599ca.png而在context.WithTimeout中则会直接调用WithDeadline,通过调用 2b4cdf7c5195f92a824627174fb2bd75.pngpropagetCancel来构建上下文之间的关联 3cb6dbf0a8b5fd68bfc7a8f886270e0c.png该函数包含3种场景
  1. 当 parent.Done() == nil,也就是 parent 不会触发取消信号时,方法会直接返回
  2. 当取消信号非context.cancelCtx、context.timerCtx、context.valueCtx时,判断 parent 是否已经触发了取消信号,如果取消了会调用child.cancel来取消child,如果没取消,会将child加入parent的等待列表
  3. 当取消信号来自其它上下文(开发者自定义的类型),会新开一个goroutine,同时监听parent.Done,child.Done,在parent取消时,也会取消child
        三、go context取消上下文        依赖context的Done方法,接收到“超时”信号后,退出for循环,终止进程的执行,示例代码如下
package mainimport (  "context"  "fmt"  "io"  "log"  "math/rand"  "net/http"  "time")func slowCall(ctx context.Context) string {  s := rand.Intn(5)  for i := 0; i     select {    case       log.Printf("Slow call done after %d seconds, reason %s\n", s, ctx.Err().Error())      return "done"    case       log.Printf("Slow call finished after %d seconds.\n", s)    default:      log.Println("select default case")    }    fmt.Println("i:", i, ", now:", time.Now())    time.Sleep(1 * time.Second)  }  return "end"}func slowHandler(w http.ResponseWriter, r *http.Request) {  res := slowCall(r.Context())  io.WriteString(w, "response:"+res+"\n")}func main() {  srv := http.Server{    Addr: ":8888",    //该值需要大于慢请求处理时间,不然无法写入响应    WriteTimeout: 5 * time.Second,    Handler:      http.TimeoutHandler(http.HandlerFunc(slowHandler), 1*time.Second, "request Timeout!\n"),  }  if err := srv.ListenAndServe(); err != nil {    fmt.Printf("Server failed: %s\n", err)  }}
for中的循环仅执行了一次就退出了 99dfbb64aa2f71ae9e3575b2a3503dd6.png四、关于写HTTP服务的建议1、要使用截止时间,理解各项超时参数的意义,确保做了完整测试,达到符合预期的控制2、对于超时取消别忘记使用上下文
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【优质项目推荐】 1、项目代码均经过严格本地测试,运行OK,确保功能稳定后才上传平台。可放心下载并立即投入使用,若遇到任何使用问题,随时欢迎私信反馈与沟通,博主会第一时间回复。 2、项目适用于计算机相关专业(如计科、信息安全、数据科学、人工智能、通信、物联网、自动化、电子信息等)的在校学生、专业教师,或企业员工,小白入门等都适用。 3、该项目不仅具有很高的学习借鉴价值,对于初学者来说,也是入门进阶的绝佳选择;当然也可以直接用于 毕设、课设、期末大作业或项目初期立项演示等。 3、开放创新:如果您有一定基础,且热爱探索钻研,可以在此代码基础上二次开发,进行修改、扩展,创造出属于自己的独特应用。 欢迎下载使用优质资源!欢迎借鉴使用,并欢迎学习交流,共同探索编程的无穷魅力! 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值