日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

负载均衡算法 — 轮询

發布時間:2024/4/11 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 负载均衡算法 — 轮询 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

負載均衡算法 — 輪詢


目錄

  • 概述
  • 簡單輪詢
  • 加權輪詢
  • 平滑加權輪詢

  • 1. 概述

  • 在分布式系統中,為了實現負載均衡,必然會涉及到負載調度算法,如 Nginx 和 RPC 服務發現等場景。常見的負載均衡算法有 輪詢、源地址 Hash、最少連接數,而 輪詢 是最簡單且應用最廣的算法。
  • 3 種常見的輪詢調度算法,分別為 簡單輪詢、加權輪詢、平滑加權輪詢。下面將用如下 4 個服務,來詳細說明輪詢調度過程。
  • 服務實例權重值
    192.168.10.1:22021
    192.168.10.2:22022
    192.168.10.3:22023
    192.168.10.4:22024

    2. 簡單輪詢

  • 簡單輪詢是輪詢算法中最簡單的一種,但由于它不支持配置負載,所以應用較少。
  • 1. 算法描述

  • 假設有 N 臺實例 S = {S1, S2, …, Sn},指示變量 currentPos 表示當前選擇的實例 ID,初始化為 -1。算法可以描述為:
  • 調度到下一個實例;
  • 若所有實例已被 調度 過一次,則從頭開始調度;
  • 每次調度重復步驟 1、2;
  • 請求currentPos選中的實例
    10192.168.10.1:2202
    21192.168.10.2:2202
    32192.168.10.3:2202
    43192.168.10.4:2202
    50192.168.10.1:2202

    2. 代碼實現

    type Round struct {curIndex intrss []string }func (r *Round) Add(params ...string) error {if len(params) == 0 {return errors.New("至少需要1個參數")}r.rss = append(r.rss, params...)return nil } func (r *Round) Next() (string, error) {if len(r.rss) == 0 {return "", errors.New("不存在參數")}curElement := r.rss[r.curIndex]r.curIndex = (r.curIndex + 1) % len(r.rss)return curElement, nil }

    3. 優缺點

  • 在實際應用中,同一個服務會部署到不同的硬件環境,會出現性能不同的情況。若直接使用簡單輪詢調度算法,給每個服務實例相同的負載,那么,必然會出現資源浪費的情況。因此為了避免這種情況,一些人就提出了下面的 加權輪詢 算法。

  • 2. 加權輪詢

  • 加權輪詢算法引入了“權”值,改進了簡單輪詢算法,可以根據硬件性能配置實例負載的權重,從而達到資源的合理利用。
  • 1. 算法描述

  • 假設有 N 臺實例 S = {S1, S2, …, Sn},權重 W = {W1, W2, …, Wn},指示變量 currentPos 表示當前選擇的實例 ID,初始化為 -1;變量 currentWeight 表示當前權重,初始值為 max(S);max(S) 表示 N 臺實例的最大權重值,gcd(S) 表示 N 臺實例權重的最大公約數。

  • 算法可以描述為:

  • 從上一次調度實例起,遍歷后面的每個實例;
  • 若所有實例已被遍歷過一次,則減小 currentWeight 為 currentWeight - gcd(S),并從頭開始遍歷;若 currentWeight 小于等于 0,則重置為 max(S);
  • 直到 遍歷的實例的權重大于等于 currentWeight 時結束,此時實例為需調度的實例;
  • 每次調度重復步驟 1、2、3;
  • 例如,上述 4 個服務,最大權重 max(S) 為 4,最大公約數 gcd(S) 為 1。其調度過程如下:

  • 請求currentPoscurrentWeight選中的實例
    134192.168.10.4:2202
    223192.168.10.3:2202
    333192.168.10.4:2202
    412192.168.10.2:2202
    ….
    921192.168.10.3:2202
    1034192.168.10.4:2202

    2. 代碼實現

    var slaveDns = map[int]map[string]interface{}{0: {"connectstring": "root@tcp(172.16.0.164:3306)/shiqu_tools?charset=utf8", "weight": 2},1: {"connectstring": "root@tcp(172.16.0.165:3306)/shiqu_tools?charset=utf8", "weight": 4},2: {"connectstring": "root@tcp(172.16.0.166:3306)/shiqu_tools?charset=utf8", "weight": 8}, }var last int = -1 //表示上一次選擇的服務器 var cw int = 0 //表示當前調度的權值 var gcd int = 2 //當前所有權重的最大公約數 比如 2,4,8 的最大公約數為:2 var devCount int = 2 //當前機器數func getDns() string {for {last = (last + 1) % len(slaveDns)if last == 0 {cw = cw - gcdif cw <= 0 {cw = getMaxWeight()if cw == 0 {return ""}}}if weight, _ := slaveDns[last]["weight"].(int); weight >= cw {return slaveDns[last]["connectstring"].(string)}} }func getMaxWeight() int {max := 0for _, v := range slaveDns {if weight, _ := v["weight"].(int); weight >= max {max = weight}}return max }func Add(addr string, weight int) {tmap := make(map[string]interface{})tmap["connectstring"] = addrtmap["weight"] = weightslaveDns[devCount] = tmapdevCount = devCount + 1if devCount == 0 {gcd = weight} else {gcd = Gcd(gcd, weight)} }func Gcd(gcd int, weight int) int {for weight != 0 {gcd, weight = weight, gcd%weight}return gcd }

    3. 優缺點

  • 加權輪詢 算法雖然通過配置實例權重,解決了 簡單輪詢 的資源利用問題,但是它還是存在一個比較明顯的 缺陷。
  • 例如:服務實例 S = {a, b, c},權重 W = {5, 1, 1},使用加權輪詢調度生成的實例序列為 {a, a, a, a, a, b, c},那么就會存在連續 5 個請求都被調度到實例 a。而實際中,這種不均勻的負載是不被允許的,因為連續請求會突然加重實例 a 的負載,可能會導致嚴重的事故。
  • 為了解決加權輪詢調度不均勻的缺陷,提出了 平滑加權輪詢 調度算法,它會生成的更均勻的調度序列 {a, a, b, a, c, a, a}。

  • 4. 平滑加權輪詢

    1. 算法描述

  • 假設有 N 臺實例 S = {S1, S2, …, Sn},配置權重 W = {W1, W2, …, Wn},有效權重 CW = {CW1, CW2, …, CWn}。每個實例 i 除了存在一個配置權重 Wi 外,還存在一個當前有效權重 CWi,且 CWi 初始化為 Wi;指示變量 currentPos 表示當前選擇的實例 ID,初始化為 -1;所有實例的配置權重和為 weightSum;

  • 那么,調度算法可以描述為:

  • 初始每個實例 i 的 當前有效權重 CWi 為 配置權重 Wi,并求得配置權重和 weightSum;
  • 選出 當前有效權重 最大 的實例,將 當前有效權重 CWi 減去所有實例的 權重和 weightSum,且變量 currentPos 指向此位置;
  • 將每個實例 i 的 當前有效權重 CWi 都加上 配置權重 Wi;
  • 取到變量 currentPos 指向的實例;
  • 每次調度重復上述步驟 2、3、4;
  • 上述 3 個服務,配置權重和 weightSum 為 7,其調度過程如下:

  • 請求選中前的當前權重currentPos選中的實例選中后的當前權重
    1{5, 1, 1}0192.168.10.1:2202{-2, 1, 1}
    2{3, 2, 2}0192.168.10.1:2202{-4, 2, 2}
    3{1, 3, 3}1192.168.10.2:2202{1, -4, 3}
    4{6, -3, 4}0192.168.10.1:2202{-1, -3, 4}
    5{4, -2, 5}2192.168.10.3:2202{4, -2, -2}
    6{9, -1, -1}0192.168.10.1:2202{2, -1, -1}
    7{7, 0, 0}0192.168.10.1:2202{0, 0, 0}
    8{5, 1, 1}0192.168.10.1:2202{-2, 1, 1}
  • 此輪詢調度算法思路首先被 Nginx 開發者提出
  • 2. 代碼實現

    type LoadBalance interface {//選擇一個后端Server//參數remove是需要排除選擇的后端ServerSelect(remove []string) *Server//更新可用Server列表UpdateServers(servers []*Server) }type Server struct {//主機地址Host string//主機名Name stringWeight int//主機是否在線Online bool }type Weighted struct {Server *ServerWeight intCurrentWeight int //當前機器權重EffectiveWeight int //機器權重 }func (this *Weighted) String() string {return fmt.Sprintf("[%s][%d]", this.Server.Host, this.Weight) }type LoadBalanceWeightedRoundRobin struct {servers []*Serverweighted []*Weighted }func NewLoadBalanceWeightedRoundRobin(servers []*Server) *LoadBalanceWeightedRoundRobin {new := &LoadBalanceWeightedRoundRobin{}new.UpdateServers(servers)return new }func (this *LoadBalanceWeightedRoundRobin) UpdateServers(servers []*Server) {if len(this.servers) == len(servers) {for _, new := range servers {isEqual := falsefor _, old := range this.servers {if new.Host == old.Host && new.Weight == old.Weight && new.Online == old.Online {isEqual = truebreak}}if isEqual == false {goto build}}return}build:log.Println("clients change")log.Println(this.servers)log.Println(servers)weighted := make([]*Weighted, 0)for _, v := range servers {if v.Online == true {w := &Weighted{Server: v,Weight: v.Weight,CurrentWeight: 0,EffectiveWeight: v.Weight,}weighted = append(weighted, w)}}this.weighted = weightedthis.servers = serverslog.Printf("weighted[%v]", this.weighted) }func (this *LoadBalanceWeightedRoundRobin) Select(remove []string) *Server {if len(this.weighted) == 0 {return nil}w := this.nextWeighted(this.weighted, remove)if w == nil {return nil}return w.Server }func (this *LoadBalanceWeightedRoundRobin) nextWeighted(servers []*Weighted, remove []string) (best *Weighted) {total := 0for i := 0; i < len(servers); i++ {w := servers[i]if w == nil {continue}isFind := falsefor _, v := range remove {if v == w.Server.Host {isFind = true}}if isFind == true {continue}w.CurrentWeight += w.EffectiveWeighttotal += w.EffectiveWeightif w.EffectiveWeight < w.Weight {w.EffectiveWeight++}if best == nil || w.CurrentWeight > best.CurrentWeight {best = w}}if best == nil {return nil}best.CurrentWeight -= totalreturn best }func (this *LoadBalanceWeightedRoundRobin) String() string {return "WeightedRoundRobin" }

    3. 小結

  • 盡管,平滑加權輪詢算法改善了加權輪詢算法調度的缺陷,即調度序列分散的不均勻,避免了實例負載突然加重的可能,但是仍然不能動態感知每個實例的負載。
  • 若由于實例權重配置不合理,或者一些其他原因加重系統負載的情況,平滑加權輪詢都無法實現每個實例的負載均衡,這時就需要 有狀態 的調度算法來完成。
  • 總結

    以上是生活随笔為你收集整理的负载均衡算法 — 轮询的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。