调整设备数限制实现方式

This commit is contained in:
wyx2685 2024-08-30 06:48:41 +09:00
parent 130e94cf45
commit 173c48a76f
No known key found for this signature in database
GPG Key ID: 8827A30FF1DB1379
6 changed files with 75 additions and 74 deletions

View File

@ -25,6 +25,8 @@ type Client struct {
userEtag string userEtag string
responseBodyHash string responseBodyHash string
LastReportOnline map[int]int LastReportOnline map[int]int
UserList *UserListBody
AliveMap *AliveMap
} }
func New(c *conf.ApiConfig) (*Client, error) { func New(c *conf.ApiConfig) (*Client, error) {
@ -71,5 +73,7 @@ func New(c *conf.ApiConfig) (*Client, error) {
APIHost: c.APIHost, APIHost: c.APIHost,
NodeType: c.NodeType, NodeType: c.NodeType,
NodeId: c.NodeID, NodeId: c.NodeID,
UserList: &UserListBody{},
AliveMap: &AliveMap{},
}, nil }, nil
} }

View File

@ -16,7 +16,6 @@ type UserInfo struct {
Uuid string `json:"uuid"` Uuid string `json:"uuid"`
SpeedLimit int `json:"speed_limit"` SpeedLimit int `json:"speed_limit"`
DeviceLimit int `json:"device_limit"` DeviceLimit int `json:"device_limit"`
AliveIp int `json:"alive_ip"`
} }
type UserListBody struct { type UserListBody struct {
@ -24,8 +23,12 @@ type UserListBody struct {
Users []UserInfo `json:"users"` Users []UserInfo `json:"users"`
} }
// GetUserList will pull user form sspanel type AliveMap struct {
func (c *Client) GetUserList() (UserList []UserInfo, err error) { Alive map[int]int `json:"alive"`
}
// GetUserList will pull user from v2board
func (c *Client) GetUserList() ([]UserInfo, error) {
const path = "/api/v1/server/UniProxy/user" const path = "/api/v1/server/UniProxy/user"
r, err := c.client.R(). r, err := c.client.R().
SetHeader("If-None-Match", c.userEtag). SetHeader("If-None-Match", c.userEtag).
@ -34,52 +37,44 @@ func (c *Client) GetUserList() (UserList []UserInfo, err error) {
if err = c.checkResponse(r, path, err); err != nil { if err = c.checkResponse(r, path, err); err != nil {
return nil, err return nil, err
} }
if r != nil { if r != nil {
defer func() { defer r.RawResponse.Body.Close()
if r.RawBody() != nil {
r.RawBody().Close()
}
}()
if r.StatusCode() == 304 {
return nil, nil
}
} else { } else {
return nil, fmt.Errorf("received nil response") return nil, fmt.Errorf("received nil response")
} }
var userList *UserListBody
if err != nil {
return nil, fmt.Errorf("read body error: %s", err)
}
if err := json.Unmarshal(r.Body(), &userList); err != nil {
return nil, fmt.Errorf("unmarshal userlist error: %s", err)
}
c.userEtag = r.Header().Get("ETag")
var userinfos []UserInfo if r.StatusCode() == 304 {
var deviceLimit, localDeviceLimit int = 0, 0 return nil, nil
for _, user := range userList.Users { } else {
// If there is still device available, add the user if err := json.Unmarshal(r.Body(), c.UserList); err != nil {
if user.DeviceLimit > 0 && user.AliveIp > 0 { return nil, fmt.Errorf("unmarshal user list error: %w", err)
lastOnline := 0
if v, ok := c.LastReportOnline[user.Id]; ok {
lastOnline = v
}
// If there are any available device.
localDeviceLimit = user.DeviceLimit - user.AliveIp + lastOnline
if localDeviceLimit > 0 {
deviceLimit = localDeviceLimit
} else if lastOnline > 0 {
deviceLimit = lastOnline
} else {
continue
}
} }
user.DeviceLimit = deviceLimit c.userEtag = r.Header().Get("ETag")
userinfos = append(userinfos, user) }
return c.UserList.Users, nil
}
// GetUserAlive will fetch the alive IPs for users
func (c *Client) GetUserAlive() (map[int]int, error) {
const path = "/api/v1/server/UniProxy/alivelist"
r, err := c.client.R().
ForceContentType("application/json").
Get(path)
if err = c.checkResponse(r, path, err); err != nil {
return nil, err
} }
return userinfos, nil if r != nil {
defer r.RawResponse.Body.Close()
} else {
return nil, fmt.Errorf("received nil response")
}
if err := json.Unmarshal(r.Body(), c.AliveMap); err != nil {
return nil, fmt.Errorf("unmarshal user alive list error: %s", err)
}
return c.AliveMap.Alive, nil
} }
type UserTraffic struct { type UserTraffic struct {

View File

@ -38,6 +38,8 @@ func buildSSUser(tag string, userInfo *panel.UserInfo, cypher string, serverKey
keyLength = 16 keyLength = 16
case "2022-blake3-aes-256-gcm": case "2022-blake3-aes-256-gcm":
keyLength = 32 keyLength = 32
case "2022-blake3-chacha20-poly1305":
keyLength = 32
} }
ssAccount := &shadowsocks_2022.User{ ssAccount := &shadowsocks_2022.User{
Level: 0, Level: 0,

View File

@ -21,13 +21,13 @@ var limiter map[string]*Limiter
func Init() { func Init() {
limiter = map[string]*Limiter{} limiter = map[string]*Limiter{}
c := task.Periodic{ c := task.Periodic{
Interval: time.Minute * 2, Interval: time.Minute * 3,
Execute: ClearOnlineIP, Execute: ClearOnlineIP,
} }
go func() { go func() {
log.WithField("Type", "Limiter"). log.WithField("Type", "Limiter").
Debug("ClearOnlineIP started") Debug("ClearOnlineIP started")
time.Sleep(time.Minute * 2) time.Sleep(time.Minute * 3)
_ = c.Start() _ = c.Start()
}() }()
} }
@ -41,6 +41,7 @@ type Limiter struct {
UserLimitInfo *sync.Map // Key: Uid value: UserLimitInfo UserLimitInfo *sync.Map // Key: Uid value: UserLimitInfo
ConnLimiter *ConnLimiter // Key: Uid value: ConnLimiter ConnLimiter *ConnLimiter // Key: Uid value: ConnLimiter
SpeedLimiter *sync.Map // key: Uid, value: *ratelimit.Bucket SpeedLimiter *sync.Map // key: Uid, value: *ratelimit.Bucket
AliveList map[int]int // Key:Id, value:alive_ip
} }
type UserLimitInfo struct { type UserLimitInfo struct {
@ -52,13 +53,14 @@ type UserLimitInfo struct {
OverLimit bool OverLimit bool
} }
func AddLimiter(tag string, l *conf.LimitConfig, users []panel.UserInfo) *Limiter { func AddLimiter(tag string, l *conf.LimitConfig, users []panel.UserInfo, aliveList map[int]int) *Limiter {
info := &Limiter{ info := &Limiter{
SpeedLimit: l.SpeedLimit, SpeedLimit: l.SpeedLimit,
UserOnlineIP: new(sync.Map), UserOnlineIP: new(sync.Map),
UserLimitInfo: new(sync.Map), UserLimitInfo: new(sync.Map),
ConnLimiter: NewConnLimiter(l.ConnLimit, l.IPLimit, l.EnableRealtime), ConnLimiter: NewConnLimiter(l.ConnLimit, l.IPLimit, l.EnableRealtime),
SpeedLimiter: new(sync.Map), SpeedLimiter: new(sync.Map),
AliveList: aliveList,
} }
uuidmap := make(map[string]int) uuidmap := make(map[string]int)
for i := range users { for i := range users {
@ -163,18 +165,23 @@ func (l *Limiter) CheckLimit(taguuid string, ip string, isTcp bool, noSSUDP bool
// Store online user for device limit // Store online user for device limit
ipMap := new(sync.Map) ipMap := new(sync.Map)
ipMap.Store(ip, uid) ipMap.Store(ip, uid)
aliveIp := l.AliveList[uid]
// If any device is online // If any device is online
if v, ok := l.UserOnlineIP.LoadOrStore(taguuid, ipMap); ok { if v, ok := l.UserOnlineIP.LoadOrStore(taguuid, ipMap); ok {
ipMap := v.(*sync.Map) ipMap := v.(*sync.Map)
// If this is a new ip // If this is a new ip
if _, ok := ipMap.LoadOrStore(ip, uid); !ok { if _, ok := ipMap.LoadOrStore(ip, uid); !ok {
counter := 0 if deviceLimit > 0 {
ipMap.Range(func(key, value interface{}) bool { if deviceLimit <= aliveIp {
counter++ ipMap.Delete(ip)
return true return nil, true
}) }
if counter > deviceLimit && deviceLimit > 0 { }
ipMap.Delete(ip) }
} else {
if deviceLimit > 0 {
if deviceLimit <= aliveIp {
l.UserOnlineIP.Delete(taguuid)
return nil, true return nil, true
} }
} }
@ -218,23 +225,3 @@ type UserIpList struct {
Uid int `json:"Uid"` Uid int `json:"Uid"`
IpList []string `json:"Ips"` IpList []string `json:"Ips"`
} }
func determineDeviceLimit(nodeLimit, userLimit int) (limit int) {
if nodeLimit == 0 || userLimit == 0 {
if nodeLimit > userLimit {
return nodeLimit
} else if nodeLimit < userLimit {
return userLimit
} else {
return 0
}
} else {
if nodeLimit > userLimit {
return userLimit
} else if nodeLimit < userLimit {
return nodeLimit
} else {
return nodeLimit
}
}
}

View File

@ -19,6 +19,7 @@ type Controller struct {
limiter *limiter.Limiter limiter *limiter.Limiter
traffic map[string]int64 traffic map[string]int64
userList []panel.UserInfo userList []panel.UserInfo
aliveMap map[int]int
info *panel.NodeInfo info *panel.NodeInfo
nodeInfoMonitorPeriodic *task.Task nodeInfoMonitorPeriodic *task.Task
userReportPeriodic *task.Task userReportPeriodic *task.Task
@ -54,6 +55,10 @@ func (c *Controller) Start() error {
if len(c.userList) == 0 { if len(c.userList) == 0 {
return errors.New("add users error: not have any user") return errors.New("add users error: not have any user")
} }
c.aliveMap, err = c.apiClient.GetUserAlive()
if err != nil {
return fmt.Errorf("failed to get user alive list: %s", err)
}
if len(c.Options.Name) == 0 { if len(c.Options.Name) == 0 {
c.tag = c.buildNodeTag(node) c.tag = c.buildNodeTag(node)
} else { } else {
@ -61,7 +66,7 @@ func (c *Controller) Start() error {
} }
// add limiter // add limiter
l := limiter.AddLimiter(c.tag, &c.LimitConfig, c.userList) l := limiter.AddLimiter(c.tag, &c.LimitConfig, c.userList, c.aliveMap)
// add rule limiter // add rule limiter
if err = l.UpdateRule(&node.Rules); err != nil { if err = l.UpdateRule(&node.Rules); err != nil {
return fmt.Errorf("update rule error: %s", err) return fmt.Errorf("update rule error: %s", err)

View File

@ -68,6 +68,11 @@ func (c *Controller) nodeInfoMonitor() (err error) {
}).Error("Get user list failed") }).Error("Get user list failed")
return nil return nil
} }
// get user alive
newA, err := c.apiClient.GetUserAlive()
if err != nil {
return err
}
if newN != nil { if newN != nil {
c.info = newN c.info = newN
// nodeInfo changed // nodeInfo changed
@ -92,7 +97,7 @@ func (c *Controller) nodeInfoMonitor() (err error) {
// Remove Old limiter // Remove Old limiter
limiter.DeleteLimiter(c.tag) limiter.DeleteLimiter(c.tag)
// Add new Limiter // Add new Limiter
l := limiter.AddLimiter(c.tag, &c.LimitConfig, c.userList) l := limiter.AddLimiter(c.tag, &c.LimitConfig, c.userList, newA)
c.limiter = l c.limiter = l
} }
// Update rule // Update rule
@ -154,7 +159,10 @@ func (c *Controller) nodeInfoMonitor() (err error) {
// exit // exit
return nil return nil
} }
// update alive list
if newA != nil {
c.limiter.AliveList = newA
}
// node no changed, check users // node no changed, check users
if len(newU) == 0 { if len(newU) == 0 {
return nil return nil