feature/balance #3
30
balance/abstract.go
Normal file
30
balance/abstract.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// Package balance...
|
||||||
|
//
|
||||||
|
// Description : balance...
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 2021-11-22 4:45 下午
|
||||||
|
package balance
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"git.zhangdeman.cn/zhangdeman/gopkg/balance/define"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IBalance 负载算法接口约束
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 4:45 下午 2021/11/22
|
||||||
|
type IBalance interface {
|
||||||
|
// GetServerNode 获取一个服务器节点
|
||||||
|
GetServerNode(req *http.Request) (*define.ServerNode, error)
|
||||||
|
// AddServerNode 新增一个服务器节点
|
||||||
|
AddServerNode(node *define.ServerNode) error
|
||||||
|
// Remove 移除一个节点
|
||||||
|
Remove(hostIP string, port int, force bool)
|
||||||
|
// GetServerNodeList 获取服务节点列表
|
||||||
|
GetServerNodeList() []*define.ServerNode
|
||||||
|
}
|
@ -7,7 +7,12 @@
|
|||||||
// Date : 2021-10-19 2:26 下午
|
// Date : 2021-10-19 2:26 下午
|
||||||
package balance
|
package balance
|
||||||
|
|
||||||
import "sync"
|
import (
|
||||||
|
"errors"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"git.zhangdeman.cn/zhangdeman/gopkg/balance/define"
|
||||||
|
)
|
||||||
|
|
||||||
// base ...
|
// base ...
|
||||||
//
|
//
|
||||||
@ -16,6 +21,7 @@ import "sync"
|
|||||||
// Date : 2:26 下午 2021/10/19
|
// Date : 2:26 下午 2021/10/19
|
||||||
type base struct {
|
type base struct {
|
||||||
lock *sync.RWMutex
|
lock *sync.RWMutex
|
||||||
|
severList []*define.ServerNode
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lock ...
|
// Lock ...
|
||||||
@ -53,3 +59,53 @@ func (b *base) RLock() {
|
|||||||
func (b *base) RUnlock() {
|
func (b *base) RUnlock() {
|
||||||
b.lock.RUnlock()
|
b.lock.RUnlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddServerNode 新添加一个服务器节点
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 10:07 下午 2021/10/20
|
||||||
|
func (b *base) AddServerNode(node *define.ServerNode) error {
|
||||||
|
if len(node.HostIP) == 0 || node.Port <= 0 {
|
||||||
|
return errors.New("host ip or port is invalid")
|
||||||
|
}
|
||||||
|
b.Lock()
|
||||||
|
defer b.Unlock()
|
||||||
|
for _, item := range b.severList {
|
||||||
|
if item.HostIP == node.HostIP && item.Port == node.Port {
|
||||||
|
return errors.New("host config is already exist")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.severList = append(b.severList, node)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove 移除一个节点, force = true , 强制删除, force = false 逻辑删除, 设置状态
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 10:19 下午 2021/10/20
|
||||||
|
func (b *base) Remove(hostIP string, port int, force bool) {
|
||||||
|
b.Lock()
|
||||||
|
defer b.Unlock()
|
||||||
|
tmpServerNode := make([]*define.ServerNode, 0)
|
||||||
|
for _, item := range b.severList {
|
||||||
|
if item.HostIP == hostIP && item.Port == port {
|
||||||
|
if force {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
item.Status = define.ServerNodeStatusRemove
|
||||||
|
}
|
||||||
|
tmpServerNode = append(tmpServerNode, item)
|
||||||
|
}
|
||||||
|
b.severList = tmpServerNode
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetServerNodeList 获取服务器节点
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 10:33 下午 2021/10/20
|
||||||
|
func (b *base) GetServerNodeList() []*define.ServerNode {
|
||||||
|
return b.severList
|
||||||
|
}
|
||||||
|
@ -16,5 +16,6 @@ type ServerNode struct {
|
|||||||
HostIP string `json:"host_ip"` // 机器IP
|
HostIP string `json:"host_ip"` // 机器IP
|
||||||
Port int `json:"port"` // 机器端口
|
Port int `json:"port"` // 机器端口
|
||||||
Status int `json:"status"` // 机器状态
|
Status int `json:"status"` // 机器状态
|
||||||
Weight int `json:"weight"` // 机器权重
|
Weight float64 `json:"weight"` // 机器权重
|
||||||
|
CurrentWeight float64 `json:"current_weight"` // 当前权重
|
||||||
}
|
}
|
||||||
|
62
balance/ip_hash.go
Normal file
62
balance/ip_hash.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
// Package balance...
|
||||||
|
//
|
||||||
|
// Description : balance...
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 2021-11-22 4:39 下午
|
||||||
|
package balance
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"git.zhangdeman.cn/zhangdeman/gopkg/util"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
"git.zhangdeman.cn/zhangdeman/gopkg/balance/define"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewIPHash ip hash 负载均衡
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 4:53 下午 2021/11/22
|
||||||
|
func NewIPHash(severList []*define.ServerNode) (IBalance, error) {
|
||||||
|
if nil == severList || len(severList) == 0 {
|
||||||
|
return nil, errors.New("sever list is empty")
|
||||||
|
}
|
||||||
|
return &IPHash{
|
||||||
|
base: base{
|
||||||
|
lock: &sync.RWMutex{},
|
||||||
|
severList: severList,
|
||||||
|
},
|
||||||
|
serverList: severList,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPHash ...
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 4:57 下午 2021/11/22
|
||||||
|
type IPHash struct {
|
||||||
|
base
|
||||||
|
serverList []*define.ServerNode
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetServerNode ...
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 4:53 下午 2021/11/22
|
||||||
|
func (ih *IPHash) GetServerNode(req *http.Request) (*define.ServerNode, error) {
|
||||||
|
clintIPHashID := util.GetHashID(util.GetRemoteIp(req))
|
||||||
|
ih.RLock()
|
||||||
|
defer ih.RUnlock()
|
||||||
|
if len(ih.serverList) == 0 {
|
||||||
|
return nil, errors.New("sever list is empty")
|
||||||
|
}
|
||||||
|
return ih.serverList[int(clintIPHashID%uint64(len(ih.serverList)))], nil
|
||||||
|
}
|
@ -8,6 +8,7 @@
|
|||||||
package balance
|
package balance
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"git.zhangdeman.cn/zhangdeman/gopkg/balance/define"
|
"git.zhangdeman.cn/zhangdeman/gopkg/balance/define"
|
||||||
@ -19,15 +20,15 @@ import (
|
|||||||
// Author : go_developer@163.com<白茶清欢>
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
//
|
//
|
||||||
// Date : 12:40 下午 2021/10/19
|
// Date : 12:40 下午 2021/10/19
|
||||||
func NewPoll(serverList []*define.ServerNode) (*Poll, error) {
|
func NewPoll(serverList []*define.ServerNode) (IBalance, error) {
|
||||||
if nil == serverList || len(serverList) == 0 {
|
if nil == serverList || len(serverList) == 0 {
|
||||||
return nil, errors.New("server list is empty")
|
return nil, errors.New("server list is empty")
|
||||||
}
|
}
|
||||||
return &Poll{
|
return &Poll{
|
||||||
base: base{
|
base: base{
|
||||||
lock: &sync.RWMutex{},
|
lock: &sync.RWMutex{},
|
||||||
|
severList: serverList,
|
||||||
},
|
},
|
||||||
serverList: serverList,
|
|
||||||
currentServerIndex: 0,
|
currentServerIndex: 0,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@ -39,7 +40,6 @@ func NewPoll(serverList []*define.ServerNode) (*Poll, error) {
|
|||||||
// Date : 12:41 下午 2021/10/19
|
// Date : 12:41 下午 2021/10/19
|
||||||
type Poll struct {
|
type Poll struct {
|
||||||
base
|
base
|
||||||
serverList []*define.ServerNode
|
|
||||||
currentServerIndex int
|
currentServerIndex int
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,21 +48,18 @@ type Poll struct {
|
|||||||
// Author : go_developer@163.com<白茶清欢>
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
//
|
//
|
||||||
// Date : 12:43 下午 2021/10/19
|
// Date : 12:43 下午 2021/10/19
|
||||||
func (p *Poll) GetServerNode() (*define.ServerNode, error) {
|
func (p *Poll) GetServerNode(req *http.Request) (*define.ServerNode, error) {
|
||||||
if len(p.serverList) == 0 {
|
p.RLock()
|
||||||
return nil, errors.New("server list is empty")
|
defer p.RUnlock()
|
||||||
}
|
|
||||||
p.Lock()
|
|
||||||
defer p.Unlock()
|
|
||||||
var (
|
var (
|
||||||
serverNode *define.ServerNode
|
serverNode *define.ServerNode
|
||||||
)
|
)
|
||||||
// 循环次数
|
// 循环次数
|
||||||
loopTimes := 0
|
loopTimes := 0
|
||||||
for loopTimes < len(p.serverList) {
|
for loopTimes < len(p.severList) {
|
||||||
loopTimes++
|
loopTimes++
|
||||||
p.currentServerIndex = (p.currentServerIndex + 1) % len(p.serverList)
|
p.currentServerIndex = (p.currentServerIndex + 1) % len(p.severList)
|
||||||
if serverNode = p.serverList[p.currentServerIndex]; serverNode.Status != define.ServerNodeStatusNormal {
|
if serverNode = p.severList[p.currentServerIndex]; serverNode.Status != define.ServerNodeStatusNormal {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
@ -74,57 +71,3 @@ func (p *Poll) GetServerNode() (*define.ServerNode, error) {
|
|||||||
|
|
||||||
return serverNode, nil
|
return serverNode, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddServerNode 新添加一个服务器节点
|
|
||||||
//
|
|
||||||
// Author : go_developer@163.com<白茶清欢>
|
|
||||||
//
|
|
||||||
// Date : 10:07 下午 2021/10/20
|
|
||||||
func (p *Poll) AddServerNode(hostIP string, port int) error {
|
|
||||||
if len(hostIP) == 0 || port <= 0 {
|
|
||||||
return errors.New("host ip or port is invalid")
|
|
||||||
}
|
|
||||||
p.Lock()
|
|
||||||
defer p.Unlock()
|
|
||||||
for _, item := range p.serverList {
|
|
||||||
if item.HostIP == hostIP && item.Port == port {
|
|
||||||
return errors.New("host config is already exist")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.serverList = append(p.serverList, &define.ServerNode{
|
|
||||||
HostIP: hostIP,
|
|
||||||
Port: port,
|
|
||||||
Status: define.ServerNodeStatusNormal,
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove 移除一个节点, force = true , 强制删除, force = false 逻辑删除, 设置状态
|
|
||||||
//
|
|
||||||
// Author : go_developer@163.com<白茶清欢>
|
|
||||||
//
|
|
||||||
// Date : 10:19 下午 2021/10/20
|
|
||||||
func (p *Poll) Remove(hostIP string, port int, force bool) {
|
|
||||||
p.Lock()
|
|
||||||
defer p.Unlock()
|
|
||||||
tmpServerNode := make([]*define.ServerNode, 0)
|
|
||||||
for _, item := range p.serverList {
|
|
||||||
if item.HostIP == hostIP && item.Port == port {
|
|
||||||
if force {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
item.Status = define.ServerNodeStatusRemove
|
|
||||||
}
|
|
||||||
tmpServerNode = append(tmpServerNode, item)
|
|
||||||
}
|
|
||||||
p.serverList = tmpServerNode
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetServerNodeList 获取服务器节点
|
|
||||||
//
|
|
||||||
// Author : go_developer@163.com<白茶清欢>
|
|
||||||
//
|
|
||||||
// Date : 10:33 下午 2021/10/20
|
|
||||||
func (p *Poll) GetServerNodeList() []*define.ServerNode {
|
|
||||||
return p.serverList
|
|
||||||
}
|
|
||||||
|
59
balance/random.go
Normal file
59
balance/random.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
// Package balance...
|
||||||
|
//
|
||||||
|
// Description : balance...
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 2021-11-22 6:27 下午
|
||||||
|
package balance
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"git.zhangdeman.cn/zhangdeman/gopkg/util"
|
||||||
|
|
||||||
|
"git.zhangdeman.cn/zhangdeman/gopkg/balance/define"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewRandom ...
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 6:29 下午 2021/11/22
|
||||||
|
func NewRandom(severList []*define.ServerNode) (IBalance, error) {
|
||||||
|
if nil == severList || len(severList) == 0 {
|
||||||
|
return nil, errors.New("sever list is empty")
|
||||||
|
}
|
||||||
|
return &Random{
|
||||||
|
base{
|
||||||
|
lock: &sync.RWMutex{},
|
||||||
|
severList: make([]*define.ServerNode, 0),
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Random 随机负载均衡
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 6:27 下午 2021/11/22
|
||||||
|
type Random struct {
|
||||||
|
base
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetServerNode ...
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 6:31 下午 2021/11/22
|
||||||
|
func (r *Random) GetServerNode(req *http.Request) (*define.ServerNode, error) {
|
||||||
|
r.RLock()
|
||||||
|
defer r.RUnlock()
|
||||||
|
if len(r.severList) == 0 {
|
||||||
|
return nil, errors.New("sever list is empty")
|
||||||
|
}
|
||||||
|
randomID := util.GetHashID(util.GenRandomString("", 128)) % uint64(len(r.severList))
|
||||||
|
return r.severList[int(randomID)], nil
|
||||||
|
}
|
114
balance/weight_serverRound_robin.go
Normal file
114
balance/weight_serverRound_robin.go
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
// Package balance...
|
||||||
|
//
|
||||||
|
// Description : balance...
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 2021-11-22 6:58 下午
|
||||||
|
package balance
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
"git.zhangdeman.cn/zhangdeman/gopkg/balance/define"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewWeightServerRoundRobin ...
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 6:58 下午 2021/11/22
|
||||||
|
func NewWeightServerRoundRobin(severList []*define.ServerNode) (IBalance, error) {
|
||||||
|
if nil == severList || len(severList) == 0 {
|
||||||
|
return nil, errors.New("sever list is empty")
|
||||||
|
}
|
||||||
|
return &WeightServerRoundRobin{
|
||||||
|
base: base{
|
||||||
|
lock: &sync.RWMutex{},
|
||||||
|
severList: severList,
|
||||||
|
},
|
||||||
|
effectiveWeight: 0,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WeightServerRoundRobin 加权轮询
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 6:58 下午 2021/11/22
|
||||||
|
type WeightServerRoundRobin struct {
|
||||||
|
base
|
||||||
|
effectiveWeight float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetServerNode ...
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 7:00 下午 2021/11/22
|
||||||
|
func (w *WeightServerRoundRobin) GetServerNode(req *http.Request) (*define.ServerNode, error) {
|
||||||
|
w.RLock()
|
||||||
|
defer w.RUnlock()
|
||||||
|
if len(w.severList) == 0 {
|
||||||
|
return nil, errors.New("sever list is empty")
|
||||||
|
}
|
||||||
|
var expectBackendServer *define.ServerNode
|
||||||
|
for _, backendServer := range w.severList {
|
||||||
|
// 给每个后端服务增加自身权重
|
||||||
|
backendServer.CurrentWeight += backendServer.Weight
|
||||||
|
if expectBackendServer == nil {
|
||||||
|
expectBackendServer = backendServer
|
||||||
|
}
|
||||||
|
if backendServer.CurrentWeight > expectBackendServer.CurrentWeight {
|
||||||
|
expectBackendServer = backendServer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 把选择的后端服务权重减掉总权重
|
||||||
|
expectBackendServer.CurrentWeight -= w.effectiveWeight
|
||||||
|
return expectBackendServer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddServerNode 新添加一个服务器节点
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 10:07 下午 2021/10/20
|
||||||
|
func (w *WeightServerRoundRobin) AddServerNode(node *define.ServerNode) error {
|
||||||
|
if len(node.HostIP) == 0 || node.Port <= 0 {
|
||||||
|
return errors.New("host ip or port is invalid")
|
||||||
|
}
|
||||||
|
w.Lock()
|
||||||
|
defer w.Unlock()
|
||||||
|
for _, item := range w.severList {
|
||||||
|
if item.HostIP == node.HostIP && item.Port == node.Port {
|
||||||
|
return errors.New("host config is already exist")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.effectiveWeight += node.Weight
|
||||||
|
w.severList = append(w.severList, node)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove 移除一个节点, force = true , 强制删除, force = false 逻辑删除, 设置状态
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 10:19 下午 2021/10/20
|
||||||
|
func (w *WeightServerRoundRobin) Remove(hostIP string, port int, force bool) {
|
||||||
|
w.Lock()
|
||||||
|
defer w.Unlock()
|
||||||
|
tmpServerNode := make([]*define.ServerNode, 0)
|
||||||
|
for _, item := range w.severList {
|
||||||
|
if item.HostIP == hostIP && item.Port == port {
|
||||||
|
if force {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
item.Status = define.ServerNodeStatusRemove
|
||||||
|
}
|
||||||
|
tmpServerNode = append(tmpServerNode, item)
|
||||||
|
}
|
||||||
|
w.severList = tmpServerNode
|
||||||
|
}
|
26
util/ip.go
26
util/ip.go
@ -7,7 +7,10 @@
|
|||||||
// Date : 2021-03-09 5:56 下午
|
// Date : 2021-03-09 5:56 下午
|
||||||
package util
|
package util
|
||||||
|
|
||||||
import "net"
|
import (
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
// GetHostIP 获取本机IP地址
|
// GetHostIP 获取本机IP地址
|
||||||
//
|
//
|
||||||
@ -27,3 +30,24 @@ func GetHostIP() string {
|
|||||||
}
|
}
|
||||||
return hostIP
|
return hostIP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetRemoteIp 获取远端IP
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 5:35 下午 2021/11/22
|
||||||
|
func GetRemoteIp(req *http.Request) string {
|
||||||
|
|
||||||
|
// Try via request
|
||||||
|
|
||||||
|
ip, _, err := net.SplitHostPort(req.RemoteAddr)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "::1"
|
||||||
|
}
|
||||||
|
userIP := net.ParseIP(ip)
|
||||||
|
if userIP == nil {
|
||||||
|
return "::1"
|
||||||
|
}
|
||||||
|
return userIP.String()
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user