IPVS的調度算法


LVS官方網站上的調度算法

IPVS在內核中的負載均衡調度是以連接為粒度的。在HTTP協議(非持久)中,每個對象從WEB服務器上獲取都需要建立一個TCP連接, 同一用戶的不同請求會被調度到不同的服務器上,所以這種細粒度的調度在一定程度上可以避免單個用戶訪問的突發性引起服務器間的負載不平衡。

在內核中的連接調度算法上,IPVS已實現了以下十種調度算法:

  • 輪叫調度(Round-Robin Scheduling)
  • 加權輪叫調度(Weighted Round-Robin Scheduling)
  • 最小連接調度(Least-Connection Scheduling)
  • 加權最小連接調度(Weighted Least-Connection Scheduling)
  • 基於局部性的最少鏈接(Locality-Based Least Connections Scheduling)
  • 帶復制的基於局部性最少鏈接(Locality-Based Least Connections with Replication Scheduling)
  • 目標地址散列調度(Destination Hashing Scheduling)
  • 源地址散列調度(Source Hashing Scheduling)
  • 最短預期延時調度(Shortest Expected Delay Scheduling)
  • 不排隊調度(Never Queue Scheduling)

內核中每種調度算法均被實現為一個內核模塊,在需要時加載

# lsmod | grep ip_vs
ip_vs_wrr               6977  1 
ip_vs_rr                6081  2 
ip_vs                  78209  7 ip_vs_wrr,ip_vs_rr

從上面的示例可以看到,由於目前只用到了rr和wrr兩種調度算法,其他調度算法的內核模塊沒有加載。

輪詢調度算法

輪叫調度(Round Robin Scheduling)算法就是以輪叫的方式依次將請求調度不同的服務器,即每次調度執行i = (i + 1) mod n,並選出第i台服務器。算法的優點是其簡潔性,它無需記錄當前所有連接的狀態,所以它是一種無狀態調度。

在系統實現時,我們引入了一個額外條件,當服務器的權值為零時,表示該服務器不可用而不被調度。這樣做的目的是將服務器切出服務(如屏蔽服務器故障和系統維護),同時與其他加權算法保持一致。

rr算法結構體

1
2
3
4
5
6
7
8
9
10
 static struct ip_vs_scheduler ip_vs_rr_scheduler = {  .name = "rr", /* name */  .refcnt = ATOMIC_INIT(0), // 引用計數  .module = THIS_MODULE, // 模塊  .n_list = LIST_HEAD_INIT(ip_vs_rr_scheduler.n_list),  .init_service = ip_vs_rr_init_svc, // 初始化 即svc->sched_data = &svc->destinations  .update_service = ip_vs_rr_update_svc, // 新rs加入時調用的函數 更新svc->sched_data,  // 實際上就是將其重新指向&svc->destinations  .schedule = ip_vs_rr_schedule, // 調度算法的具體時間,這個每個調度算法最核心的函數 }; 

rr算法的具體實現

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
 /*  * Round-Robin Scheduling (Round-Robin rs選擇調度算法)  */  static struct ip_vs_dest *  ip_vs_rr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) {  struct list_head *p, *q;  struct ip_vs_dest *dest;  IP_VS_DBG(6, "%s(): Scheduling...\n", __func__);  write_lock(&svc->sched_lock);  p = (struct list_head *)svc->sched_data; // sched_data初始化為&svc->destintions(新rs加入到時候也會初始化)  p = p->next;  q = p;  do {  /* skip list head */  if (q == &svc->destinations) {  q = q->next;  continue;  }  dest = list_entry(q, struct ip_vs_dest, n_list);  if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) && // 找到一個weight>0且沒有標記為IP_VS_DEST_F_OVERLOAD的rs  atomic_read(&dest->weight) > 0)  /* HIT */  goto out;  q = q->next;  } while (q != p); //如果p==q時,說明q遍歷一遍沒有找到合適的rs,返回NULL  write_unlock(&svc->sched_lock);  ip_vs_scheduler_err(svc, "no destination available");  return NULL;  out:  svc->sched_data = q; // 更新sched_data為q,下一次調度從q開始  write_unlock(&svc->sched_lock);  IP_VS_DBG_BUF(6, "RR: server %s:%u "  "activeconns %d refcnt %d weight %d\n",  IP_VS_DBG_ADDR(svc->af, &dest->addr), ntohs(dest->port),  atomic_read(&dest->activeconns),  atomic_read(&dest->refcnt), atomic_read(&dest->weight));  return dest; } 

輪叫調度算法假設所有服務器處理性能均相同,不管服務器的當前連接數和響應速度。該算法相對簡單,不適用於服務器組中處理性能不一的情況,而且當請求服務時間變化比較大時,輪叫調度算法容易導致服務器間的負載不平衡。

加權輪詢算法

在rr算法的基礎上引入一個weight,按weight的比例來調度rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
 /*  * Weighted Round-Robin Scheduling  */  static struct ip_vs_dest *  ip_vs_wrr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) // 加權輪詢調度算法  {  struct ip_vs_dest *dest;  struct ip_vs_wrr_mark *mark = svc->sched_data;  struct list_head *p;  IP_VS_DBG(6, "%s(): Scheduling...\n", __func__);  /*  * This loop will always terminate, because mark->cw in (0, max_weight]  * and at least one server has its weight equal to max_weight.  */  write_lock(&svc->sched_lock);  p = mark->cl; // destinations鏈表  while (1) {  if (mark->cl == &svc->destinations) {  /* it is at the head of the destination list */  if (mark->cl == mark->cl->next) { // 沒有rs 返回NULL  /* no dest entry */  ip_vs_scheduler_err(svc,  "no destination available: "  "no destinations present");  dest = NULL;  goto out;  }  mark->cl = svc->destinations.next;  mark->cw -= mark->di; // 更新cw 減去rs weight的gcd  if (mark->cw <= 0) { // 如果cw<=0,則將其改為mw  mark->cw = mark->mw;  /*  * Still zero, which means no available servers.  */  if (mark->cw == 0) { // cw還是0,即mw==0, 說明沒有rs了  mark->cl = &svc->destinations;  ip_vs_scheduler_err(svc,  "no destination available");  dest = NULL;  goto out;  }  }  } else  mark->cl = mark->cl->next; // 不是destinations表頭,直接查找下一個 

最小連接數調度算法

最小連接調度(Least-Connection Scheduling)算法是把新的連接請求分配到當前連接數最小的服務器。最小連接調度是一種動態調度算法,它通過服務器當前所活躍的連接數來估計服務器的負載情況。調度器需要記錄各個服務器已建立連接的數目,當一個請求被調度到某台服務器,其連接數加1;當連接中止或超時,其連接數減一。

在系統實現時,我們也引入當服務器的權值為零時,表示該服務器不可用而不被調度

當各個服務器有相同的處理性能時,最小連接調度算法能把負載變化大的請求分布平滑到各個服務器上,所有處理時間比較長的請求不可能被發送到同一台服務器上。但是,當各個服務器的處理能力不同時,該算法並不理想,因為TCP連接處理請求后會進入TIMEnWAIT狀態,TCP的TIMEWAIT一般為2分鍾,此時連接還占用服務器的資源,所以會出現這樣情形,性能高的服務器已處理所收到的連接,連接處於TIMEWAIT狀態,而性能低的服務器已經忙於處理所收到的連接,還不斷地收到新的連接請求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 /*  * Simply select the server with the least number of  * (activeconns<<5) + inactconns  * Except whose weight is equal to zero.  * If the weight is equal to zero, it means that the server is  * quiesced, the existing connections to the server still get  * served, but no new connection is assigned to the server.  */  list_for_each_entry(dest, &svc->destinations, n_list) {  if ((dest->flags & IP_VS_DEST_F_OVERLOAD) ||  atomic_read(&dest->weight) == 0)  continue;  doh = ip_vs_dest_conn_overhead(dest); // activeconns<<5) + inactconns  if (!least || doh < loh) {  least = dest;  loh = doh;  }  } 

加權最小連接調度

加權最小連接調度(Weighted Least-Connection Scheduling)算法是最小連接調度的超集,各個服務器用相應的權值表示其處理性能。服務器的缺省權值為1,系統管理員可以動態地設置服務器的權值。加權最小連接調度在調度新連接時盡可能使服務器的已建立連接數和其權值成比例。

基於局部性的最少鏈接調度

基於局部性的最少鏈接調度(Locality-Based Least Connections Scheduling,以下簡稱為LBLC)算法是針對請求報文的目標IP地址的負載均衡調度,目前主要用於Cache集群系統,因為在Cache集群中客戶請求報文的目標IP地址是變化的。這里假設任何后端服務器都可以處理任一請求,算法的設計目標是在服務器的負載基本平衡情況下,將相同目標IP地址的請求調度到同一台服務器,來提高各台服務器的訪問局部性和主存Cache命中率,從而整個集群系統的處理能力。

LBLC調度算法先根據請求的目標IP地址找出該目標IP地址最近使用的服務器,若該服務器是可用的且沒有超載,將請求發送到該服務器;若服務器不存在,或者該服務器超載且有服務器處於其一半的工作負載,則用“最少鏈接”的原則選出一個可用的服務器,將請求發送到該服務器。

帶復制的基於局部性最少鏈接調度

帶復制的基於局部性最少鏈接調度(Locality-Based Least Connections with Replication Scheduling,以下簡稱為LBLCR)算法也是針對目標IP地址的負載均衡,目前主要用於Cache集群系統。它與LBLC算法的不同之處是它要維護從一個目標IP地址到一組服務器的映射,而LBLC算法維護從一個目標IP地址到一台服務器的映射。對於一個“熱門”站點的服務請求,一台Cache 服務器可能會忙不過來處理這些請求。這時,LBLC調度算法會從所有的Cache服務器中按“最小連接”原則選出一台Cache服務器,映射該“熱門”站點到這台Cache服務器,很快這台Cache服務器也會超載,就會重復上述過程選出新的Cache服務器。這樣,可能會導致該“熱門”站點的映像會出現在所有的Cache服務器上,降低了Cache服務器的使用效率。LBLCR調度算法將“熱門”站點映射到一組Cache服務器(服務器集合),當該“熱門”站點的請求負載增加時,會增加集合里的Cache服務器,來處理不斷增長的負載;當該“熱門”站點的請求負載降低時,會減少集合里的Cache服務器數目。這樣,該“熱門”站點的映像不太可能出現在所有的Cache服務器上,從而提供Cache集群系統的使用效率。

LBLCR算法先根據請求的目標IP地址找出該目標IP地址對應的服務器組;按“最小連接”原則從該服務器組中選出一台服務器,若服務器沒有超載,將請求發送到該服務器;若服務器超載;則按“最小連接”原則從整個集群中選出一台服務器,將該服務器加入到服務器組中,將請求發送到該服務器。同時,當該服務器組有一段時間沒有被修改,將最忙的服務器從服務器組中刪除,以降低復制的程度。

目標地址散列調度

目標地址散列調度(Destination Hashing Scheduling)算法也是針對目標IP地址的負載均衡,但它是一種靜態映射算法,通過一個散列(Hash)函數將一個目標IP地址映射到一台服務器。

目標地址散列調度算法先根據請求的目標IP地址,作為散列鍵(Hash Key)從靜態分配的散列表找出對應的服務器,若該服務器是可用的且未超載,將請求發送到該服務器,否則返回空。

散列算法為: (dest_ip* 2654435761UL) & HASH_TAB_MASK;

原地址散列調度

地址散列調度(Source Hashing Scheduling)算法正好與目標地址散列調度算法相反,它根據請求的源IP地址,作為散列鍵(Hash Key)從靜態分配的散列表找出對應的服務器,若該服務器是可用的且未超載,將請求發送到該服務器,否則返回空。它采用的散列函數與目標地址散列調度算法的相同。

在實際應用中,源地址散列調度和目標地址散列調度可以結合使用在防火牆集群中,它們可以保證整個系統的唯一出入口。

參考資料

LVS中文網站


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM