ElasticSearch 相关辅助工具使用实践。ElasticSearch 版本 6.4.1。

启动 ElasticSearch

后台启动
/opt/elasticsearch-6.4.1/bin/elasticsearch -d

启动 kibana

nohup /opt/kibana-6.4.1-linux-x86_64/bin/kibana &

访问 kibana
http://192.168.1.91:5601/

启动 Head 插件

official github page
大致步骤:

1
2
3
4
5
6
Running with built in server
git clone git://github.com/mobz/elasticsearch-head.git
cd elasticsearch-head
cnpm install
npm start
open http://localhost:9100/

在服务器上真实的操作:

1
2
3
4
5
6
7
8
9
10
[utomcat@centOS7BasicForTest ~]$ cd ~/soft/elasticsearch-head/
[utomcat@centOS7BasicForTest elasticsearch-head]$ npm start

> elasticsearch-head@0.0.0 start /home/utomcat/soft/elasticsearch-head
> grunt server

(node:3302) ExperimentalWarning: The http2 module is an experimental API.
Running "connect:server" (connect) task
Waiting forever...
Started connect web server on http://localhost:9100

将 localhost 换成服务器的 IP 之后的[访问地址][http://192.168.1.91:9100]

创建和查询测试数据

以下,用 postman 工具操作。

  • 操作 PUT 或 POST,报错

    1
    2
    3
    4
    {
    "error": "Content-Type header [application/x-www-form-urlencoded] is not supported",
    "status": 406
    }
  • 解决

    • 将 Headers 的参数 KEY -> Content-Type 的 VALUE 由 application/x-www-form-urlencoded 改为 ‘application/json’
    • 这样就可以正确执行了。

创建索引

1
PUT http://192.168.1.91:9200/hjz-es-index-general

body JSON 参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
{
"settings": {
"number_of_replicas": 0,
"number_of_shards": 3,
"index.store.type": "niofs",
"index.query.default_field": "title",
"index.unassigned.node_left.delayed_timeout": "5m"
},
"mappings": {
"general": {
"dynamic": "strict",
"properties": {
"id": {
"type": "integer"
},
"title": {
"type": "text",
"index": true,
"analyzer": "ik_smart",
"search_analyzer": "ik_smart"
},
"author": {
"type": "text"
},
"content": {
"type": "text",
"index": true,
"analyzer": "ik_smart",
"search_analyzer": "ik_smart"
}
}
}
}
}

响应

1
2
3
4
5
{
"acknowledged": true,
"shards_acknowledged": true,
"index": "hjz-es-index-school-district"
}

写入文档(一条记录)

  • 选 POST,URL 输入 http://192.168.1.91:9200/hjz-es-index-general/general/
  • body 参数

    1
    2
    3
    4
    5
    6
    {
    "id":1,
    "title":"Elasticsearch简介",
    "author":"Andy",
    "content":"Elasticsearch是一个基于Lucene的搜索引擎"
    }
  • 执行之后,返回

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    {
    "_index": "hjz-es-index-general",
    "_type": "general",
    "_id": "-YwaKWYB2RmkJe44rsKf",
    "_version": 1,
    "result": "created",
    "_shards": {
    "total": 1,
    "successful": 1,
    "failed": 0
    },
    "_seq_no": 0,
    "_primary_term": 1
    }

查询索引所有数据

1
2
POST http://192.168.1.91:9200/hjz-es-index-general/general/_search?pretty
# body 处写 json 数据,或者 body->binary; 选择 JSON 格式的文件

注意:

  1. 192.168.1.91:9200 是 ES 得访问地址和端口
  2. hjz-es-index-general 是索引的名称
  3. general 是类型的名称
  4. 上面 uri http://192.168.1.91:9200/hjz-es-index-general/general/_search?pretty 中的索引名 hjz-es-index-general 和类型名 general 在 JSON 数据中如果有定义,可以省略;如果没有则必须在 uri 中指定。
  5. _search 是要查询

    还有 _bulk,是 rest 命令,可以批量执行多个操作

  6. pretty 是将返回的信息以可读的 JSON 形式返回。(不过 postman 自带了 pretty 的功能)
    send 之后,可以很快看到结果

通过 kibana 创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
PUT hjz-es-index-general
{
"settings": {
"number_of_replicas": 0,
"number_of_shards": 3,
"index.store.type": "niofs",
"index.query.default_field": "title",
"index.unassigned.node_left.delayed_timeout": "5m"
},
"mappings": {
"general": {
"dynamic": "strict",
"properties": {
"id": {
"type": "integer"
},
"title": {
"type": "text",
"index": true,
"analyzer": "ik_smart",
"search_analyzer": "ik_smart"
},
"author": {
"type": "text"
},
"content": {
"type": "text",
"index": true,
"analyzer": "ik_smart",
"search_analyzer": "ik_smart"
}
}
}
}
}

响应

1
2
3
4
5
{
"acknowledged": true,
"shards_acknowledged": true,
"index": "hjz-es-index-general"
}

查看索引 mapping 内容

1
http://192.168.1.91:9200/hjz-es-index-general/_mapping?pretty

结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
"hjz-es-index-general": {
"mappings": {
"general": {
"dynamic": "strict",
"properties": {
"author": {
"type": "text"
},
"content": {
"type": "text",
"analyzer": "ik_smart"
},
"id": {
"type": "integer"
},
"title": {
"type": "text",
"analyzer": "ik_smart"
}
}
}
}
}
}

文档管理

official reference

采坑记

cluster.name 配置问题

如果 cluster.name 参数写错了,会报如下错误:

1
2
3
4
2018-09-30 15:16:58.721  WARN 7832 --- [][generic][T#2]] o.e.c.t.TransportClientNodesService      : node {#transport#-1}{YGbM08bGTPO_wpv-bPWOoQ}{192.168.1.91}{192.168.1.91:9300} not part of the cluster Cluster [hjz-es], ignoring...
NoNodeAvailableException[None of the configured nodes are available: [{#transport#-1}{YGbM08bGTPO_wpv-bPWOoQ}{192.168.1.91}{192.168.1.91:9300}]
]
...

查询 cluster_name

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 输入http://192.168.1.91:9200/
{
"name" : "hjz-es",
"cluster_name" : "hjz-es-cluster",
"cluster_uuid" : "AQI-AmR2QmW5-6HhzxCjYQ",
"version" : {
"number" : "6.4.1",
"build_flavor" : "default",
"build_type" : "tar",
"build_hash" : "e36acdb",
"build_date" : "2018-09-13T22:18:07.696808Z",
"build_snapshot" : false,
"lucene_version" : "7.4.0",
"minimum_wire_compatibility_version" : "5.6.0",
"minimum_index_compatibility_version" : "5.0.0"
},
"tagline" : "You Know, for Search"
}

配置参数要写成上面 cluster_name 对应的值。

依赖问题

1
2
3
4
5
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>transport</artifactId>
<version>6.4.1</version>
</dependency>

用下面的代码,初始化 TransportClient

1
2
3
4
5
6
7
8
@Bean
public TransportClient esClient() throws UnknownHostException {
Settings settings = Settings.builder().put("cluster.name", this.esName).build();
InetSocketTransportAddress master = new InetSocketTransportAddress(InetAddress.getByName(esHost), esPort);
TransportClient client = new PreBuiltTransportClient(settings).addTransportAddress(master);

return client;
}

报错:

1
2
3
4
5
6
...
Caused by: java.lang.NoClassDefFoundError: Could not initialize class org.elasticsearch.transport.client.PreBuiltTransportClient
...
Factory method 'esClient' threw exception; nested exception is java.lang.NoClassDefFoundError: org/elasticsearch/plugins/NetworkPlugin
...
Caused by: java.lang.NoClassDefFoundError: org/elasticsearch/plugins/NetworkPlugin

网上找到的解决方案,是要增加引用

1
2
3
4
5
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>6.4.1</version>
</dependency>

同时,可将 client 依赖的版本排除

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>transport</artifactId>
<version>6.4.1</version>
<exclusions>
<exclusion>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
</exclusion>
</exclusions>
</dependency>

当然,不加 exclusions 项,maven 会自动用外面配置的最新版本,而忽略 client 依赖的 2.4.6 版本。
如果引用了最新版的 elasticsearch,InetSocketTransportAddress 将不再可用,要换成最新版定义的 TransportAddress。
值得注意的是,TransportAddress 在低版本是一个接口。

正确的代码变成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Configuration
public class ElasticSearchConfig {
@Value("${elasticsearch.host}")
private String esHost;

@Value("${elasticsearch.port}")
private int esPort;

@Value("${elasticsearch.cluster.name}")
private String esName;

@Bean
public TransportClient esClient() throws UnknownHostException {
Settings settings = Settings.builder().put("cluster.name", this.esName).build();
TransportAddress transportAddress = new TransportAddress(InetAddress.getByName(esHost), esPort);
TransportClient client = new PreBuiltTransportClient(settings).addTransportAddress(transportAddress);

return client;
}
}

Caused by: java.lang.NoClassDefFoundError: org/elasticsearch/common/transport/InetSocketTransportAddress

spring boot 1.5.10,正常,升级成 2.0.5 就报错了

1
Caused by: java.lang.NoClassDefFoundError: org/elasticsearch/common/transport/InetSocketTransportAddress

解决,引用 transport-netty4-client 最新版依赖

1
2
3
4
5
<dependency>
<groupId>org.elasticsearch.plugin</groupId>
<artifactId>transport-netty4-client</artifactId>
<version>6.4.1</version>
</dependency>

xContentBuilder.startObject() 报错:java.lang.NullPointerException

代码

1
2
XContentBuilder xContentBuilder = XContentFactory.jsonBuilder();
xContentBuilder.startObject();

报错信息

1
2
3
4
java.lang.NullPointerException
at com.fasterxml.jackson.core.json.UTF8JsonGenerator.writeStartObject(UTF8JsonGenerator.java:324)
at org.elasticsearch.common.xcontent.json.JsonXContentGenerator.writeStartObject(JsonXContentGenerator.java:151)
at org.elasticsearch.common.xcontent.XContentBuilder.startObject(XContentBuilder.java:252)

出错的源码,UTF8JsonGenerator.java:324,_outputBuffer 为 null,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
public final void writeStartObject() throws IOException
{
_verifyValueWrite("start an object");
_writeContext = _writeContext.createChildObjectContext();
if (_cfgPrettyPrinter != null) {
_cfgPrettyPrinter.writeStartObject(this);
} else {
if (_outputTail >= _outputEnd) {
_flushBuffer();
}
_outputBuffer[_outputTail++] = BYTE_LCURLY;
}
}

解决

解决方法让人啼笑皆非。
我把 elasticsearch api 的三个依赖(org.elasticsearch、org.elasticsearch.client、transport-netty4-client)的版本,全部由 6.4.1 改为 6.4.2,还是报错。

elasticsearch 依赖的 jackson-core 是 2.9.6,我又把 jackson-core 在外层引用最新版本 2.9.7,依然报错。

但是,在调试跟踪源码的过程中,多次 mvn clean compile,有一次突然就不报错了。

然后,我将 jackson-core 依赖在 pom.xml 中去掉,再次调试,正常。
最后,我将 elasticsearch api 的三个依赖,改回 6.4.1,正常。

在之前出错的过程中,我多次操作过 mvn clean compile,都没好,一度怀疑是 jackson 存在 bug,经过以上操作之后,怀疑是操作系统的问题(之前也有遇到莫名其妙的问题,重启 windows 之后就好了),于是,我重启了我的 win10,再次调试,elasticsearch api 用 6.4.1 与 6.4.2 都没有再报错了。

这个异常,之前在其中一个类的代码中没有问题,在另外一个类里面有问题,而且我反复测试过,再后来,在两个类中都有问题。

结论

在遇到这种类似的问题,有的地方报错,有的地方不报错,有时候报错有时候不报错,如果用的是 windows,最好重启一下再试,如果依然有问题再去跟踪调试和分析源代码。

多项目情况下依赖版本问题导致错误

首先是在一个 web 项目下调试通过,然后将 es 相关的类放到一个专门的 module 下,项目启动,报错如下:

1
2
3
4
5
6
7
8
9
17:40:50,798 <org.springframework.boot.SpringApplication> ERROR [restartedMain]: Application startup failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'esClient' defined in apg.common.es.config.ElasticsearchConfig: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.elasticsearch.client.transport.TransportClient]: Factory method 'esClient' threw exception; nested exception is java.lang.InstantiationError: org.elasticsearch.common.transport.TransportAddress
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:599) ~[spring-beans-4.3.14.RELEASE.jar:4.3.14.RELEASE]
...
Caused by: java.lang.InstantiationError: org.elasticsearch.common.transport.TransportAddress
at apg.common.es.config.ElasticsearchConfig.esClient(ElasticsearchConfig.java:26) ~[classes/:?]
at apg.common.es.config.ElasticsearchConfig$$EnhancerBySpringCGLIB$$b492deb9.CGLIB$esClient$0(<generated>) ~[classes/:?]
at apg.common.es.config.ElasticsearchConfig$$EnhancerBySpringCGLIB$$b492deb9$$FastClassBySpringCGLIB$$feac0256.invoke(<generated>) ~[classes/:?]
...

解决

看一下引用 es module 的依赖树,能看出来,org.elasticsearch 用到的是版本 2.4.6。

其实,es module 已经引用了最新版的 org.elasticsearch,同时,在 org.elasticsearch.client 引用里已经排除对 org.elasticsearch 的依赖,es module 的引用如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>transport</artifactId>
<version>${elasticsearch.client.version}</version>
<exclusions>
<exclusion>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
</exclusion>
<exclusion>
<groupId>org.elasticsearch.plugin</groupId>
<artifactId>transport-netty4-client</artifactId>
</exclusion>
</exclusions>
</dependency>

至于为什么会这样,暂时还没搞清楚头绪,待空了再好好研究一下 maven 依赖,或者是 maven 编译机制。

既然怀疑是版本引用导致的问题,那就好办了,为了印证这个判断,将同样的代码 copy 至引用的 web 项目,不用调试就已经显示错误了


提示 'TransportAddress' is abstract; cannot be instantiat,TransportAddress 在旧版本确实只是一个接口而已,在新版本里,变成了一个普通类。
然后,在 pom.xml 里添加新版本的引用,该提示即消失
1
2
3
4
5
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>6.4.2</version>
</dependency>

在 web 项目添加与 apg.comm.es 相同的依赖之后,再去看一下依赖树,如下所示


外层引用版本会覆盖某一个包(module)下面的引用版本,再一次运行,就正常了。

解决该问题,花了好几个小时,所以,特此记录下来,希望能帮到遇到同样问题的朋友。

网上没找到相关的解决方案,这种问题属于运行时的依赖版本不一样导致的,编译的时候是不会报错的,所以,隐藏得比较深。

建议搜索词

official reference