深入剖析SolrCloud(三)

 在上一篇中介绍了SolrCloud的第一个模块---构建管理solr集群状态信息的zookeeper集群。当我们在solr服务器启动时拥有了这样一个Zookeeper集群后,显然我们需要连接到Zookeeper集群的方便手段,在这一篇中我将对Zookeeper客户端相关的各个封装类进行分析。

SolrZkClient类是Solr服务器用来与Zookeeper集群进行通信的接口类,它包含的主要组件有:
 
  
  
  
  
  1. private ConnectionManager connManager; 
  2.   private volatile SolrZooKeeper keeper; 
  3.   private ZkCmdExecutor zkCmdExecutor = new ZkCmdExecutor(); 

其中ConnectionManager是Watcher的实现类,主要负责对客户端与Zookeeper集群之间连接的状态变化信息进行响应,关于Watcher的详细介绍,可以参考http://zookeeper.apache.org/doc/trunk/zookeeperProgrammers.html#ch_zkWatches, SolrZooKeeper类是一个包装类,没有实际意义,ZkCmdExecutor类是负责在连接失败的情况下,重试某种操作特定次数,具体的操作是ZkOperation这个抽象类的具体实现子类,其execute方法中包含了具体操作步骤,这些操作包括新建一个Znode节点,读取Znode节点数据,创建Znode路径,删除Znode节点等Zookeeper操作。 首先来看它的构造函数,先创建ConnectionManager对象来响应两端之间的状态变化信息,然后ZkClientConnectionStrategy类是一个连接策略抽象类,它包含连接和重连两种策略,并且采用模板方法模式,具体的实现是通过静态累不类ZkUpdate来实现的,DefaultConnectionStrategy是它的一个实现子类,它覆写了connect和reconnect两个连接策略方法。

 

  
  
  
  
  1. public SolrZkClient(String zkServerAddress, int zkClientTimeout, 
  2.     ZkClientConnectionStrategy strat, final OnReconnect onReconnect, int clientConnectTimeout) throws InterruptedException, 
  3.     TimeoutException, IOException { 
  4.   connManager = new ConnectionManager("ZooKeeperConnection Watcher:" 
  5.       + zkServerAddress, this, zkServerAddress, zkClientTimeout, strat, onReconnect); 
  6.   strat.connect(zkServerAddress, zkClientTimeout, connManager, 
  7.       new ZkUpdate() { 
  8.         @Override 
  9.         public void update(SolrZooKeeper zooKeeper) { 
  10.           SolrZooKeeper oldKeeper = keeper; 
  11.           keeper = zooKeeper; 
  12.           if (oldKeeper != null) { 
  13.             try { 
  14.               oldKeeper.close(); 
  15.             } catch (InterruptedException e) { 
  16.               // Restore the interrupted status 
  17.               Thread.currentThread().interrupt(); 
  18.               log.error("", e); 
  19.               throw new ZooKeeperException(SolrException.ErrorCode.SERVER_ERROR, 
  20.                   "", e); 
  21.             } 
  22.           } 
  23.         } 
  24.       }); 
  25.   connManager.waitForConnected(clientConnectTimeout); 
  26.   numOpens.incrementAndGet(); 

 

值得注意的是,构造函数中生成的ZkUpdate匿名类对象,它的update方法会被调用,

在这个方法里,会首先将已有的老的SolrZooKeeperg关闭掉,然后放置上一个新的SolrZooKeeper。做好这些准备工作以后,就会去连接Zookeeper服务器集群,

connManager.waitForConnected(clientConnectTimeout);//连接zk服务器集群,默认30秒超时时间

其实具体的连接动作是new SolrZooKeeper(serverAddress, timeout, watcher)引发的,上面那句代码只是在等待指定时间,看是否已经连接上。

如果连接Zookeeper服务器集群成功,那么就可以进行Zookeeper的常规操作了:

1) 是否已经连接

 

  
  
  
  
  1. public boolean isConnected() { 
  2.    return keeper != null && keeper.getState() == ZooKeeper.States.CONNECTED; 
  3.  } 

 

2)  是否存在某个路径的Znode

 

  
  
  
  
  1. public Stat exists(final String path, final Watcher watcher, boolean retryOnConnLoss) throws KeeperException, InterruptedException { 
  2.   if (retryOnConnLoss) { 
  3.     return zkCmdExecutor.retryOperation(new ZkOperation() { 
  4.       @Override 
  5.       public Stat execute() throws KeeperException, InterruptedException { 
  6.         return keeper.exists(path, watcher); 
  7.       } 
  8.     }); 
  9.   } else { 
  10.     return keeper.exists(path, watcher); 
  11.   } 

3) 创建一个Znode节点

 

  
  
  
  
  1. public String create(final String path, final byte data[], final List<ACL> acl, final CreateMode createMode, boolean retryOnConnLoss) throws KeeperException, InterruptedException { 
  2.   if (retryOnConnLoss) { 
  3.     return zkCmdExecutor.retryOperation(new ZkOperation() { 
  4.       @Override 
  5.       public String execute() throws KeeperException, InterruptedException { 
  6.         return keeper.create(path, data, acl, createMode); 
  7.       } 
  8.     }); 
  9.   } else { 
  10.     return keeper.create(path, data, acl, createMode); 
  11.   } 

4)  获取指定路径下的孩子Znode节点

 

  
  
  
  
  1. public List<String> getChildren(final String path, final Watcher watcher, boolean retryOnConnLoss) throws KeeperException, InterruptedException { 
  2.    if (retryOnConnLoss) { 
  3.      return zkCmdExecutor.retryOperation(new ZkOperation() { 
  4.        @Override 
  5.        public List<String> execute() throws KeeperException, InterruptedException { 
  6.          return keeper.getChildren(path, watcher); 
  7.        } 
  8.      }); 
  9.    } else { 
  10.      return keeper.getChildren(path, watcher); 
  11.    } 
  12.  } 

5) 获取指定Znode上附加的数据

 

  
  
  
  
  1. public byte[] getData(final String path, final Watcher watcher, final Stat stat, boolean retryOnConnLoss) throws KeeperException, InterruptedException { 
  2.   if (retryOnConnLoss) { 
  3.     return zkCmdExecutor.retryOperation(new ZkOperation() { 
  4.       @Override 
  5.       public byte[] execute() throws KeeperException, InterruptedException { 
  6.         return keeper.getData(path, watcher, stat); 
  7.       } 
  8.     }); 
  9.   } else { 
  10.     return keeper.getData(path, watcher, stat); 
  11.   } 

6)  在指定Znode上设置数据

 

  
  
  
  
  1. public Stat setData(final String path, final byte data[], final int version, boolean retryOnConnLoss) throws KeeperException, InterruptedException { 
  2.   if (retryOnConnLoss) { 
  3.     return zkCmdExecutor.retryOperation(new ZkOperation() { 
  4.       @Override 
  5.       public Stat execute() throws KeeperException, InterruptedException { 
  6.         return keeper.setData(path, data, version); 
  7.       } 
  8.     }); 
  9.   } else { 
  10.     return keeper.setData(path, data, version); 
  11.   } 

7) 创建路径

 

  
  
  
  
  1. public void makePath(String path, byte[] data, CreateMode createMode, Watcher watcher, boolean failOnExists, boolean retryOnConnLoss) throws KeeperException, InterruptedException { 
  2.   if (log.isInfoEnabled()) { 
  3.     log.info("makePath: " + path); 
  4.   } 
  5.   boolean retry = true
  6.    
  7.   if (path.startsWith("/")) { 
  8.     path = path.substring(1, path.length()); 
  9.   } 
  10.   String[] paths = path.split("/"); 
  11.   StringBuilder sbPath = new StringBuilder(); 
  12.   for (int i = 0; i < paths.length; i++) { 
  13.     byte[] bytes = null
  14.     String pathPiece = paths[i]; 
  15.     sbPath.append("/" + pathPiece); 
  16.     final String currentPath = sbPath.toString(); 
  17.     Object exists = exists(currentPath, watcher, retryOnConnLoss); 
  18.     if (exists == null || ((i == paths.length -1) && failOnExists)) { 
  19.       CreateMode mode = CreateMode.PERSISTENT; 
  20.       if (i == paths.length - 1) { 
  21.         mode = createMode; 
  22.         bytes = data; 
  23.         if (!retryOnConnLoss) retry = false
  24.       } 
  25.       try { 
  26.         if (retry) { 
  27.           final CreateMode finalMode = mode; 
  28.           final byte[] finalBytes = bytes; 
  29.           zkCmdExecutor.retryOperation(new ZkOperation() { 
  30.             @Override 
  31.             public Object execute() throws KeeperException, InterruptedException { 
  32.               keeper.create(currentPath, finalBytes, ZooDefs.Ids.OPEN_ACL_UNSAFE, finalMode); 
  33.               return null
  34.             } 
  35.           }); 
  36.         } else { 
  37.           keeper.create(currentPath, bytes, ZooDefs.Ids.OPEN_ACL_UNSAFE, mode); 
  38.         } 
  39.       } catch (NodeExistsException e) { 
  40.          
  41.         if (!failOnExists) { 
  42.           // TODO: version ? for now, don't worry about race 
  43.           setData(currentPath, data, -1, retryOnConnLoss); 
  44.           // set new watch 
  45.           exists(currentPath, watcher, retryOnConnLoss); 
  46.           return
  47.         } 
  48.          
  49.         // ignore unless it's the last node in the path 
  50.         if (i == paths.length - 1) { 
  51.           throw e; 
  52.         } 
  53.       } 
  54.       if(i == paths.length -1) { 
  55.         // set new watch 
  56.         exists(currentPath, watcher, retryOnConnLoss); 
  57.       } 
  58.     } else if (i == paths.length - 1) { 
  59.       // TODO: version ? for now, don't worry about race 
  60.       setData(currentPath, data, -1, retryOnConnLoss); 
  61.       // set new watch 
  62.       exists(currentPath, watcher, retryOnConnLoss); 
  63.     } 
  64.   } 

8) 删除指定Znode

 

  
  
  
  
  1. public void delete(final String path, final int version, boolean retryOnConnLoss) throws InterruptedException, KeeperException { 
  2.   if (retryOnConnLoss) { 
  3.     zkCmdExecutor.retryOperation(new ZkOperation() { 
  4.       @Override 
  5.       public Stat execute() throws KeeperException, InterruptedException { 
  6.         keeper.delete(path, version); 
  7.         return null
  8.       } 
  9.     }); 
  10.   } else { 
  11.     keeper.delete(path, version); 
  12.   } 

   我们再回过头来看看ConnectionManager类是如何响应两端的连接状态信息的变化的,它最重要的方法是process方法,当它被触发回调时,会从WatchedEvent参数中得到事件的各种状态信息,比如连接成功,会话过期(此时需要进行重连),连接断开等。

 

  
  
  
  
  1. public synchronized void process(WatchedEvent event) { 
  2.   if (log.isInfoEnabled()) { 
  3.     log.info("Watcher " + this + " name:" + name + " got event " + event + " path:" + event.getPath() + " type:" + event.getType()); 
  4.   } 
  5.  
  6.   state = event.getState(); 
  7.   if (state == KeeperState.SyncConnected) { 
  8.     connected = true
  9.     clientConnected.countDown(); 
  10.   } else if (state == KeeperState.Expired) { 
  11.     connected = false
  12.     log.info("Attempting to reconnect to recover relationship with ZooKeeper..."); 
  13.     //尝试重新连接zk服务器 
  14.     try { 
  15.       connectionStrategy.reconnect(zkServerAddress, zkClientTimeout, this, 
  16.           new ZkClientConnectionStrategy.ZkUpdate() { 
  17.             @Override 
  18.             public void update(SolrZooKeeper keeper) throws InterruptedException, TimeoutException, IOException { 
  19.               synchronized (connectionStrategy) { 
  20.                 waitForConnected(SolrZkClient.DEFAULT_CLIENT_CONNECT_TIMEOUT); 
  21.                 client.updateKeeper(keeper); 
  22.                 if (onReconnect != null) { 
  23.                   onReconnect.command(); 
  24.                 } 
  25.                 synchronized (ConnectionManager.this) { 
  26.                   ConnectionManager.this.connected = true
  27.                 } 
  28.               } 
  29.                
  30.             } 
  31.           }); 
  32.     } catch (Exception e) { 
  33.       SolrException.log(log, "", e); 
  34.     } 
  35.     log.info("Connected:" + connected); 
  36.   } else if (state == KeeperState.Disconnected) { 
  37.     connected = false
  38.   } else { 
  39.     connected = false
  40.   } 
  41.   notifyAll(); 

 

你可能感兴趣的:(java,随笔,java语言,solrCloud,休闲)