diff --git a/configs/config.yaml b/configs/config.yaml index 8174f61f..ef6e6d39 100644 --- a/configs/config.yaml +++ b/configs/config.yaml @@ -11,9 +11,9 @@ coordinator: # - allows selecting worker instances debug: false # selects free workers: - # - any (default, any free) + # - empty value (default, any free) # - ping (with the lowest ping) - selector: any + selector: # games library library: # root folder for the library (where games are stored) diff --git a/pkg/config/coordinator/config.go b/pkg/config/coordinator/config.go index 892cf4cd..a136e0c7 100644 --- a/pkg/config/coordinator/config.go +++ b/pkg/config/coordinator/config.go @@ -38,10 +38,7 @@ type Analytics struct { Gtag string } -const ( - SelectAny = "any" - SelectByPing = "ping" -) +const SelectByPing = "ping" // allows custom config path var configPath string diff --git a/pkg/coordinator/balancer.go b/pkg/coordinator/balancer.go deleted file mode 100644 index 1ee8cd4e..00000000 --- a/pkg/coordinator/balancer.go +++ /dev/null @@ -1,105 +0,0 @@ -package coordinator - -import ( - "bytes" - - "github.com/rs/xid" -) - -func (h *Hub) findWorkerByRoom(id string, region string) *Worker { - if id == "" { - return nil - } - // if there is zone param, we need to ensure the worker in that zone, - // if not we consider the room is missing - w, _ := h.workers.FindBy(func(w *Worker) bool { return w.RoomId == id && w.In(region) }) - return w -} - -func (h *Hub) getAvailableWorkers(region string) []*Worker { - var workers []*Worker - h.workers.ForEach(func(w *Worker) { - if w.HasSlot() && w.In(region) { - workers = append(workers, w) - } - }) - return workers -} - -func (h *Hub) find1stFreeWorker(region string) *Worker { - workers := h.getAvailableWorkers(region) - if len(workers) > 0 { - return workers[0] - } - return nil -} - -// findFastestWorker returns the best server for a session. -// All workers addresses are sent to user and user will ping to get latency. -// !to rewrite -func (h *Hub) findFastestWorker(region string, fn func(addresses []string) (map[string]int64, error)) *Worker { - workers := h.getAvailableWorkers(region) - if len(workers) == 0 { - return nil - } - - var addresses []string - group := map[string][]struct{}{} - for _, w := range workers { - if _, ok := group[w.PingServer]; !ok { - addresses = append(addresses, w.PingServer) - } - group[w.PingServer] = append(group[w.PingServer], struct{}{}) - } - - latencies, err := fn(addresses) - if len(latencies) == 0 || err != nil { - return nil - } - - workers = h.getAvailableWorkers(region) - if len(workers) == 0 { - return nil - } - - var bestWorker *Worker - var minLatency int64 = 1<<31 - 1 - // get a worker with the lowest latency - for addr, ping := range latencies { - if ping < minLatency { - for _, w := range workers { - if w.PingServer == addr { - bestWorker = w - } - } - minLatency = ping - } - } - return bestWorker -} - -func (h *Hub) findWorkerById(workerId string, useAllWorkers bool) *Worker { - // when we select one particular worker - if workerId != "" { - if xid_, err := xid.FromString(workerId); err == nil { - if useAllWorkers { - for _, w := range h.getAvailableWorkers("") { - if xid_.String() == w.Id().String() { - return w - } - } - } else { - for _, w := range h.getAvailableWorkers("") { - xid__, err := xid.FromString(workerId) - if err != nil { - continue - } - if bytes.Equal(xid_.Machine(), xid__.Machine()) { - return w - } - } - } - } - } - return nil -} diff --git a/pkg/coordinator/hub.go b/pkg/coordinator/hub.go index 369a1ab0..232a1224 100644 --- a/pkg/coordinator/hub.go +++ b/pkg/coordinator/hub.go @@ -1,7 +1,9 @@ package coordinator import ( + "bytes" "net/http" + "net/url" "github.com/giongto35/cloud-game/v2/pkg/api" "github.com/giongto35/cloud-game/v2/pkg/com" @@ -10,6 +12,7 @@ import ( "github.com/giongto35/cloud-game/v2/pkg/logger" "github.com/giongto35/cloud-game/v2/pkg/network" "github.com/giongto35/cloud-game/v2/pkg/service" + "github.com/rs/xid" ) type Hub struct { @@ -58,32 +61,9 @@ func (h *Hub) handleUserConnection(w http.ResponseWriter, r *http.Request) { }() usr.HandleRequests(h, h.launcher, h.conf) - q := r.URL.Query() - roomId := q.Get(api.RoomIdQueryParam) - zone := q.Get(api.ZoneQueryParam) - wid := q.Get(api.WorkerIdParam) - - usr.Log.Info().Msg("Search available workers") - var wkr *Worker - if wkr = h.findWorkerByRoom(roomId, zone); wkr != nil { - usr.Log.Info().Str("room", roomId).Msg("An existing worker has been found") - } else if wkr = h.findWorkerById(wid, h.conf.Coordinator.Debug); wkr != nil { - usr.Log.Info().Msgf("Worker with id: %v has been found", wid) - } else if h.conf.Coordinator.Selector == "" || h.conf.Coordinator.Selector == coordinator.SelectAny { - usr.Log.Debug().Msgf("Searching any free worker...") - if wkr = h.find1stFreeWorker(zone); wkr != nil { - usr.Log.Info().Msgf("Found next free worker") - } - } else if h.conf.Coordinator.Selector == coordinator.SelectByPing { - usr.Log.Debug().Msgf("Searching fastest free worker...") - if wkr = h.findFastestWorker(zone, - func(servers []string) (map[string]int64, error) { return usr.CheckLatency(servers) }); wkr != nil { - usr.Log.Info().Msg("The fastest worker has been found") - } - } - + wkr := h.findWorkerFor(usr, r.URL.Query()) if wkr == nil { - usr.Log.Warn().Msg("no free workers") + usr.Log.Info().Msg("no free workers") return } @@ -139,7 +119,7 @@ func (h *Hub) handleWorkerConnection(w http.ResponseWriter, r *http.Request) { } }() - h.log.Info().Msgf("New worker / addr: %v, port: %v, zone: %v, ping addr: %v, tag: %v", + h.log.Info().Msgf("New worker, addr: %v, port: %v, zone: %v, ping addr: %v, tag: %v", worker.Addr, worker.Port, worker.Zone, worker.PingServer, worker.Tag) worker.HandleRequests(&h.users) h.workers.Add(worker) @@ -160,3 +140,132 @@ func (h *Hub) GetServerList() (r []api.Server) { } return } + +// findWorkerFor searches a free worker for the user depending on +// various conditions. +func (h *Hub) findWorkerFor(usr *User, q url.Values) *Worker { + usr.Log.Debug().Msg("Search available workers") + roomId := q.Get(api.RoomIdQueryParam) + zone := q.Get(api.ZoneQueryParam) + wid := q.Get(api.WorkerIdParam) + + var worker *Worker + if worker = h.findWorkerByRoom(roomId, zone); worker != nil { + usr.Log.Debug().Str("room", roomId).Msg("An existing worker has been found") + } else if worker = h.findWorkerById(wid, h.conf.Coordinator.Debug); worker != nil { + usr.Log.Debug().Msgf("Worker with id: %v has been found", wid) + } else { + switch h.conf.Coordinator.Selector { + case coordinator.SelectByPing: + usr.Log.Debug().Msgf("Searching fastest free worker...") + if worker = h.findFastestWorker(zone, + func(servers []string) (map[string]int64, error) { return usr.CheckLatency(servers) }); worker != nil { + usr.Log.Debug().Msg("The fastest worker has been found") + } + default: + usr.Log.Debug().Msgf("Searching any free worker...") + if worker = h.find1stFreeWorker(zone); worker != nil { + usr.Log.Debug().Msgf("Found next free worker") + } + } + } + return worker +} + +func (h *Hub) findWorkerByRoom(id string, region string) *Worker { + if id == "" { + return nil + } + // if there is zone param, we need to ensure the worker in that zone, + // if not we consider the room is missing + w, _ := h.workers.FindBy(func(w *Worker) bool { return w.RoomId == id && w.In(region) }) + return w +} + +func (h *Hub) getAvailableWorkers(region string) []*Worker { + var workers []*Worker + h.workers.ForEach(func(w *Worker) { + if w.HasSlot() && w.In(region) { + workers = append(workers, w) + } + }) + return workers +} + +func (h *Hub) find1stFreeWorker(region string) *Worker { + workers := h.getAvailableWorkers(region) + if len(workers) > 0 { + return workers[0] + } + return nil +} + +// findFastestWorker returns the best server for a session. +// All workers addresses are sent to user and user will ping to get latency. +// !to rewrite +func (h *Hub) findFastestWorker(region string, fn func(addresses []string) (map[string]int64, error)) *Worker { + workers := h.getAvailableWorkers(region) + if len(workers) == 0 { + return nil + } + + var addresses []string + group := map[string][]struct{}{} + for _, w := range workers { + if _, ok := group[w.PingServer]; !ok { + addresses = append(addresses, w.PingServer) + } + group[w.PingServer] = append(group[w.PingServer], struct{}{}) + } + + latencies, err := fn(addresses) + if len(latencies) == 0 || err != nil { + return nil + } + + workers = h.getAvailableWorkers(region) + if len(workers) == 0 { + return nil + } + + var bestWorker *Worker + var minLatency int64 = 1<<31 - 1 + // get a worker with the lowest latency + for addr, ping := range latencies { + if ping < minLatency { + for _, w := range workers { + if w.PingServer == addr { + bestWorker = w + } + } + minLatency = ping + } + } + return bestWorker +} + +func (h *Hub) findWorkerById(workerId string, useAllWorkers bool) *Worker { + // when we select one particular worker + if workerId != "" { + if xid_, err := xid.FromString(workerId); err == nil { + if useAllWorkers { + for _, w := range h.getAvailableWorkers("") { + if xid_.String() == w.Id().String() { + return w + } + } + } else { + for _, w := range h.getAvailableWorkers("") { + xid__, err := xid.FromString(workerId) + if err != nil { + continue + } + if bytes.Equal(xid_.Machine(), xid__.Machine()) { + return w + } + } + } + } + } + return nil +}