前言
負載均衡在架構設計中是經常提到的一種方案,用以提高系統處理量。今天用幾句代碼實現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解決,效率會高一些。也可以使用鎖。