前言
負載均衡在架構設計中是經常提到的一種方案,用以提高系統處理量。今天用幾句程式碼實現Round Robin方式,用白話文說就是有活大家輪著幹。在看了Ribbion原始碼之後,確實是幾行程式碼。
實現思路
- 首先,要有一組服務列表
private List<String> serverList = new LinkedList<>();
public RoundRibbon() {
serverList.add("http://server.01");
serverList.add("http://server.02");
serverList.add("http://server.03");
serverList.add("http://server.04");
serverList.add("http://server.05");
}
- 然後要有一個全域性的索引變數,然後通過取餘的方式計算下一個服務索引:
int nextServerIndex = (currentIndex + 1) % serverList.size();
- 上面的程式碼在實戰中會有執行緒安全的問題,因此可以採用 AtomicInteger 實現。
生產實現
在 Netflix/ribbon 的 RoundRobinRule 中實現程式碼如下:
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
log.warn("no load balancer");
return null;
}
Server server = null;
int count = 0;
while (server == null && count++ < 10) {
//獲取可用的server列表
List<Server> reachableServers = lb.getReachableServers();
List<Server> allServers = lb.getAllServers();
int upCount = reachableServers.size();
int serverCount = allServers.size();
if ((upCount == 0) || (serverCount == 0)) {
log.warn("No up servers available from load balancer: " + lb);
return null;
}
//核心實現,獲取server索引
int nextServerIndex = incrementAndGetModulo(serverCount);
server = allServers.get(nextServerIndex);
if (server == null) {
/* Transient. */
Thread.yield();
continue;
}
//是這種
if (server.isAlive() && (server.isReadyToServe())) {
return (server);
}
// Next.
server = null;
}
//重試十次之後,沒有獲取到可用的服務,警告日誌
if (count >= 10) {
log.warn("No available alive servers after 10 tries from load balancer: "
+ lb);
}
return server;
}
/**
* Inspired by the implementation of {@link AtomicInteger#incrementAndGet()}.
*
* @param modulo The modulo to bound the value of the counter.
* @return The next value.
*/
private int incrementAndGetModulo(int modulo) {
for (;;) {
//獲取當前的服務索引值
int current = nextServerCyclicCounter.get();
//通過取餘的方式計算下一個索引值
int next = (current + 1) % modulo;
//通過 CAS 設定下一個搜尋引值(解決併發索引值可能重複的問題)
if (nextServerCyclicCounter.compareAndSet(current, next))
return next;
}
}
總結
輪詢方式實現很簡單,關鍵難點就是解決執行緒安全問題。例子中通過使用CAS解決,效率會高一些。也可以使用鎖。