首先,什么 是负载均衡?
负载均衡(Load Balance)指将负载(工作任务)进行平衡,分摊到多个操作单元上进行运行。
我们来举个例子说明一下:假设有多台服务器,如果有成千上万个客户端请求连接同一个服务器,那么这台服务器就会因为负载压力过大而使得网络响应速率降低,甚至会崩溃,这时候我们就需要负载均衡一下,把这些客户端均衡给多个服务器,来提高响应速率。这也是要设计实现负载均衡框架的初衷。
基于上述说明,实现负载均衡的两种方式:
1.轮询方式:轮流向节点池中的每一个服务器发送请求;
2.随机方式:通过产生随机数,请求某一个服务器
下面我们看一下具体代码,INetNode接口(用来获得某个服务器的ip和port):
public interface INetNode {
String getIp();
int getPort();
}
DefaultNetNode类(INetNode的实现类):
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 (getClass() != obj.getClass())
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+ "]";
}
}
接下来我们看另外一个接口,INetNodeBalance接口(完成增加,删除和获得一个服务器的功能):
public interface INetNodeBalance {
void addNode(INetNode node);
INetNode removeNode(INetNode node);
INetNode getNode();
}
AbstractNetNodeBalance类(定义了一个Map用来存放服务器节点,这里以节点的hashcode为值,以该节点为键存到节点池中,为服务器的增加,删除和获得做准备):
public abstract class AbstractNetNodeBalance implements INetNodeBalance {
protected final Map<Integer, INetNode> nodePool;
public AbstractNetNodeBalance() {
nodePool = new ConcurrentHashMap<Integer, INetNode>();
}
@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());
}
}
我们可以看出上述类是个抽象类,因为它的getNode()方法没有实现,我们后续需要用不同的负载均衡方式来获得服务器节点,所以需要该类的子类来完成不同方式的负载均衡,来实现getNode()方法。
下面就是轮询方式,将所有的服务器构成一个循环链,当增加服务器的时候,在链的结尾增加;当删除服务器的时候,删除指定的服务器节点;当获得一个服务器时,通过给一个currentNode指针来对这条链进行轮询,如果只有一个服务器,则获得该服务器,如果有多个服务器,那就进行遍历,保证每次均匀取得服务器。
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 PollingNetNode getNext() {
return next;
}
public void setNext(PollingNetNode next) {
this.next = next;
}
public PollingNetNode getPre() {
return pre;
}
public void setPre(PollingNetNode pre) {
this.pre = 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(node);
}
@Override
public 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 = currentNode.getNext();
}
if (pollingNode == target) {
pollingNode = pollingNode.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;
}
}
下图所示,就是对这条双向循环链中节点的增加和删除过程,也就是上述代码增加和删除服务器节点的过程。
接下来我们看一下随机方式,构建一个nodeList,随机产生一个数对nodeList的长度取余,得到的结果作为下标,取得该下标下的服务器节点。
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);
}
}
搭建好了负载均衡框架,我们可以打包成jar包,方便以后使用。接下来我们要做的《简版服务发现》就要用到这个工具。