zookeeper curator 学习及源码解析

 分两段,先分析InterProcessMutex这个类,再分析CuratorFramework。因为CuratorFramework复杂一点,是整个curator 包实现的基石。

InterProcessMutex 是一个分布式锁的实现。调试代码用的官方的2.9.1的example代码,改了一些参数,这样便于调试。

构造函数为:

public InterProcessMutex(CuratorFramework client, String path)

client是curator的CuratorFramework, path就是lock的路径,这个同一个lock的路径必须相同。

每个InterProcessMutex 有个LockInternals对象,是实现分布式的关键。

LockInternals有个StandardLockInternalsDriver作为辅助。

InterProcessMutex 主要有

acquire(),boolean acquire(long time, TimeUnit unit), release()。

accquire方法:

1.  调用了LockInternals的attemptLock 方法,如果lockPath返回不为null,则表示获取成功。

     String lockPath = internals.attemptLock(time, unit, getLockNodeBytes());

    这个InterProcessMutex是可重入的,方法是在线程本地纪录了一个重入的次数,每次重入加1,每次释放减1.

2.internals.attemptLock中,调用自己的私有方法internalLockLoop,无限循环,知道获取锁,或则异常。

while ( (client.getState() == CuratorFrameworkState.STARTED) && !haveTheLock )
{
   List<String>        children = getSortedChildren(); //得到所有lock Path的子节点
   String              sequenceNodeName = ourPath.substring(basePath.length() + 1); // +1 to include the slash
   
PredicateResults    predicateResults = driver.getsTheLock(client, children, sequenceNodeName, maxLeases); //判断自己是否得到锁,就是按名称排序之后,自己的节点的index是否为0
   if ( predicateResults.getsTheLock() ) //如果得到锁就返回
   {
       haveTheLock = true;
   }
   else
   
{    //没有拿到锁,获得前一个节点的名称,并且下面的地方添加watcher
       String  previousSequencePath = basePath + "/" + predicateResults.getPathToWatch();
       synchronized(this)
       {
           try
           
{
               // use getData() instead of exists() to avoid leaving unneeded watchers which is a type of resource leak
               
client.getData().usingWatcher(watcher).forPath(previousSequencePath);
               if ( millisToWait != null )//如果设置了时间,就等待一段时间后,删除自己的节点,并且返回false,表示没有拿到锁。返回在方法的最后,没有贴出。
               {
                   millisToWait -= (System.currentTimeMillis() - startMillis);
                   startMillis = System.currentTimeMillis();
                   if ( millisToWait <= 0 )
                   {
                       doDelete = true;    // timed out - delete our node
                       
break;
                   }

                   wait(millisToWait);
               }
               else
               
{   //没有设置时间,则一直等待。当前序节点释放的时候,watcher代码会吊用notifyAll方法,让以上的wait方法放回,再次尝试获取锁。
                   wait();
               }
           }
           catch ( KeeperException.NoNodeException e )
           {
               // it has been deleted (i.e. lock released). Try to acquire again
           
}
       }
   }
}

release方法很简单:

每次释放,线程本地纪录的次数减1,等于0的时候,就把自己的节点删除了。


下面分析 CuratorFramework接口以及实现类

这个类比较复杂,涉及重试机制,短线重连。分析这个类,首先需要对zookeeper官方的client包有所了解。

CuratorFrameworkImpl这个类是默认实现,有一下成员

private final CuratorZookeeperClient client; //封装关于zookeeper client的实现
private final ListenerContainer<CuratorListener> listeners; //监听器列表
private final ListenerContainer<UnhandledErrorListener> unhandledErrorListeners;
private final ThreadFactory threadFactory;
private final int maxCloseWaitMs; //关闭时候等待的ms数
private final BlockingQueue<OperationAndData<?>> backgroundOperations;//后台操作以及返回队列
private final NamespaceImpl namespace; //命名空间
private final ConnectionStateManager connectionStateManager; //连接状态管理器
private final List<AuthInfo> authInfos; //认证信息
...以下省略

CuratorFramework接口主要方法有:create(),delete(),checkExists(),inTransaction(),sync(),getChildren()以及start()和close()等等方法,调用这个方法会返回各种Builder接口的实现对象,基本都是实现了Pathable接口的。

Pathable接口有一个 forPath方法,这一连续的办法,实现了CuratorFramework的流式操作。

@Override
public void start()
{    。。。。。省略的代码
   try
   
{    
       connectionStateManager.start(); // ordering dependency - must be called before client.start()

       
final ConnectionStateListener listener = new ConnectionStateListener()
       {
           @Override
           
public void stateChanged(CuratorFramework client, ConnectionState newState)
           {
               if ( ConnectionState.CONNECTED == newState || ConnectionState.RECONNECTED == newState )
               {
                   logAsErrorConnectionErrors.set(true);
               }
           }
       };
       this.getConnectionStateListenable().addListener(listener); //注册一个state的监听器
       client.start(); //CuratorZookeeperClient的start,调用了ConnectState的start方法。开启了一个zookeeper连接
       executorService = Executors.newFixedThreadPool(2, threadFactory);  // 1 for listeners, 1 for background ops 提交到background操作的线程池。
       
executorService.submit(new Callable<Object>()
       {
           @Override
           
public Object call() throws Exception
           {
               backgroundOperationsLoop();
               return null;
           }
       });
   }
   catch ( Exception e )
   {
       handleBackgroundOperationException(null, e);
   }
}

CuratorZookeeperClient对象,有个ConnectionState成员变量,后者有个HandleHolder。这个HandleHolder持有了一个内部的interface,真正持有了一个ZooKeeper对象:

private interface Helper
{
   ZooKeeper getZooKeeper() throws Exception;
   String getConnectionString();
}

ConnectionState实现了zookeeper的Watcher接口,并把自己注册到了ZooKeeper连接中。

如果 event.getType() == Watcher.Event.EventType.None,则证明是连接状态发生了变化,如果是Expired,则调用reset方法,重新建立一个连接。旧的引用,没有做处理,可能是个隐患。

其他zookeeper的操作方法,操作会被封装成一个Callable<T> proc,提交到RetryLoop这个类static方法callWithRetry做重试及处理。

callWithRetry方法中,回调用CuratorZookeeperClient的blockUntilConnectedOrTimedOut,这个方法如果发现ConnectionState的状态不是连接上的,会注册一个tempWatcher,阻塞当前线程到连接成功或则超时。

CuratorFramework给我提供出来的功能就是如上述:

1. 连接超时的重连机制。(KeeperState.Unknown;KeeperState.NoSyncConnected,情况下也可能重连

2. 操作的时候的重试机制。(等待连接成功才会操作,失败可以有重试策略)

有个特别的流式操作,就是inTransaction(),它的实现,利用了zookeeper client支持MultiTransactionRecord对象,可以容纳多个操作,作为一个request。

你可能感兴趣的:(zookeeper curator 学习及源码解析)