简介
es节点启动时,做的最重要的一件事就是加入集群,今天分析下es节点加入集群的源码。
主要知识点
es基础
基于lucene的分布式搜索引擎,不在累述,可以自行查阅资料。
Bully算法
提到分布式选举算法,大家都知道Paxos算法,但是这个算法比较复杂,es选取的是一个很简单的Bully算法,算法描述如下:
- 某个进程通过广播,获取所有能够连接到的进程的ID。
- 如果发现自己是ID最大的进程,则广播自己成为master节点,并广播该消息。
- 如果进程发现有其他进程比自己的ID大,则认为自己不能成为master,等待master广播。
整个选主流程非常简单,下面我们去es代码中一窥究竟。
代码分析
Node启动的时候做了很多事情,我们这次主要关注的涉及以下代码,discovery的默认实现是ZenDiscovery。
public Node start() throws NodeValidationException {
...
// 启动Discovery,这个模块用来发现其他节点。
discovery.start();
transportService.acceptIncomingRequests();
// 开始加入集群
discovery.startInitialJoin();
...
}
ZenDiscovery是Es默认实现的节点发现服务,上面调用了startInitialJoin()方法:
@Override
public void startInitialJoin() {
// start the join thread from a cluster state update. See {@link JoinThreadControl} for details.
clusterService.submitStateUpdateTask("initial_join", new LocalClusterUpdateTask() {
@Override
public ClusterTasksResult<LocalClusterUpdateTask> execute(ClusterState currentState) throws Exception {
// do the join on a different thread, the DiscoveryService waits for 30s anyhow till it is discovered
joinThreadControl.startNewThreadIfNotRunning(); return unchanged();
}
@Override
public void onFailure(String source, @org.elasticsearch.common.Nullable Exception e) {
logger.warn("failed to start initial join process", e);
}
});
}
这里直接向clusterService提交了一个任务,在execute方法中,通过joinThreadControl来启动一个线程进行join。JoinThreadControl是ZenDistovery的内部类,主要用来控制join线程,保证只有一个线程执行join任务,以及join成功后的处理,这样可以使得startInitialJoin()方法迅速返回,不阻塞node节点的start方法。
下面来看下startNewThreadIfNotRunning()方法:
public void startNewThreadIfNotRunning() {
ClusterService.assertClusterStateThread();
if (joinThreadActive()) {
return;
}
threadPool.generic().execute(new Runnable() {
@Override
public void run() {
Thread currentThread = Thread.currentThread();
if (!currentJoinThread.compareAndSet(null, currentThread)) {
return;
}
while (running.get() && joinThreadActive(currentThread)) {
try {
innerJoinCluster();
return;
} catch (Exception e) {
logger.error("unexpected error while joining cluster,trying again", e);
// Because we catch any exception here, we want to know in
// tests if an uncaught exception got to this point and the test infra uncaught exception
// leak detection can catch this. In practise no uncaught exception should leak
assert ExceptionsHelper.reThrowIfNotNull(e);