【说明】
本文以elasticsearch 2.4版本进行。elasticsearch代码变化快,本位内容为2.4版本Elasticsearch源码。
【代码结构】
git上elasticsearch目前如下,其源码在核心core目录
【elasticsearch脚本】
elasticsearch部署目录的./bin/elasticsear脚本中,暴露了elasticsearch服务的启动类:org.elasticsearch.bootstrap.Elasticsearch
launch_service()
{
pidpath=$1
daemonized=$2
props=$3
es_parms="-Delasticsearch"
if [ "x$pidpath" != "x" ]; then
es_parms="$es_parms -Des.pidfile=$pidpath"
fi
# Make sure we dont use any predefined locale, as we check some exception message strings and rely on english language
# As those strings are created by the OS, they are dependant on the configured locale
LANG=en_US.UTF-8
LC_ALL=en_US.UTF-8
export HOSTNAME=`hostname -s`
# The es-foreground option will tell Elasticsearch not to close stdout/stderr, but it's up to us not to daemonize.
if [ "x$daemonized" = "x" ]; then
es_parms="$es_parms -Des.foreground=yes"
exec "$JAVA" $JAVA_OPTS $ES_JAVA_OPTS $es_parms -Des.path.home="$ES_HOME" -cp "$ES_CLASSPATH" $props \
org.elasticsearch.bootstrap.Elasticsearch
# exec without running it in the background, makes it replace this shell, we'll never get here...
# no need to return something
else
# Startup Elasticsearch, background it, and write the pid.
exec "$JAVA" $JAVA_OPTS $ES_JAVA_OPTS $es_parms -Des.path.home="$ES_HOME" -cp "$ES_CLASSPATH" $props \
org.elasticsearch.bootstrap.Elasticsearch <&- &
return $?
fi
}
【org.elasticsearch.bootstrap.Elasticsearch】
- 提供了main方法,接收args参数;
- 调用org.elasticsearch.bootstrap.Bootstrap的init方法执行初始化
public final class Elasticsearch {
......
/**
* Main entry point for starting elasticsearch
*/
public static void main(String[] args) throws StartupError {
// we want the JVM to think there is a security manager installed so that if internal policy decisions that would be based on the
// presence of a security manager or lack thereof act as if there is a security manager present (e.g., DNS cache policy)
System.setSecurityManager(new SecurityManager() {
@Override
public void checkPermission(Permission perm) {
// grant all permissions so that we can later set the security manager to the one that we want
}
});
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);
}
}
......
}
【org.elasticsearch.bootstrap.Bootstrap】
- 校验启动参数合法性,获取配置文件参数;
- 设置环境信息与配置信息;
- 执行start方法
- 调用org.elasticsearch.node.Node#start方法初始化节点;
- 启动keepAliveThread线程,keepAliveThread在Bootstrap构造方法中定义:使用CountDownLatch进行线程挂载;
final class Bootstrap {
......
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();
}
});
}
private void start() {
node.start();
keepAliveThread.start();
}
static void init(String[] args) throws Throwable {
// 初始化es log默认配置
System.setProperty("es.logger.prefix", "");
// 启动参数校验
BootstrapCLIParser bootstrapCLIParser = new BootstrapCLIParser();
CliTool.ExitStatus status = bootstrapCLIParser.execute(args);
if (CliTool.ExitStatus.OK != status) {
exit(status.status());
}
INSTANCE = new Bootstrap();
// 判断是否以console模式启动
boolean foreground = !"false".equals(System.getProperty("es.foreground", System.getProperty("es-foreground")));
// 使用service wapper启动,视为非console启动
if (System.getProperty("wrapper.service", "XXX").equalsIgnoreCase("true")) {
foreground = false;
}
// 初始化环境
Environment environment = initialSettings(foreground);
// 初始化配置
Settings settings = environment.settings();
LogConfigurator.configure(settings, true);
// 配置检查
checkForCustomConfFile();
if (environment.pidFile() != null) {
PidFile.create(environment.pidFile(), true);
}
// 若未设置文件句柄数,使用最大值
if (System.getProperty("es.max-open-files", "false").equals("true")) {
ESLogger logger = Loggers.getLogger(Bootstrap.class);
logger.info("max_open_files [{}]", ProcessProbe.getInstance().getMaxFileDescriptorCount());
}
// jvm启动模式为client时,警告用户;server模式的jvm性能高
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");
}
try {
if (!foreground) {
Loggers.disableConsoleLogging();
closeSystOut();
}
// jvm检查,避免jvm存在bug导致数据损坏(Checks that the current JVM is "ok". This means it doesn't have severe bugs that cause data corruption)
JVMCheck.check();
// 设置环境信息
INSTANCE.setup(true, settings, environment);
// 启动
INSTANCE.start();
if (!foreground) {
closeSysError();
}
} catch (Throwable e) {
// disable console logging, so user does not see the exception twice (jvm will show it already)
if (foreground) {
Loggers.disableConsoleLogging();
}
ESLogger logger = Loggers.getLogger(Bootstrap.class);
if (INSTANCE.node != null) {
logger = Loggers.getLogger(Bootstrap.class, INSTANCE.node.settings().get("name"));
}
// HACK, it sucks to do this, but we will run users out of disk space otherwise
if (e instanceof CreationException) {
// guice: log the shortened exc to the log file
ByteArrayOutputStream os = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(os, false, "UTF-8");
new StartupError(e).printStackTrace(ps);
ps.flush();
logger.error("Guice Exception: {}", os.toString("UTF-8"));
} else {
// full exception
logger.error("Exception", e);
}
// re-enable it if appropriate, so they can see any logging during the shutdown process
if (foreground) {
Loggers.enableConsoleLogging();
}
throw e;
}
}
......
}
【org.elasticsearch.node.Node】
- Elasticsearch使用google的Guice进行依赖管理,Node类的构造方法将接口与实现进行绑定并由Guice的Injector管理。
- 判断节点是否已经启动,若启动返回该节点;
- 从Injector获取具体的实例,执行start方法,执行过程由内而外:单节点底层服务围内,通信服务次之,server服务最后;
public class Node implements Releasable {
......
public Node(Settings preparedSettings) {
this(InternalSettingsPreparer.prepareEnvironment(preparedSettings, null), Version.CURRENT, Collections.<Class<? extends Plugin>>emptyList());
}
protected Node(Environment tmpEnv, Version version, Collection<Class<? extends Plugin>> classpathPlugins) {
Settings tmpSettings = settingsBuilder().put(tmpEnv.settings())
.put(Client.CLIENT_TYPE_SETTING, CLIENT_TYPE).build();
tmpSettings = TribeService.processSettings(tmpSettings);
ESLogger logger = Loggers.getLogger(Node.class, tmpSettings.get("name"));
logger.info("version[{}], pid[{}], build[{}/{}]", version, JvmInfo.jvmInfo().pid(), Build.CURRENT.hashShort(), Build.CURRENT.timestamp());
logger.info("initializing ...");
if (logger.isDebugEnabled()) {
logger.debug("using config [{}], data [{}], logs [{}], plugins [{}]",
tmpEnv.configFile(), Arrays.toString(tmpEnv.dataFiles()), tmpEnv.logsFile(), tmpEnv.pluginsFile());
}
this.pluginsService = new PluginsService(tmpSettings, tmpEnv.modulesFile(), tmpEnv.pluginsFile(), classpathPlugins);
this.settings = pluginsService.updatedSettings();
// create the environment based on the finalized (processed) view of the settings
this.environment = new Environment(this.settings());
final NodeEnvironment nodeEnvironment;
try {
nodeEnvironment = new NodeEnvironment(this.settings, this.environment);
} catch (IOException ex) {
throw new IllegalStateException("Failed to created node environment", ex);
}
final ThreadPool threadPool = new ThreadPool(settings);
NamedWriteableRegistry namedWriteableRegistry = new NamedWriteableRegistry();
boolean success = false;
try {
ModulesBuilder modules = new ModulesBuilder();
modules.add(new Version.Module(version));
modules.add(new CircuitBreakerModule(settings));
// 插件模块加载
for (Module pluginModule : pluginsService.nodeModules()) {
modules.add(pluginModule);
}
modules.add(new PluginsModule(pluginsService));
modules.add(new SettingsModule(this.settings));
modules.add(new NodeModule(this));
modules.add(new NetworkModule(namedWriteableRegistry));
modules.add(new ScriptModule(this.settings));
modules.add(new EnvironmentModule(environment));
modules.add(new NodeEnvironmentModule(nodeEnvironment));
modules.add(new ClusterNameModule(this.settings));
modules.add(new ThreadPoolModule(threadPool));
modules.add(new DiscoveryModule(this.settings));
modules.add(new ClusterModule(this.settings));
modules.add(new RestModule(this.settings));
modules.add(new TransportModule(settings, namedWriteableRegistry));
if (settings.getAsBoolean(HTTP_ENABLED, true)) {
modules.add(new HttpServerModule(settings));
}
modules.add(new IndicesModule());
modules.add(new SearchModule());
modules.add(new ActionModule(false));
modules.add(new MonitorModule(settings));
modules.add(new GatewayModule(settings));
modules.add(new NodeClientModule());
modules.add(new ShapeModule());
modules.add(new PercolatorModule());
modules.add(new ResourceWatcherModule());
modules.add(new RepositoriesModule());
modules.add(new TribeModule());
pluginsService.processModules(modules);
injector = modules.createInjector();
client = injector.getInstance(Client.class);
threadPool.setNodeSettingsService(injector.getInstance(NodeSettingsService.class));
success = true;
} finally {
if (!success) {
nodeEnvironment.close();
ThreadPool.terminate(threadPool, 10, TimeUnit.SECONDS);
}
}
logger.info("initialized");
}
public Node start() {
// 若已经启动,返回当前当前Node
if (!lifecycle.moveToStarted()) {
return this;
}
ESLogger logger = Loggers.getLogger(Node.class, settings.get("name"));
logger.info("starting ...");
injector.getInstance(Discovery.class).setRoutingService(injector.getInstance(RoutingService.class));
// 插件加载
for (Class<? extends LifecycleComponent> plugin : pluginsService.nodeServices()) {
injector.getInstance(plugin).start();
}
injector.getInstance(MappingUpdatedAction.class).setClient(client);
injector.getInstance(IndicesService.class).start();
injector.getInstance(IndexingMemoryController.class).start();
injector.getInstance(IndicesClusterStateService.class).start();
injector.getInstance(IndicesTTLService.class).start();
injector.getInstance(SnapshotsService.class).start();
injector.getInstance(SnapshotShardsService.class).start();
injector.getInstance(RoutingService.class).start();
injector.getInstance(SearchService.class).start();
injector.getInstance(MonitorService.class).start();
injector.getInstance(RestController.class).start();
// TODO hack around circular dependencies problems
injector.getInstance(GatewayAllocator.class).setReallocation(injector.getInstance(ClusterService.class), injector.getInstance(RoutingService.class));
injector.getInstance(ResourceWatcherService.class).start();
injector.getInstance(GatewayService.class).start();
// Start the transport service now so the publish address will be added to the local disco node in ClusterService
TransportService transportService = injector.getInstance(TransportService.class);
transportService.start();
injector.getInstance(ClusterService.class).start();
// start after cluster service so the local disco is known
DiscoveryService discoService = injector.getInstance(DiscoveryService.class).start();
transportService.acceptIncomingRequests();
discoService.joinClusterAndWaitForInitialState();
if (settings.getAsBoolean("http.enabled", true)) {
injector.getInstance(HttpServer.class).start();
}
injector.getInstance(TribeService.class).start();
if (settings.getAsBoolean("node.portsfile", false)) {
if (settings.getAsBoolean("http.enabled", true)) {
HttpServerTransport http = injector.getInstance(HttpServerTransport.class);
writePortsFile("http", http.boundAddress());
}
TransportService transport = injector.getInstance(TransportService.class);
writePortsFile("transport", transport.boundAddress());
}
logger.info("started");
return this;
}
......
}
至此服务启动完成。
es启动涉及3个类
org.elasticsearch.bootstrap.Elasticsearch | 服务启动入口 |
org.elasticsearch.bootstrap.Bootstrap | 启动参数校验与获取配置信息,挂载服务 |
org.elasticsearch.node.Node | 服务、插件等功能启动启动 |