285 lines
7.0 KiB
Go
285 lines
7.0 KiB
Go
package util
|
|
|
|
import (
|
|
"fmt"
|
|
"math/rand"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
type ServerConfig struct {
|
|
ServerId int `db:"ServerId"`
|
|
Host string `db:"Host"`
|
|
Port int `db:"Port"`
|
|
MaxOnline int `db:"MaxOnline"`
|
|
Online int `db:"Online"`
|
|
Version string `db:"version"`
|
|
Status int `db:"Status"`
|
|
Weight int `db:"Weight"`
|
|
WorkDir string `db:"work_dir"`
|
|
Ecs int `db:"ecs"`
|
|
Name string `db:"ServerName"`
|
|
AppId int `db:"AppId"`
|
|
NodeType int `db:"node_type"`
|
|
}
|
|
|
|
var serverList map[int][]ServerConfig
|
|
|
|
func LoginResponse(c *gin.Context, AppId, AreaCode int, Version string) {
|
|
Uid := c.Query("uid")
|
|
Port, Host, ServerId := GetUserInfo(AppId, AreaCode, Uid, Version)
|
|
LoginSuccess(c, "login success", Port, Host, ServerId)
|
|
}
|
|
|
|
func GetUserInfo(AppId, AreaCode int, Uid, Version string) (int, string, int) {
|
|
// now := time.Now().UnixMilli()
|
|
lockUid(Uid)
|
|
defer unlockUid(Uid)
|
|
// Db := MPool.GetGameDB()
|
|
// defer Db.Close()
|
|
var ServerId int
|
|
var Host string
|
|
var Port int
|
|
ServerId = 0
|
|
ServerList := GetServerInfo(AppId, AreaCode)
|
|
if len(ServerList) == 0 || Uid == "" || Version == "" {
|
|
return 0, "", 0
|
|
}
|
|
node, err := GetUserConnectNode(Uid)
|
|
if err != nil {
|
|
node = 0
|
|
}
|
|
if node > 0 {
|
|
for _, server := range ServerList {
|
|
if server.ServerId == node && server.Status == 1 && Version == server.Version {
|
|
ServerId = server.ServerId
|
|
Host = server.Host
|
|
Port = server.Port
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if ServerId == 0 {
|
|
new_server_list := make([]ServerConfig, 0)
|
|
// 先尝试找版本号完全匹配且未满的服务器
|
|
for _, server := range ServerList {
|
|
if server.Status != 1 {
|
|
continue
|
|
}
|
|
if Version != "" && server.Version == Version {
|
|
new_server_list = append(new_server_list, server)
|
|
}
|
|
}
|
|
// 过滤出未满员且状态正常的候选服务器
|
|
// 加权随机选择
|
|
if len(new_server_list) > 0 {
|
|
totalWeight := 0
|
|
for _, s := range new_server_list {
|
|
w := s.Weight
|
|
if w <= 0 {
|
|
w = 1
|
|
}
|
|
totalWeight += w
|
|
}
|
|
if totalWeight == 0 {
|
|
// 兜底,选择第一个
|
|
ServerId = new_server_list[0].ServerId
|
|
Host = new_server_list[0].Host
|
|
Port = new_server_list[0].Port
|
|
} else {
|
|
r := rand.Intn(totalWeight)
|
|
acc := 0
|
|
for _, s := range new_server_list {
|
|
w := s.Weight
|
|
if w <= 0 {
|
|
w = 1
|
|
}
|
|
acc += w
|
|
if r < acc {
|
|
ServerId = s.ServerId
|
|
Host = s.Host
|
|
Port = s.Port
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ServerId == 0 {
|
|
ServerId = ServerList[0].ServerId
|
|
Host = ServerList[0].Host
|
|
Port = ServerList[0].Port
|
|
}
|
|
// 更新玩家所在节点
|
|
if err == nil {
|
|
SaveUserConnectNode(Uid, ServerId)
|
|
}
|
|
//log.Printf("GetUserInfo selected server %d for uid %s, cost time %dms\n", ServerId, Uid, time.Now().UnixMilli()-now)
|
|
return Port, Host, ServerId
|
|
}
|
|
|
|
// per-UID lock to serialize concurrent requests for the same uid
|
|
type uidLockRef struct {
|
|
mu sync.Mutex
|
|
refs int
|
|
}
|
|
|
|
var (
|
|
uidLocks = make(map[string]*uidLockRef)
|
|
uidLocksMu sync.Mutex
|
|
)
|
|
|
|
func lockUid(uid string) {
|
|
if uid == "" {
|
|
return
|
|
}
|
|
uidLocksMu.Lock()
|
|
ref, ok := uidLocks[uid]
|
|
if !ok {
|
|
ref = &uidLockRef{refs: 1}
|
|
uidLocks[uid] = ref
|
|
uidLocksMu.Unlock()
|
|
ref.mu.Lock()
|
|
return
|
|
}
|
|
ref.refs++
|
|
uidLocksMu.Unlock()
|
|
ref.mu.Lock()
|
|
}
|
|
|
|
func unlockUid(uid string) {
|
|
if uid == "" {
|
|
return
|
|
}
|
|
uidLocksMu.Lock()
|
|
ref, ok := uidLocks[uid]
|
|
if !ok {
|
|
uidLocksMu.Unlock()
|
|
return
|
|
}
|
|
ref.refs--
|
|
if ref.refs <= 0 {
|
|
delete(uidLocks, uid)
|
|
uidLocksMu.Unlock()
|
|
ref.mu.Unlock()
|
|
return
|
|
}
|
|
uidLocksMu.Unlock()
|
|
ref.mu.Unlock()
|
|
}
|
|
|
|
// func GetServerInfo(AppId, AreaCode int) []ServerConfig {
|
|
// Db := MPool.GetGameDB()
|
|
// defer Db.Close()
|
|
// rows, err := Db.Query("SELECT ServerId, Status, Host, Port, MaxOnline, Online, version, weight FROM server WHERE AppId = ? AND Status = 1 AND node_type = 1 ORDER BY ServerId", AppId)
|
|
// if err != nil {
|
|
// return nil
|
|
// }
|
|
// defer rows.Close()
|
|
// var servers []ServerConfig
|
|
// for rows.Next() {
|
|
// var server ServerConfig
|
|
// if err := rows.Scan(&server.ServerId, &server.Status, &server.Host, &server.Port, &server.MaxOnline, &server.Online, &server.Version, &server.Weight); err != nil {
|
|
// continue
|
|
// }
|
|
// servers = append(servers, server)
|
|
// }
|
|
// return servers
|
|
// }
|
|
|
|
func GetServer(AppId, ServerId int) ServerConfig {
|
|
Db := MPool.GetGameDB()
|
|
defer Db.Close()
|
|
var server ServerConfig
|
|
err := Db.Get(&server, "SELECT ServerId, ServerName, Status, Host, Port, MaxOnline, Online, version, ecs, work_dir, node_type FROM server WHERE AppId = ? AND ServerId = ?", AppId, ServerId)
|
|
if err != nil {
|
|
return ServerConfig{}
|
|
}
|
|
return server
|
|
}
|
|
|
|
// VersionDistance 计算两个版本号之间的跨度
|
|
// 版本号格式为 major.minor.patch (例如: 1.0.0)
|
|
// 返回值越大表示版本差异越大
|
|
func VersionDistance(v1, v2 string) int {
|
|
parse := func(version string) (int, int, int) {
|
|
var major, minor, patch int
|
|
fmt.Sscanf(version, "%d.%d.%d", &major, &minor, &patch)
|
|
return major, minor, patch
|
|
}
|
|
|
|
major1, minor1, patch1 := parse(v1)
|
|
major2, minor2, patch2 := parse(v2)
|
|
|
|
majorDiff := abs(major1 - major2)
|
|
minorDiff := abs(minor1 - minor2)
|
|
patchDiff := abs(patch1 - patch2)
|
|
|
|
// 主版本号差异权重最高,次版本号次之,补丁版本号最低
|
|
return majorDiff*10000 + minorDiff*100 + patchDiff
|
|
}
|
|
|
|
func abs(x int) int {
|
|
if x < 0 {
|
|
return -x
|
|
}
|
|
return x
|
|
}
|
|
|
|
func GetServerInfo(AppId, AreaCode int) []ServerConfig {
|
|
if serverList != nil {
|
|
return serverList[AppId]
|
|
}
|
|
return []ServerConfig{}
|
|
}
|
|
|
|
func MonitorServerList() {
|
|
_initServerList()
|
|
ticker := time.NewTicker(1 * time.Minute)
|
|
defer ticker.Stop()
|
|
for range ticker.C {
|
|
_initServerList()
|
|
}
|
|
}
|
|
|
|
func _initServerList() {
|
|
Db := MPool.GetGameDB()
|
|
defer Db.Close()
|
|
rows, err := Db.Query("SELECT AppId, ServerId, Status, Host, Port, MaxOnline, Online, version, weight FROM server WHERE Status = 1 AND node_type = 1 ORDER BY ServerId")
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer rows.Close()
|
|
tempServerList := make(map[int][]ServerConfig)
|
|
for rows.Next() {
|
|
var server ServerConfig
|
|
if err := rows.Scan(&server.AppId, &server.ServerId, &server.Status, &server.Host, &server.Port, &server.MaxOnline, &server.Online, &server.Version, &server.Weight); err != nil {
|
|
continue
|
|
}
|
|
tempServerList[server.AppId] = append(tempServerList[server.AppId], server)
|
|
}
|
|
serverList = tempServerList
|
|
}
|
|
|
|
func GetAllServersByAppId(appid int) []ServerConfig {
|
|
Db := MPool.GetGameDB()
|
|
defer Db.Close()
|
|
rows, err := Db.Query("SELECT ServerId, ServerName, Status, Host, Port, MaxOnline, Online, version, ecs, work_dir, node_type FROM server WHERE AppId = ? ORDER BY ServerId", appid)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
defer rows.Close()
|
|
var servers []ServerConfig
|
|
for rows.Next() {
|
|
var server ServerConfig
|
|
if err := rows.Scan(&server.ServerId, &server.Name, &server.Status, &server.Host, &server.Port, &server.MaxOnline, &server.Online, &server.Version, &server.Ecs, &server.WorkDir, &server.NodeType); err != nil {
|
|
continue
|
|
}
|
|
servers = append(servers, server)
|
|
}
|
|
return servers
|
|
}
|