|
|
package service_base
|
|
|
|
|
|
import (
|
|
|
"app/internal/msg_inner"
|
|
|
"core/tools"
|
|
|
"time"
|
|
|
)
|
|
|
|
|
|
type LB_TYPE int32
|
|
|
|
|
|
const (
|
|
|
LBTypeNone LB_TYPE = iota
|
|
|
LBTypeFrontend
|
|
|
LBTypeBackend
|
|
|
)
|
|
|
const (
|
|
|
CHECK_ALIVE_RETRY_MAX = 3 //被动检测存活 重试次数
|
|
|
FRONTEND_CHECKALIVE_INTERVAL = 30 //前端检测存活的间隔 秒
|
|
|
FRONTEND_ROUTER_REFRESH_INTERVAL = 60 //前端路由刷新时间 秒
|
|
|
BAKCEND_REPORT_INTERVAL = 30 //后端主动上报的间隔 秒
|
|
|
)
|
|
|
|
|
|
func NewLoadBalanceManager() ILoadBalanceManager {
|
|
|
return &LoadBalanceManager{
|
|
|
backendMap: make(map[string]*LoadBalanceNode),
|
|
|
}
|
|
|
}
|
|
|
|
|
|
var _ ILoadBalanceManager = (*LoadBalanceManager)(nil) //interface实现检查
|
|
|
type ILoadBalanceManager interface {
|
|
|
Init(serviceBase IServiceBase) ILoadBalanceManager
|
|
|
StartAsFrontend() //作为前端启动
|
|
|
StartAsBackend(frontendAppId string) //作为后端启动
|
|
|
GetBestBackend() (addr, appId string) //获得路由后的后端地址 目前策略:根据后端上报的rank选择最优
|
|
|
}
|
|
|
type LoadBalanceManager struct {
|
|
|
serviceBase IServiceBase
|
|
|
frontendAppId string
|
|
|
bestBackendAppId string
|
|
|
backendMap map[string]*LoadBalanceNode
|
|
|
lbType LB_TYPE
|
|
|
}
|
|
|
|
|
|
type LoadBalanceNode struct {
|
|
|
appId string
|
|
|
address string
|
|
|
rank float64
|
|
|
isAlive bool
|
|
|
}
|
|
|
|
|
|
func (s *LoadBalanceManager) Init(serviceBase IServiceBase) ILoadBalanceManager {
|
|
|
s.serviceBase = serviceBase
|
|
|
s.initAPI()
|
|
|
return s
|
|
|
}
|
|
|
|
|
|
func (s *LoadBalanceManager) StartAsFrontend() {
|
|
|
tools.AssertTrue(s.lbType == LBTypeNone, "StartAsFrontend fail, lbType is not LBTypeNone")
|
|
|
s.lbType = LBTypeFrontend
|
|
|
act := s.serviceBase.Actor()
|
|
|
act.AddTimer(time.Second*FRONTEND_CHECKALIVE_INTERVAL, -1, func(dt int64) {
|
|
|
for _, node := range s.backendMap {
|
|
|
var timerId int64
|
|
|
retry := CHECK_ALIVE_RETRY_MAX
|
|
|
act.Send(act.Id(), func() {
|
|
|
timerId = act.AddTimer(time.Second, -1, func(dt int64) {
|
|
|
if _, err := act.RequestWait(node.appId, &msg_inner.CheckAlive{}); err == nil {
|
|
|
act.CancelTimer(timerId)
|
|
|
node.isAlive = true
|
|
|
} else {
|
|
|
retry--
|
|
|
if retry < 0 {
|
|
|
act.CancelTimer(timerId)
|
|
|
node.isAlive = false
|
|
|
}
|
|
|
}
|
|
|
})
|
|
|
})
|
|
|
}
|
|
|
})
|
|
|
act.AddTimer(time.Second*FRONTEND_ROUTER_REFRESH_INTERVAL, -1, func(dt int64) {
|
|
|
if len(s.backendMap) == 0 {
|
|
|
return
|
|
|
}
|
|
|
var bestAppId string
|
|
|
var bestRank float64
|
|
|
for _, node := range s.backendMap {
|
|
|
if bestAppId == "" || node.rank < bestRank {
|
|
|
bestAppId = node.appId
|
|
|
bestRank = node.rank
|
|
|
}
|
|
|
}
|
|
|
s.bestBackendAppId = bestAppId
|
|
|
})
|
|
|
}
|
|
|
|
|
|
func (s *LoadBalanceManager) StartAsBackend(frontendAppId string) {
|
|
|
tools.AssertTrue(s.lbType == LBTypeNone, "StartAsFrontend fail, lbType is not LBTypeNone")
|
|
|
s.lbType = LBTypeBackend
|
|
|
s.frontendAppId = frontendAppId
|
|
|
var addr string
|
|
|
if s.serviceBase.Config().Has("client_addr") {
|
|
|
_, port := tools.AddressSplit(s.serviceBase.Config().String("client_addr"))
|
|
|
addr = tools.AddressMerge(tools.GetOutboundIP().String(), port)
|
|
|
}
|
|
|
act := s.serviceBase.Actor()
|
|
|
act.AddTimer(time.Second*BAKCEND_REPORT_INTERVAL, -1, func(dt int64) {
|
|
|
act.Send(s.frontendAppId, &msg_inner.LoadBalanceReport{
|
|
|
Rank: 0,
|
|
|
Address: addr,
|
|
|
})
|
|
|
})
|
|
|
}
|
|
|
|
|
|
func (s *LoadBalanceManager) GetBestBackend() (addr, appId string) {
|
|
|
tools.AssertTrue(s.lbType == LBTypeFrontend, "GetBestBackend fail, lbType is not LBTypeFrontend")
|
|
|
if node, ok := s.backendMap[s.bestBackendAppId]; ok {
|
|
|
addr, appId = node.address, node.appId
|
|
|
}
|
|
|
return
|
|
|
}
|