1
0

loadbalance.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. package loadbalance
  2. import (
  3. "strings"
  4. "sync"
  5. "time"
  6. "github.com/google/uuid"
  7. "github.com/gorilla/sessions"
  8. "imuslab.com/zoraxy/mod/dynamicproxy/dpcore"
  9. "imuslab.com/zoraxy/mod/geodb"
  10. "imuslab.com/zoraxy/mod/info/logger"
  11. )
  12. /*
  13. Load Balancer
  14. Handleing load balance request for upstream destinations
  15. */
  16. type Options struct {
  17. SystemUUID string //Use for the session store
  18. UseActiveHealthCheck bool //Use active health check, default to false
  19. Geodb *geodb.Store //GeoIP resolver for checking incoming request origin country
  20. Logger *logger.Logger
  21. }
  22. type RouteManager struct {
  23. SessionStore *sessions.CookieStore
  24. OnlineStatus sync.Map //Store the online status notify by uptime monitor
  25. Options Options //Options for the load balancer
  26. cacheTicker *time.Ticker //Ticker for cache cleanup
  27. cacheTickerStop chan bool //Stop the cache cleanup
  28. }
  29. /* Upstream or Origin Server */
  30. type Upstream struct {
  31. //Upstream Proxy Configs
  32. OriginIpOrDomain string //Target IP address or domain name with port
  33. RequireTLS bool //Require TLS connection
  34. SkipCertValidations bool //Set to true to accept self signed certs
  35. SkipWebSocketOriginCheck bool //Skip origin check on websocket upgrade connections
  36. //Load balancing configs
  37. Weight int //Random weight for round robin, 0 for fallback only
  38. MaxConn int //TODO: Maxmium connection to this server, 0 for unlimited
  39. //currentConnectionCounts atomic.Uint64 //Counter for number of client currently connected
  40. proxy *dpcore.ReverseProxy
  41. }
  42. // Create a new load balancer
  43. func NewLoadBalancer(options *Options) *RouteManager {
  44. if options.SystemUUID == "" {
  45. //System UUID not passed in. Use random key
  46. options.SystemUUID = uuid.New().String()
  47. }
  48. //Create a ticker for cache cleanup every 12 hours
  49. cacheTicker := time.NewTicker(12 * time.Hour)
  50. cacheTickerStop := make(chan bool)
  51. go func() {
  52. options.Logger.PrintAndLog("LoadBalancer", "Upstream state cache ticker started", nil)
  53. for {
  54. select {
  55. case <-cacheTickerStop:
  56. return
  57. case <-cacheTicker.C:
  58. //Clean up the cache
  59. options.Logger.PrintAndLog("LoadBalancer", "Cleaning up upstream state cache", nil)
  60. }
  61. }
  62. }()
  63. //Generate a session store for stickySession
  64. store := sessions.NewCookieStore([]byte(options.SystemUUID))
  65. return &RouteManager{
  66. SessionStore: store,
  67. OnlineStatus: sync.Map{},
  68. Options: *options,
  69. cacheTicker: cacheTicker,
  70. cacheTickerStop: cacheTickerStop,
  71. }
  72. }
  73. // UpstreamsReady checks if the group of upstreams contains at least one
  74. // origin server that is ready
  75. func (m *RouteManager) UpstreamsReady(upstreams []*Upstream) bool {
  76. for _, upstream := range upstreams {
  77. if upstream.IsReady() {
  78. return true
  79. }
  80. }
  81. return false
  82. }
  83. // String format and convert a list of upstream into a string representations
  84. func GetUpstreamsAsString(upstreams []*Upstream) string {
  85. targets := []string{}
  86. for _, upstream := range upstreams {
  87. targets = append(targets, upstream.String())
  88. }
  89. if len(targets) == 0 {
  90. //No upstream
  91. return "(no upstream config)"
  92. }
  93. return strings.Join(targets, ", ")
  94. }
  95. func (m *RouteManager) Close() {
  96. //Close the session store
  97. m.SessionStore.MaxAge(0)
  98. //Stop the cache cleanup
  99. if m.cacheTicker != nil {
  100. m.cacheTicker.Stop()
  101. }
  102. close(m.cacheTickerStop)
  103. }
  104. // Log Println, replace all log.Println or fmt.Println with this
  105. func (m *RouteManager) println(message string, err error) {
  106. m.Options.Logger.PrintAndLog("LoadBalancer", message, err)
  107. }