springBoot 整合 ZooKeeper Java客户端之 Apache Curator 实战
一、添加项目所需依赖:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <!-- Apache Curator 包含了几个包: curator-client:提供一些客户端的操作,例如重试策略等 curator-framework:对zookeeper的底层api的一些封装 curator-recipes:封装了一些高级特性,如:Cache事件监听、选举、分布式锁、分布式计数器、分布式Barrier等--> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>2.13.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> </dependencies>
二、连接zooKeeper 服务,使用 Client API:
1、 application.properties自定义配置: 使用 @ConfigurationProperties 、@EnableConfigurationProperties 注解用来属性映射类
apache.zookeeper.connect-url=127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183 #连接地址,此地址为单机集群;
apache.zookeeper.session-timeout=60000 #会话超时时间,默认60000ms
apache.zookeeper.connection-timeout=15000 #连接创建超时时间,默认15000ms
apache.zookeeper.scheme=digest #访问控制 验证策略
apache.zookeeper.auth-id=username:password #权限 Id
apache.retrypolicy.base-sleep-time=1000 #重连策略,初始化间隔时间
apache.retrypolicy.max-retries=3 #重连次数
apache.retrypolicy.max-sleep=2147483647 #重连最长时间
2、配置类:
@ConfigurationProperties(prefix = "apache.zookeeper") @Configuration public class ApacheZooKeeperProperties { private String connectUrl; private int sessionTimeout; private int connectionTimeout; private String scheme; private String authId; public String getConnectUrl() { return connectUrl; } public void setConnectUrl(String connectUrl) { this.connectUrl = connectUrl; } public int getSessionTimeout() { return sessionTimeout; } public void setSessionTimeout(int sessionTimeout) { this.sessionTimeout = sessionTimeout; } public int getConnectionTimeout() { return connectionTimeout; } public void setConnectionTimeout(int connectionTimeout) { this.connectionTimeout = connectionTimeout; } public String getScheme() { return scheme; } public void setScheme(String scheme) { this.scheme = scheme; } public String getAuthId() { return authId; } public void setAuthId(String authId) { this.authId = authId; } }
@ConfigurationProperties(prefix = "apache.retrypolicy") @Configuration public class ApacheRetryPolicy { private int baseSleepTime = 1000; private int maxRetries = 29; private int maxSleep = 2147483647; public int getBaseSleepTime() { return baseSleepTime; } public void setBaseSleepTime(int baseSleepTime) { this.baseSleepTime = baseSleepTime; } public int getMaxRetries() { return maxRetries; } public void setMaxRetries(int maxRetries) { this.maxRetries = maxRetries; } public int getMaxSleep() { return maxSleep; } public void setMaxSleep(int maxSleep) { this.maxSleep = maxSleep; } }
3、演示代码:
/** * 演示 Apache Curator API * 1、增删查改 * 2、ACL 访问权限控制 * 3、注册 watch 事件的三个接口 */ @RestController public class CuratorClientApiText { private Logger logger = LoggerFactory.getLogger(CuratorClientApiText.class); @Autowired private CuratorFramework zkClient; @Autowired private ApacheZooKeeperProperties apacheZooKeeperProperties; private String userParentPath = "/user"; private String userPersistent = "/user/persistent"; private String userEphemeral = "/ephemeral"; private String userPersistentSequential = "/user/persistent_sequential"; private String userEphemeralSequential = "/ephemeral_sequential"; private String result = ""; /** * Curator API 是链式调用风格,遇到 forPath 接口就触发ZooKeeper 调用 * * 将演示创建 ZooKeeper 四种数据模型 * * @return */ @GetMapping("/create/node") public String createNode(){ try { //添加 acl 用户 List<ACL> aclList = new ArrayList<>(); aclList.add(new ACL(ZooDefs.Perms.ALL, new Id(apacheZooKeeperProperties.getScheme(), DigestAuthenticationProvider.generateDigest(apacheZooKeeperProperties.getAuthId())))); /** * CuratorListener监听,此监听主要针对background通知和错误通知; * 使用 watched() 只会观察一次,只对该节点本身 create、delete、setData 有效; * 使用 inBackground() 会异步监听到返回信息,一旦使用该接口,就不会有返回值 */ zkClient.getCuratorListenable().addListener(new CuratorListenerImpl()); /** * NodeCache可以监听节点本身创建、删除,以及内容的变化,但对于子节点的变化不会监听 */ NodeCache nodeCache = new NodeCache(zkClient,userParentPath); NodeCacheListenerImpl nodeCacheListener = new NodeCacheListenerImpl(); nodeCacheListener.setNodeCache(nodeCache); nodeCache.start(true); // 设置为 true 把该节点数据存储到本地 nodeCache.getListenable().addListener(nodeCacheListener); /** * PathChildrenCache用于监听所有子节点的变化 */ PathChildrenCache pathChildrenCache = new PathChildrenCache(zkClient, userParentPath, true); /** * StartMode: 初始化方式 * POST_INITIALIZED_EVENT : 异步初始化之后触发事件 * NORMAL:异步初始化 * BUILD_INITIAL_CACHE:同步初始化 */ pathChildrenCache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT); pathChildrenCache.getListenable().addListener(new PathChildrenCacheListenerImpl()); // 注册 watch 事件 /*Stat stat = zkClient.checkExists() .watched() .forPath(userParentPath); logger.info("/userParentPath 路径状态..." + stat); stat = zkClient.checkExists() .watched() .forPath(userPersistent); logger.info("/userPersistent 路径状态..." + stat); stat = zkClient.checkExists() .watched() .forPath(userEphemeral); logger.info("/userEphemeral 路径状态..." + stat);*/ //持久节点 creatingParentContainersIfNeeded() 接口自动递归创建所需节点的父节点 result = zkClient.create() .creatingParentContainersIfNeeded() .withMode(CreateMode.PERSISTENT) .withACL(aclList) .forPath(userPersistent, "userPersistentData".getBytes()); Thread.sleep(1000 * 5); logger.info("持久节点..." + result); //临时节点 不能有子节点 result = zkClient.create() .creatingParentContainersIfNeeded() .withMode(CreateMode.EPHEMERAL) .forPath(userEphemeral,"userEphemeralData".getBytes()); Thread.sleep(1000 * 5); logger.info("临时节点..." + result); //持久序列节点 result = zkClient.create() .creatingParentContainersIfNeeded() .withMode(CreateMode.PERSISTENT_SEQUENTIAL) .forPath(userPersistentSequential,"userPersistentSequentialData".getBytes()); Thread.sleep(1000 * 5); logger.info("持久有序节点..." + result); //临时序列节点 result = zkClient.create() .creatingParentContainersIfNeeded() .withMode(CreateMode.EPHEMERAL_SEQUENTIAL) .forPath(userEphemeralSequential,"userEphemeralSequentialData".getBytes()); Thread.sleep(1000 * 5); logger.info("临时有序节点..." + result); }catch(Exception e){ logger.info("创建节点失败..."); e.printStackTrace(); } return "curator api 创建节点"; } @GetMapping("/node/children") public String nodeChildren(){ try{ Stat stat = zkClient.checkExists().watched().forPath(userParentPath); Thread.sleep(1000 * 5); logger.info("判断节点是否存在..." + stat); List<String> userPaths = zkClient.getChildren().forPath(userParentPath); Thread.sleep(1000 * 5); logger.info("获取所有子节点..." + userPaths); }catch(Exception e){ logger.info("失败..."); e.printStackTrace(); } return "curator api 获取所有子节点"; } @GetMapping("/data/node") public String dataNode(){ try{ //获取一个节点的内容 byte[] bytes = zkClient.getData().forPath(userPersistent); Thread.sleep(1000 * 5); logger.info("获取节点数据..." + new String(bytes)); //修改一个节点的内容 Stat stat = zkClient.setData().forPath(userPersistent, "updateUserPersistentData".getBytes()); Thread.sleep(1000 * 5); logger.info("修改节点数据..." + stat); }catch(Exception e){ logger.info("失败..."); e.printStackTrace(); } return "curator api 获取、修改节点数据"; } @GetMapping("/delete/node") public String deleteNode(){ try{ //删除一个节点,强制指定版本进行删除 Stat stat = new Stat(); zkClient.getData().storingStatIn(stat).forPath(userPersistent); zkClient.delete().withVersion(stat.getVersion()).forPath(userPersistent); Thread.sleep(1000 * 5); //删除一个节点,并且递归删除其所有子节点 zkClient.delete().deletingChildrenIfNeeded().forPath(userParentPath); Thread.sleep(1000 * 5); }catch(Exception e){ logger.info("删除节点失败..."); e.printStackTrace(); } return "curator api 删除节点"; } }
/** * 连接zooKeeper server,获得zkClient */ @Configuration @EnableConfigurationProperties(value = {ApacheZooKeeperProperties.class, ApacheRetryPolicy.class}) public class ApacheCuratorConfig { private Logger logger = LoggerFactory.getLogger(ApacheCuratorConfig.class); @Autowired private ApacheZooKeeperProperties apacheZooKeeperProperties; @Autowired private ApacheRetryPolicy apacheRetryPolicy; CuratorFramework client = null; @Bean public CuratorFramework getCuratorFramework(){ logger.info("zooKeeper client init..."); try { //当zk连接时失败的重连策略 RetryPolicy retryPolicy = new ExponentialBackoffRetry(apacheRetryPolicy.getBaseSleepTime(), apacheRetryPolicy.getMaxRetries()); //获得实例对象,拿到ZK client //CuratorFramework client = CuratorFrameworkFactory.newClient(apacheZooKeeperProperties.getConnectUrl(), apacheZooKeeperProperties.getSessionTimeout(), apacheZooKeeperProperties.getConnectionTimeout(), retryPolicy); List<AuthInfo> authInfos = new ArrayList<>(); authInfos.add(new AuthInfo(apacheZooKeeperProperties.getScheme(), apacheZooKeeperProperties.getAuthId().getBytes())); client = CuratorFrameworkFactory.builder() .authorization(authInfos) .connectString(apacheZooKeeperProperties.getConnectUrl()) .sessionTimeoutMs(apacheZooKeeperProperties.getSessionTimeout()) .connectionTimeoutMs(apacheZooKeeperProperties.getConnectionTimeout()) .retryPolicy(retryPolicy) .namespace("workspace") .build(); client.start(); logger.info("zooKeeper client start..."); }catch (Exception e){ logger.info("zooKeeper connect error..."); e.printStackTrace(); } return client; } }