Go语言中的上下文超时(Context Timeout)

huangapple go评论71阅读模式
英文:

Context Timeout in GoLang

问题

使用Go API中的context.WithTimeout(context, time)实现了上下文超时。

期望的结果是,如果我将超时设置为10秒,并且如果API运行时间超过10秒,它应该停止并返回超时错误。然而,如果我在其中添加了一个sleep,API仍然会继续运行并返回200输出。除了在随机点手动检查经过的时间之外,是否有其他解决方案?看起来context.WithTimeout()没有实现其目的。

英文:

Implemented a context timeout in Go APIs using

context.WithTimeout(context,time)

Expectation is that if I set it to 10 seconds and if the API happens to run longer, it should stop and return Timeout error. However, if I add a sleep in between , the API still runs and gives 200 output. Is there any solution to this other than manually checking at random points for the time elapsed ? Looks like context.WithTimeout() doesnt solve its purpose.

答案1

得分: 1

所以,这是一个最小的代码示例,说明了如何使用context.WithTimeout函数:

server.go

package main

import (
	"fmt"
	"net/http"
	"time"
)

func main() {
	http.HandleFunc("/bad", badResponse)
	http.HandleFunc("/nice", niceResponse)
    
    http.ListenAndServe(":8099", nil)
}

// badResponse是一个执行时间过长的过程
func badResponse(w http.ResponseWriter, r *http.Request) {
	fmt.Println("收到请求,可能需要很长时间才能回复")
	time.Sleep(3 * time.Second)
	fmt.Fprintf(w, "糟糕的响应!")
}

// niceResponse是一个及时响应的过程
func niceResponse(w http.ResponseWriter, r *http.Request) {
	fmt.Println("收到请求,将快速返回")
	fmt.Fprintf(w, "好的响应!")
}

client.go

package main

import (
	"context"
	"io/ioutil"
	"log"
	"net/http"
	"time"
)

func main () {
  req, err := http.NewRequest(http.MethodGet, "http://localhost:8099/bad", nil)
  if err != nil {
    log.Fatal(err)
  }
  ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
  defer cancel()

  req = req.WithContext(ctx)
  c := &http.Client{}
  res, err := c.Do(req)
  if err != nil {
    log.Fatal(err)
  }
  defer res.Body.Close()

  out, err := ioutil.ReadAll(res.Body)
  if err != nil {
    log.Fatal(err)
  }

  log.Println(string(out))
}

在上面的示例中,客户端必须失败,因为超时时间小于服务器延迟时间。

英文:

So, this is a minimum code to exemplify how the context.WithTimeout can work:

server.go

package main

import (
	"fmt"
	"net/http"
	"time"
)

func main() {
	http.HandleFunc("/bad", badResponse)
	http.HandleFunc("/nice", niceResponse)
    
    http.ListenAndServe(":8099", nil)
}

// badResponse is a procedure that takes too much time
func badResponse(w http.ResponseWriter, r *http.Request) {
	fmt.Println("got request, it may take too long to answer")
	time.Sleep(3 * time.Second)
	fmt.Fprintf(w, "bad response!")
}

// niceResponse is a procedure that respond in time
func niceResponse(w http.ResponseWriter, r *http.Request) {
	fmt.Println("got request, will return fast")
	fmt.Fprintf(w, "nice response!")
}

client.go

package main

import (
	"context"
	"io/ioutil"
	"log"
	"net/http"
	"time"
)

func main () {
  req, err := http.NewRequest(http.MethodGet, "http://localhost:8099/bad", nil)
  if err != nil {
    log.Fatal(err)
  }
  ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
  defer cancel()

  req = req.WithContext(ctx)
  c := &http.Client{}
  res, err := c.Do(req)
  if err != nil {
    log.Fatal(err)
  }
  defer res.Body.Close()

  out, err := ioutil.ReadAll(res.Body)
  if err != nil {
    log.Fatal(err)
  }

  log.Println(string(out))
}

In the example above the client must fail because the timeout is lower than the client server delay.

huangapple
  • 本文由 发表于 2022年11月8日 19:58:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/74360347.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定