经过前面 20 多章的学习,我们已经对 ES 有个比较深刻的认识了,后面几章的内容我们主要是阅读源码。

为了后续方便阅读和调试 ES 的源码,本章将基于 ES 7.13.0(可以根据你需要的版本进行选择,按道理 7.x 的都可以)、jdk15、IDEA 2020.3 来搭建源码阅读环境,并且简单介绍一些 IDEA 的调试技巧。最后简单介绍一下 ES 源码包的目录结构,为后面阅读源码做准备。

一、导入 ES 源码

下面是官网中使用 IDEA 导入 ES 源码的指导:

官网Idea导入.png

指导中给出了 jdk 和 IDEA 的版本依赖和导入的操作流程,简单到不行的一段话,却让非常多想使用IDEA 跑起 ES 的同学犯难。ok,下面我们就来在 IDEA 中导入 ES 源码。

安装 java 环境

这里我选择直接在 IDEA 里安装 jdk,打开 File -> Project Structtrue:

IDEA下载jdk.png

如上图,点击 ‘Download JDK’ 选择 jdk15 后,进行下载安装即可。下面是我的SDK 列表(需要注意的是,官方指导文档上说需要将 SDK 命名为15,我们跟着做就可以了):

配置SDKs-15.png

如上图,可以看到我已经添加了多个 SDK 了,然后可以在 Project 这个 tab 里设置当前项目使用的SDK

选择project的SDK.png

当设置好后就可以使用这个版本的 SDK 来运行对应的项目了。

下载和导入源码

首先下载 ES 7.13.0 的源码,可以在 github码云上下载。下载后解压得到源码目录,然后按照官网说的方法导入即可:

  1. File -> Open
  2. 在弹出的子对话框中选择对应项目的目录里的 build.gradle
  3. 在弹出的子对话框中选择 Open as Project

配置运行 Gradle 的 JVM

导入后将会自动下载对应的 Gradle 执行文件,这个等一下就可以了。然后需要配置 Gradle 运行的JVM!!因为高版本的 Gradle 需要 JVM 11 以上的,这里我们就统一用 JVM 15 了。

配置Gradle入口.png

如下图,然后选择 jvm 15:

选gradle的jvm.png

配置 Gradle 的源为阿里云仓库

因为 maven 的源都在国外,下载依赖库比较慢,所以我们需要配置国内的依赖源:

  1. // common maven publishing configuration
  2. allprojects {
  3. group = 'org.elasticsearch'
  4. version = VersionProperties.elasticsearch
  5. description = "Elasticsearch subproject ${project.path}"
  6. repositories {
  7. maven {
  8. url 'https://maven.aliyun.com/repository/public/'
  9. }
  10. mavenLocal()
  11. mavenCentral()
  12. }
  13. }

如上示例,在项目顶层的 build.gradle 中设置依赖源为阿里云仓库。配置完成后,其效果如下图:

配置阿里云仓库.png

ok,到这里我们可以 rebuild 一下 Gradle 了,等 Gradle 解析成功后,可以尝试构建项目啦。如果Gradle 解析时出现 ‘distributionSha256Sum xxxx’ 的错误(我在 win 10 上进行解析是会的),将gradle/wrapper/gradle-wrapper.properties 中的 distributionSha256Sum=xxx 一行注释掉即可。

  1. ......
  2. zipStoreBase=GRADLE_USER_HOME
  3. zipStorePath=wrapper/dists
  4. #distributionSha256Sum=ca42877db3519b667cd531c414be517b294b0467059d401e7133f0e55b9bf265

编译运行 ES

即使完成上面所有操作,这个时候还是没法运行的,会报各种错误,请继续下面的配置!

复制对应版本的配置目录和模块目录

在编译运行 ES 之前,需要把同一版本的 config、modules、plugins 复制过来。下载对应版本的 ES 运行文件,解压后复制 config、modules、plugins 到某个目录即可,这个目录为后面我们运行 ES 的目录,在我的环境中为 run_home:

run_home.png

编辑 elasticsearch.yml 文件

这里我们需要在 elasticsearch.yml 文件中设置 ES node 的名字和设置 xpack.ml.enabled 为 false。

  1. node.name: node-1
  2. xpack.ml.enabled: false

修改 Java 代码的安全策略管理

ES 是启用了Java 代码安全策略管理的,这里需要修改一下,新建一个 elasticsearch.policy 文件放到上面的 run_home/config 目录里:

29.源码阅读:源码阅读环境搭建 - 图9

elasticsearch.policy 文件的内容如下:

  1. grant {
  2. permission javax.management.MBeanTruxtPermission "register";
  3. permission javax.management.MBeanServerPermission "createMBeanServer";
  4. permission java.lang.RuntimePermission "createClassLoader";
  5. permission java.lang.RuntimePermission "getClassLoader";
  6. permission java.lang.RuntimePermission "setContextClassLoader";
  7. };

即使设置了如上配置,并且运行时指定了这个policy文件:

  1. -Djava.security.policy=your_path/elasticsearch.policy

运行时还是会报错的:

  1. org.elasticsearch.bootstrap.StartupException: java.lang.IllegalArgumentException:
  2. Unknown codebases [codebase.elasticsearch-plugin-classloader, codebase.elasticsearch, codebase.elasticsearch-secure-sm]
  3. in policy file [file:/Users/UserName/coding/elasticsearch-7.13.0/server/out/production/resources/org/elasticsearch/bootstrap/security.policy]

异常的提醒还是很明显的,bootstrap/security.policy 文件中定义的 codebase.elasticsearch-plugin-classloader 、codebase.elasticsearch、codebase.elasticsearch-secure-sm 这三个 codebases 不存在!!!于是查看 server/src/main/resources/org/elasticsearch/bootstrap/security.policy :

  1. // SecurityManager impl:
  2. // Must have all permissions to properly perform access checks
  3. grant codeBase "${codebase.elasticsearch-secure-sm}" {
  4. permission java.security.AllPermission;
  5. };
  6. // Elasticsearch core:
  7. // These are only allowed inside the server jar, not in plugins
  8. grant codeBase "${codebase.elasticsearch}" {
  9. // needed for loading plugins which may expect the context class loader to be set
  10. permission java.lang.RuntimePermission "setContextClassLoader";
  11. };
  12. grant codeBase "${codebase.elasticsearch-plugin-classloader}" {
  13. // needed to create the classloader which allows plugins to extend other plugins
  14. permission java.lang.RuntimePermission "createClassLoader";
  15. };

确实存在这个几个 codeBase 的安全策略的定义,但为啥它们不存在呢?其实我们在 IDEA 中运行 ES 的时候,IDEA 传递的是它生成的 class 文件所在的目录,而不是对应的 .jar 文件,所以报找不到这些 codeBase 的异常。感兴趣的话可以调试 org.elasticsearch.bootstrap.ESPolicy.java 类来分析这部分的流程。

针对这个异常,最简单的操作就是把这些 codeBase 的检查去掉(直接注释掉),并且在我们自定义的 elasticsearch.policy 中设置如下内容即可:

  1. grant {
  2. permission java.lang.RuntimePermission "createClassLoader";
  3. permission java.lang.RuntimePermission "getClassLoader";
  4. permission java.lang.RuntimePermission "setContextClassLoader";
  5. };

设置启动的VM参数

ES 的启动类为 server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java:

29.源码阅读:源码阅读环境搭建 - 图10

点击运行按钮 IDEA 会自动生成运行的配置(此次运行肯定出错的啦~),然后修改 IDEA 的运行配置: 29.源码阅读:源码阅读环境搭建 - 图11

在弹出的配置界面中进行相应的设置:

29.源码阅读:源码阅读环境搭建 - 图12

如上图,如果你没有 VM 配置的输入框,需要点击 Modify options 进行添加。VM Option 的内容为:

  1. -Des.path.home=your_path/elasticsearch-7.13.0/run_home
  2. -Des.path.conf=your_path/elasticsearch-7.13.0/run_home/config
  3. -Dlog4j2.disable.jmx=true
  4. -Djava.security.policy=your_path/elasticsearch-7.13.0/run_home/config/elasticsearch.policy
  5. -Des.path.plugins=your_path/elasticsearch-7.13.0/run_home/plugins
  6. -Dtests.security.manager=false
  7. -Dtests.jarhell.check=false

如上配置,需要将 your_path 设置为你的路径。除了 VM 参数,还需要设置 Include dependencies wirh “Provided” scope,同样也是点击 Modify options 按钮进行添加。

运行与测试

如果你按照上述的操作一步步来操作,现在可以点击运行按钮运行ES了。使用以下指令查看集群状态:

  1. curl http://localhost:9200/_cluster/health

29.源码阅读:源码阅读环境搭建 - 图13

ok,至此 ES 的源码导入已经完成。

二、IDEA 调试工具

ES 源码模块多,代码量大,如果没有 IDEA 进行辅助,一头扎进源码堆里,很容易出不来。下面介绍一下 IDEA 的断点组和代码书签。

断点组

选中代码行并且打上断点,打开菜单栏: Run -> Breakpoints(或者你可以直接右键点击断点,弹出的框中选择 More),打开的对话框中可以看到你刚才添加的断点信息,选择对应的断点,右键单击,选择 Move to Group(如果有组存在,移到对应的组,如果没有新建即可)。

29.源码阅读:源码阅读环境搭建 - 图14

如上图,点击左边的复选框可以批量启用或者禁用一组断点。

IDEA 断点调试有两种策略,All 和 Thread。其中 All 是默认的,在这种策略下,如果一个类中打了两个断点,当命中一个断点出现暂停,而另一个线程进入并且命中第二个断点时,IDEA将不会暂停的!!!当我们在调试多线程程序时,如果需要第二个线程进入也要暂停的话,需要设置为 Thread 类型。右键单击断点即可设置,点击 Done 进行保存。

29.源码阅读:源码阅读环境搭建 - 图15

代码书签

如果想对某个流程上的关键代码进行标记收藏可以使用代码书签的功能,可以快速找到某个功能点的代码。

光标选中代码行,在我的环境,mac 上按 F3(带 bar 的 mac 需要按 Fn 才能出来 F3 )而 windows 10 的话按 F11,此时当前选中行会被标记,它的前面多了个对号或者标签符号。如果快捷键不生效,或者有快捷键冲突,可以在选中行最左边即行号的右边处用鼠标右键单击,弹出框中选择 Bookmarks 即可。然后 mac 中使用 command + F3 而 Windows 中使用 Shift + F11 组合键在弹出的对话框中对选中的书签进行重命名,点击对话框的编辑按钮(左上角的笔头)进行编辑即可。同样如果快捷键不生效,可以点击对应的标签符号进行编辑。

29.源码阅读:源码阅读环境搭建 - 图16

三、ES 源码目录简介

打开项目,在外层有很多目录,下面的是比较重要的:

  • docs,项目的文档。
  • distribution,用于构建 tar、zip、rpm、deb 包的目录。
  • libs,用于构建项目其他部分的依赖库,这些库都是内部使用的。
  • modules,ES 默认提供的功能,但是没有在 server 中构建。
  • plugins,官方支持的插件。
  • qa,就是 QA,测试和质量相关。
  • server,ES 服务的代码,我们要看的模块源码都在这里。
  • test,项目的测试框架,使用这个测试框架来测试服务、插件等。
  • x-pack,商业许可相关的功能代码。

进入到 server 包,可以看到各个模块、功能都有对应的包,下面挑几个来进行介绍:

  • bootstrap,服务启动相关的功能在实现,其中 Elasticsearch.java 类是服务启动的入口。
  • cluster,集群管理相关的功能在这里实现,例如集群状态维护、集群层面的配置信息等。
  • discovery,主要负责集群节点发现、主节点选举等功能的实现。
  • gateway,负责对 Master 广播的集群状态数据进行持久化存储。
  • http,提供客户端使用 HTTP 访问 ES API 的功能。
  • index,提供单个索引级别的管理功能。
  • indices,负责维护索引全局级别的设置和索引数据恢复等功能。
  • transport,负责内部节点间通信功能的实现。

ES 源码的模块比较多,但基本上都是见名识意,后续你可以慢慢了解。

四、总结

今天为你介绍了 ES 源码的导入,并且简单介绍了 IDEA 用于调试和源码阅读的两个小技巧,最后还介绍了 ES 源码包结构和各个模块对应的功能。

总的来说,ES 源码的导入比较简单,按照教程做应该没啥问题的。后续的内容我们就慢慢来解读部分功能对应的源码实现吧。