timeoutinterceptor.go 1.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
  1. package serverinterceptors
  2. import (
  3. "context"
  4. "fmt"
  5. "runtime/debug"
  6. "strings"
  7. "sync"
  8. "time"
  9. "google.golang.org/grpc"
  10. )
  11. // UnaryTimeoutInterceptor returns a func that sets timeout to incoming unary requests.
  12. func UnaryTimeoutInterceptor(timeout time.Duration) grpc.UnaryServerInterceptor {
  13. return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo,
  14. handler grpc.UnaryHandler) (interface{}, error) {
  15. ctx, cancel := context.WithTimeout(ctx, timeout)
  16. defer cancel()
  17. var resp interface{}
  18. var err error
  19. var lock sync.Mutex
  20. done := make(chan struct{})
  21. // create channel with buffer size 1 to avoid goroutine leak
  22. panicChan := make(chan interface{}, 1)
  23. go func() {
  24. defer func() {
  25. if p := recover(); p != nil {
  26. // attach call stack to avoid missing in different goroutine
  27. panicChan <- fmt.Sprintf("%+v\n\n%s", p, strings.TrimSpace(string(debug.Stack())))
  28. }
  29. }()
  30. lock.Lock()
  31. defer lock.Unlock()
  32. resp, err = handler(ctx, req)
  33. close(done)
  34. }()
  35. select {
  36. case p := <-panicChan:
  37. panic(p)
  38. case <-done:
  39. lock.Lock()
  40. defer lock.Unlock()
  41. return resp, err
  42. case <-ctx.Done():
  43. return nil, ctx.Err()
  44. }
  45. }
  46. }