版权声明:本文为博主原创文章,未经博主允许不得转载。 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