日期作者版本备注
2022-10-03dingbinthu@163.comv1.0

本文讲述在centos 7操作系统上从无到有实现IDEA+Kibana+elasticsearch本地调试elaticsearch源码的操作过程。

1. Git

稳妥起见,升级git到比较新的版本,如git version 2.24.1。

在linux执行git clone github上开源代码会发生卡死或半天没反应等现象,一般设置如下即可:

git config --global --unset https.proxy

git config --global --unset http.proxy

git config --global http.sslVerify "false"

git config --global http.postBuffer 5000000000 #最管用的,5g,单位Byte.

2. 下载Elasticsearch源码

本文调试的是Elasticsearch 7.10.2版本,注意后文kibana选择一定要与此版本完全一致才行。
注意: Elasticsearch 7.16.2也是目前场景下经常用的版本,其对应的Lucene版本是Lucene 8.10.1。

mkdir es-source-dir

cd es-source-dir

git clone https://github.com/elastic/elasticsearch.git elasticsearch-7.10.git

cd elasticsearch-7.10.git

git branch -a | grep 7.

git tag | grep 7.10

git checkout v7.10.2

源码路径根路径记作$es_code_root,方便下文使用

3.设置 Jdk

cd $es_code_root

vim CONTRIBUTING.md 浏览得知此版本的elasticsearch运行需要jdk14

vim ~/.bashrc

export JAVA_HOME=/path/to/jdk-14.0.2

稳妥起见,我们将 jdk1.8也安装上,后续测试可能用的着。

4. 准备Maven

wget https://dlcdn.apache.org/maven/maven-3/3.8.6/binaries/apache-maven-3.8.6-bin.tar.gz --no-check-certificate

export M2_HOME=/opt/chilly/app/apache-maven-3.8.6

mvn --version

settings.xml 文件一般存在于两个位置:

全局配置: ${maven.home}/conf/settings.xml
用户配置: ${user.home}/.m2/settings.xml
注意:用户配置优先于全局配置。${user.home} 和所有其他系统属性只能在 3.0+版本上使用。请注意 windows 和 Linux 使用变量的区别。

配置优先级:局部配置优先于全局配置。

配置优先级从高到低:pom.xml > user settings > global settings

如果这些文件同时存在,在应用配置时,会合并它们的内容,如果有重复的配置,优先级高的配置会覆盖优先级低的。

1) vim ${M2_HOME}/conf/settings.xml

Default: ${user.home}/.m2/repository

<localRepository>/path/to/local/repo</localRepository>

2) vim ~/.m2/settings.xml

3) project pom.xml

settings.xml 重点要注意的是修改localRepository 为实际希望下载存储jar包的地方。

<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">

    <!-- 默认的值是${user.home}/.m2/repository -->
    <localRepository>E:\apache\repository</localRepository>

    <!-- 如果Maven要试图与用户交互来得到输入就设置为true,否则就设置为false,默认为true。 -->
    <interactiveMode>true</interactiveMode>

    <!-- 如果Maven使用${user.home}/.m2/plugin-registry.xml来管理plugin的版本,就设置为true,默认为false。 -->
    <usePluginRegistry>false</usePluginRegistry>

    <!-- 如果构建系统要在离线模式下工作,设置为true,默认为false。 如果构建服务器因为网络故障或者安全问题不能与远程仓库相连,那么这个设置是非常有用的。 -->
    <offline>false</offline>

    <mirrors>
        <mirror>
            <id>aliyunmaven</id>
            <mirrorOf>*</mirrorOf><!--*代表所有的jar包都到阿里云下载-->
            <!--<mirrorOf>central</mirrorOf>--><!--central代表只有中央仓库的jar包才到阿里云下载-->
            <name>阿里云公共仓库</name>
            <url>https://maven.aliyun.com/repository/public</url>
        </mirror>

        <!--老版阿里云公共仓库-->
        <!--
        <mirror>
            <id>nexus-aliyun</id>
            <mirrorOf>central</mirrorOf>
            <name>Nexus aliyun</name>
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
        </mirror>
        -->
        
        <!-- 中央仓库在中国的镜像 -->
        <mirror>
            <id>maven.net.cn</id>
            <name>Mirror from Maven in china</name>
            <url>http://maven.net.cn/content/groups/public/</url>
            <mirrorOf>central</mirrorOf>
        </mirror>
    </mirrors>




    <!-- settings.xml中的profile是pom.xml中的profile的简洁形式。 它包含了激活(activation),仓库(repositories),插件仓库(pluginRepositories)和属性(properties)元素。 
        profile元素仅包含这四个元素是因为他们涉及到整个的构建系统,而不是个别的POM配置。 如果settings中的profile被激活,那么它的值将重载POM或者profiles.xml中的任何相等ID的profiles。 -->
    <profiles>
        <profile>
            <id>default</id>
            <activation>
                <activeByDefault>true</activeByDefault>
                <jdk>1.8</jdk>
            </activation>
            <!-- 远程仓库-->
            <repositories>
                <!-- 阿里云远程仓库-->
                <repository>
                    <id>aliyun</id>
                    <name>aliyun maven Repository</name>
                    <url>https://maven.aliyun.com/repository/public</url>
                    <releases>
                        <enabled>true</enabled>
                    </releases>
                    <snapshots>
                        <enabled>false</enabled>
                    </snapshots>
                </repository>
                <repository>
                    <id>spring-milestone</id>
                    <name>Spring Milestone Repository</name>
                    <url>http://repo.spring.io/milestone</url>
                    <releases>
                        <enabled>true</enabled>
                    </releases>
                    <snapshots>
                        <enabled>false</enabled>
                    </snapshots>
                    <layout>default</layout>
                </repository>
                <repository>
                    <id>spring-snapshot</id>
                    <name>Spring Snapshot Repository</name>
                    <url>http://repo.spring.io/snapshot</url>
                    <releases>
                        <enabled>false</enabled>
                    </releases>
                    <snapshots>
                        <enabled>true</enabled>
                    </snapshots>
                    <layout>default</layout>
                </repository>
            </repositories>
            <!-- 镜像,添加了会先从阿里云仓库下载-->
            <pluginRepositories>
                <pluginRepository>
                    <id>aliyun-plugin</id>
                    <url>https://maven.aliyun.com/repository/public</url>
                    <releases>
                        <enabled>true</enabled>
                    </releases>
                    <snapshots>
                        <enabled>false</enabled>
                    </snapshots>
                </pluginRepository>
            </pluginRepositories>
        </profile>
    </profiles>
    <!-- activations是profile的关键,就像POM中的profiles,profile的能力在于它在特定情况下可以修改一些值。 
        而这些情况是通过activation来指定的。 -->
    <!-- <activeProfiles/> -->
</settings>
另外须注意:在idea中配置maven配置文件和指定本地依赖jar包存储仓库路径非常重要,尤其注意,强烈建议将多个项目的local repository 设置到同一个路径,以重复利用下载的依赖jar包。如下图所示:

image-20221220075609358

5. 准备Gradle

越来越多的java项目使用gradle来管理和构建。gradle是比maven更高级的源码构建和管理方案,它不仅仅用于java,设置c++/python等多种语言都可以用来构建。安装完gradle后在命令行执行gradle init就会交互式提示用户选择语言和项目类型等帮助用户创建一个指定语言和指定类型的项目工程雏形。(有兴趣的读者可以试试,很好玩)

cd $es_root_dir

vim gradle/wrapper/gradle-wrapper.properties 发现elasticsearch源码工程用的gradle版本6.6.1

还发现配置文件里配置了:distributionBase=GRADLE_USER_HOME 和zipStoreBase=GRADLE_USER_HOME 都表明 elasticsearch 编译过程依赖的jar包都会下载到 GRADLE_USER_HOME环境变量值对应的目录下。(如果该变量未定义,gradle会默认将jar包放到 ~/.gradle下。)

5.1 原生gradle

wget https://downloads.gradle-dn.com/distributions/gradle-6.8.3-all.zip

找gradle命令是通过~/.bashrc 中export GRADLE_HOME=/path/to/gradle

而gradle 的任何命令的执行都依赖于环境变量GRADLE_USER_HOME的值,该变量值含义为gradle 配置和缓存下载所有的根目录(caches子目录)。

然后首次执行gradle -v 会发现~/.gradle目录被生成了,该目录下还有几个子目录。

此时删除.gradle目录,同时export GRADLE_USER_HOME=~/temp

再执行gradle -v,会发现~/.gradle目录不再生成,在GRADLE_USER_HOME环境变量值目录下对应生成了相关子目录。

至于gradle的配置文件都是在GRADLE_USER_HOME目录下init.gradle 以及子目录init.d下的*.gradle(按文件名字母顺序依次读取加载)。比如修改国内源就是在这些地方的配置文件里添加(本项目没有修改源)。下载的依赖jar包等都被放在GRADLE_USER_HOME下caches子目录下。

因此,强烈建议在~/.bashrc中 export GRADLE_USER_HOME=/path/to/gradle/user/home来配置gradle下载依赖包的路径。

注意:如果gradle 执行任务过程中发现Read Time out等类似报错,可采取以下两个解决方案:
1.修改国内源,方法是在GRADLE_USER_HOME 变量值对应的目录下新建init.gradle文件,在该文件中写入如下内容:

allprojects{
    repositories {
        def ALIYUN_REPOSITORY_URL = 'https://maven.aliyun.com/repository/public/'
        def ALIYUN_GRADLE_PLUGIN_URL = 'https://maven.aliyun.com/repository/gradle-plugin/'
        all { ArtifactRepository repo ->
            if(repo instanceof MavenArtifactRepository){
                def url = repo.url.toString()
                if (url.startsWith('https://repo1.maven.org/maven2/')) {
                    project.logger.lifecycle "Repository ${repo.url} replaced by $ALIYUN_REPOSITORY_URL."
                    remove repo
                }
                if (url.startsWith('https://jcenter.bintray.com/')) {
                    project.logger.lifecycle "Repository ${repo.url} replaced by $ALIYUN_REPOSITORY_URL."
                    remove repo
                }
                if (url.startsWith('https://plugins.gradle.org/m2/')) {
                    project.logger.lifecycle "Repository ${repo.url} replaced by $ALIYUN_GRADLE_PLUGIN_URL."
                    remove repo
                }
            }
        }
        maven { url ALIYUN_REPOSITORY_URL }
        maven { url ALIYUN_GRADLE_PLUGIN_URL }
    }
}

2.执行gradle命令时加上超时参数如下:(单位ms)
-Dorg.gradle.internal.http.socketTimeout=600000000
-Dorg.gradle.internal.http.connectionTimeout=600000000

比如:./gradlew -Dorg.gradle.internal.http.socketTimeout=600000000 -Dorg.gradle.internal.http.connectionTimeout=600000000 -i clean localDistro

注意:-Dorg.gradle.internal.http.socketTimeout 如果不设置的特别大,有可能因为gradle源导致因为下载速度慢导致下载的包不完整而导致无法验证完整的包。

5.2 Gradle wrapper

有了gradle,但是不同项目需要的gradle版本可能不一样。因此有必要将特定版本的gradle与特定的项目绑定起来,这就用到了gradle wrapper。

执行gradle init 或gradle wrapper 会生成如下的目录结构:

image-20221208181743970

简单解释一下: gradew和gradle.bat 分别是linux和windows下的gradle wrapper 命令名称。比如gradle -v 由gradlew -v代替。

gradle/wrapper目录下保证gradle wrapper特性能正确工作的数据组成。一个典型的gradle/wrapper/gradle-wrapper.properies内容组成如下:

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

当执行gradlew脚本时会首先去读取当前目录下gradle/wrapper/gradle-wrapper.properties文件,确定要下载的gradle的url,下载的zip压缩包的根目录,解压后的目录,gradle配置的根目录即distributionBase。然后才是去读取settings.gradle和build.gradle来执行定义的任务。gradle clean/build/tasks是常见的命令。

6. 编译打包elasticsearch工程

cd $es_code_root

export GRADLE_USER_HOME=~/data/gradle_user_home 这一句如果在~/.bashrc中export设置过了就不用再设置了。

执行./gradlew localDistro 编译打包es,显示如下表明编译成功。

BUILD SUCCESSFUL in 6m 39s
如果出现诸如Read Timeout 等报错,可尝试执行如下解决:

./gradlew  -Dorg.gradle.internal.http.socketTimeout=600000000  -Dorg.gradle.internal.http.connectionTimeout=600000000 -i clean localDistro

当前目录下的build/distribution/local/elasticsearch-7.10.2-SNAPSHOT 即时生成的发行包(大概500多MB),拷贝走改改配置可以直接启动es服务端。

注意es源码中包含lucene源码的,调试es源码就包含了调试lucene源码,不需要再单独下载lucene源码了。

7. 命令行启动/关闭elasticsearch服务

mkdir es-run-dir 记为 $es_run_dir

直接将编译打包好的build/distribution/local/elasticsearch-7.10.2-SNAPSHOT 目录拷贝到$es_run_dir

cd $es_run_dir

简单配置下eleasticsearch服务的配置文件config/elasticsearch.yml,如下:

[chilly@cent7ax config]$ diff elasticsearch.yml.old elasticsearch.yml.modified
23c23
< #node.name: node-1
---
> node.name: node-1
55c55
< #network.host: 192.168.0.1
---
> network.host: 0.0.0.0
68c68
< #discovery.seed_hosts: ["host1", "host2"]
---
> discovery.seed_hosts: ["192.168.0.210"]
72c72
< #cluster.initial_master_nodes: ["node-1", "node-2"]
---
> cluster.initial_master_nodes: ["node-1"]
88a89,95
> #
> #添加如下内容
> http.cors.enabled: true
> http.cors.allow-origin: "*"
> http.cors.allow-headers: Authorization
> xpack.security.enabled: true
> xpack.security.transport.ssl.enabled: true

变动说明如下:

放开node.name的注释,并在文件最后加上几行用于设置elastisearch服务访问密码认证的设置。注意如果想在外网访问elasticsearch服务,还需要配置一下elastcisearch.yml中的network.host 为本机实际ip或0.0.0.0,同时把discovery.seed_hosts设置为本机实际ip,设置cluster.initial_master_nodes。注意本机防火墙是否屏蔽了9200和9300等若干elasticsearch服务端口。

最后在命令上执行命令: ./bin/elasticsearch 或 ./bin/elasticsearch -d -p pid 启动elasticsearch服务。

此时如果直接在浏览器输入 127.0.0.1:9200 会发现提示要输入户名和密码。

此时注意是在elasticsearch服务启动的前提下,在命令行执行elasticsearch-setup-passwords interactive 以命令行交互方式设置密码。之后登陆时一般以用户名elastic用户登录执行查询。

命令行关闭elasticsearch服务命令为:pkill -F pid。

以下是命令行常见curl操作elasticsearch示例展示:

curl http://127.0.0.1:9200/_cat/health?v --user elastic:elastic
curl http://127.0.0.1:9200/_cat/nodes?v --user elastic:elastic
curl http://127.0.0.1:9200/_cat/indices?v --user elastic:elastic
curl -XPUT http://127.0.0.1:9200/position?pretty --user elastic:elastic
curl -XDELETE http://127.0.0.1:9200/position?pretty --user elastic:elastic
curl -XGET http://127.0.0.1:9200/_all/_mapping?pretty --user elastic:elastic
curl -XGET http://127.0.0.1:9200/position/_mapping?pretty --user elastic:elastic
curl -H "Content-Type:application/json" -XPOST 'http://localhost:9200/_all/_search?pretty' -d ' { "query":{ "match_all":{} } }' --user elastic:elastic
curl -H "Content-Type:application/json" -XPOST 'http://localhost:9200/_all/_search?pretty' -d ' { "query":{ "term":{"title":"小明"} } }' --user elastic:elastic
curl -H "Content-Type:application/json"  -XPOST http://127.0.0.1:9200/position1/_doc?pretty -d ' { "title": "小", "price":10.1, "desc":"" }' --user elastic:elastic
curl  -XPUT http://127.0.0.1:9200/position/?pretty  -H 'content-Type:application/json'  -d  ' {
    "mappings":{
        "properties":{
            "title":{
                "type":"text"
            },
            "description":{
                "type":"text"
            },
            "price":{
                "type":"double"
            },
            "onSale":{
                "type":"boolean"
            },
            "type":{
                "type":"integer"
            },
            "createDate":{
                "type":"date"
            }
        }
    }
}'  --user elastic:elastic
curl  -XPUT http://127.0.0.1:9200/position2/_mappings?pretty  -H 'content-Type:application/json'  -d  ' {
    "properties":{
        "title":{
            "type":"text"
        },
        "description":{
            "type":"text"
        },
        "price":{
            "type":"double"
        },
        "onSale":{
            "type":"boolean"
        },
        "type":{
            "type":"integer"
        },
        "createDate":{
            "type":"date"
        }
    }
}' --user elastic:elastic


注意: 后续上面这些用法都将在kibana dev-tool控制台以更精炼的方式使用,有自动提示等功能,

因此在kibana dev-tool开发控制台上执行上面curl语句是更推荐的方式。

8. 下载和安装、启动kibana

wget https://artifacts.elastic.co/downloads/kibana/kibana-7.10.2-linux-x86_64.tar.gz 注意:kibana版本必须与elasticsearch版本一致。

vim config/kibana.yml

--- kibana.yml  2021-01-13 10:07:15.000000000 +0800
+++ kibana.yml.modified 2022-12-19 16:17:03.079294242 +0800
@@ -4,7 +4,7 @@
 # Specifies the address to which the Kibana server will bind. IP addresses and host names are both valid values.
 # The default is 'localhost', which usually means remote machines will not be able to connect.
 # To allow connections from remote users, set this parameter to a non-loopback address.
-#server.host: "localhost"
+server.host: "0.0.0.0"

 # Enables you to specify a path to mount Kibana at if you are running behind a proxy.
 # Use the `server.rewriteBasePath` setting to tell Kibana if it should remove the basePath
@@ -38,8 +38,8 @@
 # the username and password that the Kibana server uses to perform maintenance on the Kibana
 # index at startup. Your Kibana users still need to authenticate with Elasticsearch, which
 # is proxied through the Kibana server.
-#elasticsearch.username: "kibana_system"
-#elasticsearch.password: "pass"
+elasticsearch.username: "elastic"
+elasticsearch.password: "elastic"

 # Enables SSL and paths to the PEM-format SSL certificate and SSL key files, respectively.
 # These settings enable SSL for outgoing requests from the Kibana server to the browser.

启动 kibana : ./bin/kibana

注意: 访问kibana主页:192.168.0.210:5601时,经常遇到一种情况,输入用户名和密码 总是提示不成功,一种经常的原因是:elasticsearch 进程的磁盘空间不足,导致elasticsearch自动切换成了只读模式。

访问http://192.168.0.210:5601/app/dev_tools#/console

image-20221219161459312

9. 下载和配置idea

推荐使用经典版本2020版linux idea,地址是链接:https://pan.baidu.com/s/1JYHt0JqonCxz3FOit7ypoQ?pwd=f1w8 。直接解压后执行./bin/idea.sh即可启动,第一次启动后倒入配置文件(主要是设置一些快捷键等):https://pan.baidu.com/s/1hgAxt_IVIgbERUCG0SgSCA?pwd=yvnh 。注意如果要重新设置导入idea的配置文件,可删除 ~/.config/JetBrains即可。链接:https://pan.baidu.com/s/1OwuyRKsIHqpWqGtSuU8q9Q?pwd=lqmg 可帮助以本地磁盘上插件方式导入。

注意有的时候,注意String,Path等这种jdk基础类都飘红,可能是因为jdk版本过高等造成,换一个jdk一般就能解决。

启动后比较重要的是设置好jdk和maven、gradle的配置文件和仓库等。

image-20221220075609358

image-20221220082633449

image-20221220105852313

10. 在idea中启动elasticsearch debug模式

10.1 导入es源码到idea

启动./bin/idea.sh 导入elasticsearch工程源码,即前文下载的v7.10.2版本。配置好需要的jdk14,配置好maven和gradle。

10.2 运行es源码

idea中点击$es_code_root/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java,找到main函数,右键点击Run 'Elasticsearch main()',会运行错误,然后点击Run/Edit Configurations...,在VM options中填入以下配置,切记将$es_run_root修改为前文第7节中es服务运行的根路径:

Run/Edit Configurations...,在VM options填入以下值:

-Dlog4j2.disable.jmx=true
-Des.path.home=$es_run_root
-Des.path.conf=$es_run_root/config
-Djava.security.policy=$es_run_root/config/java.policy
-Xms2g
-Xmx2g

然后在$es_run_root/config目录下创建文件java.policy,并写入以下配置:

grant {
    permission javax.management.MBeanServerPermission "createMBeanServer";
    permission java.lang.RuntimePermission "createClassLoader";
    permission java.lang.RuntimePermission "getClassLoader";
    permission java.lang.RuntimePermission "setContextClassLoader";
    permission org.elasticsearch.secure_sm.ThreadPermission
        "modifyArbitraryThread";
    permission org.elasticsearch.secure_sm.ThreadPermission
        "modifyArbitraryThreadGroup";
};

$es_run_root/config/elasticsearch.yml的配置方式保留前文第7节的对该文件的配置即可。

如果启动过程报错出现如下,你需要在Run/Edit Configurations...,在VM options填入-Xms2g -Xmx2g。

[1] bootstrap checks failed
[1]: initial heap size [327155712] not equal to maximum heap size [5217714176]; this can cause resize pauses

然后,运行Elasticsearch即可,待启动后,在浏览器输入http://localhost:9200/,显示类似第四步骤中的信息则证明启动成功:

如果出现异常信息“java.lang.NoClassDefFoundError: org/elasticsearch/plugins/ExtendedPluginsClassLoader”,则重新打开Run Configuration,勾选 include dependency with provided scope后,重新运行即可。如果还是报该异常,则可尝试修改server模块下的build.gradle中的compileOnly project(':libs:plugin-classloader')为compile project(':libs:plugin-classloader') 。

image-20221220105656244

接着同时启动kibana后,在浏览器输入http://localhost:5601即可通过kibana给idea debug启动的es服务发送查询了。

注意:

  1. 通过kibana Dev Tool平台上发送查询到idea debug本地启动的elasticsearch上调试代码时,经常会发现kibana后台老有一些查询在不定期地进行干扰我们的调试,一种临时解决方案是 DELETE .kibana_task_manager_1 这个索引,基本能解决这个问题。但更完美的解决方案还是设置指定indexName的条件断点,比如如下断点条件:
    searchContext.request().shardId.index.name.equals("your_index_name")
  2. elasticsearch远程调试只需要在远程server段es的conf目录下jvm.options文件最后一行加入如下代码然后正常启动即可:
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=0.0.0.0:5005

3.考虑到elasticsearch工程编译需要下载很多依赖,耗时较长不说,很多依赖下载非常缓慢。第一次通过IDEA导入elasticsearch项目第一次gradle会默认下载所有依赖,下载缓慢,且很多依赖下载可能根本无法完成,一个巧妙的解决办法是从别的地方能成功编译好相同版本的elasticsearch, 然后拷贝它的$GRADLE_USER_HOME目录下的caches目录到IDEA配置得GRADLE_USER_hOME的,然后设置一下IDEA使用gradle offline模式即gradle不从网络下载依赖,采用离线模式使用GRADLE_USER_HOME目录下caches中已经下载好的依赖,IDEA 设置gradle offline方法如下:
image.png

  1. 在IDEA中调试Elasticsearch源码时经常会发生代码乱跳的情况,因为elasticsearch内部多线程所致,要防止代码多线程乱跳干扰调试,设置断点的时候将Suspend中的Thread 按钮勾选上即可。如下图:

image.png


apollo008
151 声望9 粉丝

走完这一生,如果我和你在一起会变得更好,那我们就在一起,否则我就丢下你。我回顾我最光辉的时刻就是和不同的人在一起,变得更好的最长连续时刻。