When a man speaks of himself, he will get carried away. — Carmen By Melimei

For a better reading experience, please go to wechat official account: Golang timeout is not controlled, then you also match gopher?

1. Introduction

Do you know why timeout needs to be controlled? Recall in your head how the code timeout control was implemented. So today to bring you a timeout control practice, to help you in the project with ease, nonsense not to say, directly on the dry goods.

2. Why should timeout be controlled

  • If the request time is too long, the user may have left the page and the server is consuming resources to process the request. The result is meaningless.
  • A long time of server processing consumes too many resources, resulting in reduced concurrency and even unavailability.

In Golang, a request is usually completed by multiple serial or parallel subtasks, each of which may be another internal request. When this request times out, we need to quickly return to free up resources, such as goroutine, file descriptors, etc.

3. Populist logic

Sleep(time.minute) return nil} func requestDo(CTX context.context, job interface{}) error { return Do(job) }Copy the code

This implementation may be intolerable to the user, because once the call to Do times out, the user side will wait for a long time with no response, and the user will leave the page with a sad complaint.

4. Elite Logic

func requestDo(ctx context.Context, job interface{}) error { ctx, cancel := context.WithTimeout(ctx, Time.second *2) defer cancel() done := make(chan error) go func() {// Do < -do (job)}() select {case Err := <-done: // Return err case < -ctx.done (): // Timeout control return ctx.err ()}}Copy the code

Do you see the problem, dear friends, bring your eyes, identify the bugs in the code, come here.

Ok, let me tell you what’s wrong with it:

If the timeout is controlled to return case < -ctx.done (), then the requestDo contract is terminated, but our Do contract is still executing. When it is ready to return and the Done contract is not accepted by goroutine, then the Do contract is stuck. It’s causing a compromise

5. Superman logic

One solution to the above bug is to initialize done with a size size space.

done := make(chan error, 1)
Copy the code

In this case, when the Do protocol timeout returns, the result is automatically written to done, and the Do protocol exits automatically so that the protocol does not leak.

After all, no one accepts done. Therefore, it is clearly told here that there is no impact. Chan is an object after all, and Golang’s channel resources can be automatically GC out.

6. What if there is panic in Do

If a panic occurs in a Do, you will find that panic cannot be caught because other goroutines created by panic in the requestDo cannot be caught.

Buffer size = 1; buffer size = 1; buffer size = 1

panicChan := make(chan interface{}, 1)
Copy the code

Please see:

func requestDo(ctx context.Context, job interface{}) error { ctx, cancel := context.WithTimeout(ctx, time.Second*2) defer cancel() done := make(chan error) panicChan := make(chan interface{}, 1) go func() { defer func() { if p := recover(); p ! = nil { panicChan <- p } }() done <- Do(job) }() select { case err := <-done: return err case p := <-panicChan: Panic case < -ctx.done (): return ctx.err ()}}Copy the code

Panic can now be handled in the requestDo caller.

7. Summary

You see, through the above knowledge, I believe you can write a more sound timeout control logic, then what are you waiting for, as soon as possible practice.

8. Pay attention to official accounts

Wechat official account: Stack Future

Hope you pay attention to ha, original is not easy, ask for praise, ask for attention, ask for share