話不多說前言
負(fù)載均衡 簡單來說就像分班考試,入學(xué)人數(shù)太多,部分同學(xué)去重點(diǎn)班A,部分去奧數(shù)班B,部分去少年班C,作者本人去的是普通基礎(chǔ)班,其實(shí)更適合終極一班
ELB
Elastic Load Balancing(ELB)以亞馬遜提供的ELB為例,
主要提供三種負(fù)載均衡器:應(yīng)用負(fù)載均衡(ALB),網(wǎng)絡(luò)負(fù)載均衡,經(jīng)典負(fù)載均衡。
幾種負(fù)載均衡器比較
-
經(jīng)典負(fù)載均衡器
- 請求級別和連接級別
- 可在多個 Amazon EC2 實(shí)例之間提供基本的負(fù)載均衡。Classic Load Balancer 適用于在 EC2-Classic 網(wǎng)絡(luò)內(nèi)構(gòu)建的應(yīng)用程序
-
網(wǎng)絡(luò)負(fù)載均衡
- 網(wǎng)絡(luò)負(fù)載均衡器運(yùn)行于連接級別(第 4 層),
- 可將流量路由至 Amazon Virtual Private Cloud (Amazon VPC) 內(nèi)的不同目標(biāo),每秒能夠處理數(shù)百萬請求,同時能保持超低延遲。
- 網(wǎng)絡(luò)負(fù)載均衡器還針對處理突發(fā)和不穩(wěn)定的流量模式進(jìn)行了優(yōu)化。
-
應(yīng)用負(fù)載均衡器件
- 最適合 HTTP 和 HTTPS 流量的負(fù)載均衡,
- 面向交付包括微服務(wù)和容器在內(nèi)的現(xiàn)代應(yīng)用程序架構(gòu),提供高級請求路由功能。
負(fù)載均衡算法和實(shí)現(xiàn)
- 輪詢法
- 所有請求按照順序分發(fā),不常用,比如分班,按報名順序分班
// 加鎖,保證線程安全
public String robin() {
synchronized (serverIndex) {
if (serverIndex >= serverList.size()) {
serverIndex = -1;
}
return serverList.get(++serverIndex);
}
}
-
加權(quán)輪詢
- 輪詢基礎(chǔ)上加權(quán)重,好一點(diǎn)的機(jī)器權(quán)重大,也不常用了,以分班為例,教師大的分過去學(xué)生多
-
隨機(jī)/加權(quán)隨機(jī)
- 隨機(jī)分配,次數(shù)多的時候接近于輪詢,以分班為例,就是抽簽分班。
public String random() {
int randomIndex = new Random().nextInt(serverList.size());
return serverList.get(randomIndex);
}
-
最小連接
- 那個服務(wù)器連接少,就分流到那個機(jī)器,在機(jī)器性能不是均等的時候,這個方法容易把請求分到性能很差的機(jī)器上,也不好
- 以分班為例,就是報名人數(shù)少的班級,優(yōu)先調(diào)劑。
-
源頭地址hash
- 教材案例
public String hash(String remoteIp) {
Integer hashCode = remoteIp.hashCode();
serverIndex = hashCode % serverList.size();
return serverList.get(serverIndex);
}
- 一致性hash
- 便于擴(kuò)展不影響大部分原有節(jié)點(diǎn)
- 為了均衡加上了虛擬節(jié)點(diǎn)
public class ConsistHash {
private String[] servers;
// 真實(shí)結(jié)點(diǎn)列表
private static List<String> realNodes = new LinkedList<>();
// 虛擬節(jié)點(diǎn),key表示虛擬節(jié)點(diǎn)的hash值,value表示虛擬節(jié)點(diǎn)的名稱
private static SortedMap<Integer, String> virtualNodes =
new TreeMap<>();
// 虛擬節(jié)點(diǎn)數(shù)目
private static final int VIRTUAL_NODES = 150;
public ConsistHash(String[] servers) {
this.servers = servers;
}
private void getNode() {
// 原始的服務(wù)器添加到真實(shí)結(jié)點(diǎn)列表中
Collections.addAll(realNodes, servers);
// 添加虛擬節(jié)點(diǎn)
for (String str : realNodes) {
for (int i = 0; i < VIRTUAL_NODES; i++) {
String virtualNodeName = str + "&&VN" + String.valueOf(i);
int hash = getHash(virtualNodeName);
virtualNodes.put(hash, virtualNodeName);
}
}
}
// 使用FNV1_32_HASH算法計(jì)算服務(wù)器的Hash值,這里不使用重寫hashCode的方法,最終效果沒區(qū)別
private static int getHash(String str) {
final int p = 16777619;
int hash = (int) 2166136261L;
for (int i = 0; i < str.length(); i++) {
hash = (hash ^ str.charAt(i)) * p;
}
hash += hash << 13;
hash ^= hash >> 7;
hash += hash << 3;
hash ^= hash >> 17;
hash += hash << 5;
if (hash < 0) {
hash = Math.abs(hash);
}
return hash;
}
// 得到應(yīng)當(dāng)路由到的結(jié)點(diǎn)
public static String getServer(String node) {
// 得到帶路由的結(jié)點(diǎn)的Hash值
int hash = getHash(node);
// 得到大于該Hash值的所有Map
SortedMap<Integer, String> subMap =
virtualNodes.tailMap(hash);
// 第一個Key就是順時針過去離node最近的那個結(jié)點(diǎn)
Integer i = subMap.firstKey();
// 返回對應(yīng)的虛擬節(jié)點(diǎn)名稱,這里字符串稍微截取一下
String virtualNode = subMap.get(i);
return virtualNode.substring(0, virtualNode.indexOf("&&"));
}
}
負(fù)載均衡系統(tǒng)設(shè)計(jì)需要考慮的其他因素
- 實(shí)例健康檢查(這個通常配合注冊中心實(shí)現(xiàn))
- 機(jī)器自動擴(kuò)展(用一致性負(fù)載均衡在擴(kuò)容時影響機(jī)器少)
- 監(jiān)控服務(wù),根據(jù)cpu使用量大小和流量大小,處理時長自動完成添加或者縮減實(shí)例服務(wù)
- 應(yīng)用負(fù)載均衡可以按照功能分發(fā)到對應(yīng)的集群,比如登錄給登錄集群,支付給支付集群
這一篇就到這里了,
源碼github