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 &
访问 kibanahttp://192.168.1.91:5601/
启动 Head 插件
official github page
大致步骤:1
2
3
4
5
6Running 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’ - 这样就可以正确执行了。
- 将 Headers 的参数 KEY -> Content-Type 的 VALUE 由
创建索引
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 | POST http://192.168.1.91:9200/hjz-es-index-general/general/_search?pretty |
注意:
- 192.168.1.91:9200 是 ES 得访问地址和端口
- hjz-es-index-general 是索引的名称
- general 是类型的名称
- 上面 uri
http://192.168.1.91:9200/hjz-es-index-general/general/_search?pretty
中的索引名hjz-es-index-general
和类型名general
在 JSON 数据中如果有定义,可以省略;如果没有则必须在 uri 中指定。 - _search 是要查询
还有 _bulk,是 rest 命令,可以批量执行多个操作
- pretty 是将返回的信息以可读的 JSON 形式返回。(不过 postman 自带了 pretty 的功能)
send 之后,可以很快看到结果
通过 kibana 创建
1 | PUT hjz-es-index-general |
响应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"
}
}
}
}
}
}
文档管理
采坑记
cluster.name 配置问题
如果 cluster.name 参数写错了,会报如下错误:1
2
3
42018-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_name1
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 | <dependency> |
用下面的代码,初始化 TransportClient1
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
public class ElasticSearchConfig {
"${elasticsearch.host}") (
private String esHost;
"${elasticsearch.port}") (
private int esPort;
"${elasticsearch.cluster.name}") (
private String esName;
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
2XContentBuilder xContentBuilder = XContentFactory.jsonBuilder();
xContentBuilder.startObject();
报错信息1
2
3
4java.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
917: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)下面的引用版本,再一次运行,就正常了。
解决该问题,花了好几个小时,所以,特此记录下来,希望能帮到遇到同样问题的朋友。
网上没找到相关的解决方案,这种问题属于运行时的依赖版本不一样导致的,编译的时候是不会报错的,所以,隐藏得比较深。