zookeeper部署集群一般是基数(3,5,7等),目的节约资源。
常用查询端口占用情况的dos命令:
执行zookeeper的get时,除了能看到值,还能看到很多对应的属性等等:
多个客户端可以向zookeeper里面存有序信息,这样可以从里面取出最小的来作为leader执行代码,达到一个分布式锁的一个效果。
导入依赖:
<dependency>
<groupId>org.apache.zookeepergroupId>
<artifactId>zookeeperartifactId>
<version>3.8.0version>
dependency>
客户端类的相关操作:
package client;
import java.util.List;
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 Client1 {
public static void main(String[] args) throws Exception {
// CountDownLatch c = new CountDownLatch(1);
// ZooKeeperWatcher zw= new ZooKeeperWatcher();
//${__setProperty(Token,${token_1},)}
// ${__property(Token)}
// wx/goods/detail
// id 1181045 false text/plain true
W1 w1 = new W1();
//
ZooKeeper zk = new ZooKeeper("127.0.0.1:2181", 10000, w1);
//
w1.zk = zk;
// zk.exists("/aa1", true);
// zk.getData("/aa1", true, null);
zk.getChildren("/aa1", true);
//
Thread.sleep(2000000);
// String rs = zk.create("/aa1/a1", "127.0.0.2:8082".getBytes(),Ids.OPEN_ACL_UNSAFE ,CreateMode.EPHEMERAL_SEQUENTIAL );
// System.out.println(rs+"-----------------");
// byte [] b= zk.getData("/getOrder/a", true, null);
// System.out.println(new String(b));
// List children = zk.getChildren("/getOrder", true);
// System.out.println(children);
// for(String p:children) {
//
// byte [] b=zk.getData("/getOrder/"+p, true, null);
// System.out.println(p+" : "+new String(b));
// }
// zw.zk = zk;
// c.await();
}
}
监听器watcher对应的代码:
package client;
import java.util.List;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooKeeper;
public class W1 implements Watcher{
public ZooKeeper zk = null;
@Override
public void process(WatchedEvent event) {
System.out.println("进入 process 。。。。。event = " + event);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (event == null) {
return;
}
// 连接状态
KeeperState keeperState = event.getState();
// 事件类型
EventType eventType = event.getType();
// 受影响的path
String path = event.getPath();
if (KeeperState.SyncConnected == keeperState) {
// 成功连接上ZK服务器
if (EventType.None == eventType) {
System.out.println( "成功连接上ZK服务器");
}
// 创建节点
else if (EventType.NodeCreated == eventType) {
System.out.println( "节点创建");
try {
zk.exists("/aa1", true);
// zk.getChildren("/aa1", true);
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
}
// 更新节点
else if (EventType.NodeDataChanged == eventType) {
System.out.println( "节点数据更新");
try {
// zk.exists("/aa1", true);
zk.getChildren("/aa1", true);
} catch (Exception e) {
e.printStackTrace();
}
}
// 更新子节点
else if (EventType.NodeChildrenChanged == eventType) {
System.out.println("子节点变更");
try {
// zk.exists("/aa1", true);
List<String >children = zk.getChildren("/aa1", true);
System.out.println(children);
for(String p:children) {
byte [] b=zk.getData("/aa1/"+p, true, null);
System.out.println(p+" : "+new String(b));
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 删除节点
else if (EventType.NodeDeleted == eventType) {
try {
zk.exists("/aa1", true);
// zk.getChildren("/aa1", true);
} catch (KeeperException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("节点 " + path + " 被删除");
} else
;
} else if (KeeperState.Disconnected == keeperState) {
System.out.println("与ZK服务器断开连接");
} else if (KeeperState.AuthFailed == keeperState) {
System.out.println("权限检查失败");
} else if (KeeperState.Expired == keeperState) {
System.out.println("会话失效");
} else
;
System.out.println("--------------------------------------------");
}
}
上面是经常用到的一些代码逻辑!!
一个zookeeper服务器,三台SpringBoot系统(一台consumer服务消费者,两台服务提供者)。
思想流程:
zk-consumer01 服务消费者代码如下:
Watcher01类对象:
package com.itholmes.config;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;
import javax.servlet.ServletContext;
import java.util.ArrayList;
import java.util.List;
public class Watcher01 implements Watcher {
@Autowired
ServletContext servletContext;
public ZooKeeper zk = null;
@Override
public void process(WatchedEvent event) {
System.out.println("进入process---event = " + event);
//这里对别后台使用
List<String> children = new ArrayList<>();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (event == null){
return;
}
//连接状态
Event.KeeperState keeperState = event.getState();
//事件类型
Event.EventType eventType = event.getType();
//受影响的path
String path = event.getPath();
if (Event.KeeperState.SyncConnected== keeperState){
//成功连接上ZK服务器
if (Event.EventType.None == eventType){
System.out.println("成功连接上ZK服务器");
//拿到最新的zk数据
try {
children = zk.getChildren("/provider", true);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//创建节点
else if (Event.EventType.NodeCreated == eventType){
System.out.println("节点创建");
try {
//zk.exists(path,true);
//zk.getChildren("/aa1", true);
//拿到最新的zk数据
children = zk.getChildren("/provider", true);
System.out.println("节点创建");
} catch (Exception e) {
e.printStackTrace();
}
}
//更新子节点
else if (Event.EventType.NodeChildrenChanged == eventType){
System.out.println("子节点变更");
try {
//zk.exists(path,true);
//拿到最新的zk数据
children = zk.getChildren("/provider", true);
System.out.println("子节点变更");
} catch (Exception e) {
e.printStackTrace();
}
}
//删除节点
else if (Event.EventType.NodeDeleted == eventType){
try {
//zk.exists(path,true);
//拿到最新的zk数据
children = zk.getChildren("/provider", true);
System.out.println("删除节点");
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("节点 " + path + " 被删除");
}else
;
ArrayList<Object> list_IP = new ArrayList<>();
for (String child : children) {
byte[] data = new byte[0];
try {
data = zk.getData("/provider/" + child, true, null);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
list_IP.add(new String(data));
}
//查看获取到的IP
for (Object o : list_IP) {
System.out.println(o.toString());
}
//通过随机数来获取
//int i = (int)(Math.random() * (size - 1 - 0 + 1) + 0);
servletContext.setAttribute("zk",list_IP);
} else if (Event.KeeperState.Disconnected == keeperState) {
System.out.println("与ZK服务器断开连接");
} else if (Event.KeeperState.AuthFailed == keeperState) {
System.out.println("权限检查失败");
} else if (Event.KeeperState.Expired == keeperState) {
System.out.println("会话失效");
} else
;
System.out.println("--------------------------------------------");
}
}
ZkConfig类:
package com.itholmes.config;
import org.apache.zookeeper.ZooKeeper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
@Configuration
public class Zkconfig {
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
@Bean
public Watcher01 myZkWatcher() {
//创建一个watcher
return new Watcher01();
}
@Bean
public ZooKeeper create(Watcher01 myZkWatcher) throws IOException {
System.out.println("准备...");
ZooKeeper zk = new ZooKeeper("150.158.199.52:2181", 3000,myZkWatcher );
myZkWatcher.zk = zk;
System.out.println("连接..");
return zk;
}
}
MathRandom类:
package com.itholmes.utils;
import java.util.List;
public class MathRandom {
public static String getRandomString(List<String> list){
int size = list.size();
int i = (int)(Math.random() * (size - 1 - 0 + 1) + 0);
return list.get(i);
}
}
Consumer01类:
package com.itholmes.controller;
import com.itholmes.utils.MathRandom;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
@RestController
public class Consumer01 {
@Resource
RestTemplate restTemplate;
@GetMapping("/consumer/pay")
public String testZk(HttpServletRequest request){
ServletContext servletContext = request.getServletContext();
List<String> zk = (List<String>)servletContext.getAttribute("zk");
String randomString = MathRandom.getRandomString(zk);
//我要拿到zookeeper那边的信息,放到这里
String forObject = restTemplate.getForObject("http://" + randomString + "/provider", String.class);
return forObject;
}
}
SpringBoot主启动类:
package com.itholmes;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class ZkComsumer01Main {
public static void main(String[] args) {
SpringApplication.run(ZkComsumer01Main.class,args);
}
}
zk-consumer8001(项目里面打错了应该是zk-provider8001) 服务消费者代码如下:
MyListener类:
package com.itholmes.listener;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class MyListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("项目启动~~~~");
try {
ZooKeeper zk = new ZooKeeper("150.158.199.52:2181", 10000, null);
zk.create("/provider/consumer","127.0.0.1:8001".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("项目销毁~~~");
}
}
MyServletConfig类
package com.itholmes.config;
import com.itholmes.listener.MyListener;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyServletConfig {
//将自己的servlet添加进容器
@Bean
public ServletListenerRegistrationBean getMyListener(){
//传入自己的监听器
ServletListenerRegistrationBean servletListenerRegistrationBean = new ServletListenerRegistrationBean(new MyListener());
return servletListenerRegistrationBean;
}
}
MyController类:
package com.itholmes.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
@RequestMapping("/provider")
public String provider(){
return "你好,我是8001";
}
}
ZkConsumer8001Main类:
package com.itholmes;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class ZkConsumer8001Main {
public static void main(String[] args) {
SpringApplication.run(ZkConsumer8001Main.class,args);
}
}
按照上面方式,在创建一个provider服务提供者,记得修改端口号就可以。
之后就可以启动三台项目和zoo keeper服务器,来进行测试运行,对zookeeper节点的增加删除等修改操作,尝试消费者端这边能否实时更新!
项目地址:https://gitee.com/it-sherlock/java-design-pattern-summary/tree/master/zookeeper%E5%AE%9E%E7%8E%B0%E7%AE%80%E6%98%93%E7%89%88dubbo%E6%A1%86%E6%9E%B6/zookeeper-project01
因为,zookeeper对象的watcher , 每个客户端都需要进行心跳检测,所以zookeeper对象必须一直存在。
如果你想在Spring Boot启动的时候运行一些特定的代码,你可以实现接口 ApplicationRunner或者 CommandLineRunner,这两个接口实现方式一样,它们都只提供了一个run方法。
如果有多个Runner启动器,可以通过order接口或者注解来排序:
创建一个新类,在这个类中,创建一个static变量,随时赋值也是可以的!
第一种:listener:项目启动后,生成对象,并且对象一直存在。
第二种:bean放到ioc容器:项目启动后,bean就执行,并且对象一直存在。
第三种:放到servletContext中:可以通过启动器。