Golang負(fù)載均衡和保活設(shè)計(jì)原理示例探究
引言
通過(guò)本項(xiàng)目可以學(xué)到什么?
負(fù)載均衡算法代碼實(shí)現(xiàn)
調(diào)度器如何維護(hù)節(jié)點(diǎn)Live方案(參考)
loadbalance 負(fù)載均衡算法
隨機(jī)算法,輪詢算法,加權(quán)輪詢算法
隨機(jī)算法
// 隨機(jī)
type Rand struct {
addrs []string
}
func (r *Rand) Add(param []string) error {
iflen(param) != 1 {
return ErrParam
}
r.addrs = append(r.addrs, param[0])
returnnil
}
// 隨機(jī)種子,每次隨機(jī)返回一個(gè)
func (r *Rand) Get() (string, error) {
iflen(r.addrs) == 0 {
return"", ErrNoAddr
}
rand.Seed(time.Now().UnixNano())
idx := rand.Intn(len(r.addrs))
return r.addrs[idx], nil
}
輪詢算法
// 輪詢
type RoundRobin struct {
addrs []string
curIdx int
}
func (r *RoundRobin) Add(param []string) error {
iflen(param) != 1 {
return ErrParam
}
r.addrs = append(r.addrs, param[0])
returnnil
}
func (r *RoundRobin) Get() (string, error) {
iflen(r.addrs) == 0 || r.curIdx >= len(r.addrs) {
return"", ErrNoAddr
}
addr := r.addrs[r.curIdx]
r.curIdx = (r.curIdx + 1) % len(r.addrs) // 對(duì)curIdx每次+1
return addr, nil
}
加權(quán)輪詢算法
實(shí)現(xiàn)原理
對(duì)節(jié)點(diǎn)設(shè)置權(quán)重,權(quán)重越大被選中次數(shù)越高,節(jié)點(diǎn)被選中的次數(shù)≈(本節(jié)點(diǎn)權(quán)重/全部權(quán)重) * 總次數(shù)。
舉例說(shuō)明:
節(jié)點(diǎn)權(quán)重為:weight [a=1,b=2,c=5]
節(jié)點(diǎn)當(dāng)前權(quán)重為curWeight:初始值為[a=0,b=0,c=0],變化規(guī)則為:curWeight + weight
全部權(quán)重sumWeight: 代表所有節(jié)點(diǎn)初始權(quán)重之和 1+2+5=8
第一次請(qǐng)求:
curWeight 為 [a=0+1,b=0+2,c=0+5] ,選中最大的c做為本次輸出,之后c節(jié)點(diǎn)的權(quán)重需要減去sumWeight,調(diào)整后 [a=1,b=2,c=5-8] 也就是 [a=1,b=2,c=-3]
第二次請(qǐng)求:
curWeight 為 [a=1+1,b=2+2,c=-3+5] 結(jié)果為 [a=2,b=4,c=2],選中最大的b作為本次輸出,之后節(jié)點(diǎn)權(quán)重變更為 [a=2,b=-4,c=2]
第三次請(qǐng)求:
curWeight 為 [a=2+1,b=-4+2,c=2+5] 結(jié)果為 [a=3,b=-2,c=7],又輪到c(權(quán)重大的好處體現(xiàn)出來(lái)了),之后節(jié)點(diǎn)權(quán)重變更為 [a=3,b=-2,c=-1]
第四次請(qǐng)求:
[a=3,b=-2,c=-1] 加權(quán)后[a=4,b=0,c=4],a與c相等,優(yōu)先選前者輸出a
type WeigthRoundRobin struct {
weightAddrs []*weightAddr
}
type weightAddr struct {
addr string// 地址
weight int// 權(quán)重
curWeight int// 計(jì)算使用
}
func (w *WeigthRoundRobin) Add(param []string) error {
iflen(param) != 2 {
return ErrParam
}
weight, err := strconv.Atoi(param[1])
if err != nil {
return err
}
w.weightAddrs = append(w.weightAddrs, &weightAddr{
addr: param[0],
weight: weight,
curWeight: 0,
})
returnnil
}
func (w *WeigthRoundRobin) Get() (string, error) {
iflen(w.weightAddrs) == 0 {
return"", ErrNoAddr
}
maxWeight := math.MinInt
idx := 0
sumWeight := 0// 權(quán)重總和
for k, weightAddr := range w.weightAddrs {
sumWeight += weightAddr.weight // 權(quán)重總和
weightAddr.curWeight += weightAddr.weight // 加上權(quán)重
if weightAddr.curWeight > maxWeight { // 記錄最大值
maxWeight = weightAddr.curWeight
idx = k
}
}
w.weightAddrs[idx].curWeight -= sumWeight // 減去權(quán)重總和
return w.weightAddrs[idx].addr, nil// 返回最大權(quán)重的結(jié)果
}有效服務(wù)維護(hù)方案(參考)
方案一:heart【心跳】
前置配置:
1.啟動(dòng)docker
docker-compose -f docker-compose-env.yml up -d zookeeper docker-compose -f docker-compose-env.yml up -d kafka
2.修改本地hostsvim /etc/hosts增加 127.0.0.1 kafka

3.kafka常用腳本【可選】 [進(jìn)入docker中*.sh位于 /opt/kafka/bin目錄](méi)
# 創(chuàng)建topic ./kafka-topics.sh --create --zookeeper zookeeper:2181 --replication-factor 1 --partitions 3 --topic easy_topic # 列出所有topic ./kafka-topics.sh --list --zookeeper zookeeper:2181 # 從頭開(kāi)始消費(fèi) ./kafka-console-consumer.sh --bootstrap-server kafka:9092 --from-beginning --topic easy_topic # 消費(fèi)組【從最新的消息消費(fèi)】 ./kafka-console-consumer.sh --bootstrap-server kafka:9092 --consumer-property group.id=testGroup --topic easy_topic # 從最新開(kāi)始消費(fèi) ./kafka-console-consumer.sh --bootstrap-server kafka:9092 --topic easy_topic # 生產(chǎn)消息 ./kafka-console-producer.sh --broker-list kafka:9092 --topic easy_topic
調(diào)度器訂閱kafka消息,同時(shí)維護(hù)一個(gè)有效服務(wù),然后按照負(fù)載均衡策略分發(fā)請(qǐng)求。

func main() {
// 服務(wù)負(fù)責(zé)發(fā)送心跳
go heart.RunHeartBeat()
// 調(diào)度器負(fù)責(zé)接收心跳
go heart.ListenHeartbeat()
// 利用負(fù)載均衡獲取
lb := loadbalance.LoadBalanceFactory(loadbalance.BalanceTypeRand)
gofunc() {
for {
time.Sleep(5 * time.Second)
for _, v := range heart.GetAddrList() {
lb.Add([]string{v})
}
fmt.Println(lb.Get())
}
}()
sigusr1 := make(chan os.Signal, 1)
signal.Notify(sigusr1, syscall.SIGTERM)
<-sigusr1
}
方案二:health【健康檢查】
通過(guò)HTTP請(qǐng)求服務(wù),維護(hù)活躍節(jié)點(diǎn)

func main() {
health.AddAddr("https://www.sina.com.cn/", "https://www.baidu.com/", "http://www.aajklsdfjklsd")
go health.HealthCheck()
time.Sleep(50 * time.Second)
alist := health.GetAliveAddrList()
for i := 0; i < len(alist); i++ {
fmt.Println(alist[i])
}
var block = make(chanbool)
<-block
}
以上就是Golang負(fù)載均衡和?;钤O(shè)計(jì)原理示例探究的詳細(xì)內(nèi)容,更多關(guān)于Golang負(fù)載均衡保活設(shè)計(jì)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
解讀unsafe.Pointer和uintptr的區(qū)別
這篇文章主要介紹了解讀unsafe.Pointer和uintptr的區(qū)別及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02
go語(yǔ)言數(shù)據(jù)結(jié)構(gòu)之前綴樹(shù)Trie
這篇文章主要介紹了go語(yǔ)言數(shù)據(jù)結(jié)構(gòu)之前綴樹(shù)Trie,文章圍繞主題展開(kāi)詳細(xì)內(nèi)容介紹,具有一定得參考價(jià)值,需要的小伙伴可以參考一下2022-05-05
一文掌握Go語(yǔ)言并發(fā)編程必備的Mutex互斥鎖
Go 語(yǔ)言提供了 sync 包,其中包括 Mutex 互斥鎖、RWMutex 讀寫(xiě)鎖等同步機(jī)制,本篇博客將著重介紹 Mutex 互斥鎖的基本原理,需要的可以參考一下2023-04-04
golang 監(jiān)聽(tīng)服務(wù)的信號(hào),實(shí)現(xiàn)平滑啟動(dòng),linux信號(hào)說(shuō)明詳解
這篇文章主要介紹了golang 監(jiān)聽(tīng)服務(wù)的信號(hào),實(shí)現(xiàn)平滑啟動(dòng),linux信號(hào)說(shuō)明詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-05-05
Golang 錯(cuò)誤捕獲Panic與Recover的使用
對(duì)于Go語(yǔ)言的錯(cuò)誤是通過(guò)返回值的方式,本文主要介紹了Golang 錯(cuò)誤捕獲Panic與Recover的使用,文中根據(jù)實(shí)例編碼詳細(xì)介紹的十分詳盡,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03
golang簡(jiǎn)易令牌桶算法實(shí)現(xiàn)代碼
這篇文章主要介紹了golang簡(jiǎn)易令牌桶算法實(shí)現(xiàn)代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
Go切片擴(kuò)容機(jī)制詳細(xì)說(shuō)明和舉例
Go 語(yǔ)言中的切片是一種動(dòng)態(tài)數(shù)組,它可以自動(dòng)擴(kuò)容和縮容以適應(yīng)不同的數(shù)據(jù)量,這篇文章主要給大家介紹了關(guān)于Go切片擴(kuò)容機(jī)制詳細(xì)說(shuō)明和舉例的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-03-03
Golang?WorkerPool線程池并發(fā)模式示例詳解
這篇文章主要為大家介紹了Golang?WorkerPool線程池并發(fā)模式示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08

