首先来看看百度百科对于负载均衡的描述:
负载均衡建立在现有网络结构之上,它提供了一种廉价有效透明的方法扩展网络设备和服务器的带宽、增加吞吐量、加强网络数据处理能力、提高网络的灵活性和可用性。
负载均衡(Load Balance)其意思就是分摊到多个操作单元上进行执行,例如Web服务器、FTP服务器、企业关键应用服务器和其它关键任务服务器等,从而共同完成工作任务。(负载均衡) 我对于负载均衡的理解是,因为网络的一些原因,会使得众多服务器种某个服务器压力过于大,导致传输缓慢,我们可以提供一种方法,去均衡一下各个服务器之间的关系,使他们能够均衡的去完成网络传输,而不至于因为某些服务器的崩溃,使得整个系统变得延迟缓慢。下面,我们来进行一下负载均衡框架的实现,这将会对我以后的项目等产生很大的影响。
为了方便理解,先画个图表示:
图中可以看到客户端有很多的服务器可以选择,那如何进行选择,能保证各个服务器的连接数量相对均衡,就需要实现负载均衡。
这里有两种实现思路:
一: 将所有的服务器形成一条循环链,增加的时候,在链的末尾增加,删除的时候,将指定的服务器进行删除即可,取得一个服务器的时候,通过给一个currentNode指针来对这条链进行轮询,如果当前只有一个服务器,那就是这个服务器,如果有多个服务器,那就进行遍历,保证每次将这些服务器均匀取得。
来看具体代码:
首先我们需要一个INetNode来取得服务器的ip和port:
public interface INetNode {
String getIp();
int getPort();
}
public class DefaultNetNode implements INetNode {
private String ip;
private int port;
public DefaultNetNode() {
}
public DefaultNetNode(String ip, int port) {
this.ip = ip;
this.port = port;
}
public void setIp(String ip) {
this.ip = ip;
}
public void setPort(int port) {
this.port = port;
}
@Override
public String getIp() {
return ip;
}
@Override
public int getPort() {
return port;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((ip == null) ? 0 : ip.hashCode());
result = prime * result + port;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof DefaultNetNode))
return false;
DefaultNetNode other = (DefaultNetNode) obj;
if (ip == null) {
if (other.ip != null)
return false;
} else if (!ip.equals(other.ip))
return false;
if (port != other.port)
return false;
return true;
}
@Override
public String toString() {
return "[" + ip + ":" + port + "]";
}
}
需要定义该框架的功能:增加,删除,取得服务器:
public interface INetNodeBalance {
void addNode(INetNode node);
INetNode removeNode(INetNode node);
INetNode getNode();
}
针对这个接口,我们需要进行扩展,给一个map来存放所有的服务器:
public abstract class AbstractNetNodeBalance implements INetNodeBalance {
protected final Map<Integer, INetNode> nodePool;
public AbstractNetNodeBalance() {
this.nodePool = new ConcurrentHashMap<>();
}
@Override
public void addNode(INetNode node) {
if (node == null) {
return;
}
int nodeHashCode = node.hashCode();
if (nodePool.containsKey(nodeHashCode)) {
return;
}
nodePool.put(nodeHashCode, node);
}
public boolean isNodePoolEmpty() {
return nodePool.isEmpty();
}
@Override
public INetNode removeNode(INetNode node) {
if (node == null) {
return null;
}
return nodePool.remove(node.hashCode());
}
}
可以发现,这里用到了抽象类,为什么要给抽象类呢?很简单,因为我们接口定义了三个方法,而在AbstractNetNodeBalance 类中,只实现了两个,get方法还没有给出,因为get方法需要进行不同的负载均衡,所以,他应该由他的子类进行实现,不同的子类,它的负载均衡方法是不一样的。
下面看我的轮询方式:
public class PollingNetNode extends DefaultNetNode {
private PollingNetNode next;
private PollingNetNode pre;
public PollingNetNode() {
this.next = this;
this.pre = this;
}
public PollingNetNode(String ip, int port) {
super(ip, port);
this.next = this;
this.pre = this;
}
public void setNext(PollingNetNode next) {
this.next = next;
}
public void setPre(PollingNetNode pre) {
this.pre = pre;
}
public PollingNetNode getNext() {
return this.next;
}
public PollingNetNode getPre() {
return this.pre;
}
}
public class PollingBalance extends AbstractNetNodeBalance {
private PollingNetNode pollingNode;
private PollingNetNode currentNode;
public PollingBalance() {
super();
}
@Override
public synchronized void addNode(INetNode node) {
PollingNetNode newNode = new PollingNetNode(node.getIp(), node.getPort());
if (pollingNode == null) {
pollingNode = newNode;
currentNode = pollingNode;
super.addNode(newNode);
return;
}
newNode.setPre(pollingNode.getPre());
newNode.setNext(pollingNode);
pollingNode.setPre(newNode);
newNode.getPre().setNext(newNode);
super.addNode(newNode);
}
@Override
public synchronized INetNode removeNode(INetNode node) {
PollingNetNode target = new PollingNetNode(node.getIp(), node.getPort());
target = (PollingNetNode) super.removeNode(target);
if (target == null) {
return null;
}
if (isNodePoolEmpty()) {
pollingNode = null;
currentNode = null;
return pollingNode;
}
if (currentNode == target) {
currentNode = target.getNext();
}
if (pollingNode == target) {
pollingNode = target.getNext();
}
target.getPre().setNext(target.getNext());
target.getNext().setPre(target.getPre());
target.setPre(target);
target.setNext(target);
return target;
}
@Override
public synchronized INetNode getNode() {
INetNode result = currentNode;
if (currentNode != null) {
currentNode = currentNode.getNext();
}
return result;
}
}
对于上述的轮询,可以画个图来进行表示:
第二种实现思路,通过随机数的方式,也就是,产生一个随机数,对服务器的总个数进行取余,获得的那个下标就是所取的服务器。
public class RandomBalance extends AbstractNetNodeBalance {
private final List<INetNode> nodeList;
public RandomBalance() {
super();
nodeList = new LinkedList<>();
}
@Override
public void addNode(INetNode node) {
super.addNode(node);
if (node == null || nodeList.contains(node)) {
return;
}
nodeList.add(node);
}
@Override
public INetNode removeNode(INetNode node) {
if (node == null || !nodeList.contains(node)) {
return null;
}
nodeList.remove(node);
return super.removeNode(node);
}
@Override
public INetNode getNode() {
if (isNodePoolEmpty()) {
return null;
}
int index = ((int) (Math.random() * 10000)) % nodeList.size();
return this.nodeList.get(index);
}
}
上述是我自己实现的两个负载均衡思路,并且写成了接口,以后,我们想要进行负载均衡的时候,只需要提供一个set方法,set接口的实现类进去就可以直接调用负载均衡了。