Three ways for golang to realize timeout exit

Time:2020-10-1

Some time ago, I found that there was a service interface on the line, which always gave intermittent alarms. Sometimes it was two or three times a day, and sometimes it was not in a day.

The logic of alarm is that another HTTP interface is called asynchronously in one interface, and this HTTP interface call has timed out. But I went to ask the students in charge of this HTTP interface. They said that their interfaces were all millisecond level, and they also monitored the screenshots. If there is a picture, there is a truth. What else can I say.

However, time-out does exist, but the request may not have been sent to other people’s service.

This kind of accidental problem is not easy to recur. It’s very annoying to give an alarm occasionally. The first reaction is to solve the problem first, and the idea is simple. Try again after failure.

resolvent

Let’s not talk about the retrial strategy. Let’s talk about when to trigger the retrial.

We can try again when there is an error in an interface request and an err is thrown. However, this is not easy to control. If a request goes out and fails to respond for more than ten seconds, the coroutine will have to wait for the error to be reported before it can try again. This wastes life~

Therefore, in combination with the millisecond response index given by the students above, you can set a timeout. If no result is returned after the specified timeout, try again (this article is not the key point).

func AsyncCall() {
 ctx, cancel := context.WithTimeout(context.Background(), time.Duration(time.Millisecond*800))
 defer cancel()
 go func(ctx context.Context) {
 //Send HTTP request
 }()

 select {
 case <-ctx.Done():
 fmt.Println("call successfully!!!")
 return
 case <-time.After(time.Duration(time.Millisecond * 900)):
 fmt.Println("timeout!!!")
 return
 }
}

explain

1. Set a context with an effective time of 800 ms through the withtimeout of context.

2. The context will end after 800 milliseconds of running out or after the method execution is completed. At the end of the process, the ctx.Done Send a signal.

3. Someone may ask, why do you add this when you have set the effective time of context here time.After What about it?

This is because the context in the method is self declared, and the corresponding timeout can be set manually. However, in most scenarios, the CTX here is passed from upstream all the time. We don’t know how much time is left for the context passed from upstream, so we can pass it at this time time.After It is necessary to set an expected timeout.

4. Note that you need to remember to callcancel()Otherwise, even if the execution is completed in advance, the context will not be released until 800 ms later.

summary

The above timeout control is used in combinationctx.Doneand time.After 。

The done channel is responsible for monitoring when the context is finished. If the time.After The set timeout is up, and you haven’t finished, so I won’t wait to execute the logic code after the timeout.

infer other things from one fact

So, in addition to the above timeout control strategy, are there any other routines?

Yes, but similar.

Type 1: use 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) {
 //Send HTTP request
 }()

 select {
 case <-ctx.Done():
 timer.Stop()
 timer.Reset(time.Second)
 fmt.Println("call successfully!!!")
 return
 case <-timer.C:
 fmt.Println("timeout!!!")
 return
 }
}

Here will be the main difference time.After It’s replaced bytime.NewTimerIn the same way, if the interface call is completed ahead of time, the done signal is monitored and the timer is turned off.

Otherwise, the business logic after timeout will be executed after the specified timer, that is, 900 Ms.

The second is to use channels

func AsyncCall() {
 ctx := context.Background()
 done := make(chan struct{}, 1)

 go func(ctx context.Context) {
 //Send HTTP request
 done <- struct{}{}
 }()

 select {
 case <-done:
 fmt.Println("call successfully!!!")
 return
 case <-time.After(time.Duration(800 * time.Millisecond)):
 fmt.Println("timeout!!!")
 return
 }
}

1. In this paper, the channel can be used to communicate between coprocesses. When the call is successful, the signal is sent to the done channel.

2. Done, if the signal is on time.After If it is received before the timeout, it will return normally, otherwise it will go to time.After The timeout logic is used to execute the timeout logic code.

3. Here are channels and time.After Combination, you can also use channels and time.NewTimer Combination.

summary

This article mainly introduces how to realize timeout control, there are three kinds

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

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

3、channel + time.After/time.NewTimer

This article introduces the three ways to realize the timeout exit of golang. Please search the previous articles of developeppaer or continue to browse the related articles below for more related contents of golang timeout exit. I hope you can support developeppaer more in the future!

Recommended Today

Introduction to mongodb foundation of distributed document storage database

1、 Introduction to mongodb Mongodb is a document-based NoSQL database developed with C + + language, which is easy to expand, scale, high-performance, open source, schema free. The so-called NoSQL means that it not only means SQL, but also has some features of SQL, but also is better than SQL in terms of performance and […]