mirror of
https://github.com/wyx2685/V2bX.git
synced 2025-01-22 09:58:14 -05:00
调整设备数限制实现方式
This commit is contained in:
parent
130e94cf45
commit
173c48a76f
@ -25,6 +25,8 @@ type Client struct {
|
||||
userEtag string
|
||||
responseBodyHash string
|
||||
LastReportOnline map[int]int
|
||||
UserList *UserListBody
|
||||
AliveMap *AliveMap
|
||||
}
|
||||
|
||||
func New(c *conf.ApiConfig) (*Client, error) {
|
||||
@ -71,5 +73,7 @@ func New(c *conf.ApiConfig) (*Client, error) {
|
||||
APIHost: c.APIHost,
|
||||
NodeType: c.NodeType,
|
||||
NodeId: c.NodeID,
|
||||
UserList: &UserListBody{},
|
||||
AliveMap: &AliveMap{},
|
||||
}, nil
|
||||
}
|
||||
|
@ -16,7 +16,6 @@ type UserInfo struct {
|
||||
Uuid string `json:"uuid"`
|
||||
SpeedLimit int `json:"speed_limit"`
|
||||
DeviceLimit int `json:"device_limit"`
|
||||
AliveIp int `json:"alive_ip"`
|
||||
}
|
||||
|
||||
type UserListBody struct {
|
||||
@ -24,8 +23,12 @@ type UserListBody struct {
|
||||
Users []UserInfo `json:"users"`
|
||||
}
|
||||
|
||||
// GetUserList will pull user form sspanel
|
||||
func (c *Client) GetUserList() (UserList []UserInfo, err error) {
|
||||
type AliveMap struct {
|
||||
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"
|
||||
r, err := c.client.R().
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if r != nil {
|
||||
defer func() {
|
||||
if r.RawBody() != nil {
|
||||
r.RawBody().Close()
|
||||
}
|
||||
}()
|
||||
if r.StatusCode() == 304 {
|
||||
return nil, nil
|
||||
}
|
||||
defer r.RawResponse.Body.Close()
|
||||
} else {
|
||||
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
|
||||
var deviceLimit, localDeviceLimit int = 0, 0
|
||||
for _, user := range userList.Users {
|
||||
// If there is still device available, add the user
|
||||
if user.DeviceLimit > 0 && user.AliveIp > 0 {
|
||||
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
|
||||
}
|
||||
if r.StatusCode() == 304 {
|
||||
return nil, nil
|
||||
} else {
|
||||
if err := json.Unmarshal(r.Body(), c.UserList); err != nil {
|
||||
return nil, fmt.Errorf("unmarshal user list error: %w", err)
|
||||
}
|
||||
user.DeviceLimit = deviceLimit
|
||||
userinfos = append(userinfos, user)
|
||||
c.userEtag = r.Header().Get("ETag")
|
||||
}
|
||||
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 {
|
||||
|
@ -38,6 +38,8 @@ func buildSSUser(tag string, userInfo *panel.UserInfo, cypher string, serverKey
|
||||
keyLength = 16
|
||||
case "2022-blake3-aes-256-gcm":
|
||||
keyLength = 32
|
||||
case "2022-blake3-chacha20-poly1305":
|
||||
keyLength = 32
|
||||
}
|
||||
ssAccount := &shadowsocks_2022.User{
|
||||
Level: 0,
|
||||
|
@ -21,13 +21,13 @@ var limiter map[string]*Limiter
|
||||
func Init() {
|
||||
limiter = map[string]*Limiter{}
|
||||
c := task.Periodic{
|
||||
Interval: time.Minute * 2,
|
||||
Interval: time.Minute * 3,
|
||||
Execute: ClearOnlineIP,
|
||||
}
|
||||
go func() {
|
||||
log.WithField("Type", "Limiter").
|
||||
Debug("ClearOnlineIP started")
|
||||
time.Sleep(time.Minute * 2)
|
||||
time.Sleep(time.Minute * 3)
|
||||
_ = c.Start()
|
||||
}()
|
||||
}
|
||||
@ -41,6 +41,7 @@ type Limiter struct {
|
||||
UserLimitInfo *sync.Map // Key: Uid value: UserLimitInfo
|
||||
ConnLimiter *ConnLimiter // Key: Uid value: ConnLimiter
|
||||
SpeedLimiter *sync.Map // key: Uid, value: *ratelimit.Bucket
|
||||
AliveList map[int]int // Key:Id, value:alive_ip
|
||||
}
|
||||
|
||||
type UserLimitInfo struct {
|
||||
@ -52,13 +53,14 @@ type UserLimitInfo struct {
|
||||
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{
|
||||
SpeedLimit: l.SpeedLimit,
|
||||
UserOnlineIP: new(sync.Map),
|
||||
UserLimitInfo: new(sync.Map),
|
||||
ConnLimiter: NewConnLimiter(l.ConnLimit, l.IPLimit, l.EnableRealtime),
|
||||
SpeedLimiter: new(sync.Map),
|
||||
AliveList: aliveList,
|
||||
}
|
||||
uuidmap := make(map[string]int)
|
||||
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
|
||||
ipMap := new(sync.Map)
|
||||
ipMap.Store(ip, uid)
|
||||
aliveIp := l.AliveList[uid]
|
||||
// If any device is online
|
||||
if v, ok := l.UserOnlineIP.LoadOrStore(taguuid, ipMap); ok {
|
||||
ipMap := v.(*sync.Map)
|
||||
// If this is a new ip
|
||||
if _, ok := ipMap.LoadOrStore(ip, uid); !ok {
|
||||
counter := 0
|
||||
ipMap.Range(func(key, value interface{}) bool {
|
||||
counter++
|
||||
return true
|
||||
})
|
||||
if counter > deviceLimit && deviceLimit > 0 {
|
||||
ipMap.Delete(ip)
|
||||
if deviceLimit > 0 {
|
||||
if deviceLimit <= aliveIp {
|
||||
ipMap.Delete(ip)
|
||||
return nil, true
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if deviceLimit > 0 {
|
||||
if deviceLimit <= aliveIp {
|
||||
l.UserOnlineIP.Delete(taguuid)
|
||||
return nil, true
|
||||
}
|
||||
}
|
||||
@ -218,23 +225,3 @@ type UserIpList struct {
|
||||
Uid int `json:"Uid"`
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ type Controller struct {
|
||||
limiter *limiter.Limiter
|
||||
traffic map[string]int64
|
||||
userList []panel.UserInfo
|
||||
aliveMap map[int]int
|
||||
info *panel.NodeInfo
|
||||
nodeInfoMonitorPeriodic *task.Task
|
||||
userReportPeriodic *task.Task
|
||||
@ -54,6 +55,10 @@ func (c *Controller) Start() error {
|
||||
if len(c.userList) == 0 {
|
||||
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 {
|
||||
c.tag = c.buildNodeTag(node)
|
||||
} else {
|
||||
@ -61,7 +66,7 @@ func (c *Controller) Start() error {
|
||||
}
|
||||
|
||||
// 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
|
||||
if err = l.UpdateRule(&node.Rules); err != nil {
|
||||
return fmt.Errorf("update rule error: %s", err)
|
||||
|
12
node/task.go
12
node/task.go
@ -68,6 +68,11 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
||||
}).Error("Get user list failed")
|
||||
return nil
|
||||
}
|
||||
// get user alive
|
||||
newA, err := c.apiClient.GetUserAlive()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if newN != nil {
|
||||
c.info = newN
|
||||
// nodeInfo changed
|
||||
@ -92,7 +97,7 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
||||
// Remove Old limiter
|
||||
limiter.DeleteLimiter(c.tag)
|
||||
// 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
|
||||
}
|
||||
// Update rule
|
||||
@ -154,7 +159,10 @@ func (c *Controller) nodeInfoMonitor() (err error) {
|
||||
// exit
|
||||
return nil
|
||||
}
|
||||
|
||||
// update alive list
|
||||
if newA != nil {
|
||||
c.limiter.AliveList = newA
|
||||
}
|
||||
// node no changed, check users
|
||||
if len(newU) == 0 {
|
||||
return nil
|
||||
|
Loading…
Reference in New Issue
Block a user