几行代码实现负载均衡轮询算法
前言
负载均衡在架构设计中是经常提到的一种方案,用以提高系统处理量。今天用几句代码实现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解决,效率会高一些。也可以使用锁。
多学点,总不会吃亏的。