1.1:Zookeeper介绍
ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务(consumer能感知到provider状态发生变化),提供的功能:
配置维护、 将好多配置文件放入zookeeper进行管理
分布式同步、 协调服务(consumer能感知到provider状态发生变化)
域名服务、组服务等。
1.2:Zookeeper集群的特征
安装zk集群的时候,安装奇数个节点。
奇数台和偶数台的容错能力一样。
防止脑裂(活锁)出现
Zookeeper容错能决定的,容错能力指的是在一个集群环境中,允许同时有几台服务器宕机
Zk集群中需要半数以上的服务器正常工作,集群才可以工作,半数以上宕机,整个集群停止工作
2:使用Zk集群实现服务注册与发现
2.1实现集群服务发布和注册
2.1.1创建zk-rmi-cluseter-provider(jar)项目
2.1.2添加依赖
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bjsxt.zk.rmi</groupId>
<artifactId>zk-rmi-cluster-provider</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<!-- 添加公共资源依赖 -->
<dependency>
<groupId>com.bjsxt.resources</groupId>
<artifactId>rmi-resources</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- zk的api依赖 -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.8</version>
</dependency>
</dependencies>
</project>
2.1.3拷贝OrdersServiceImpl实现类
2.1.4 公共资源存放类
package com.bjsxt.app;
public interface Constants {
//ZK的链接地址
String ZK_HOST="192.168.25.101:2181,192.168.25.101:2182,192.168.25.101:2183";
//链接超时时间
Integer TIME_OUT=5000;
//用来进行服务注册管理的节点
String PROVIDER="/provider";
//实现服务注册的节点
String URL=PROVIDER+"/url";
/***
* /provider
* |---url000000000000001
* |---url000000000000002
* |---url000000000000003
*
*/
}
2.1.5创建ProviderApp实现服务发布和注册
package com.bjsxt.app;
import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;
import java.util.concurrent.CountDownLatch;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs.Ids;
import com.bjsxt.service.OrdersService;
import com.bjsxt.service.impl.OrdersServiceImpl;
import org.apache.zookeeper.ZooKeeper;
public class ProviderApp {
//实现线程同步
private CountDownLatch latch=new CountDownLatch(1);
/***
* 建立和zk集群的建立
*/
public ZooKeeper getConnection() {
ZooKeeper zk=null;
try {
zk=new ZooKeeper(Constants.ZK_HOST, Constants.TIME_OUT, new Watcher() {
@Override
public void process(WatchedEvent event) {
// 判断是否和zk集群建立链接
if(event.getState()==Event.KeeperState.SyncConnected) {
latch.countDown(); //唤醒阻塞的线程
}
}
});
//阻塞主线程
latch.await();
}catch(Exception ex) {
ex.printStackTrace();
}
return zk;
}
/***
* 创建瞬时的,顺序节点,同时将自己的url写入到瞬时节点
*/
public void providerRegister(String url) {
try {
ZooKeeper zk=getConnection();
if(zk!=null) {
//创建瞬时,顺序节点
zk.create(Constants.URL, //指定的节点路径,注意自己手动创建/provider
url.getBytes(), //节点中保存的数据
Ids.OPEN_ACL_UNSAFE, //权限
CreateMode.EPHEMERAL_SEQUENTIAL);//节点类型
}
}catch(Exception ex) {
ex.printStackTrace();
}
}
/***
* 实现服务的发布
*/
public void pulisher(Integer port) {
try {
//指定暴露的远程服务的访问端口
LocateRegistry.createRegistry(port);
//创建OrdersServiceImpl实现类
OrdersService remote=new OrdersServiceImpl();
//定义远程访问的url
String name="rmi://localhost:"+port+"/sxt";
//进行给remote对象绑定一个远程访问的地址
Naming.bind(name, remote);
//调用服务的注册方法,完成服务注册
providerRegister(name);
System.out.println("==========发布远程服务===========9999");
}catch(Exception ex) {
ex.printStackTrace();
}
}
/***
* 发布集群服务
*/
public static void main(String[] args) {
ProviderApp app=new ProviderApp();
app.pulisher(9999);
}
}
3:实现服务的发现和消费
3.2.1创建zk-rmi-cluster-consumer(jar)项目
3.2.2添加依赖
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bjsxt.zk.rmi.consumer</groupId>
<artifactId>zk-rmi-cluster-consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<!-- 添加公共资源依赖 -->
<dependency>
<groupId>com.bjsxt.resources</groupId>
<artifactId>rmi-resources</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- zk的api依赖 -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.8</version>
</dependency>
</dependencies>
</project>
3.2.3拷贝Constants接口
3.2.4创建ConsumerApp启动发现和消费服务
package com.bjsxt.app;
import java.rmi.ConnectException;
import java.rmi.Naming;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import com.bjsxt.pojo.Orders;
import com.bjsxt.service.OrdersService;
/***
* 实现服务的发现和消费
* @author EDZ
*
*/
public class ConsumerApp {
//缓存可用的服务地址列表
private volatile List<String> urls=new ArrayList<>();
//实现线程同步
private CountDownLatch latch=new CountDownLatch(1);
/***
* 建立和zk集群的建立
*/
public ZooKeeper getConnection() {
ZooKeeper zk=null;
try {
zk=new ZooKeeper(Constants.ZK_HOST, Constants.TIME_OUT, new Watcher() {
@Override
public void process(WatchedEvent event) {
// 判断是否和zk集群建立链接
if(event.getState()==Event.KeeperState.SyncConnected) {
latch.countDown(); //唤醒阻塞的线程
}
}
});
//阻塞主线程
latch.await();
}catch(Exception ex) {
ex.printStackTrace();
}
return zk;
}
/***
* 读取zk集群中注册的服务
*/
public void providerFetch() {
try {
ZooKeeper zk=getConnection();
if(zk!=null) {
//读取/provider接下的所有子节点
List<String> children = zk.getChildren(Constants.PROVIDER, new Watcher() {
/***
* 观察/provider节点的子节点是否有变化
*/
@Override
public void process(WatchedEvent event) {
// TODO Auto-generated method stub
if(event.getType()==Event.EventType.NodeChildrenChanged) {
System.out.println("========子节点发生变化========");
providerFetch(); //如果有子节点变化,回调重新读取
}
}
});
//保存读取的服务地址
List<String> path=new ArrayList<>();
//遍历子节点集合children
for(String node:children) {
//System.out.println(node);
//获得node节点中的数据
byte[] data = zk.getData(Constants.PROVIDER+"/"+node, false, null);
//byte[]转化为String
String url=new String(data);
path.add(url);
}
this.urls=path;
}
}catch(Exception ex) {
ex.printStackTrace();
}
}
/***
* 完成服务消费
*/
public void consumer() {
while (true) {
try {
// 产生一个随机数
int index = ThreadLocalRandom.current().nextInt(this.urls.size());
// 远程服务的访问地址
String name = this.urls.get(index);
// 获得远程服务的代理对象
OrdersService remote = (OrdersService) Naming.lookup(name);
// 获得会员订单的集合
List<Orders> list = remote.loadOrdersList(111111);
for (Orders orders : list) {
System.out.println(orders.getId() + "\t"
+ orders.getVip() + "\t" + orders.getOdate() + "\t"
+ orders.getRemark());
}
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 无参数构造方法
*/
public ConsumerApp() {
providerFetch();
}
public static void main(String[] args) {
ConsumerApp app=new ConsumerApp();
app.consumer();
}
}
4:Zookeeper配置文件的发布
4.1实现文件上传
将文件上传到配置中心zk集群
4.1.1创建config-serve项目
4.1.2添加依赖
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bjsxt.config.server</groupId>
<artifactId>config-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<!-- zk的api依赖 -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.8</version>
</dependency>
</dependencies>
</project>
4.1.3classpath下创建db.properties
4.1.4创建Constants接口
package com.bjsxt.app;
public interface Constants {
//ZK的链接地址
String ZK_HOST="192.168.25.101:2181,192.168.25.101:2182,192.168.25.101:2183";
//链接超时时间
Integer TIME_OUT=5000;
//用来保存配置文件的节点
String CONFIG="/db.properties";
}
4.1.5上传db.properties
package com.bjsxt.app;
import java.io.InputStream;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
public class UploadFile {
//实现线程同步
private CountDownLatch latch=new CountDownLatch(1);
/***
* 建立和zk集群的建立
*/
public ZooKeeper getConnection() {
ZooKeeper zk=null;
try {
zk=new ZooKeeper(Constants.ZK_HOST, Constants.TIME_OUT, new Watcher() {
@Override
public void process(WatchedEvent event) {
// 判断是否和zk集群建立链接
if(event.getState()==Event.KeeperState.SyncConnected) {
latch.countDown(); //唤醒阻塞的线程
}
}
});
//阻塞主线程
latch.await();
}catch(Exception ex) {
ex.printStackTrace();
}
return zk;
}
/***
* 配置文件上传到zk集群
*/
public void uploadConfigFile() {
try {
//从UploadFile所在的包下加载
//InputStream is=UploadFile.class.getResourceAsStream("db.properties");
//获得需要上传的文件对应的输入流对象,从classpath目录下加载指定的文件
InputStream is=UploadFile.class.getClassLoader()
.getResourceAsStream("db.properties");
//创建byte[]用来保存读取的数据
byte[] buf=new byte[1024];
int len=0;
byte[] data= {};//声明数组
//通过is流读取db.properties文件中的内容
while((len=is.read(buf))!=-1) {
//将buf中的数据元素拷贝 data
data = Arrays.copyOf(buf,len);
}
System.out.println(is);
//链接zk
ZooKeeper zk=getConnection();
zk.create(Constants.CONFIG, data, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println("-----------实现配置文件上传------------");
}catch(Exception ex) {
ex.printStackTrace();
}
}
public static void main(String[] args) {
UploadFile upload=new UploadFile();
upload.uploadConfigFile();
}
}
5.从配置中心读取配置文件
5.1.1创建config-red项目
5.1.2添加依赖
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bjsxt.red</groupId>
<artifactId>config-red</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<!-- zk的api依赖 -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.8</version>
</dependency>
</dependencies>
</project>
5.1.3拷贝Constants接口
5.1.4读取配置文件
package com.bjsxt.app;
import java.io.ByteArrayInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
public class RedApp {
//实现线程同步
private CountDownLatch latch=new CountDownLatch(1);
/***
* 建立和zk集群的建立
*/
public ZooKeeper getConnection() {
ZooKeeper zk=null;
try {
zk=new ZooKeeper(Constants.ZK_HOST, Constants.TIME_OUT, new Watcher() {
@Override
public void process(WatchedEvent event) {
// 判断是否和zk集群建立链接
if(event.getState()==Event.KeeperState.SyncConnected) {
latch.countDown(); //唤醒阻塞的线程
}
}
});
//阻塞主线程
latch.await();
}catch(Exception ex) {
ex.printStackTrace();
}
return zk;
}
/***
* 读取配置文件,写入本地的db.properties文件
* @param args
*/
public void readConfigFile() {
try {
ZooKeeper zk=getConnection();
if(zk!=null) {
//获得db.properties节点中的数据
byte[] data = zk.getData(Constants.CONFIG, new Watcher() {
@Override
public void process(WatchedEvent event) {
// db.properties节点中的数据发生变化
if(event.getType()==Event.EventType.NodeDataChanged) {
System.out.println("======配置文件进行更新=======");
readConfigFile();//回调,重新读取配置文件
}
}
}, null);
//创建ByteArrayOutputStream输出流
/*ByteArrayOutputStream out=new ByteArrayOutputStream();
out.write(data);*/
//将data中的数据封装为ByteArrayInputStream对象
ByteArrayInputStream bais=new ByteArrayInputStream(data);
//创建Properties对象
Properties properties=new Properties();
//将inputStream中的数据封装到Properties
properties.load(bais);
//创建文件字符输出流对象
FileWriter out=new FileWriter("src\\main\\resources\\db.properties");
//将properties对象中的键值对数据,持久化db.properties键值对文件
properties.store(out, "----comment----");
}
}catch(Exception ex) {
ex.printStackTrace();
}
}
public static void main(String[] args) {
RedApp app=new RedApp();
app.readConfigFile();
//让jvm阻塞
try {
System.in.read();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}