前言

上章,我们简单了解了下ZooKeeper内的成员变量及调度服务.本章主要讲解下ZooKeeper的负载均衡.


正文

假设我们有3台服务器用于负载.那么有一台Client,其该怎么链接哪台机器呢?当其链接的主机宕机后该链接哪台服务器呢?我们可以使用NginxKeepAlive或者心跳机制.而使用今天的主角ZooKeeper,其也可以实现.

原始架构:
ZooKeeper 使用场景之 负载均衡
新架构:
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]

相关文章: