理论篇:git
Curator是Netflix开源的一套ZooKeeper客户端框架. Netflix在使用ZooKeeper的过程当中发现ZooKeeper自带的客户端太底层, 应用方在使用的时候须要本身处理不少事情, 因而在它的基础上包装了一下, 提供了一套更好用的客户端框架. Netflix在用ZooKeeper的过程当中遇到的问题, 咱们也遇到了, 因此开始研究一下, 首先从他在github上的源码, wiki文档以及Netflix的技术blog入手.
看完官方的文档以后, 发现Curator主要解决了三类问题: github
Curator列举的ZooKeeper使用过程当中的几个问题
初始化链接的问题: 在client与server之间握手创建链接的过程当中, 若是握手失败, 执行全部的同步方法(好比create, getData等)将抛出异常
自动恢复(failover)的问题: 当client与一台server的链接丢失,并试图去链接另一台server时, client将回到初始链接模式
session过时的问题: 在极端状况下, 出现ZooKeeper session过时, 客户端须要本身去监听该状态并从新建立ZooKeeper实例 .
对可恢复异常的处理:当在server端建立一个有序ZNode, 而在将节点名返回给客户端时崩溃, 此时client端抛出可恢复的异常, 用户须要本身捕获这些异常并进行重试
使用场景的问题:Zookeeper提供了一些标准的使用场景支持, 可是ZooKeeper对这些功能的使用说明文档不多, 并且很容易用错. 在一些极端场景下如何处理, zk并无给出详细的文档说明. 好比共享锁服务, 当服务器端建立临时顺序节点成功, 可是在客户端接收到节点名以前挂掉了, 若是不能很好的处理这种状况, 将致使死锁.
Curator主要从如下几个方面下降了zk使用的复杂性:
重试机制:提供可插拔的重试机制, 它将给捕获全部可恢复的异常配置一个重试策略, 而且内部也提供了几种标准的重试策略(好比指数补偿).
链接状态监控: Curator初始化以后会一直的对zk链接进行监听, 一旦发现链接状态发生变化, 将做出相应的处理.
zk客户端实例管理:Curator对zk客户端到server集群链接进行管理. 并在须要的状况, 重建zk实例, 保证与zk集群的可靠链接
各类使用场景支持:Curator实现zk支持的大部分使用场景支持(甚至包括zk自身不支持的场景), 这些实现都遵循了zk的最佳实践, 并考虑了各类极端状况.
Curator经过以上的处理, 让用户专一于自身的业务自己, 而无需花费更多的精力在zk自己. spring
实操篇:安全
CuratorFrameworkFactory类提供了两个方法, 一个工厂方法newClient, 一个构建方法build. 使用工厂方法newClient能够建立一个默认的实例, 而build构建方法能够对实例进行定制. 当CuratorFramework实例构建完成, 紧接着调用start()方法, 在应用结束的时候, 须要调用close()方法. CuratorFramework是线程安全的. 在一个应用中能够共享同一个zk集群的CuratorFramework. 服务器
核心对象CuratorFramework的建立以下:session
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3); CuratorFramework client = CuratorFrameworkFactory.builder() .connectString("") .sessionTimeoutMs(5000) .connectionTimeoutMs(5000) .retryPolicy(retryPolicy) .build(); client.start();
须要使用分布式锁的地方,代码以下:框架
String lockOn= "test"; InterProcessMutex mutex = new InterProcessMutex(curatorFramework,lockOn); boolean locked =mutex.acquire(0,TimeUnit.SECONDS);
//finally部分
mutex.release();
分布式锁经常使用于定时任务,使用自定义注解,使用spring aspect around, 在真正的代码执行以前尝试获取锁,获取不到直接退出,获取到锁的,执行具体业务,代码以下:分布式
@Aspect public class DistributedLockAspect{ @Pointcut("@annotation(com.**.**.DistributedLock") public void methodAspect(){}; @Around("methodAspect()") public Object execute(ProceedingJoinPoint joinPoint) throws Exception{ String lockPath = "/opt/zookeeper/lock"; InterProcessMutex mutex = new InterProcessMutex(cruatorFramework,lockPath); try{ boolean locked = mutex.acquire(0,TimeUnit.SECONDS); if(!locked){ return null; }else{ return joinPoint.proceed(); } }catch(Exception e){ e.printStackTrace(); }finally{ mutex.release(); } } }
自定义注解:ui
1 @Target(ElementType.METHOD) 2 @Retention(RetentionPolicy.RUNTIME) 3 public @interface DistributedLock{ 4 String lockPath(); 5 }
注意事项:spa
1. CuratorFramework对象建议在应用中作单例处理,在具体使用处 注入使用, 并在应用结束前销毁,代码以下:
@Configration public class CuratorConfigration{ @Bean public CuratorFramework initCuratorFramework(){ //忽略 // 参照前面 CuratorFramework 对象建立部分 } }
2. 在aspect部分将curatorFramework对象进行关闭
@PreDestroy public void destroy(){ CloseableUtils.closeQuietly(curatorFramework); }