Golang implements three ways to timeout exit

  • 2020-09-28 08:56:26
  • OfStack

Some time ago, I found that there was a service interface on the line. It was always an intermittent alarm, sometimes two or three times a day, sometimes no alarm at all.

The logic of the alarm is that in one interface another HTTP interface is asynchronously called and the HTTP interface call has timed out. But I asked the students in charge of the HTTP interface, they said their interfaces were all millisecond level, and the screenshots monitored picture is truth, what else could I say.

However, the timeout does exist, but the request may not reach the service side.

This kind of accidental problem is not easy to repeat, occasionally come to an alarm is also very annoying, the first reaction is to solve the problem, the idea is simple, after failure to try again.

The solution

Leaving aside the retry strategy, let's talk about when retries are triggered.

We can retry err when an interface request goes awry, but this is hard to control. If a request goes out without a response for 10 seconds or so, the coroutine will waste its life waiting for it to report an error before retrying

Therefore, in combination with the millisecond response indicator given by the students above, a timeout period can be set. If no result is returned after the specified timeout period, try again (this retry 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
 }
}

instructions

1. Set an context with an effective time of 800 ms through WithTimeout of context.

2. The context will end after 800 milliseconds of exhaustion or after method execution has completed, sending a signal to channel ES30en.Done at the end.

3. Some people may ask, you have set the valid time of context here, why add this time.After?

context within this is because the method of his statement, you can manually set the timeout of corresponding, but in most scenarios, the ctx passed from upstream 1, how much is left for the upstream passed context time, we don't know, so this time by time. After set 1 the timeout time it is very necessary.

4. Remember to call cancel() Otherwise, even if the execution is completed ahead of time, context will be released 800 milliseconds later.

conclusion

The above timeout control is used in conjunction with ctx.Done And time After.

The Done channel is responsible for monitoring when context is finished. If the timeout set at ES59en.After has expired and you are not finished, Then I will not wait and execute the logic code after the timeout.

Take 1 3

So, other than the above time-out control strategy, are there any other routines?

B: Yes, but it's all the same.

Type 1: Use ES69en.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
 }
}

The main difference here is to replace time.After time.NewTimer If the interface call completes prematurely, listen for the Done signal and then turn off the timer.

Otherwise, the post-timeout business logic executes after the specified timer, 900 milliseconds.

Type 2: 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. Here, the channel can communicate between coroutines, and send signals to done channel after successful invocation.

2. Monitor Done signal. If it is received before the timeout period of ES92en. After, it will return normally; otherwise, go to the timeout logic of ES94en. After and execute the timeout logic code.

3. A combination of channel and ES98en. After is used here, or a combination of channel and ES100en. NewTimer can be used.

conclusion

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

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

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

3, channel + time. After/time. NewTimer


Related articles: