슬 래 그 Elastic Search 소스 코드 분석 - 시작 프로 세 스 (상)

20778 단어 elasticsearch자바
나 를 주목 하 다
옮 겨 싣 기 원본 주 소 는 다음 과 같 습 니 다.http://www.54tianzhisheng.cn/2018/08/11/es-code02/
전제 조건
위의 글 은 ElasticSearch 소스 코드 해석 - 환경 구축 을 썼 습 니 다. 그 중에서 server 모듈 을 여 는 Elasticsearch 류 를 시작 하 라 고 했 습 니 다. org. elasticsearch. bootstrap. Elasticsearch. 안에 있 는 main 함 수 를 실행 하면 ElasticSearch 를 시작 할 수 있 습 니 다. 이 글 은 시작 절 차 를 설명 합 니 다. 지면 이 많 기 때문에 두 편 으로 나 누 어 썼 습 니 다.
시작 프로 세 스
main 방법 입구
입 구 를 볼 수 있 는 것 은 main 방법 입 니 다. 방법 은 권한 을 검사 한 다음 에 오류 로그 모니터 (로그 설정 전에 상태 로그 에 error 가 나타 나 지 않도록 합 니 다) 입 니 다. 그 다음 에 Elasticsearch 대상 을 만 든 다음 에 정적 방법 main 방법 (18 줄) 을 호출 하여 만 든 대상 과 인자, Terminal 기본 값 을 전달 합 니 다.정적 main 방법 에서 elasticsearch. main 방법 을 호출 합 니 다.
public static void main(final String[] args) throws Exception {         //1、  
    // 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
        }
    });
    LogConfigurator.registerErrorListener();                            //
    final Elasticsearch elasticsearch = new Elasticsearch();
    int status = main(args, elasticsearch, Terminal.DEFAULT); //2、  Elasticsearch.main  
    if (status != ExitCodes.OK) {
        exit(status);
    }
}

static int main(final String[] args, final Elasticsearch elasticsearch, final Terminal terminal) throws Exception {
    return elasticsearch.main(args, terminal);  //3、command main
}

Elasticsearch 클래스 는 Environment Aware Command 클래스 를 계 승 했 기 때문에 Environment Aware Command 클래스 는 Command 클래스 를 계 승 했 지만 Elasticsearch 클래스 는 main 방법 을 다시 쓰 지 않 았 기 때문에 위 에서 호출 한 elasticsearch. main 은 사실 Command 의 main 방법 을 호출 했 습 니 다. 코드 는 다음 과 같 습 니 다.
/** Parses options for this command from args and executes it. */
public final int main(String[] args, Terminal terminal) throws Exception {
    if (addShutdownHook()) {                                                //  Runtime.getRuntime().addShutdownHook      Hook,         Hook
        shutdownHookThread = new Thread(() -> {
            try {
                this.close();
            } catch (final IOException e) {
                try (
                    StringWriter sw = new StringWriter();
                    PrintWriter pw = new PrintWriter(sw)) {
                    e.printStackTrace(pw);
                    terminal.println(sw.toString());
                } catch (final IOException impossible) {
                    // StringWriter#close declares a checked IOException from the Closeable interface but the Javadocs for StringWriter
                    // say that an exception here is impossible
                    throw new AssertionError(impossible);
                }
            }
        });
        Runtime.getRuntime().addShutdownHook(shutdownHookThread);
    }

    beforeMain.run();

    try {
        mainWithoutErrorHandling(args, terminal);//4、mainWithoutErrorHandling
    } catch (OptionException e) {
        printHelp(terminal);
        terminal.println(Terminal.Verbosity.SILENT, "ERROR: " + e.getMessage());
        return ExitCodes.USAGE;
    } catch (UserException e) {
        if (e.exitCode == ExitCodes.USAGE) {
            printHelp(terminal);
        }
        terminal.println(Terminal.Verbosity.SILENT, "ERROR: " + e.getMessage());
        return e.exitCode;
    }
    return ExitCodes.OK;
}

위의 코드 는 처음에 체크 함 수 를 이용 하여 프로그램 이 종 료 될 때 이 Hook 을 터치 합 니 다. 이 방법 은 주요 코드 는 main Without Error Handling () 방법 입 니 다. 그 다음 에 아래 는 catch 유지 방법 이 던 진 이상 입 니 다. 방법 코드 는 다음 과 같 습 니 다.
/*** Executes the command, but all errors are thrown. */
void mainWithoutErrorHandling(String[] args, Terminal terminal) throws Exception {
    final OptionSet options = parser.parse(args);
    if (options.has(helpOption)) {
        printHelp(terminal);
        return;
    }
    if (options.has(silentOption)) {
        terminal.setVerbosity(Terminal.Verbosity.SILENT);
    } else if (options.has(verboseOption)) {
        terminal.setVerbosity(Terminal.Verbosity.VERBOSE);
    } else {
        terminal.setVerbosity(Terminal.Verbosity.NORMAL);
    }
    execute(terminal, options);//5、   EnvironmentAwareCommand    execute(),(   command     execute  )
}

위의 코드 는 3 ~ 14 줄 에서 들 어 오 는 인 자 를 분석 하고 terminal 을 설정 합 니 다. 중요 한 execute () 방법 은 Environment Aware Command 의 execute () (Command 류 의 추상 적 인 execute 방법 을 다시 썼 습 니 다) 입 니 다. 위의 상속 도 에서 Environment Aware Command 가 Command 를 계승 하고 다시 쓴 execute 방법 코드 는 다음 과 같 습 니 다.
@Override
protected void execute(Terminal terminal, OptionSet options) throws Exception {
    final Map settings = new HashMap<>();
    for (final KeyValuePair kvp : settingOption.values(options)) {
        if (kvp.value.isEmpty()) {
            throw new UserException(ExitCodes.USAGE, "setting [" + kvp.key + "] must not be empty");
        }
        if (settings.containsKey(kvp.key)) {
            final String message = String.format(
                Locale.ROOT, "setting [%s] already set, saw [%s] and [%s]",
                kvp.key, settings.get(kvp.key), kvp.value);
            throw new UserException(ExitCodes.USAGE, message);
        }
        settings.put(kvp.key, kvp.value);
    }
    //6、    ide    vm options     path.data、path.home、path.logs
    putSystemPropertyIfSettingIsMissing(settings, "path.data", "es.path.data");
    putSystemPropertyIfSettingIsMissing(settings, "path.home", "es.path.home");
    putSystemPropertyIfSettingIsMissing(settings, "path.logs", "es.path.logs");

    execute(terminal, options, createEnv(terminal, settings));//7、    createEnv     
    //9、  elasticsearch execute  ,elasticsearch    EnvironmentAwareCommand    execute  
}

방법 앞 에는 전송 참조 에 따라 설정 을 판단 합 니 다. 설정 이 비어 있 으 면 putSystem Property IfSetting IsMissing 방법 으로 바로 이동 합 니 다. 여기 에는 path. data, path. home, path. logs 설정 es 의 data, home, logs 디 렉 터 리 세 가지 속성 이 설정 되 어 있 습 니 다. 여 기 는 우리 ide 가 설정 한 vm options 에 따라 설정 되 어 있 습 니 다.이것 도 우리 가 지난 글 에서 말 한 설정 정보 입 니 다. 설정 하지 않 으 면 바로 잘못 보고 하 는 이유 입 니 다.다음은 putSystem Property IfSetting IsMissing 방법 코드 에서 어떻게 하 는 지 보 겠 습 니 다.
/** Ensure the given setting exists, reading it from system properties if not already set. */
private static void putSystemPropertyIfSettingIsMissing(final Map settings, final String setting, final String key) {
    final String value = System.getProperty(key);//  key(es.path.data)     
    if (value != null) {
        if (settings.containsKey(setting)) {
            final String message =
                String.format(
                Locale.ROOT,
                "duplicate setting [%s] found via command-line [%s] and system property [%s]",
                setting, settings.get(setting), value);
            throw new IllegalArgumentException(message);
        } else {
            settings.put(setting, value);
        }
    }
}

이 세 가지 방법 을 실행 한 후:
이 방법 에서 벗 어 나 계속 보면 execute 방법 이 방법 을 호출 한 것 을 발견 할 수 있 습 니 다.
 execute(terminal, options, createEnv(terminal, settings));

여기 서 먼저 createEnv(terminal, settings) 방법 을 살 펴 보 자.
protected Environment createEnv(final Terminal terminal, final Map settings) throws UserException {
    final String esPathConf = System.getProperty("es.path.conf");//8、     vm options      es.path.conf
    if (esPathConf == null) {
        throw new UserException(ExitCodes.CONFIG, "the system property [es.path.conf] must be set");
    }
    return InternalSettingsPreparer.prepareEnvironment(Settings.EMPTY, terminal, settings, getConfigPath(esPathConf));  //8、     prepareEnvironment
}

우리 ide vm options 에 설 치 된 es. path. conf 를 읽 습 니 다. 이전 글 에서 도 이 설정 을 꼭 해 야 합 니 다. es 가 시 작 될 때 우리 의 설정 과 플러그 인 을 불 러 오기 때 문 입 니 다.위 코드 6 줄 의 prepare Environment 방법 을 계속 살 펴 보 겠 습 니 다.
public static Environment prepareEnvironment(Settings input, Terminal terminal, Map properties, Path configPath) {
    // just create enough settings to build the environment, to get the config dir
    Settings.Builder output = Settings.builder();
    initializeSettings(output, input, properties);
    Environment environment = new Environment(output.build(), configPath);

    //   es.path.conf             yml    ,           
    if (Files.exists(environment.configFile().resolve("elasticsearch.yaml"))) {
        throw new SettingsException("elasticsearch.yaml was deprecated in 5.5.0 and must be renamed to elasticsearch.yml");
    }

    if (Files.exists(environment.configFile().resolve("elasticsearch.json"))) {
        throw new SettingsException("elasticsearch.json was deprecated in 5.5.0 and must be converted to elasticsearch.yml");
    }

    output = Settings.builder(); // start with a fresh output
    Path path = environment.configFile().resolve("elasticsearch.yml");
    if (Files.exists(path)) {
        try {
            output.loadFromPath(path);  //             
        } catch (IOException e) {
            throw new SettingsException("Failed to load settings from " + path.toString(), e);
        }
    }

    // re-initialize settings now that the config file has been loaded
    initializeSettings(output, input, properties);          //        
    finalizeSettings(output, terminal);

    environment = new Environment(output.build(), configPath);

    // we put back the path.logs so we can use it in the logging configuration file
    output.put(Environment.PATH_LOGS_SETTING.getKey(), environment.logsFile().toAbsolutePath().normalize().toString());
    return new Environment(output.build(), configPath);
}

준 비 된 환경 은 위의 그림 과 같 습 니 다. 구 축 된 환경 을 통 해 프로필 elasticsearch. yml 이 yml 로 끝 나 는 지 확인 하고, Yml 나 json 으로 끝 나 는 경우 이상 을 던 집 니 다 (5.5.0 버 전의 다른 두 가지 형식 이 만 료 되 었 습 니 다. yml 형식 만 사용 할 수 있 습 니 다). 그리고 이 프로필 을 불 러 와 서 내용 (KV 구조) 을 읽 습 니 다.
createEnv 방법 에서 벗 어 나 execute 방법 을 계속 봅 시다.
Environment Aware Command 류 의 execute 방법 코드 는 다음 과 같 습 니 다.
protected abstract void execute(Terminal terminal, OptionSet options, Environment env) throws Exception;

이것 은 추상 적 인 방법 입 니 다. 그러면 그의 실현 방법 은 Elasticsearch 류 에서 코드 는 다음 과 같 습 니 다.
@Override
protected void execute(Terminal terminal, OptionSet options, Environment env) throws UserException {
    if (options.nonOptionArguments().isEmpty() == false) {
        throw new UserException(ExitCodes.USAGE, "Positional arguments not allowed, found " + options.nonOptionArguments());
    }
    if (options.has(versionOption)) {
        final String versionOutput = String.format(
            Locale.ROOT,
            "Version: %s, Build: %s/%s/%s/%s, JVM: %s",
            Version.displayVersion(Version.CURRENT, Build.CURRENT.isSnapshot()),
            Build.CURRENT.flavor().displayName(),
            Build.CURRENT.type().displayName(),
            Build.CURRENT.shortHash(),
            Build.CURRENT.date(),
            JvmInfo.jvmInfo().version());
        terminal.println(versionOutput);
        return;
    }

    final boolean daemonize = options.has(daemonizeOption);
    final Path pidFile = pidfileOption.value(options);
    final boolean quiet = options.has(quietOption);

    // a misconfigured java.io.tmpdir can cause hard-to-diagnose problems later, so reject it immediately
    try {
        env.validateTmpFile();
    } catch (IOException e) {
        throw new UserException(ExitCodes.CONFIG, e.getMessage());
    }
    try {
        init(daemonize, pidFile, quiet, env);    //10、   
    } catch (NodeValidationException e) {
        throw new UserException(ExitCodes.CONFIG, e.getMessage());
    }
}

위의 코드 에 서 는 주로 init(daemonize, pidFile, quiet, env); 초기 화 방법 을 보 세 요.
void init(final boolean daemonize, final Path pidFile, final boolean quiet, Environment initialEnv)
    throws NodeValidationException, UserException {
    try {
        Bootstrap.init(!daemonize, pidFile, quiet, initialEnv); //11、   Bootstrap    init   
    } catch (BootstrapException | RuntimeException e) {
        // format exceptions to the console in a special way
        // to avoid 2MB stacktraces from guice, etc.
        throw new StartupException(e);
    }
}

init 방법
Bootstrap 의 정적 init 방법 은 다음 과 같 습 니 다.
static void init(
    final boolean foreground,
    final Path pidFile,
    final boolean quiet,
    final Environment initialEnv) throws BootstrapException, NodeValidationException, UserException {
    // force the class initializer for BootstrapInfo to run before
    // the security manager is installed
    BootstrapInfo.init();

    INSTANCE = new Bootstrap();   //12、     Bootstrap   

    final SecureSettings keystore = loadSecureSettings(initialEnv);//                   
    final Environment environment = createEnvironment(foreground, pidFile, keystore, initialEnv.settings(), initialEnv.configFile());   //        
    try {
        LogConfigurator.configure(environment);   //13、log     
    } catch (IOException e) {
        throw new BootstrapException(e);
    }
    if (environment.pidFile() != null) {
        try {
            PidFile.create(environment.pidFile(), true);
        } catch (IOException e) {
            throw new BootstrapException(e);
        }
    }

    final boolean closeStandardStreams = (foreground == false) || quiet;
    try {
        if (closeStandardStreams) {
            final Logger rootLogger = ESLoggerFactory.getRootLogger();
            final Appender maybeConsoleAppender = Loggers.findAppender(rootLogger, ConsoleAppender.class);
            if (maybeConsoleAppender != null) {
                Loggers.removeAppender(rootLogger, maybeConsoleAppender);
            }
            closeSystOut();
        }

        // fail if somebody replaced the lucene jars
        checkLucene();             //14、  Lucene  

// install the default uncaught exception handler; must be done before security is initialized as we do not want to grant the runtime permission setDefaultUncaughtExceptionHandler
        Thread.setDefaultUncaughtExceptionHandler(
            new ElasticsearchUncaughtExceptionHandler(() -> Node.NODE_NAME_SETTING.get(environment.settings())));

        INSTANCE.setup(true, environment);      //15、   setup   

        try {
            // any secure settings must be read during node construction
            IOUtils.close(keystore);
        } catch (IOException e) {
            throw new BootstrapException(e);
        }

        INSTANCE.start();         //26、   start   

        if (closeStandardStreams) {
            closeSysError();
        }
    } catch (NodeValidationException | RuntimeException e) {
        // disable console logging, so user does not see the exception twice (jvm will show it already)
        final Logger rootLogger = ESLoggerFactory.getRootLogger();
        final Appender maybeConsoleAppender = Loggers.findAppender(rootLogger, ConsoleAppender.class);
        if (foreground && maybeConsoleAppender != null) {
            Loggers.removeAppender(rootLogger, maybeConsoleAppender);
        }
        Logger logger = Loggers.getLogger(Bootstrap.class);
        if (INSTANCE.node != null) {
            logger = Loggers.getLogger(Bootstrap.class, Node.NODE_NAME_SETTING.get(INSTANCE.node.settings()));
        }
        // 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 = null;
            try {
                ps = new PrintStream(os, false, "UTF-8");
            } catch (UnsupportedEncodingException uee) {
                assert false;
                e.addSuppressed(uee);
            }
            new StartupException(e).printStackTrace(ps);
            ps.flush();
            try {
                logger.error("Guice Exception: {}", os.toString("UTF-8"));
            } catch (UnsupportedEncodingException uee) {
                assert false;
                e.addSuppressed(uee);
            }
        } else if (e instanceof NodeValidationException) {
            logger.error("node validation exception
{}", e.getMessage()); } else { // full exception logger.error("Exception", e); } // re-enable it if appropriate, so they can see any logging during the shutdown process if (foreground && maybeConsoleAppender != null) { Loggers.addAppender(rootLogger, maybeConsoleAppender); } throw e; } }

이 방법 은 주로 다음 과 같다.
1. Bootstrap 인 스 턴 스 생 성
2. 보안 모듈 을 등록 하면 관련 설정 을 불 러 옵 니 다.
3. Elasticsearch 가 실행 되 는 필수 환경 과 관련 설정 을 만 듭 니 다. 예 를 들 어 config, scripts, plugins, modules, logs, lib, bin 등 설정 디 렉 터 리 를 실행 환경 에 불 러 옵 니 다.
4. 로그 설정 환경, 로그 컨 텍스트 생 성
5. PID 파일 이 존재 하 는 지 확인 하고 존재 하지 않 으 면 PID 파일 생 성
6. Lucene 버 전 검사
7. setup 방법 을 호출 합 니 다 (현재 환경 으로 노드 를 만 듭 니 다)
setup 방법
private void setup(boolean addShutdownHook, Environment environment) throws BootstrapException {
    Settings settings = environment.settings();//        
    try {
        spawner.spawnNativeControllers(environment);
    } catch (IOException e) {
        throw new BootstrapException(e);
    }
    initializeNatives(
        environment.tmpFile(),
        BootstrapSettings.MEMORY_LOCK_SETTING.get(settings),
        BootstrapSettings.SYSTEM_CALL_FILTER_SETTING.get(settings),
        BootstrapSettings.CTRLHANDLER_SETTING.get(settings));
    // initialize probes before the security manager is installed
    initializeProbes();
    if (addShutdownHook) {
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                try {
                    IOUtils.close(node, spawner);
                    LoggerContext context = (LoggerContext) LogManager.getContext(false);
                    Configurator.shutdown(context);
                } catch (IOException ex) {
                    throw new ElasticsearchException("failed to stop node", ex);
                }
            }
        });
    }
    try {
        // look for jar hell
        final Logger logger = ESLoggerFactory.getLogger(JarHell.class);
        JarHell.checkJarHell(logger::debug);
    } catch (IOException | URISyntaxException e) {
        throw new BootstrapException(e);
    }
    // Log ifconfig output before SecurityManager is installed
    IfConfig.logIfNecessary();
    // install SM after natives, shutdown hooks, etc.
    try {
        Security.configure(environment, BootstrapSettings.SECURITY_FILTER_BAD_DEFAULTS_SETTING.get(settings));
    } catch (IOException | NoSuchAlgorithmException e) {
        throw new BootstrapException(e);
    }
    node = new Node(environment) {              //16、    
        @Override
        protected void validateNodeBeforeAcceptingRequests(
            final BootstrapContext context,
            final BoundTransportAddress boundTransportAddress, List checks) throws NodeValidationException {
            BootstrapChecks.check(context, boundTransportAddress, checks);
        }
    };
}

위의 코드 는 마지막 으로 Node 노드 의 생 성 입 니 다. 이 글 은 Node 의 생 성 을 말 하지 않 습 니 다. 다음 글 은 Node 노드 의 생 성과 ES 노드 를 본 격 적 으로 시작 하 는 것 을 잘 이야기 하 겠 습 니 다.
총결산
이 글 은 주로 대략적인 시작 절 차 를 직렬 로 연결 합 니 다. 편폭 이 비교적 많 기 때문에 두 편 으로 나 누 었 습 니 다. 먼저 세부 사항 을 잠 그 지 않 고 뒤의 절차 가 시작 되 는 글 을 다 쓴 후에 우 리 는 단일 한 버클 디 테 일 을 합 니 다.
관련 글
1. 슬 래 그 닭 은 왜 Elastic Search 소스 코드 를 봐 야 합 니까?
2. 슬 래 그 닭 의 Elastic Search 소스 분석 - 환경 구축
3. 슬 래 그 닭 의 Elastic Search 소스 코드 분석 - 시작 절차 (상)
4. 슬 래 그 닭 의 Elastic Search 소스 코드 분석 - 시작 절차 (하)
5. Elasticsearch 시리즈 글 (1): Elasticsearch 기본 단어 기 와 중간 단어 기 간 의 비교 및 사용 방법
6. Elasticsearch 시리즈 글 (2): 전문 검색엔진 Elasticsearch 클 러 스 터 구축 입문 강좌
7. Elasticsearch 시리즈 글 (3): ElasticSearch 클 러 스 터 모니터링
8. Elasticsearch 시리즈 글 (4): ElasticSearch 단일 노드 모니터링
9. Elasticsearch 시리즈 글 (5): ELK 실시 간 로그 분석 플랫폼 환경 구축
10. IDEA 원 격 Debug Elastic Search 에서

좋은 웹페이지 즐겨찾기