director.go 1.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. package proxy
  2. import (
  3. "errors"
  4. "log"
  5. "net/url"
  6. "sync"
  7. "time"
  8. )
  9. const (
  10. // amount of time an endpoint will be held in a failed
  11. // state before being reconsidered for proxied requests
  12. endpointFailureWait = 5 * time.Second
  13. )
  14. func newDirector(scheme string, addrs []string) (*director, error) {
  15. if len(addrs) == 0 {
  16. return nil, errors.New("one or more upstream addresses required")
  17. }
  18. endpoints := make([]*endpoint, len(addrs))
  19. for i, addr := range addrs {
  20. u := url.URL{Scheme: scheme, Host: addr}
  21. endpoints[i] = newEndpoint(u)
  22. }
  23. d := director{ep: endpoints}
  24. return &d, nil
  25. }
  26. type director struct {
  27. ep []*endpoint
  28. }
  29. func (d *director) endpoints() []*endpoint {
  30. filtered := make([]*endpoint, 0)
  31. for _, ep := range d.ep {
  32. if ep.Available {
  33. filtered = append(filtered, ep)
  34. }
  35. }
  36. return filtered
  37. }
  38. func newEndpoint(u url.URL) *endpoint {
  39. ep := endpoint{
  40. URL: u,
  41. Available: true,
  42. failFunc: timedUnavailabilityFunc(endpointFailureWait),
  43. }
  44. return &ep
  45. }
  46. type endpoint struct {
  47. sync.Mutex
  48. URL url.URL
  49. Available bool
  50. failFunc func(ep *endpoint)
  51. }
  52. func (ep *endpoint) Failed() {
  53. ep.Lock()
  54. if !ep.Available {
  55. ep.Unlock()
  56. return
  57. }
  58. ep.Available = false
  59. ep.Unlock()
  60. log.Printf("proxy: marked endpoint %s unavailable", ep.URL.String())
  61. if ep.failFunc == nil {
  62. log.Printf("proxy: no failFunc defined, endpoint %s will be unavailable forever.", ep.URL.String())
  63. return
  64. }
  65. ep.failFunc(ep)
  66. }
  67. func timedUnavailabilityFunc(wait time.Duration) func(*endpoint) {
  68. return func(ep *endpoint) {
  69. time.AfterFunc(wait, func() {
  70. ep.Available = true
  71. log.Printf("proxy: marked endpoint %s available", ep.URL.String())
  72. })
  73. }
  74. }