前言
上章,我们简单了解了下ZooKeeper内的成员变量及调度服务.本章主要讲解下ZooKeeper的负载均衡.
正文
假设我们有3台服务器用于负载.那么有一台Client,其该怎么链接哪台机器呢?当其链接的主机宕机后该链接哪台服务器呢?我们可以使用Nginx或KeepAlive或者心跳机制.而使用今天的主角ZooKeeper,其也可以实现.
原始架构:
新架构:
- 服务器结点: 可以在
ZooKeeper集群内存储一个/servers的目录结构.各个结点的负载存储如下:/servers/servers1 <int>. - 客户端结点: 可以通过
ZooKeeper集群寻找负载最小的结点.此外,还可以设置监听器, 当server结点宕机后,ZooKeeper将删除服务器结点目录,Client结点收到通知,重新进行负载. - Tips: 创建的ZooKeeper结点皆为临时结点.(详见ZooKeeper结点类型)
编码
DistributedServer
package com.yanxml.zookeeper.demos.server;
import java.io.IOException;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import org.apache.zookeeper.ZooDefs.Ids;
/**
* 分布式上线集群.(服务端)
* com.yanxml.zookeeper.demos.server.DistributedServer
* */
public class DistributedServer {
private static final String connectString = "192.168.31.60:2181,192.168.31.61:2181,192.168.31.62:2181";
private static final int sessionTimeout = 2000;
private static final String parentNode = "/servers/";
private ZooKeeper zk = null;
public void getConnect() throws IOException{
zk = new ZooKeeper(connectString, sessionTimeout, new Watcher(){
@Override
public void process(WatchedEvent event) {
// 收到事件通知后的回调函数(我们自己的处理逻辑)
System.out.println(event.getType()+"---"+event.getPath());
try{
zk.getChildren("/", true);
}catch(Exception e){
}
}
});
}
// 进行父节点检测 (这边只做单层的结点判断 - 如果多层的话也可以进行判断.)
public boolean parntNodeCheck() throws KeeperException, InterruptedException{
boolean flag = true;
Stat exists = zk.exists(parentNode.substring(0, parentNode.length()-1), false);
if(null == exists){
String create = zk.create(parentNode.substring(0, parentNode.length()-1), parentNode.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println(parentNode+"is init...." + create);
}
return flag;
}
// 创建到zk的客户端链接.
public void registerServer(String hostname) throws KeeperException, InterruptedException{
parntNodeCheck();
String create = zk.create(parentNode+hostname, hostname.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
System.out.println(hostname+"is online...." + create);
}
// 业务功能
public void handleBussiness(String hostname){
System.out.println(hostname + " is working...");
// 实现死循环(让业务一直运行下去.)
while(true){
}
}
public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
// 获取ZK链接
DistributedServer servier = new DistributedServer();
servier.getConnect();
if(null != args[0]){
servier.registerServer(args[0]);
servier.handleBussiness(args[0]);
}
// System.out.println(parentNode.substring(0, parentNode.length()-1));
}
}
DistributedClient
package com.yanxml.zookeeper.demos.server;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
/**
* 分布式上线集群.(客户端)
*
* */
public class DistributedClient {
private static final String connectString = "192.168.31.60:2181,192.168.31.61:2181,192.168.31.62:2181";
private static final int sessionTimeout = 2000;
private static final String parentNode = "/servers";
// volatile 意义(多线程使用时候使用.)
private volatile List<String> serverList;
private ZooKeeper zk = null;
public void getConnect() throws IOException{
zk = new ZooKeeper(connectString, sessionTimeout, new Watcher(){
@Override
public void process(WatchedEvent event) {
// 收到事件通知后的回调函数(我们自己的处理逻辑)
System.out.println(event.getType()+"---"+event.getPath());
try{
// 重新更新服务器列表.并且注册了监听
getServerList();
}catch(Exception e){
}
}
});
}
public void getServerList() throws KeeperException, InterruptedException{
// 获取服务器子结点信息 并对父结点进行监听
List<String> children = zk.getChildren(parentNode, true);
// 创建一个局部的list来存储服务器信息
List<String> servers = new ArrayList<>();
for(String child:children){
// child是子结点的结点名称
byte[] data = zk.getData(parentNode+"/"+child, false, null);
servers.add(new String(data));
}
// 把 servers赋值给成员变量serverlist,以提供给各个线程使用.
serverList = servers;
// 打印下服务器列表
System.out.println(serverList);
}
// 业务功能
public void handleBussiness(){
System.out.println("Client" + " is working...");
// 实现死循环(让业务一直运行下去.)
while(true){
}
}
public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
// 获取zk链接
DistributedClient client = new DistributedClient();
client.getConnect();
// 获取servers的子结点信息(并监听),从中获取服务器信息列表
client.getServerList();
// 业务线程启动
client.handleBussiness();
}
}
运行
我们使用Maven打包后,分别带参数启动.
# 启动服务器2
localhost:target Sean$ java -cp zookeeper-0.0.1-SNAPSHOT.jar com.yanxml.zookeeper.demos.server.DistributedServer mac1
Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8
2019-03-23 18:01:54,945 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:zookeeper.version=3.4.6-1569965, built on 02/20/2014 09:09 GMT
2019-03-23 18:01:54,946 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:host.name=localhost
2019-03-23 18:01:54,946 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:java.version=1.8.0_102
2019-03-23 18:01:54,946 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:java.vendor=Oracle Corporation
2019-03-23 18:01:54,946 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:java.home=/Library/Java/JavaVirtualMachines/jdk1.8.0_102.jdk/Contents/Home/jre
2019-03-23 18:01:54,946 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:java.class.path=zookeeper-0.0.1-SNAPSHOT.jar
2019-03-23 18:01:54,946 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:java.library.path=/Users/Sean/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.
2019-03-23 18:01:54,946 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:java.io.tmpdir=/var/folders/lm/j_tf25pd1bn1lvf3nm1qkjd40000gn/T/
2019-03-23 18:01:54,947 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:java.compiler=<NA>
2019-03-23 18:01:54,947 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:os.name=Mac OS X
2019-03-23 18:01:54,947 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:os.arch=x86_64
2019-03-23 18:01:54,947 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:os.version=10.14
2019-03-23 18:01:54,947 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:user.name=Sean
2019-03-23 18:01:54,948 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:user.home=/Users/Sean
2019-03-23 18:01:54,948 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:user.dir=/Users/Sean/Documents/Gitrep/bigdata/zookeeper/target
2019-03-23 18:01:54,951 [main] INFO [org.apache.zookeeper.ZooKeeper] - Initiating client connection, connectString=192.168.31.60:2181,192.168.31.61:2181,192.168.31.62:2181 sessionTimeout=2000 [email protected]099f62
2019-03-23 18:01:54,988 [main-SendThread(192.168.31.60:2181)] INFO [org.apache.zookeeper.ClientCnxn] - Opening socket connection to server 192.168.31.60/192.168.31.60:2181. Will not attempt to authenticate using SASL (unknown error)
2019-03-23 18:01:55,119 [main-SendThread(192.168.31.60:2181)] INFO [org.apache.zookeeper.ClientCnxn] - Socket connection established to 192.168.31.60/192.168.31.60:2181, initiating session
2019-03-23 18:01:55,145 [main-SendThread(192.168.31.60:2181)] INFO [org.apache.zookeeper.ClientCnxn] - Session establishment complete on server 192.168.31.60/192.168.31.60:2181, sessionid = 0x169a99d0b810003, negotiated timeout = 4000
None---null
mac1is online..../servers/mac1
mac1 is working...
# 启动服务器1
localhost:target Sean$ java -cp zookeeper-0.0.1-SNAPSHOT.jar com.yanxml.zookeeper.demos.server.DistributedServer mac0
Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8
2019-03-23 18:01:51,870 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:zookeeper.version=3.4.6-1569965, built on 02/20/2014 09:09 GMT
2019-03-23 18:01:51,871 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:host.name=localhost
2019-03-23 18:01:51,871 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:java.version=1.8.0_102
2019-03-23 18:01:51,871 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:java.vendor=Oracle Corporation
2019-03-23 18:01:51,871 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:java.home=/Library/Java/JavaVirtualMachines/jdk1.8.0_102.jdk/Contents/Home/jre
2019-03-23 18:01:51,871 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:java.class.path=zookeeper-0.0.1-SNAPSHOT.jar
2019-03-23 18:01:51,871 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:java.library.path=/Users/Sean/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.
2019-03-23 18:01:51,871 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:java.io.tmpdir=/var/folders/lm/j_tf25pd1bn1lvf3nm1qkjd40000gn/T/
2019-03-23 18:01:51,871 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:java.compiler=<NA>
2019-03-23 18:01:51,872 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:os.name=Mac OS X
2019-03-23 18:01:51,872 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:os.arch=x86_64
2019-03-23 18:01:51,872 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:os.version=10.14
2019-03-23 18:01:51,872 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:user.name=Sean
2019-03-23 18:01:51,872 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:user.home=/Users/Sean
2019-03-23 18:01:51,872 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:user.dir=/Users/Sean/Documents/Gitrep/bigdata/zookeeper/target
2019-03-23 18:01:51,874 [main] INFO [org.apache.zookeeper.ZooKeeper] - Initiating client connection, connectString=192.168.31.60:2181,192.168.31.61:2181,192.168.31.62:2181 sessionTimeout=2000 [email protected]099f62
2019-03-23 18:01:51,908 [main-SendThread(192.168.31.62:2181)] INFO [org.apache.zookeeper.ClientCnxn] - Opening socket connection to server 192.168.31.62/192.168.31.62:2181. Will not attempt to authenticate using SASL (unknown error)
2019-03-23 18:01:52,040 [main-SendThread(192.168.31.62:2181)] INFO [org.apache.zookeeper.ClientCnxn] - Socket connection established to 192.168.31.62/192.168.31.62:2181, initiating session
2019-03-23 18:01:52,079 [main-SendThread(192.168.31.62:2181)] INFO [org.apache.zookeeper.ClientCnxn] - Session establishment complete on server 192.168.31.62/192.168.31.62:2181, sessionid = 0x369a99d1a8d0002, negotiated timeout = 4000
None---null
mac0is online..../servers/mac0
mac0 is working...
# 启动客户端
localhost:target Sean$ java -cp zookeeper-0.0.1-SNAPSHOT.jar com.yanxml.zookeeper.demos.server.DistributedClient
Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8
2019-03-23 18:01:47,117 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:zookeeper.version=3.4.6-1569965, built on 02/20/2014 09:09 GMT
2019-03-23 18:01:47,118 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:host.name=localhost
2019-03-23 18:01:47,118 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:java.version=1.8.0_102
2019-03-23 18:01:47,118 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:java.vendor=Oracle Corporation
2019-03-23 18:01:47,118 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:java.home=/Library/Java/JavaVirtualMachines/jdk1.8.0_102.jdk/Contents/Home/jre
2019-03-23 18:01:47,118 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:java.class.path=zookeeper-0.0.1-SNAPSHOT.jar
2019-03-23 18:01:47,118 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:java.library.path=/Users/Sean/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.
2019-03-23 18:01:47,118 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:java.io.tmpdir=/var/folders/lm/j_tf25pd1bn1lvf3nm1qkjd40000gn/T/
2019-03-23 18:01:47,118 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:java.compiler=<NA>
2019-03-23 18:01:47,119 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:os.name=Mac OS X
2019-03-23 18:01:47,119 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:os.arch=x86_64
2019-03-23 18:01:47,119 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:os.version=10.14
2019-03-23 18:01:47,119 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:user.name=Sean
2019-03-23 18:01:47,119 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:user.home=/Users/Sean
2019-03-23 18:01:47,119 [main] INFO [org.apache.zookeeper.ZooKeeper] - Client environment:user.dir=/Users/Sean/Documents/Gitrep/bigdata/zookeeper/target
2019-03-23 18:01:47,120 [main] INFO [org.apache.zookeeper.ZooKeeper] - Initiating client connection, connectString=192.168.31.60:2181,192.168.31.61:2181,192.168.31.62:2181 sessionTimeout=2000 [email protected]099f62
2019-03-23 18:01:47,155 [main-SendThread(192.168.31.61:2181)] INFO [org.apache.zookeeper.ClientCnxn] - Opening socket connection to server 192.168.31.61/192.168.31.61:2181. Will not attempt to authenticate using SASL (unknown error)
2019-03-23 18:01:47,244 [main-SendThread(192.168.31.61:2181)] INFO [org.apache.zookeeper.ClientCnxn] - Socket connection established to 192.168.31.61/192.168.31.61:2181, initiating session
2019-03-23 18:01:47,268 [main-SendThread(192.168.31.61:2181)] INFO [org.apache.zookeeper.ClientCnxn] - Session establishment complete on server 192.168.31.61/192.168.31.61:2181, sessionid = 0x269a99d0b8c0003, negotiated timeout = 4000
None---null
[]
[]
Client is working...
-- 关闭服务器2
NodeChildrenChanged---/servers
[mac0]
-- 启动服务器2(自动感知)
NodeChildrenChanged---/servers
[mac1, mac0]