前言:nginx可以进行反向代理、负载均衡、http服务、正向代理等,而zk可以通过其“目录”节点,进行负载均衡、分布式锁等。此文章简单讲解了zk的负载均衡实现。
如果不同意,邮我打脸 zwh_mc@sina.com 。
zookeeper是一个针对大型分布式系统的可靠的协调系统,提供的功能包括命名服务、配置维护、分布式同步、集群服务等。本文将着重介绍zookeeper的应用场景之一,负载均衡。
分布式集群
由于zookeeper主要扮演的角色是分布式集群中的协调者,所以首先介绍一下分布式和集群的概念。简单来说,分布式是将一个完整的系统拆分
成多个能实现不同业务需求的系统分布在多个地方,而集群是只将业务需求相同系统或服务复杂多份放在不同的服务器实现负载均衡,减轻服务器压力。用个比喻来说,小饭店原来只有一个厨师,切菜洗菜备料炒菜全干。后来客人多了,厨房一个厨师忙不过来,又请了个厨师,两个厨师都能炒一样的菜,这两个厨师的关系是集群。为了让厨师专心炒菜,把菜做到极致,又请了个配菜师负责切菜,备菜,备料,厨师和配菜师的关系是分布式,一个配菜师也忙不过来了,又请了个配菜师,两个配菜师关系是集群。
与dubbo的配合
zookeeper在一个分布式集群中既可以作为服务注册中心,来协调几个业务需求不同的分布式服务,也可以在一个集群中实现负载均衡,最典型的实例是zookeeper与Dubbo的配合使用,dubbo是阿里巴巴的RPC框架(如果没有接触过,在本文中可以理解为webservice)。zookeeper 在dubbo组成的分布式服务中就实现了一个服务注册中心的角色,既可以实现分布式服务的协调功能也可以实现负载均衡的功能
如下图所示,把ZooKeeper作为一个服务的注册中心,在其中登记每个服务,每台服务器知道自己是属于哪个服务,在服务器启动时,自己向所属服务进行登记,这样,一个树形的服务结构就呈现出来了,根据这样一个树形服务结构,RPC服务的消费者可以很轻松的找到它所需求的服务信息。同时在一个service节点下可以注册多个业务逻辑相同的服务,以实现负载均衡。
负载均衡
zookeeper实现负载均衡其实原理很简单,zookeeper 的数据存储类似于liunx的目录结构。首先建立servers节点,并建立监听器监视servers子节点的状态(用于在服务器增添时及时同步当前集群中服务器列表)。在每个服务器启动时,在servers节点下建立子节点worker server(可以用服务器地址命名),并在对应的字节点下存入服务器的相关信息。这样,我们在zookeeper服务器上可以获取当前集群中的服务器列表及相关信息,可以自定义一个负载均衡算法,在每个请求过来时从zookeeper服务器中获取当前集群服务器列表,根据算法选出其中一个服务器来处理请求。
代码
serviceAProvider,和serviceBProvider是模拟提供相同服务service-A的集群 模拟IP地址分别为92.168.58.131,92.168.58.132,
zookeeper服务器地址为172.19.100.29:2181。
import org.I0Itec.zkclient.ZkClient;
import org.apache.zookeeper.data.Stat;
public class ServiceAProvider {
private String serviceName="service-A";
public void init(){
String severList="172.19.100.29:2181";
String rootPath="/servers";
ZkClient zk=new ZkClient(severList, 5000, 5000);
if(!zk.exists(rootPath)){
zk.createPersistent(rootPath);
}
if(!zk.exists(rootPath+"/"+serviceName)){
zk.createPersistent(rootPath+"/"+serviceName);
}
//获取本服务器IP
String ip="92.168.58.131";
Stat stat=new Stat();
zk.createPersistent(rootPath+"/"+serviceName+"/"+ip);
System.out.println("znode:"+rootPath+"/"+serviceName+"/"+ip+"创建完成");
}
//提供服务
public void provide(){
}
public static void main(String[]args) throws Exception {
ServiceAProvider service = new ServiceAProvider();
service.init();
Thread.sleep(1000*60*60*24);
}
}
import org.I0Itec.zkclient.ZkClient;
import org.apache.zookeeper.data.Stat;
public class ServiceBProvider {
private String serviceName="service-A";
public void init(){
String severList="172.19.100.29:2181";
String rootPath="/servers";
ZkClient zk=new ZkClient(severList, 5000, 5000);
if(!zk.exists(rootPath)){
zk.createPersistent(rootPath);
}
if(!zk.exists(rootPath+"/"+serviceName)){
zk.createPersistent(rootPath+"/"+serviceName);
}
//获取本服务器IP
String ip="92.168.58.132";
zk.createPersistent(rootPath+"/"+serviceName+"/"+ip);
System.out.println("znode:"+rootPath+"/"+serviceName+"/"+ip+"创建完成");
}
//提供服务
public void provide(){
}
public static void main(String[]args) throws Exception {
ServiceBProvider service = new ServiceBProvider();
service.init();
Thread.sleep(1000*60*60*24);
}
}
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import org.I0Itec.zkclient.IZkChildListener;
import org.I0Itec.zkclient.ZkClient;
public class ServiceConsumer {
private List<String> serverList=new ArrayList<String>();
private String serviceName="service-A";
public void init(){
String zkServerList="172.19.100.29:2181";
String servicePath="/servers/"+serviceName;
ZkClient zk=new ZkClient(zkServerList);
if(zk.exists(servicePath)){
serverList=zk.getChildren(servicePath);
System.out.println("当前服务的地址"+serverList);
}else{
System.out.println("service is not exist!");
}
zk.subscribeChildChanges(servicePath, new IZkChildListener() {
public void handleChildChange(String parentPath, List<String> currentChilds)
throws Exception {
serverList=currentChilds;
}
});
}
//消费服务
public void consume(){
//通过负责均衡算法,得到一台服务器进行调用
int index = getRandomNum(0,1);
System.out.println("调用" + serverList.get(index)+"提供的服务:" + serviceName);
}
public int getRandomNum(int min,int max){
Random rdm = new Random();
return rdm.nextInt(max-min+1)+min;
}
public static void main(String[] args)throws Exception {
ServiceConsumer consumer = new ServiceConsumer();
consumer.init();
consumer.consume();
Thread.sleep(1000*60*60*24);
}
}