

package util
import (
"fmt"
"hash/crc32"
"math/rand"
"sort"
"time"
)
type HttpServer struct { //目標server類
Host string
Weight int
CWeight int //當前權重
Status string //健康檢查
FailCount int //計數器,默認是0
SuccessCount int //檢查到連續成功,當連續成功的次數達到這個值,把宕機的的機器的FailCount立刻重置為0,加快服務器啟動速度
}
type HttpServers []*HttpServer
func (p HttpServers) Len() int { return len(p) }
func (p HttpServers) Less(i, j int) bool { return p[i].CWeight > p[j].CWeight }
func (p HttpServers) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func NewHttpServer(host string, weight int) *HttpServer {
return &HttpServer{Host: host, Weight: weight, CWeight: 0}
}
type LoadBalance struct { //負載均衡類
Servers HttpServers
CurIndex int //指向當前訪問的服務器
}
func NewLoadBalance() *LoadBalance {
return &LoadBalance{Servers: make([]*HttpServer, 0)}
}
func (this *LoadBalance) AddServer(server *HttpServer) {
this.Servers = append(this.Servers, server)
}
func (this *LoadBalance) SelectForRand() *HttpServer {
rand.Seed(time.Now().UnixNano())
index := rand.Intn(len(this.Servers))
fmt.Println(index)
return this.Servers[index]
}
func (this *LoadBalance) SelectByIpHash(ip string) *HttpServer {
index := int(crc32.ChecksumIEEE([]byte(ip))) % len(this.Servers) //通過取余永遠index都不會大於this.servers的長度
return this.Servers[index]
}
func (this *LoadBalance) SelectByWeight(ip string) *HttpServer { //加權隨機算法
rand.Seed(time.Now().UnixNano())
index := rand.Intn(len(ServerIndices)) //這里因為權重表為15個1和5個0組成,所以產生0到19的隨機數
fmt.Println(this.Servers[ServerIndices[index]])
return this.Servers[ServerIndices[index]] //通過隨機數的索引獲得服務器索引進而獲得地址
}
func (this *LoadBalance) SelectByWeightBetter(ip string) *HttpServer {
rand.Seed(time.Now().UnixNano())
sumList := make([]int, len(this.Servers))
sum := 0
for i := 0; i < len(this.Servers); i++ {
sum += this.Servers[i].Weight
sumList[i] = sum
}
_rand := rand.Intn(sum)
for index, value := range sumList {
if _rand < value {
return this.Servers[index]
}
}
return this.Servers[0]
}
func (this *LoadBalance) RoundRobin() *HttpServer {
server := this.Servers[this.CurIndex]
//this.CurIndex ++
//if this.CurIndex >= len(this.Servers) {
// this.CurIndex = 0
//}
this.CurIndex = (this.CurIndex + 1) % len(this.Servers)
if server.Status == "Down" { //如果當前節點宕機了,則遞歸查找可以用的服務器
return this.RoundRobin()
}
return server
}
func (this *LoadBalance) RoundRobinByWeight() *HttpServer {
server := this.Servers[ServerIndices[this.CurIndex]]
this.CurIndex = (this.CurIndex + 1) % len(ServerIndices)
return server
}
func (this *LoadBalance) RoundRobinByWeight2() *HttpServer { //加權輪詢 ,使用區間算法
server := this.Servers[0]
sum := 0
//3:1:1
for i := 0; i < len(this.Servers); i++ {
sum += this.Servers[i].Weight //第一次是3 [0,3) [3,4) [4,5)
if this.CurIndex < sum {
server = this.Servers[i]
if this.CurIndex == sum-1 && i != len(this.Servers)-1 {
this.CurIndex++
} else {
this.CurIndex = (this.CurIndex + 1) % sum //這里是重要的一步
}
fmt.Println(this.CurIndex)
break
}
}
return server
}
func (this *LoadBalance) RoundRobinByWeight3() *HttpServer { //平滑加權輪詢
for _, s := range this.Servers {
s.CWeight = s.CWeight + s.Weight
}
sort.Sort(this.Servers)
max := this.Servers[0]
max.CWeight = max.CWeight - SumWeight
return max
}
var LB *LoadBalance
var ServerIndices []int
var SumWeight int
func checkServers(servers HttpServers) {
t:= time.NewTicker(time.Second*3)
check:=NewHtttpChecker(servers)
for {
select{
case <- t.C:
check.Check(time.Second*2)
for _,s:=range servers{
fmt.Println(s.Host,s.Status,s.FailCount)
}
fmt.Println("---------------------------------")
}
}
}
func init() {
LB = NewLoadBalance()
LB.AddServer(NewHttpServer("http://localhost:12346", 3)) //web1
LB.AddServer(NewHttpServer("http://localhost:12347", 1)) //web2
LB.AddServer(NewHttpServer("http://localhost:12348", 1)) //web2
for index, server := range LB.Servers {
if server.Weight > 0 {
for i := 0; i < server.Weight; i++ {
ServerIndices = append(ServerIndices, index)
}
}
SumWeight = SumWeight + server.Weight //計算加權總和
}
go checkServers(LB.Servers)
//fmt.Println(ServerIndices)
}
package util
import (
"net/http"
"time"
)
type HttpChecker struct {
Servers HttpServers
FailMax int
RecovCount int //連續成功到達這個值,就會被標識為UP
}
func NewHtttpChecker(servers HttpServers) *HttpChecker {
return &HttpChecker{Servers: servers, FailMax: 6, RecovCount: 3}
}
func (this *HttpChecker) Check(timeout time.Duration) {
client := http.Client{}
for _, server := range this.Servers {
res, err := client.Head(server.Host)
if res != nil {
defer res.Body.Close()
}
if err != nil { //宕機了
this.Fail(server)
continue
}
if res.StatusCode >= 200 && res.StatusCode < 400 {
this.Success(server)
} else {
this.Fail(server)
}
}
}
func (this *HttpChecker) Fail(server *HttpServer) {
if server.FailCount >= this.FailMax { //超過閾值
server.Status = "DOWN"
} else {
server.FailCount++
}
server.SuccessCount = 0
}
func (this *HttpChecker) Success(server *HttpServer) {
if server.FailCount > 0 {
server.FailCount--
server.SuccessCount++
if server.SuccessCount == this.RecovCount {
server.FailCount = 0
server.Status = "UP"
server.SuccessCount = 0
}
} else {
server.Status = "UP"
}
}