作者 | 京東云開發者-紀卓志
什么是負載均衡技術
負載均衡器是一種軟件或硬件設備,它起到了將網絡流量分散到一組服務器的作用,可以防止任何一臺服務器過載。負載均衡算法就是負載均衡器用來在服務器之間分配網絡流量的邏輯(算法是一組預定義的規則),有時候也叫做負載均衡的類型。負載均衡算法的種類非常多,包括從簡單的輪詢負載均衡算法到基于響應狀態信息的自適應負載均衡算法。 負載均衡算法的選擇會影響負載分配機制的有效性,從而影響性能和業務連續性(也就是對外承諾的 SLA),選擇正確的負載均衡算法會對應用程序性能產生重大影響。 本文將會介紹常見的負載均衡算法,并結合主流負載均衡軟件或硬件設備介紹各種負載均衡算法的實現方式。
常見負載均衡算法介紹
Round Robin(輪詢負載均衡算法)
在所有負載均衡算法中,輪詢負載均衡算法是最簡單的、最常用的負載均衡算法。客戶端請求以簡單的輪換方式分發到應用程序服務器上。例如,假設有三臺應用程序服務器:第一個客戶端請求發送到第一臺應用程序服務器,第二個客戶端請求發送到第二臺應用程序服務器,第三個客戶端請求發送到第三臺應用程序服務器,第四個客戶端請求重新從第一臺應用程序服務器開始,依次往復。
輪詢負載均衡適合所有客戶端請求都需要相同的服務器負載,并且所有的服務器實例都具有相同的服務器容量和資源(比如網絡帶寬和存儲)
Weighted Round Robin(加權輪詢負載均衡算法)
加權負載均衡算法與輪詢算法相似,增加了根據每個服務器的相對容量來將請求分散到不同服務器的能力。它適合將傳入的客戶端請求分散到一組具有不同功能或具有不同負載容量的服務器上。服務器集群管理員根據一個標準為每個應用程序服務器分配一個權重,這個標準表示每個服務器對請求的相對處理能力。 例如,如果在其他資源都是無窮多的情況下,假如服務器 #1 的 CPU 核心數是服務器 #2 和服務器 #3 的 CPU 核心數的二倍,那么服務器 #1 的權重更高,而服務器 #2 和 #3 的權重相同(都比 #1 低)。如果我們有 4 個連續的客戶端請求,那么有 2 次請求發送到 #1,另外 2 次請求分別發送到 #2 和 #3。
加權輪詢負載均衡算法描述的是在一段時間內負載的分布情況,不同的加權輪詢負載均衡算法可能會產生不同的選擇序列,不應該對處理下一次負載的服務器進行假設。
Least Connections(最少連接負載均衡算法)
最少連接負載均衡算法又叫做最少等待請求算法(Least Outstanding Request, LOR)。最少連接負載均衡是一種動態負載均衡算法,客戶端請求被分發到在接收到請求時活動連接數最少的應用服務器。在應用服務器具有類似規格的情況下,一臺服務器可能會因為連接數過多而過載(無法接收請求也屬于過載),這個算法考慮了活動連接負載。這種技術適合具有不同連接時間的傳入請求(多機房)以及一組在處理能力和可用資源方面相對相似的服務器。
Weighted Least Connections(加權最少連接負載均衡算法)
加權最少連接建立在最少連接負載均衡算法上,考慮不同的應用程序服務器特性。與加權輪詢負載均衡算法相同,服務器集群管理員根據一個標準為每個應用程序服務器分配一個權重,這個標準表示每個服務器對請求的相對處理能力。負載均衡器根據活動鏈接和分配的服務器權重做出負載平衡決策(例如,使用連接數乘以權重的倒數,選擇值最高的服務器)。
Resource Based(基于資源的負載均衡算法)
基于資源的負載均衡算法又叫做自適應負載均衡算法。基于資源的負載均衡算法根據后端服務器提供的狀態指標來做出決策。這個狀態指標可以由一個運行在服務器上的自定義應用程序(比如 agent),或從基礎設施提供方的開放接口獲取。負載均衡器定期查詢每臺服務器的狀態指標,然后適當地調整服務器的動態權重。 在這種方式下,負載均衡算法實際上是在每臺真實服務器上執行健康檢查。這個算法適用于任何需要來自每臺服務器的詳細健康檢查信息來做出負載均衡決策的情況。 例如:此算法適用于工作負載多變且需要詳細的應用程序性能和狀態來評估服務器運行狀態的任何應用程序(例如 CPU 密集型的最短路徑計算,或其他高性能計算場景)。
Fixed Weighting(固定權重負載均衡算法)
固定權重負載均衡算法允許服務器集群管理員根據他們的標準為每個應用程序服務器分配一個權重,以表示每個服務器的相對流量處理能力。權重最高的應用服務器將接收所有流量。如果權重最高的應用服務器出現故障,所有流量將會被引導到下一個權重最高的應用服務器。此方法適用于單個服務器能夠處理所有預期傳入請求的工作負載,如果當前活動的服務器發生故障,一個或多個 “熱備用” 服務器可以直接用于承擔負載。
Weighted Response Time(加權響應時間負載均衡算法)
加權響應時間負載均衡算法使用應用程序的響應時間來計算服務器權重。響應速度最快的應用程序服務器接收下一個請求。此方法適用于應用程序響應時間是最重要的問題的場景。 當應用程序提供的是對外開放服務時尤為重要,因為對外開放服務都會為合作伙伴提供服務級別協議(Service Level Argument,SLA),而 SLA 中承諾的主要就是服務的可用性與服務的響應時間(TP99、TP999 等)。
Source IP Hash(源地址哈希負載均衡算法)
源地址哈希負載均衡算法使用客戶端請求的源 IP 與目標 IP 地址生成唯一的哈希密鑰,用于將客戶端請求分配給特定的服務器。如果傳輸層會話中斷,可以重新密鑰,因此客戶端請求將會被定向到它之前使用的統一服務器。當客戶端對于每個連續連接始終返回到同一服務器至關重要時,此方法最適用。
服務端研發經常接觸的數據庫事務就適用于此場景
Consistent Hash(一致性哈希負載均衡算法)
一致性哈希負載均衡算法類似于源地址哈希,不同在于一致性哈希負載均衡算法可以使用任意應用參數組成唯一的哈希密鑰,并且當服務器集群發生變化時可以盡可能少地進行數據遷移。
常見負載均衡算法實現
本節將會介紹各種常見負載均衡算法的實現方式,某些負載均衡算法具有多種不同的實現方式,并且每種實現方式都有各自適用的場景,這些不同的實現方式也會在本節進行介紹。同時本節中會假設所有的請求都是線性的,不會處理并發安全相關的細節。
Round Robin(輪詢負載均衡算法)
在所有負載均衡算法中,輪詢負載均衡算法實現起來最簡單,只需要一個變量表示當前位置并不斷增加即可。
public class RoundRobinLoadBalancer { private final List instances; private int position; public RoundRobinLoadBalancer(List instances) { this.instances = instances; this.position = ThreadLocalRandom.current().nextInt(instances.size()); } public ServiceInstance peek(HttpServletRequest request) { int peeked = (position++) & Integer.MAX_VALUE; return instances.get(peeked % instances.size()); } }這里有兩個需要注意的點
當我們初始化位置時,需要將其設置為一個隨機值,避免多個負載均衡器同時請求同一個服務器,造成服務器的瞬時壓力
在位置自增時,需要忽略符號位,因為 Java 沒有無符號整數,所以當位置的值超出整型最大值時會變成負值導致拋出異常。至于為什么不能使用絕對值,是因為整型的最小值沒有對應的絕對值,得到的依舊是負值 (Spring Cloud #1074)
Weighted Round Robin(加權輪詢負載均衡算法)
加權輪詢負載均衡算法有很多主流的實現,并且各自都有各自的優點。雖然加權負載均衡產生任意符合全總分配比例分布的選擇序列都是合適的,但在短時間窗口內是否能夠選擇盡可能多的節點提供服務仍是評價加權負載均衡實現的質量的關鍵指標。
數組展開方式
數組展開實現方式是一種適用空間換時間的策略,適用于較小的服務器集群或專用型負載均衡設備。它的優點是速度非常快,與 Round Robin 實現完全一致。它的缺點也很明顯,當權重的總和很大時會帶來很大的內存開銷
public class WeightedLoadBalancer { private final List instances; private int position; public WeightedLoadBalancer(List instances) { this.instances = expandByWeight(instances); } public ServiceInstance peek(HttpServletRequest request) { int peeked = (position++) & Integer.MAX_VALUE; return instances.get(peeked % instances.size()); } private List expandByWeight(List instances) { List newInstances = new ArrayList<>(); for (ServiceInstance instance : instances) { int bound = instance.getWeight(); for (int w = 0; weight < bound; weight++) { newInstances.add(instance); } } Collections.shuffle(newInstances); return newInstances; } }這里有三個需要注意的點:
當實例按權重展開成數組的時候,可能會出現實例權重都很大,但是它們的最大公約數不為 1,這個時候可以使用最大公約數來減少展開后的數組大小。因為最大公約數的諸多限制,例如任意自然數 N 與 N+1 互質,任意自然數 N 與 1 互質,所以很容易出現優化失敗的情況,因此本示例并未給出,感興趣的可以去看 Spring Cloud 相關 PR(Spring Cloud #1140)
在實例按權重展開成數組后,需要對得到的數組進行洗牌,以保證流量盡可能均勻,避免連續請求相同實例(Java 中實現的洗牌算法是 Fisher-Yates 算法,其他語言可以自行實現)
因為是在構建負載均衡器的時候按權重展開成數組的,所以在負載均衡器構建完成后無法再改變實例的權值,對于頻繁動態變更權重的場景不適用
上界收斂選擇方式
上界收斂選擇方式提前計算出所有權重的最大值,并將初始上界設置為所有權重的最大值,接下來我們一輪一輪地去遍歷所有實例,并找到權重大于等于上界的實例。當前輪遍歷結束后,所有大于等于上界的元素都被選取到了,接下來開始嘗試權重更低的節點,直到最后上界為 0 時,將其重新置為最大值。目前 openresty (有人在issue #44上分析了這種算法)
public class WeightedLoadBalancer { private final List instances; private final int max; private final int gcd; private int bound; private int position; public WeightedLoadBalancer(List instances) { this.instances = instances; this.max = calculateMaxByWeight(instances); this.gcd = calculateGcdByWeight(instances); this.position = ThreadLocalRandom.current().nextInt(instances.size()); } public ServiceInstance peek(HttpServletRequest request) { if (bound == 0) { bound = max; } while (instances.size() > 0) { for (int peeked = position; peeked < instances.size(); peeked++) { ServiceInstance instance = instances.get(peeked); if (instance.getWeight() >= bound) { position = peeked + 1; return instance; } } position = 0; bound = bound - gcd; } return null; } private static int calculateMaxByWeight(List instances) { int max = 0; for (ServiceInstance instance : instances) { if (instance.getWeight() > max) { max = instance.getWeight(); } } return max; } private static int calculateGcdByWeight(List instances) { int gcd = 0; for (ServiceInstance instance : instances) { gcd = gcd(gcd, instance.getWeight()); } return gcd; } private static int gcd(int a, int b) { if (b == 0) { return a; } return gcd(b, a % b); } }這里面有四個需要注意的點:
如果是短頻率請求,將會一直訪問高權重實例,導致在短時間窗口內負載看起來并不均勻。這個可以通過改變方向,從下界向上界逼近來解決。
每一輪后降低上界的值可以取所有權重的最大公約數,因為如果每次下降 1 的話,中間這些輪會反復請求權重最高的那些實例,導致負載不均衡。
雖然最大公約數可以減少下降次數,但是如果權重相差非常多,并且所有元素都是互質的(n 與 n+1 互質,任意自然數 n 與 1 互質,在實踐中非常容易出現),那么在上界下降的過程中將會帶來很多空轉。這個可以參考廣度優先遍歷的思想,使用先入先出的隊列來減少空轉。
與數組展開方式遇到的問題相同,因為是在構建負載均衡器的時候計算最大公約數的值,所以對于頻繁動態變更權重的場景依舊會有很大的性能開銷,但是相較于數組展開方式可以避免頻繁動態分配數組導致的性能與內存碎片問題
權重輪轉實現
權重輪轉算法中將會存儲兩個權重的值,一個是不會變化的原始權重,一個是會隨著每次選擇變化的當前權重。權重輪轉實現中維護了一個循環不變量 —— 所有節點的當前權重的和為 0。每輪遍歷過程中所有實例的有效權重都會增加它的原始權重,并選擇出當前權重最高的節點。選擇出權重最高的節點后將它的當前權重減去所有實例權重的總和,以避免它再次被選擇。NGINX 中加權輪詢負載均衡算法使用此實現(NGINX)。這種算法的優勢是它很平滑,低權重節點的等待時間較短,并且每輪權重輪轉的最小正周期很小,是所有服務器實例權重的和。。
在 NGINX 中又叫做平滑加權負載均衡(Smooth Weighted Load Balancing,SWRR)。
public class WeightedLoadBalancer { private final List instances; public WeightedLoadBalancer(List instances) { this.instances = instances; } public ServiceInstance peek(HttpServletRequest request) { ServiceInstance best = null; int total = 0; for (ServiceInstance instance : instances) { total += instance.getWeight(); instance.setCurrentWeight(instance.getCurrentWeight() + instance.getWeight()); if (best == null || instance.getCurrentWeight() > best.getCurrentWeight()) { best = instance; } } if (best != null) { best.setCurrentWeight(best.getCurrentWeight() - total); } return best; } }這里面有三個需要注意的點:
權重輪轉非常適合實例變化頻率非常高的集合,因為它不需要提前構建數據結構
權重輪轉實現的效率與實例數量相關,時間復雜度是 O (n),當集群服務器數量非常大時需要限制每次參與選擇的服務器數量(Spring Cloud #1111)
權重輪轉實現需要修改服務器實例的數據結構,當服務實例是由其他機構提供時無法使用此實現
EDF(Earliest Deadline First)實現
EDF 算法最早被用在 CPU 調度上,EDF 是搶占式單處理器調度的最佳調度算法。EDF 實現與權重輪轉實現相似,引入了名為 deadline 的額外變量,可以認為權重越高的服務器實例完成任務的時間越快,那么在假設所有請求的成本相同時,所需要花費的時間是權重的倒數,所以可以很自然地選擇可以最早空閑出來提供服務的服務器實例,并將任務分配給它。 實現 EDF 算法只需要將每個下游服務器實例與 deadline 綁定,然后以 deadline 為優先級維護到優先隊列中,并不斷取出隊首元素,調整它的 deadline,并將它重新提交到優先隊列中。知名 Service Mesh 代理 envoy 使用了此方法實現加權負載均衡(envoy),以及螞蟻開源網絡代理 mosn 中也實現了此方法(mosn #1920)
public class WeightedLoadBalancer { private final PriorityQueue entries; public WeightedLoadBalancer(List instances) { this.entries = instances.stream().map(EdfEntry::new).collect(Collectors.toCollection(PriorityQueue::new)); } public ServiceInstance peek(HttpServletRequest request) { EdfEntry entry = entries.poll(); if (entry == null) { return null; } ServiceInstance instance = entry.instance; entry.deadline = entry.deadline + 1.0 / instance.getWeight(); entries.add(entry); return instance; } private static class EdfEntry implements Comparable { final ServiceInstance instance; double deadline; EdfEntry(ServiceInstance instance) { this.instance = instance; this.deadline = 1.0 / instance.getWeight(); } @Override public int compareTo(EdfEntry o) { return Double.compare(deadline, o.deadline); } } }EDF 每次選擇的算法復雜度為 O (log (n)),相較于數組展開要慢,但相較于上界收斂選擇在最壞情況下以及權重輪轉都需要 O (n) 的時間復雜度來說,其性能表現的非常好,并且對于超大集群,其性能下降不明顯。其空間復雜度為 O (n),不會造成很大的內存開銷。
Least Connections(最少連接負載均衡算法)
遍歷比較方式
最簡單的實現方式,遍歷所有實例,并找出當前連接數最少的實例
public class LeastConnectionLoadBalancer { private final List instances; public LeastConnectionLoadBalancer(List instances) { this.instances = instances; } public ServiceInstance peek(HttpServletRequest request) { ServiceInstance best = null; for (ServiceInstance instance : instances) { if (best == null || instance.getConnections() < best.getConnections()) { best = instance; } } if (best != null) { best.setConnections(best.getConnections() + 1); } return best; } }堆維護方式 所有動態有序集合都可以通過優先隊列來實現,與 EDF 算法相同,取出隊首的元素,修改它的優先級,并放回隊列中
public class LeastConnectionLoadBalancer { private final PriorityQueue instances; public LeastConnectionLoadBalancer(List instances) { this.instances = instances.stream().collect(toCollection( () -> new PriorityQueue<>(comparingInt(ServiceInstance::getConnections)))); } public ServiceInstance peek(HttpServletRequest request) { ServiceInstance best = instances.poll(); if (best == null) { return null; } best.setConnections(best.getConnections() + 1); return best; } }
Weighted Least Connections(加權最少連接負載均衡算法)
加權最少連接負載均衡算法的實現方式與最少連接負載均衡算法相同,只是在計算時增加了權重相關的參數
遍歷比較方式
public class LeastConnectionLoadBalancer { private final List instances; public LeastConnectionLoadBalancer(List instances) { this.instances = instances; } public ServiceInstance peek(HttpServletRequest request) { ServiceInstance best = null; for (ServiceInstance instance : instances) { if (best == null || instance.getConnections() * best.getWeight() < best.getConnections() * instance.getWeight()) { best = instance; } } if (best != null) { best.setConnections(best.getConnections() + 1); } return best; } }
Tips,在不等式中 a/b < c/d 與 ad < bc 等價,并且可以避免除法帶來的性能與精度問題
堆維護方式
public class LeastConnectionLoadBalancer { private final PriorityQueue instances; public LeastConnectionLoadBalancer(List instances) { this.instances = instances.stream().collect(toCollection( () -> new PriorityQueue<>(comparingDouble(ServiceInstance::getWeightedConnections)))); } public ServiceInstance peek(HttpServletRequest request) { ServiceInstance best = instances.poll(); if (best == null) { return null; } best.setConnections(best.getConnections() + 1); best.setWeightedConnections(1.0 * best.getConnections() / best.getWeight()); return best; } }Weighted Response Time(加權響應時間負載均衡算法)
加權響應時間負載均衡算法使用統計學的方法,通過歷史的響應時間來得到預測值,使用這個預測值來選擇相對更優的服務器實例。得到預測值的方法有很多,包括時間窗口內的平均值、時間窗口內的 TP99、歷史所有響應時間的指數移動加權平均數(EWMA)等等。其中 Linkerd 與 APISIX 使用了 EWMA 算法(Linkerd和APISIX)。 通過歷史的響應時間來得到預測值這個操作通常是 CPU 開銷很大的,實際使用時可以不用遍歷所有元素,而是使用 K - 臨近元素或直接隨機選擇兩個元素進行比較即可,這種啟發式方法辦法無法保證全局最優但是可以保證不至于全局最差。
Source IP Hash(源地址哈希負載均衡算法)
源地址哈希負載均衡以任意算法將請求地址映射成整型數,并將這個整型數映射到實例列表的下標
public class IpHashLoadBalancer { private final List instances; public IpHashLoadBalancer(List instances) { this.instances = instances; } public ServiceInstance peek(HttpServletRequest request) { int h = hashCode(request); return instances.get(h % instances.size()); } private int hashCode(HttpServletRequest request) { String xForwardedFor = request.getHeader("X-Forwarded-For"); if (xForwardedFor != null) { return xForwardedFor.hashCode(); } else { return request.getRemoteAddr().hashCode(); } } }這里有一個需要注意的點:
面向公網提供服務的負載均衡器前面可能會經過任意多層反向代理服務器,為了獲取到真實的源地址,需要先獲取 X-Forwarded-For 頭部,如果該頭部不存在再去獲取 TCP 連接的源地址
負載均衡技術擴展
服務注冊表與發現(Service Registry and Service Discovery)
在維護大型服務器集群時,服務器實例隨時都有可能被創建或移除,當服務器被創建或移除時,集群管理員需要到各個負載均衡設備上去更新服務器實例列表。 服務注冊表會在內部維護服務對應的服務器實例列表。在服務器實例被創建并成功運行服務后,服務器實例會去服務注冊表中注冊自身,包括網絡地址(IPv4/IPv6)、服務端口號、服務協議(TCP/TLS/HTTP/HTTPS)以及自身提供的服務名稱等等,有的服務注冊表本身也會提供主動健康檢查的能力(如 Eureka 與 Consul)。在服務器實例正常退出時會在服務注冊表執行反注冊邏輯,這個時候服務注冊表就會將這個服務器實例從服務器實例列表中移除。即使服務器實例異常退出導致無法執行反注冊邏輯,服務注冊表也會通過主動健康檢查機制將這個異常的服務器實例從服務器實例列表中移除。 在擁有服務注冊表后,負載均衡設備不需要再手動維護服務器實例列表,而是當請求到來時從服務注冊表中拉取對應的服務器實例列表,并在這個服務器實例列表中進行負載均衡。為了提高服務的可用性,負載均衡設備會在本地(內存或本地文件注冊表)緩存這些服務器實例列表,以避免由于負載均衡設備與服務注冊表無法連接而導致服務不可用。 緩存及重新獲取服務器列表的策略根據不同業務場景有不同的實現,在 Spring Cloud Loadbalancer 中是通過緩存過期而觸發重新獲取的邏輯(Spring Cloud),當服務注冊表不可用時,因為負載均衡設備中無可用的服務器備份而導致服務完全不可用;在大部分的負載均衡設備中將緩存獲取與更新邏輯改為定時器主動刷新的機制,這樣當服務注冊表不可用時可以主動決定是否將舊數據標記為過期。盡管本地緩存可以提高服務的可用性,但是要注意負載均衡設備在使用的仍舊是舊的服務提供方列表,當長時間無法獲取到新的服務提供方列表時,負載均衡設備應當舍棄舊的服務提供方列表,并將服務不可用的問題暴露出來,通過基礎設施提供的監控與告警能力通知集群管理員來進行處理。
健康檢查(Health Check)
健康檢查本質是一個預定規則,它向負載均衡器背后的服務器集群中的所有成員發送相同的請求,以確定每個成員服務器是否可以接受客戶端請求。 對于某些類型的健康檢查,通過評估來自服務器的響應以及收到服務器響應所需的時間以確定每個成員服務器的運行狀態。通常情況下,當成員服務器的狀態變為不健康時,負載均衡器應該快速地將其從服務器實例列表中移除,并在成員服務器狀態恢復正常時將其重新添加回服務器實例列表中。
對于網絡層負載均衡器(也叫做 NLB 或 L4LB),通過建立 TCP 連接,根據是否能夠成功建立連接以及建立連接所需要的時間來確定成員服務器的狀態。
對于應用層負載均衡器(也叫做 ALB 或 L7LB),通過發送應用層協議(不只是 HTTP 協議)定義的用于健康檢查的請求報文,并根據響應報文內容以及整個請求從建立連接到完整收到所有響應所花費的時間來確定成員服務器的狀態。
應用負載均衡器沒有固定的模式。例如,對于提供 HTTP 協議服務的應用,可以提供用于健康檢查的 URL,設置通過健康檢查的 HTTP 狀態碼(或狀態碼集),并驗證響應報文中用于表示服務器狀態的字段(通過 JSONPath 或 XMLPath 等提取)是否是預期值等方式來確認成員服務器狀態;對于 RPC 協議,可以提供專門的 ping-pong 服務,負載均衡器根據 RPC 協議組裝請求報文,并發送 ping 請求到成員服務器上,并根據成員服務器返回的內容是否為 pong 響應來確認成員服務器的狀態,具體設計可以參考 websocket 的 ping-pong 機制(RFC 6455)。
慢啟動(Slow Start)
負載均衡器中的慢啟動思想來自于 TCP 的擁塞控制理論,其核心也是為了避免大量請求涌入剛剛啟動完成的應用程序,導致大量請求阻塞、超時或異常的情況。 眾所周知,Java 是半編譯半解釋型語言,包括 Java 語言在內,現代解釋型語言的解釋器都帶有即時編譯器(Just In Time,JIT),JIT 編譯器會跟蹤每個方法的執行點,對那些熱點路徑(Hotspot)進行更高效的優化,這也是 Hotspot JVM 名字的由來。而 JIT 對熱點路徑的優化全都來自于自應用程序啟動以來的所有方法調用,也就是說應用程序的系統承載能力是隨著程序的運行而不斷得到強化的,經過 JIT 優化的 Java 代碼甚至可以得到近似 GCC 中 O3(最高級別)優化的性能。跟多關于 JIT 編譯器的細節可以看 Oracle 的 Java 開發者指南(Java Developer Guide)。 同時,現代應用程序都不可避免的會使用本地緩存,當應用程序剛剛啟動時內存中的緩存是空的,隨著應用程序的運行,不斷地訪問外部系統獲取數據并將數據寫入到內存緩存中,應用程序與外部系統的交互會不斷減少,應用程序的系統承載能力也會逐漸達到峰值。 上面是應用程序在啟動后性能不斷提升的因素中最常見的,初次之外還有很多的因素。所以為了避免大量請求涌入剛剛啟動完成的應用程序的現象發生,負載均衡器會通過慢啟動的方式,隨著服務器運行不斷增加這些服務器實例的權重,最終達到服務器的實際權重,從而達到動態調整分配給這些服務器實例的流量的效果。 服務器權重變化算法有很多,包括隨時間線性增長、隨時間對數增長、隨時間指數增長、隨時間變冪增長、與隨時間按 Logistic 增長等。目前京東服務框架(JSF)實現的是隨時間線性增長;envoy 實現了隨時間變冪增長,并引入了漸進因子來調整變化速率(envoy slow start)。
總結
負載均衡技術是網絡代理與網關組件最核心的組成部分,本文簡單介紹了什么是負載均衡技術、常見的負載均衡算法以及常見負載均衡算法的實現,并給出了負載均衡技術的擴展,為將來更深入學習網絡代理相關技術打下基礎。 因本人才學疏淺經驗能力有限,文中難免會有疏忽和遺漏,以及不連貫的地方,歡迎大家與我溝通交流給出建議。
-
服務器
+關注
關注
12文章
9329瀏覽量
86131 -
負載均衡
+關注
關注
0文章
113瀏覽量
12396
原文標題:解密負載均衡技術和負載均衡算法
文章出處:【微信號:OSC開源社區,微信公眾號:OSC開源社區】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論