Nginx負荷等化アルゴリズム--重み付けポーリング(golang実装)

7378 ワード

Nginx負荷等化アルゴリズム--重み付けポーリング
最近getwayに関する資料を見ていると、Nginxの負荷等化に関するアルゴリズムが少し多いのですが、少し乱れているので整理しておきます...間違ったところがあれば指摘してください.
一、Nginx負荷等化ポーリング(round-robin)
重み付けポーリングを言う前に簡単にポーリングについてお話しします
1.nginxでの構成
upstream cluster {
    server 192.168.0.14;
    server 192.168.0.15;
}

location / {
   proxy_set_header X-Real-IP $remote_addr;               //    IP
   proxy_pass http://cluster;                           //    cluster 
    
}

2.簡単な紹介
ポーリングは負荷等化における比較的基礎的なアルゴリズムとして機能し,彼の実装には追加のパラメータを構成する必要はない.簡単な理解:配置ファイルの中で全部でN台のサーバーを配置して、ポーリングのアルゴリズムはサービスのノードのリストを遍歴して、そしてノードの順序によって毎回1台のサーバーを選んで要求を処理して、すべてのノードが遍歴した後に、再び始めます
3.特徴
ポーリングアルゴリズムでは,サーバ1台あたりの処理要求の数がほぼ横ばいであり,要求時間ごとに1つずつ割り当てられるため,クラスタサーバの性能が近い場合にのみ適用でき,平均割り当てはサーバ1台あたりのベアラ量をほぼ横ばいにする.しかし、クラスタ・サーバのパフォーマンスがバラツキがある場合、このようなアルゴリズムは、リソースの割り当てが不合理になり、一部のリクエストがブロックされ、一部のサーバ・リソースが浪費されます.上記の問題を解決するために、ポーリングアルゴリズムをアップグレードし、重み付けポーリングアルゴリズムを導入し、クラスタ内のパフォーマンスの差が大きいサーバにもリソースを合理的に割り当てることができます.資源をできるだけ最大化し合理的に利用する
4.実装(ここではgolangシミュレーションを用いて実装)

type RoundRobinBalance struct {
    curIndex int
    rss []string
}

/**
 * @Author: yang
 * @Description:    
 * @Date: 2021/4/7 15:36
 */
func (r *RoundRobinBalance) Add (params ...string) error{
    if len(params) == 0 {
        return errors.New("params len 1 at least")
    }
    addr := params[0]
    r.rss = append(r.rss, addr)

    return nil
}

/**
 * @Author: yang
 * @Description:      
 * @Date: 2021/4/7 15:36
 */
func (r *RoundRobinBalance) Next () string {
    if len(r.rss) == 0 {
        return ""
    }
    lens := len(r.rss)
    if r.curIndex >= lens {
        r.curIndex = 0
    }
    curAdd := r.rss[r.curIndex ]
    r.curIndex = (r.curIndex + 1) % lens
    return curAdd
}

5.テスト
簡単に呼び出して結果を見る
/**
 * @Author: yang
 * @Description:  
 * @Date: 2021/4/7 15:36
 */
func main(){
    rb := new(RoundRobinBalance)
    rb.Add("127.0.0.1:80")
    rb.Add("127.0.0.1:81")
    rb.Add("127.0.0.1:82")
    rb.Add("127.0.0.1:83")

    fmt.Println(rb.Next())
    fmt.Println(rb.Next())
    fmt.Println(rb.Next())
    fmt.Println(rb.Next())
    fmt.Println(rb.Next())
    fmt.Println(rb.Next())
}

go run main.go 

127.0.0.1:80
127.0.0.1:81
127.0.0.1:82
127.0.0.1:83
127.0.0.1:80
127.0.0.1:81

二、Nginx負荷等化の重み付けポーリング(weighted-round-robin)
テーマに入る
1.nginx構成
http {  
    upstream cluster {  
        server 192.168.1.2 weight=5;  
        server 192.168.1.3 weight=3;  
        server 192.168.1.4 weight=1;  
    }  

location / {
       proxy_set_header X-Real-IP $remote_addr;               //    IP
       proxy_pass http://cluster;                           //    cluster 
   }
   

2.重み付けアルゴリズムの概要-特徴
サーバの構成、導入されたアプリケーションの数、ネットワークの状況などによって、サーバの処理能力が異なるため、簡単なポーリングアルゴリズムは適用されなくなり、重み付けポーリングアルゴリズムが導入されました.サーバの異なる処理能力に応じて、サーバごとに異なる重み値を割り当てます.異なる重み値に基づいて異なるサーバを対応するサーバに割り当てる.
リクエスト数が多い場合、各サービス処理リクエストの数の比は重みの比になります.
3.アルゴリズムの説明
Nginx重み付けポーリングアルゴリズムでは、各ノードに3つの重み付け変数があります.
  • Weight:構成の重み、構成ファイルに基づいて各サーバノードの重み
  • を初期化する
  • currentWeight:ノードの現在の重みは、初期化時に構成された重みであり、その後、
  • が変更されます.
  • effectiveWeight:有効な重み、初期値はweightで、通信中にノード異常が発見された場合-1、その後再び本ノードを選択し、呼び出しに成功した場合+1、weightに戻るまで.このパラメータは降格に使用できます.あるいはあなたの設定した権限修正です.


  • Nginx重み付けポーリングアルゴリズムの論理実装
  • はすべてのノードをポーリングし、現在の状態におけるすべてのノードのeffectiveWeightの和をtotalWeightとして計算する.
  • 各ノードのcurrentWeight、currentWeight=currentWeight+effectiveWeightを更新します.すべてのノードcurrentWeightの中で最大のノードを選択します.
  • 選択中のノードは再びcurrentWeightを更新し、currentWeight=currentWeight-totalWeight;

  • 4.簡単な例
    注意:インプリメンテーションでは健康診断は考慮されません.すなわち、すべてのノードが100%利用可能であるため、
    effectiveWeight=
    weight
    仮定:現在、3つのノード{A,B,C}のそれぞれの重みは、{4,2,1}である.リクエスト7回
    N回目のリクエスト
    請求前currentWeight
    選択したノード
    要求後currentWeight
    1
    [serverA=4, serverB=2, serverC=1]
    serverA
    [serverA=1, serverB=4, serverC=2]
    2
    [serverA=1, serverB=4, serverC=2]
    serverB
    [serverA=5, serverB=-1, serverC=3]
    3
    [serverA=5, serverB=-1, serverC=3]
    serverA
    [serverA=2, serverB=1, serverC=4]
    4
    [serverA=2, serverB=1, serverC=4]
    serverA
    [serverA=-1, serverB=3, serverC=5]
    5
    [serverA=-1, serverB=3, serverC=5]
    serverC
    [serverA=3, serverB=5, serverC=-1]
    6
    [serverA=3, serverB=5, serverC=-1]
    serverA
    [serverA=0, serverB=7, serverC=0]
    7
    [serverA=0, serverB=7, serverC=0]
    serverB
    [serverA=4, serverB=2, serverC=1]
    totaoWeight = 4 + 2 + 1 = 7
    最初のリクエスト:serverA=4+4=8、serverB=2+2=4、serverC=1+1=2.一番大きいのはserverAです.だからserverAを選択します.次にserverA=8-7=1になります.結果:serverA=1,serverB=4,serverC=2
    2回目のリクエスト:serverA=1+4=5;serverB = 4 + 2 = 6 ; serverC = 2 + 1 = 3;一番大きいのはserverBです.だからserverBを選択します.次にserverB=6-7=1になります.結果:serverA=5,serverB=-1,serverC=3
    このように推す...
    5.コード実装
    golangで上記の論理を実現します.
    
    type WeightRoundRobinBalance struct {
        curIndex int
        rss []*WeightNode
    }
    
    type WeightNode struct {
        weight int //      ,                      
        currentWeight int //      ,     
        effectiveWeight int //    ,    weight,            , -1 ,         ,       +1,     weight 。       ,      ,     。
        addr string //    addr
    }
    
    /**
     * @Author: yang
     * @Description:    
     * @Date: 2021/4/7 15:36
     */
    func (r *WeightRoundRobinBalance) Add (params ...string) error{
        if len(params) != 2{
            return errors.New("params len need 2")
        }
        // @Todo    
        addr := params[0]
        parInt, err  := strconv.ParseInt(params[1], 10, 64)
        if err != nil {
            return err
        }
        node := &WeightNode{
            weight: int(parInt),
            effectiveWeight: int(parInt),  //          =      
            currentWeight: int(parInt), //          =      
            addr: addr,
        }
        r.rss = append(r.rss, node)
        return nil
    }
    
    /**
     * @Author: yang
     * @Description:      
     * @Date: 2021/4/7 15:36
     */
    func (r *WeightRoundRobinBalance) Next () string {
        // @Todo     
        if len(r.rss) == 0 {
            return ""
        }
    
        totalWeight := 0
        var maxWeightNode *WeightNode
        for key , node  := range r.rss {
            // @Todo             effectiveWeight  totalWeight
            totalWeight += node.effectiveWeight
    
            // @Todo   currentWeight
            node.currentWeight += node.effectiveWeight
    
            // @Todo        
            if maxWeightNode == nil ||  maxWeightNode.currentWeight < node.currentWeight {
                maxWeightNode = node
                r.curIndex = key
            }
        }
    
        // @Todo        currentWeight
        maxWeightNode.currentWeight -= totalWeight
    
        // @Todo   addr
        return maxWeightNode.addr
    }
    

    6.テスト検証
    /**
     * @Author: yang
     * @Description:  
     * @Date: 2021/4/7 15:36
     */
    func main(){
        rb := new(WeightRoundRobinBalance)
        rb.Add("127.0.0.1:80", "4")
        rb.Add("127.0.0.1:81", "2")
        rb.Add("127.0.0.1:82", "1")
    
        fmt.Println(rb.Next())
        fmt.Println(rb.Next())
        fmt.Println(rb.Next())
        fmt.Println(rb.Next())
        fmt.Println(rb.Next())
        fmt.Println(rb.Next())
        fmt.Println(rb.Next())
    }
    

    次の結果を実行します.
    run main.go
    
    127.0.0.1:80
    127.0.0.1:81
    127.0.0.1:80
    127.0.0.1:80
    127.0.0.1:82
    127.0.0.1:80
    127.0.0.1:81