一、Zookeeper安装与搭建
服务器集群和注册中心集群不一样的:
服务器集群为了解决高并发、注册中心集群
1.1、windows环境安装
环境要求:必须要有jdk环境,本次讲课使用jdk1.8(Zookeeper是Java写的,而且结构类似XML)
1.安装jdk
2.安装Zookeeper. 在官网http://zookeeper.apache.org/下载zookeeper.我下载的是zookeeper-3.4.6版本。
解压zookeeper-3.4.6至D:\machine\zookeeper-3.4.6.
在D:\machine 新建data及log目录。
3.ZooKeeper的安装模式分为三种,分别为:单机模式(stand-alone)、集群模式和集群伪分布模式。ZooKeeper 单机模式的安装相对比较简单,如果第一次接触ZooKeeper的话,建议安装ZooKeeper单机模式或者集群伪分布模式。
安装单击模式。 至D:\machine\zookeeper-3.4.6\conf 复制 zoo_sample.cfg 并粘贴到当前目录下,命名zoo.cfg.
1.2、Zookeeper集群环境搭建(Linux)
环境要求:必须要有jdk环境,本次讲课使用jdk1.8
(1)结构
一共三个节点(zk服务器集群规模不小于3个节点),要求服务器之间系统时间保持一致
(2)上传zk并且解压
进行解压: tar -zxvf zookeeper-3.4.6.tar.gz
重命名: mv zookeeper-3.4.6 zookeeper
(3)修改zookeeper环境变量
vi /etc/profile
export JAVA_HOME=/opt/jdk1.8.0_71
export ZOOKEEPER_HOME=/usr/local/zookeeper
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
export PATH=$JAVA_HOME/bin:$ZOOKEEPER_HOME/bin:$PATH
source /etc/profile
(4)修改zoo_sample.cfg文件
cd /usr/local/zookeeper/conf
mv zoo_sample.cfg zoo.cfg
修改conf: vi zoo.cfg 修改两处
(1) dataDir=/usr/local/zookeeper/data(注意同时在zookeeper创建data目录)
(2)最后面添加
server.0=bhz:2888:3888
server.1=hadoop1:2888:3888
server.2=hadoop2:2888:3888
(5)创建服务器标识
服务器标识配置:
创建文件夹: mkdir data
创建文件myid并填写内容为0: vi
myid (内容为服务器标识 : 0)
(6)复制zookeeper
进行复制zookeeper目录到hadoop01和hadoop02
还有/etc/profile文件
把hadoop01、 hadoop02中的myid文件里的值修改为1和2
路径(vi /usr/local/zookeeper/data/myid)
(7)启动zookeeper
启动zookeeper:
路径: /usr/local/zookeeper/bin
执行: zkServer.sh start
(注意这里3台机器都要进行启动)
状态: zkServer.sh
status(在三个节点上检验zk的mode,一个leader和俩个follower)
1.3、Zookeeper配置文件介绍
# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just
# example sakes.
dataDir=/home/myuser/zooA/data
# the port at which the clients will connect
clientPort=2181
# ZooKeeper server and its port no. # ZooKeeper ensemble should know about every other machine in the ensemble # specify server id by creating 'myid' file in the dataDir # use hostname instead of IP address for convenient maintenance
server.1=127.0.0.1:2888:3888
server.2=127.0.0.1:2988:3988
server.3=127.0.0.1:2088:3088
#
# Be sure to read the maintenance section of the
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
# autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature <br>
#autopurge.purgeInterval=1
dataLogDir=/home/myuser/zooA/log
(1)tickTime:心跳时间,为了确保连接存在的,以毫秒为单位,最小超时时间为两个心跳时间
(2)initLimit:多少个心跳时间内,允许其他server连接并初始化数据,如果ZooKeeper管理的数据较大,则应相应增大这个值
(3)clientPort:服务的监听端口
(4)dataDir:用于存放内存数据库快照的文件夹,同时用于集群的myid文件也存在这个文件夹里(注意:一个配置文件只能包含一个dataDir字样,即使它被注释掉了。)
(5)dataLogDir:用于单独设置transaction log的目录,transaction log分离可以避免和普通log还有快照的竞争
(6)syncLimit:多少个tickTime内,允许follower同步,如果follower落后太多,则会被丢弃。
(7)server.A=B:C:D:
A是一个数字,表示这个是第几号服务器,B是这个服务器的ip地址
C第一个端口用来集群成员的信息交换,表示的是这个服务器与集群中的Leader服务器交换信息的端口
D是在leader挂掉时专门用来进行选举leader所用
二、Java操作Zookeeper
2.1、创建节点(znode) 方法:
create:
提供了两套创建节点的方法,同步和异步创建节点方式。
同步方式:
参数1,节点路径《名称) : InodeName (不允许递归创建节点,也就是说在父节点不存在
的情况下,不允许创建子节点)
参数2,节点内容: 要求类型是字节数组(也就是说,不支持序列化方式,如果需要实现序
列化,可使用java相关序列化框架,如Hessian、Kryo框架)
参數3,节点权限: 使用Ids.OPEN_ACL_UNSAFE开放权限即可。(这个参数一般在权展
没有太高要求的场景下,没必要关注)
参数4,节点类型: 创建节点的类型: CreateMode,提供四种首点象型
PERSISTENT 持久化节点
PERSISTENT_SEQUENTIAL 顺序自动编号持久化节点,这种节点会根据当前已存在的节点数自动加 1
EPHEMERAL 临时节点, 客户端session超时这类节点就会被自动删除
EPHEMERAL_SEQUENTIAL 临时自动编号节点
2.2、Maven依赖信息
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.6</version>
</dependency>
2.3、Zookeeper客户端连接
CreateMode类型分为4种:
- 1.PERSISTENT--持久型,永久保存在磁盘
- 2.PERSISTENT_SEQUENTIAL--持久顺序型,节点重复时会自动ID自增保证唯一性
- 3.EPHEMERAL--临时型
- 4.EPHEMERAL_SEQUENTIAL--临时顺序型
ublic class Test001 {
//ZK连接地址
private static final String ADDRES = "127.0.0.1:2181";
//session 会话,超时会话时间
private static final int SESSION_OUTTIME = 2000;
//信号量,阻塞程序执行,用户等待zookeeper连接成功,发送成功信号,
private static final CountDownLatch countDownLatch = new CountDownLatch(1);
public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
//创建一个链接
ZooKeeper zk = new ZooKeeper(ADDRES, SESSION_OUTTIME, new Watcher() {
//通过Watcher监听节点是否发生变化
public void process(WatchedEvent event) {
// 获取事件状态
KeeperState keeperState = event.getState();
// 获取事件类型
EventType eventType = event.getType();
if (KeeperState.SyncConnected == keeperState) {
if (EventType.None == eventType) {
countDownLatch.countDown();
System.out.println("zk 启动连接...");
}
}
}
});
// 进行阻塞
//CreateMode有四种类型
countDownLatch.await();
String result = zk.create("/itmayeidu_Lasting", "Lasting".getBytes(), Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT);
System.out.println(result);
zk.close();
}
}
2.4、创建Zookeeper节点信息
1. 创建持久节点,并且允许任何服务器可以操作
String result = zk.create("/itmayiedu_Lasting", "Lasting".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println("result:" + result);
2. 创建临时节点
String result = zk.create("/itmayiedu_temp", "temp".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
System.out.println("result:" + result);
2.5、Watcher
(1)什么是Watcher
接口类Watcher用于表示一个标准的事件处理器,其定义了事件通知相关的逻辑,包含KeeperState(通知状态)和EventType(事件类型)两个枚举类,同时定义了事件的回调方法:process(WatchedEvent event)
(2)什么是Watcher接口
同一个事件类型在不同的通知状态中代表的含义有所不同
回调方法process():
process方法是Watcher接口中的一个回调方法,当ZooKeeper向客户端发送一个Watcher事件通知时,客户端就会对相应的process方法进行回调,从而实现对事件的处理。process方法的定义如下:
abstract public void process(WatchedEvent event);
WatchedEvent包含了每一个事件的三个基本属性:通知状态(keeperState),事件类型(EventType)和节点路径(path)
ZooKeeper使用WatchedEvent对象来封装服务端事件并传递给Watcher,从而方便回调方法process对服务端事件进行处理。
(3)Watcher代码
public class ZkClientWatcher implements Watcher {
// 集群连接地址
private static final String CONNECT_ADDRES = "192.168.110.159:2181,192.168.110.160:2181,192.168.110.162:2181";
// 会话超时时间
private static final int SESSIONTIME = 2000;
// 信号量,让zk在连接之前等待,连接成功后才能往下走.
private static final CountDownLatch countDownLatch = new CountDownLatch(1);
private static String LOG_MAIN = "【main】 ";
private ZooKeeper zk;
public void createConnection(String connectAddres, int sessionTimeOut) {
try {
zk = new ZooKeeper(connectAddres, sessionTimeOut, this);
System.out.println(LOG_MAIN + "zk 开始启动连接服务器....");
countDownLatch.await();
} catch (Exception e) {
e.printStackTrace();
}
}
public boolean createPath(String path, String data) {
try {
this.exists(path, true);
this.zk.create(path, data.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println(LOG_MAIN + "节点创建成功, Path:" + path + ",data:" + data);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 判断指定节点是否存在
*
* @param path
* 节点路径
*/
public Stat exists(String path, boolean needWatch) {
try {
return this.zk.exists(path, needWatch);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public boolean updateNode(String path,String data) throws KeeperException, InterruptedException {
exists(path, true);
this.zk.setData(path, data.getBytes(), -1);
return false;
}
public void process(WatchedEvent watchedEvent) {
// 获取事件状态
KeeperState keeperState = watchedEvent.getState();
// 获取事件类型
EventType eventType = watchedEvent.getType();
// zk 路径
String path = watchedEvent.getPath();
System.out.println("进入到 process() keeperState:" + keeperState + ", eventType:" + eventType + ", path:" + path);
// 判断是否建立连接
if (KeeperState.SyncConnected == keeperState) {
if (EventType.None == eventType) {
// 如果建立建立成功,让后程序往下走
System.out.println(LOG_MAIN + "zk 建立连接成功!");
countDownLatch.countDown();
} else if (EventType.NodeCreated == eventType) {
System.out.println(LOG_MAIN + "事件通知,新增node节点" + path);
} else if (EventType.NodeDataChanged == eventType) {
System.out.println(LOG_MAIN + "事件通知,当前node节点" + path + "被修改....");
}
else if (EventType.NodeDeleted == eventType) {
System.out.println(LOG_MAIN + "事件通知,当前node节点" + path + "被删除....");
}
}
System.out.println("--------------------------------------------------------");
}
public static void main(String[] args) throws KeeperException, InterruptedException {
ZkClientWatcher zkClientWatcher = new ZkClientWatcher();
zkClientWatcher.createConnection(CONNECT_ADDRES, SESSIONTIME);
// boolean createResult = zkClientWatcher.createPath("/p15", "pa-644064");
zkClientWatcher.updateNode("/pa2","7894561");
}
}
三、使用Zookeeper实现负载均衡原理(Dubbo服务调用负载均衡)
思路:服务器端将启动的服务注册到ZK注册中心,采用临时节点。客户端从ZK节点上获取最新服务节点信息,本地使用负载均衡算法,随机分配服务器。
创建项目工程:
3.1、Maven依赖
<dependencies>
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.8</version>
</dependency>
</dependencies>
3.2、创建Server服务端,将服务注册到ZK节点上
ZkServerScoekt服务
//##ServerScoekt服务端
public class ZkServerScoekt implements Runnable {
private int port = 18080;
public static void main(String[] args) throws IOException {
int port = 18080;
ZkServerScoekt server = new ZkServerScoekt(port);
Thread thread = new Thread(server);
thread.start();
}
public ZkServerScoekt(int port) {
this.port = port;
}
public void run() {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(port);
System.out.println("Server start port:" + port);
Socket socket = null;
while (true) {
socket = serverSocket.accept();
new Thread(new ServerHandler(socket)).start();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (serverSocket != null) {
serverSocket.close();
}
} catch (Exception e2) {
}
}
}
}
3.3、ServerHandler
public class ServerHandler implements Runnable {
private Socket socket;
public ServerHandler(Socket socket) {
this.socket = socket;
}
public void run() {
BufferedReader in = null;
PrintWriter out = null;
try {
in = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
out = new PrintWriter(this.socket.getOutputStream(), true);
String body = null;
while (true) {
body = in.readLine();
if (body == null)
break;
System.out.println("Receive : " + body);
out.println("Hello, " + body);
}
} catch (Exception e) {
if (in != null) {
try {
in.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
if (out != null) {
out.close();
}
if (this.socket != null) {
try {
this.socket.close();
} catch (IOException e1) {
e1.printStackTrace();
}
this.socket = null;
}
}
}
}
3.4、ZkServerClient
public class ZkServerClient {
public static List<String> listServer = new ArrayList<String>();
public static void main(String[] args) {
initServer();
ZkServerClient client= new ZkServerClient();
BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
while (true) {
String name;
try {
name = console.readLine();
if ("exit".equals(name)) {
System.exit(0);
}
client.send(name);
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 注册所有server
public static void initServer() {
listServer.clear();
//放在集合里边是为了集群
listServer.add("127.0.0.1:18080");
}
// 获取当前server信息
public static String getServer() {
return listServer.get(0);
}
public void send(String name) {
String server = ZkServerClient.getServer();//getServer默认取第0个
String[] cfg = server.split(":");
Socket socket = null;
BufferedReader in = null;
PrintWriter out = null;
try {
socket = new Socket(cfg[0], Integer.parseInt(cfg[1]));
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
out.println(name);
while (true) {
String resp = in.readLine();
if (resp == null)
break;
else if (resp.length() > 0) {
System.out.println("Receive : " + resp);
break;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (out != null) {
out.close();
}
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
3.5、改造ZkServerScoekt
//再启动一个服务18081
public class ZkServerScoekt implements Runnable {
private static int port = 18081;
public static void main(String[] args) throws IOException {
ZkServerScoekt server = new ZkServerScoekt(port);
Thread thread = new Thread(server);
thread.start();
}
public ZkServerScoekt(int port) {
this.port = port;
}
public void regServer() {
// 向ZooKeeper注册当前服务器
ZkClient client = new ZkClient("127.0.0.1:2181", 60000, 1000);
String path = "/test/server" + port;
if (client.exists(path))
client.delete(path);
client.createEphemeral(path, "127.0.0.1:" + port);
}
public void run() {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(port);
regServer();
System.out.println("Server start port:" + port);
Socket socket = null;
while (true) {
socket = serverSocket.accept();
new Thread(new ServerHandler(socket)).start();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (serverSocket != null) {
serverSocket.close();
}
} catch (Exception e2) {
}
}
}
}
3.6、改造ZkServerClient
public class ZkServerClient {
public static List<String> listServer = new ArrayList<String>();
public static String parent = "/test";
public static void main(String[] args) {
initServer();
ZkServerClient client = new ZkServerClient();
BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
while (true) {
String name;
try {
name = console.readLine();
if ("exit".equals(name)) {
System.exit(0);
}
client.send(name);
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 注册所有server
public static void initServer() {
// listServer.add("127.0.0.1:18080");
final ZkClient zkClient = new ZkClient("127.0.0.1:2181", 6000, 1000);
List<String> children = zkClient.getChildren(parent);
getChilds(zkClient, children);
// 监听事件
zkClient.subscribeChildChanges(parent, new IZkChildListener() {
public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {
getChilds(zkClient, currentChilds);
}
});
}
private static void getChilds(ZkClient zkClient, List<String> currentChilds) {
listServer.clear();
for (String p : currentChilds) {
String pathValue = (String) zkClient.readData(parent + "/" + p);
listServer.add(pathValue);
}
serverCount = listServer.size();
System.out.println("从zk读取到信息:" + listServer.toString());
}
// 请求次数
private static int reqestCount = 1;
// 服务数量
private static int serverCount = 0;
// 获取当前server信息
public static String getServer() {
// 实现负载均衡
String serverName = listServer.get(reqestCount % serverCount);
++reqestCount;
return serverName;
}
public void send(String name) {
String server = ZkServerClient.getServer();
String[] cfg = server.split(":");
Socket socket = null;
BufferedReader in = null;
PrintWriter out = null;
try {
socket = new Socket(cfg[0], Integer.parseInt(cfg[1]));
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
out.println(name);
while (true) {
String resp = in.readLine();
if (resp == null)
break;
else if (resp.length() > 0) {
System.out.println("Receive : " + resp);
break;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (out != null) {
out.close();
}
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}