 Elasticsearch---启动初始化源码学习

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/JY_He/article/details/79654992

Elasticsearch整个大致流程:从Elastcisearch类的main方法入口,然后调用了BootStrap类的中的init方法,该方法会对环境和配置进行一系列的检测和初始化,其中最为主要的两个方法就是setup方法和start方法,setup的方法通过ModulesBuilder采用gucie进行不同模块的注入,start方法进行启动(keepAliveThread和node中的模块),如下图所示:


elasticsearch启动流程图

1.	org.elasticsearch.bootstrap.Elasticsearch类:
	一个私有的构造函数:private Elasticsearch() {}
	一个main方法:
public static void main(String[] args) throws StartupError {
        try {
            Bootstrap.init(args);
        } catch (Throwable t) {
//format exceptions to the console in a special way
// to avoid 2MB stacktraces from guice, etc.
            throw new StartupError(t);
        }
    }
	close的方法:
static void close(String[] args) {
        Bootstrap.stop();
    }
2.	org.elasticsearch.bootstrap.Bootstrap类:
从Elasticsearch类中的main函数看到调用了Bootstrap的init方法,es的启动初始化主要在这个类中完成(代码比较多,不一一贴出):
	该类中主要有三个实例:
private volatile Node node;
private final CountDownLatch keepAliveLatch = new CountDownLatch(1);
private final Thread keepAliveThread;
	构造函数:
		Bootstrap() {
        keepAliveThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    keepAliveLatch.await();
                } catch (InterruptedException e) {
        // bail out
                }
            }
        }, "elasticsearch[keepAlive/" + Version.CURRENT + "]");
        keepAliveThread.setDaemon(false);
// keep this thread alive (non daemon thread) until we shutdown
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                keepAliveLatch.countDown();
            }
        });
}
主要实现keepAliveThread的run方法,该线程调用keepAliveLatch的await()方法,keepAliveLatch的初始值为1。如果keepAliveLatch值没有递减为0时,该线程一直等待,相当于一个HeartBeat,用于表示ES是否活着。
其中,Runtime.getRuntime().addShutdownHook(shutdownHook);
       这个方法的意思就是在jvm中增加一个关闭的钩子,当jvm关闭的时候,会执行系统中已经设置的所有通过方法addShutdownHook添加的钩子,当系统执行完这些钩子后,jvm才会关闭。所以这些钩子可以在jvm关闭的时候进行内存清理、对象销毁等操作。
	init函数主要做了几件事情:
1.	设置系统属性:System.setProperty("es.logger.prefix", "");
2.	获取BootStrap实例的INSTANCE:INSTANCE = new Bootstrap();
3.	获取运行环境,创建pid文件:
// 获取运行时环境 
Environment environment = initialSettings(foreground);
 // 从运行时环境获取配置
Settings settings = environment.settings();
LogConfigurator.configure(settings, true);
checkForCustomConfFile();
		// 创建pid文件
 	if (environment.pidFile() != null) {
     	PidFile.create(environment.pidFile(), true);
  	}
4.	判断是否设置了是否打开文件最大数,并判断是否以Client VM方式启动VM:
//如果启动时是HotSpot的Client VM,则打印log信息,提醒采用Sever VM,以获得更好的性能
if(JvmInfo.jvmInfo().getVmName().toLowerCase(Locale.ROOT).contains("client")) {
ESLogger logger = Loggers.getLogger(Bootstrap.class);
logger.warn("jvm uses the client vm, make sure to run `java` with the server vm for best performance by adding `-server` to the command line");
    }
Client VM(-client):为在客户端环境中减少启动时间而优化;
Server VM(-server):为在服务器环境中最大化程序执行速度而设计。
即前者启动快,后者运行快。
5.	判断是否打印系统日志:
if (!foreground) {
	  // 如果foreground是false则不打印日志,关闭系统输出 
   Loggers.disableConsoleLogging();
   closeSystOut();
}
6.	检测JVM版本:
// fail if using broken version
JVMCheck.check();
7.	INSTANCE执行setup:INSTANCE.setup(true, settings, environment);
8.	INSTANCE执行start:  INSTANCE.start();

3.	BootStrap类的setup函数,主要做以下几件事:
1.	对bootstrap.mlockall和bootstrap.memory_lock信息进行检测,并打印log日志:
final Boolean mlockall = settings.getAsBoolean("bootstrap.mlockall", null);
if (mlockall != null) {
 deprecationLogger.deprecated("setting [bootstrap.mlockall] is deprecated; use [bootstrap.memory_lock]");
}

final Boolean memoryLock = settings.getAsBoolean("bootstrap.memory_lock", null);
// both bootstrap.mlockall and bootstrap.memory_lock are set, refuse to start
if (mlockall != null && memoryLock != null) {
throw new IllegalArgumentException("both [bootstrap.mlockall] and [bootstrap.memory_lock] configured,"+ " just use [bootstrap.memory_lock]");
}
2.	本地初始化:
initializeNatives(environment.tmpFile(),
memoryLock != null ? memoryLock : mlockall != null ? mlockall : false,
    settings.getAsBoolean("bootstrap.seccomp", true),
settings.getAsBoolean("bootstrap.ctrlhandler", true));
3.	初始化两种Probes,这两种probe将提供给es的stats api所需的一些es进程和os层面的信息:
static void initializeProbes() {
      // Force probes to be loaded
   ProcessProbe.getInstance();
   OsProbe.getInstance();
}
4.	添加关闭钩子,在VM关闭前,关闭node:
   if (addShutdownHook) {
     Runtime.getRuntime().addShutdownHook(new Thread() {
     @Override
     public void run() {
     if (node != null) {
          node.close();
              }
            }
          });
        }
5.	检查JarHell,checkJarHell()方法中,打印classpath信息:
JarHell.checkJarHell();
6.	安装SM(security manager):
setupSecurity(settings, environment);
7.	Node对象的builder,
Settings nodeSettings = Settings.settingsBuilder().put(settings)
.put(InternalSettingsPreparer.IGNORE_SYSTEM_PROPERTIES_SETTING, true).build();
NodeBuilder nodeBuilder = NodeBuilder.nodeBuilder().settings(nodeSettings);
node = nodeBuilder.build();

4.	Node类:
1.	node = nodeBuilder.builder(),调用nodeBuilder的builder方法:
//Builds the node without starting it
public Node build() {
return new Node(settings.build());
}
然后,跟进到Node类的构造函数:
public Node(Settings preparedSettings) {
this(InternalSettingsPreparer.prepareEnvironment(preparedSettings, null), Version.CURRENT, Collections.<Class<? extends Plugin>>emptyList());
  }
 			再调用内部的protected的构造函数:
protected Node(Environment tmpEnv, Version version, Collection<Class<? extends Plugin>> classpathPlugins){….}
其中,通过创建ModulesBuilder对象,然后调用add方法,进行elasticsearch的不同模块注入,同时生成一个Injector对象。
injector = modules.createInjector();
public Injector createInjector() {
        Injector injector = Guice.createInjector(modules);
        Injectors.cleanCaches(injector);
// in ES, we always create all instances as if they are eager singletons
// this allows for considerable memory savings (no need to store construction info) as well as cycles
        ((InjectorImpl) injector).readOnlyAllSingletons();
        return injector;
 }
这里使用了Guice的Injector进行注入与获取实例,Guice是google开源的一个依赖注入的框架:
(1)	使用@injectguice会扫描inject注解,并对方法中出现的参数实例寻找对应的注册的实例进行初始化;
(2)	bind接口将接口跟具体的实现类进行绑定;
(3)	使用Injector引导应用程序。
因此,每个module里面都有一个方法configure()方法用于对象和实现类作绑定。
Eg:
SettingsModule:
public class SettingsModule extends AbstractModule {

    private final Settings settings;

    public SettingsModule(Settings settings) {
        this.settings = settings;
    }

    @Override
    protected void configure() {
        bind(Settings.class).toInstance(settings);
        bind(SettingsFilter.class).asEagerSingleton();
    }
}
Elasticsearch中的module都是继承AbstractModule,然后重写自己的configure方法,实现对象实现类之间的绑定。
5.	BootStrap类的start方法:
两个操作:node.start和keepAliveThread.start
private void start() {
   node.start();
   keepAliveThread.start();
}
这个时候才真正启动了node和keepAliveThread线程,其中keepAliveThread线程会在elasticsearch退出前一直阻塞等待递减为0。
1.	Node的start函数:
主要通过injector对node中的不同组件分别调用不同的实例的start方法进行启动:
injector.getInstance(XXXX.class).start();
2.	启动keepAliveThread线程;
至此,整个Elasticsearch完成启动。

文章有误,欢迎指出!

参考文章:http://blog.csdn.net/guo_jia_liang/article/details/53080005

                 http://blog.csdn.net/u010994304/article/details/50452890

猜你喜欢

转载自blog.csdn.net/JY_He/article/details/79654992