基于Solr4.4
先看一下ZK的目录结构
/collections----solr core的节点,集群的leader节点的选举在该节点下
--collection1
--leader_elect
---shard1
--election
其下是shard1的节点列表
---shard2
---election
其下是shard2的节点列表
---leaders
---shard1
---shard2
/configs
--myconf
其下是solr-home下的配置文件列表
/live_nodes----当前活动的节点列表
/overseer_elect/leader---overseer表示集群的状态维护的节点,
由该节点根据其下queue节点中的信息来更新clusterstate节点内容
{"id":"90338461809508352-192.168.1.111:8983_solr-n_0000000002"}
/overseer_elect/election/
其下是供elect的节点列表
/overseer/queue
/overseer/collection-queue-work
/overseer/queue-work
/clusterstate.json---集群的状态信息,每当该节点内容有变化时,
所有的节点都会根据本地内存中的clusterstate状态
1、集群启动过程
在这个过程中,主要通过zookeeper client对象,创建用于维护集群状态的Overseer节点(/overseer_elect),
用于Overseer的选举;
Overseer通过后台线程检测DistributeQueue(对该对象的操作是对zookeeper/overseer/queue节点的的读写)的变化
来更新集群状态节点(/clusterstate)的值;
创建相关的节点的(/live_nodes、/clusterstate.json、/aliases.json)watcher来更新集群的状态对象ClusterState。
首先集群启动过程是CoreContainer.load方法开始的
调用ZkController对象的构造函数中初始化SolrZkClient,
创建solrZkClient对象的过程包括创建ConnectionManager,该对象用于对zookeeper的连接进行管理,
是作为默认的watcher对象注册的;
参见DefaultConnectionStrategy.connect,创建了SolrZookeeper客户端连接对象,
该对象实现了zookeeper客户端接口,
并利用ZkUpdate回调更新SolrZkClient的成员变量keeper。
ConnectionManager对连接的管理,参见process接口,
if (state == KeeperState.SyncConnected)
//调用listeners回调
connectionStrategy.connected();
} else if (state == KeeperState.Expired) {
//创建新的SolrZookeeper客户端连接对象(实现了zookeeper客户端接口),
并利用ZkUpdate回调更新SolrZkClient的成员变量keeper
connectionStrategy.reconnect(zkServerAddress, zkClientTimeout, this, new ZkClientConnectionStrategy.ZkUpdate());
} else if (state == KeeperState.Disconnected) {
//调用listeners回调
connectionStrategy.disconnected();
}
在zookeeper连接完成后(ConnectionManager收到连接成功通知),如果connection过期,而重新连接,
会回调客户端OnReconnect命令接口,进行重新连接的过程
参见ZkController构造函数,
a、创建zkClient
zkClient = new SolrZkClient(zkServerAddress, zkClientTimeout, zkClientConnectTimeout,
// on reconnect, reload cloud info
new OnReconnect() {
@Override
public void command() {
//关键步骤如下:
//创建用于维护集群状态的对象Overseer
//overseer的节点
ZkController.this.overseer = new Overseer(shardHandler, adminPath, zkStateReader);
ElectionContext context = new OverseerElectionContext(zkClient, overseer, getNodeName());
//leader选举,基本原理是选举"/overseer_elect/election下seq最小的节点即为leader",
每个节点watch并它次小节点的变化,
//如果有变化通知,则checkIfIamLeader,
//如果当前节点上最小的,那么就runIamLeaderProcess,
更新"/overseer_elect/leader"下的节点信息,
//并调用Overseer.start(id)来启动两个后台线程,
ClusterStateUpdater(在process中更新/clusterstate.json节点的值)
和OverseerCollectionProcessor(处理管理collection的请求)
//这两个线程的职位分别如下,
//ClusterStateUpdater负责从queue获取任务,设置到queue-work中,进行处理工作;
//OverseerCollectionProcessor负责collection相关的Collection-queue-work的处理工作
overseerElector.joinElection(context, true);
//创建集群状态节点/clusterstate.json的watcher,监听该节点的变化(zkClient.exists(CLUSTER_STATE, new Watcher())),
//在watcher的process方法中,读取/clusterstate.json、/live_nodes节点数据到ZkStateReader.clusterState中
//创建/live_nodes节点下的watcher,在process方法中更新clusterState;
//创建/aliases.json节点的watcher,在process方法中更新clusterState;
//根据集群的状态clusterstate,更新ClusterState对象(创建Slice(Range)....)
zkStateReader.createClusterStateWatchersAndUpdate();
//创建/live_nodes
createEphemeralLiveNode();
......
}
b、zkClient创建完成后,利用该SolrZkClient,创建两个分布式队列DistributedQueue,queue、collection-queue,
创建用于选举leader的LeaderElector对象,
创建Zookeeper状态读取器ZkStateReader,
初始化init
该过程同上面的onReconnect的步骤是类似的,这里不再阐述。
2、加载core--创建collection
以上启动过程完成后,会异步加载core
a、发布core信息到zookeeper中,使得更新cloudstate状态
CoreContainer.preRegisterInZk
--ZkController.preRegister(CoreDescriptor)
---ZkController.publish(CoreDescriptor,String,Boolean)
--- overseerJobQueue.offer(ZkStateReader.toJSON(m));//放进queue中
b、zookeeper中创建/collections节点
--CoreContainer.create(CoreDescriptor)
--ZkContainer.createFromZk()
--ZkContainer.createCollectionZkNode(CloudDescriptor)//创建/collections节点
c、zookeeper中进行leader的选举/collections/collection1/leader_elect/shard1/election
----CoreContainer.registerCore(p.isTransient(), name, c, false);
--ZkController.registerInZk(ZkCore)
--zkController.joinElection
//创建/collections/collection1/leader_elect/shard1/election节点,进行leader选举,
//选举算法和overseer是一样的,都是seqno最小的为leader;leader选举完成后,
通过发送到queue中,更新cloudstate状态
---ShardLeaderElectionContext(init)
---LeaderElector.checkIfIamLeader
--LeaderElector.runIamLeaderProcess
---ShardLeaderElectionContext.runLeaderProcess
//放进queue中
---Overseer.getInQueue(zkClient).offer(ZkStateReader.toJSON(m));