0%

SpringBoot2.x(十二)整合ElasticSearch

文末附较新ElasticSearch全套教程

本文将介绍Linux7环境下如何安装ElasticSearch、ElasticSearch常见启动异常解决方法、SpringBoot2.x整合ElasticSearch。

简介

  • elasticsearch下文简称 es是一个解决大数据搜索(TB/PB级别)的框架。
  • 对比数据库,indextypedocument的理解:
mysql database table record
elasticsearch index type document

特点

全文检索,结构化检索,数据统计、分析,接近实时处理,分布式搜索(可部署数百台服务器),处理PB级别的数据
,搜索纠错,自动完成

使用场景

日志搜索,数据聚合,数据监控,报表统计分析

国内外使用者

维基百科,Stack Overflow,GitHub

新特性

  • elasticsearch6.2.x版本基于Lucene 7.x,更快,性能进一步提升,对应的序列化组件,升级到Jackson 2.8
  • 推荐使用5.0版本推出的Java REST/HTTP客户端,依赖少,比Transport使用更方便,在基准测试中,性能并不输于Transport客户端,
  • 在5.0到6.0版本中,每次有对应的API更新, 文档中也说明,推荐使用这种方式进行开发使用
  • 所有可用节点间的负载均衡在节点故障和特定响应代码的情况下进行故障转移,失败的连接处罚(失败的节点是否重试取决于失败的连续次数;失败的失败次数越多,客户端在再次尝试同一节点之前等待的时间越长)
  • (重要)不再支持一个索引库里面多个type,6.x版本已经禁止一个index里面多个type,所以一个index索引库只能存在1个type

官方文档

安装

安装配置

1
wget  https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.6.8.tar.gz
  • 将安装包解压到 /export/server(我的linux用来存放服务器应用的目录)下

配置环境变量

Java

由于 es是由java开发的,es5.6依赖 jdk1.8,下载 jdk1.8之后再 /etc/profile中配置java环境变量

1
2
3
4
5
# java env
JAVA_HOME=/export/software/jdk1.8.0_161
CLASSPATH=.:$JAVA_HOME/lib:$JAVA_HOME/lib/tools.jar
PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH
export JAVA_HOME CLASSPATH PATH

/export/software是我存放软件的目录,我将下载的 jdk1.8解压到了该目录(jdk1.8.0_161

es

配置 es变量,方便启动 es/etc/profile

1
2
3
# es env
ES_HOME=/export/server/elasticsearch-5.6.8
PATH=$PATH:$ES_HOME/bin

配置es端口和连接IP白名单

修改 /export/server/elasticsearch-5.6.8/config/elasticsearch.yml中的 Network部分:

1
2
3
4
5
6
7
8
9
10
11
# ---------------------------------- Network -----------------------------------
#
# Set the bind address to a specific IP (IPv4 or IPv6):
#
network.host: 0.0.0.0 #绑定可连接当前es服务的IP白名单,0.0.0.0表示任何主机都可访问
#
# Set a custom port for HTTP:
#
http.port: 9200 #es服务http端口
#
# For more information, consult the network module documentation.

如果的你主机被公网访问,那么为了安全考虑不建议你设置 network.host0.0.0.0


启动

非root角色启动

经过上面一些列的配置后就可以启动 es了,但是不能以 root用户启动,否则会报错(es防止黑客入侵可获取 root权限)如下:

1
2
[2018-07-22T13:30:17,864][WARN ][o.e.b.ElasticsearchUncaughtExceptionHandler] [] uncaught exception in thread [main] org.elasticsearch.bootstrap.StartupException:java.lang.RuntimeException: can not run elasticsearch as root
...

你可以通过 useradd -m 用户名的命令创建一个用户(如 zaw):

1
useradd -m zaw

并给 zaw用户设置密码:

1
passwd zaw #回车后输入密码

zaw用户赋予 es目录所有权限

1
chmod 777 -R /export/server/elasticsearch-5.6.8

切换到 zaw用户:

1
2
[root@pinyoyougou-docker config]# su zaw
[zaw@pinyoyougou-docker config]$

启动 eselasticsearches/bin下的一个命令):

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
[zaw@pinyoyougou-docker root]$ elasticsearch
[2018-07-22T13:44:37,924][INFO ][o.e.n.Node ] [] initializing ...
[2018-07-22T13:44:39,037][INFO ][o.e.e.NodeEnvironment ] [ISgfHbl] using [1] data paths, mounts [[/ (r ootfs)]], net usable_space [12.5gb], net total_space [16.9gb], spins? [unknown], types [rootfs]
[2018-07-22T13:44:39,038][INFO ][o.e.e.NodeEnvironment ] [ISgfHbl] heap size [1.9gb], compressed ordin ary object pointers [true]
[2018-07-22T13:44:39,048][INFO ][o.e.n.Node ] node name [ISgfHbl] derived from node ID [ISg fHblQQqyenLY8JrqXzw]; set [node.name] to override
[2018-07-22T13:44:39,049][INFO ][o.e.n.Node ] version[5.6.8], pid[3839], build[688ecce/2018 -02-16T16:46:30.010Z], OS[Linux/3.10.0-514.el7.x86_64/amd64], JVM[Oracle Corporation/Java HotSpot(TM) 64- Bit Server VM/1.8.0_161/25.161-b12]
[2018-07-22T13:44:39,049][INFO ][o.e.n.Node ] JVM arguments [-Xms2g, -Xmx2g, -XX:+UseConcMa rkSweepGC, -XX:CMSInitiatingOccupancyFraction=75, -XX:+UseCMSInitiatingOccupancyOnly, -XX:+AlwaysPreTouch , -Xss1m, -Djava.awt.headless=true, -Dfile.encoding=UTF-8, -Djna.nosys=true, -Djdk.io.permissionsUseCanon icalPath=true, -Dio.netty.noUnsafe=true, -Dio.netty.noKeySetOptimization=true, -Dio.netty.recycler.maxCap acityPerThread=0, -Dlog4j.shutdownHookEnabled=false, -Dlog4j2.disable.jmx=true, -Dlog4j.skipJansi=true, - XX:+HeapDumpOnOutOfMemoryError, -Des.path.home=/export/server/elasticsearch-5.6.8]
[2018-07-22T13:44:45,319][INFO ][o.e.p.PluginsService ] [ISgfHbl] loaded module [aggs-matrix-stats]
[2018-07-22T13:44:45,335][INFO ][o.e.p.PluginsService ] [ISgfHbl] loaded module [ingest-common]
[2018-07-22T13:44:45,336][INFO ][o.e.p.PluginsService ] [ISgfHbl] loaded module [lang-expression]
[2018-07-22T13:44:45,336][INFO ][o.e.p.PluginsService ] [ISgfHbl] loaded module [lang-groovy]
[2018-07-22T13:44:45,336][INFO ][o.e.p.PluginsService ] [ISgfHbl] loaded module [lang-mustache]
[2018-07-22T13:44:45,336][INFO ][o.e.p.PluginsService ] [ISgfHbl] loaded module [lang-painless]
[2018-07-22T13:44:45,336][INFO ][o.e.p.PluginsService ] [ISgfHbl] loaded module [parent-join]
[2018-07-22T13:44:45,336][INFO ][o.e.p.PluginsService ] [ISgfHbl] loaded module [percolator]
[2018-07-22T13:44:45,336][INFO ][o.e.p.PluginsService ] [ISgfHbl] loaded module [reindex]
[2018-07-22T13:44:45,336][INFO ][o.e.p.PluginsService ] [ISgfHbl] loaded module [transport-netty3]
[2018-07-22T13:44:45,336][INFO ][o.e.p.PluginsService ] [ISgfHbl] loaded module [transport-netty4]
[2018-07-22T13:44:45,337][INFO ][o.e.p.PluginsService ] [ISgfHbl] no plugins loaded
[2018-07-22T13:44:55,510][INFO ][o.e.d.DiscoveryModule ] [ISgfHbl] using discovery type [zen]
[2018-07-22T13:44:57,353][INFO ][o.e.n.Node ] initialized
[2018-07-22T13:44:57,353][INFO ][o.e.n.Node ] [ISgfHbl] starting ...
[2018-07-22T13:45:08,276][INFO ][o.e.t.TransportService ] [ISgfHbl] publish_address {192.168.25.135:930 0}, bound_addresses {[::]:9300}
[2018-07-22T13:45:08,297][INFO ][o.e.b.BootstrapChecks ] [ISgfHbl] bound or publishing to a non-loopba ck address, enforcing bootstrap checks
[2018-07-22T13:45:09,937][INFO ][o.e.m.j.JvmGcMonitorService] [ISgfHbl] [gc][young][12][8] duration [967m s], collections [1]/[1.5s], total [967ms]/[4.2s], memory [78.8mb]->[66mb]/[1.9gb], all_pools {[young] [48 .6mb]->[33.2mb]/[66.5mb]}{[survivor] [8.3mb]->[8.3mb]/[8.3mb]}{[old] [21.8mb]->[24.5mb]/[1.9gb]}
[2018-07-22T13:45:09,939][WARN ][o.e.m.j.JvmGcMonitorService] [ISgfHbl] [gc][12] overhead, spent [967ms] collecting in the last [1.5s]
[2018-07-22T13:45:11,470][INFO ][o.e.c.s.ClusterService ] [ISgfHbl] new_master {ISgfHbl}{ISgfHblQQqyenL Y8JrqXzw}{aWtcpw25Qo-qBczOAtnnCw}{192.168.25.135}{192.168.25.135:9300}, reason: zen-disco-elected-as-mast er ([0] nodes joined)
[2018-07-22T13:45:11,792][INFO ][o.e.h.n.Netty4HttpServerTransport] [ISgfHbl] publish_address {192.168.25 .135:9200}, bound_addresses {[::]:9200}
[2018-07-22T13:45:11,793][INFO ][o.e.n.Node ] [ISgfHbl] started

后台启动

使用 elasticsearch -d(daemonize)或 elasticsearch &可在后台启动 elasticsearch从而避免关闭终端也会关闭 es

常见启动时异常

异常1

1
max file descriptors [4096] for elasticsearch process is too low, increase to at least [65536]

原因

启动 es 的用户拥有的可创建文件描述的权限太低,至少需要65536。

解决办法

切换到root用户

1
2
[zaw@pinyoyougou-docker config]$ su
密码: #root用户密码

修改/etc/security/limits.conf,在文件尾添加如下内容:

1
2
zaw hard nofile 65536
zaw soft nofile 65536

其中 zaw是你创建的用来启动 es的用户

切换到 zaw用户启动 es

1
2
[root@pinyoyougou-docker ~]# su zaw
[zaw@pinyoyougou-docker root]$ elasticsearch

异常2

1
max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

解决办法

切换到 root用户修改/etc/sysctl.conf ,在文件末尾添加:

1
vm.max_map_count=655360

执行 sysctl -p使更改生效,再切换到 zaw启动 es

异常3

1
Java HotSpot(TM) 64-Bit Server VM warning: INFO: os::commit_memory(0x0000000085330000, 2060255232, 0) failed; error='Cannot allocate memory' (errno=12)

需增加服务器内存,或者在 es/config/jvm.options中修改如下两处:

1
2
3
4
5
# Xms represents the initial size of total heap space
# Xmx represents the maximum size of total heap space

-Xms128M
-Xmx128M

异常4

1
max number of threads [1024] for user [work] likely too low, increase to at least [2048]

使用 root修改 /etc/security/limits.d/下以 -nproc.conf结尾的文件:

1
2
*          soft    nproc     2048
root soft nproc unlimited

异常5

1
Exception in thread "main" java.nio.file.AccessDeniedException: /usr/local/software/temp/elasticsearch-6.2.2/config/jvm.options

当前用户权限不够,切换到 root,赋予普通用户操作 es目录所有权

1
chmod 777 -R /export/server/elasticsearch-5.6.8

测试

访问 192.168.25.135:9200若响应结果如下则 es部署成功

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"name" : "ISgfHbl",
"cluster_name" : "elasticsearch",
"cluster_uuid" : "8XJxpaVvRkytn7SQ4AYBaQ",
"version" : {
"number" : "5.6.8",
"build_hash" : "688ecce",
"build_date" : "2018-02-16T16:46:30.010Z",
"build_snapshot" : false,
"lucene_version" : "6.6.1"
},
"tagline" : "You Know, for Search"
}

192.168.25.135是我部署es的虚拟机IP

如果访问失败,那你需要看一下 es服务所在主机的防火墙是否开放 9200端口

如果使用的是虚拟机,你可以通过 service iptables stop(centos6.x)或 systemctl stop firewalld(centos7.x)直接关闭防火墙。


es-http快速入门

es5.6http操作es快速入门:官方文档

使用PostMan测试es接口

关于 PostMan的使用可以参考 SpringBoot2.x(二)SpringBoot接口Http这篇文章。下面将根据官方文档提供接口测试 es的使用

  • 查看集群状态:192.168.25.135:9200/_cat/health?v (GET提交)
1
2
epoch      timestamp cluster       status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent
1532244920 15:35:20 elasticsearch green 1 1 0 0 0 0 0 0 - 100.0%

当前就一个 es,下文将省略esIP和端口(192.168.25.135:9200

  • 查看各节点信息:/_cat/nodes?v(GET)
1
2
ip             heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
192.168.25.135 3 92 0 0.00 0.01 0.05 mdi * ISgfHbl
  • 查看索引(数据库)列表:/_cat/indices?v(GET)
1
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size

​ 当前还未创建 index,因此只显示表头信息

  • 创建 index:/customer?pretty(PUT)
1
2
3
4
5
{
"acknowledged": true,
"shards_acknowledged": true,
"index": "customer"
}

常见异常原因:

  • 要使用PUT提交方式
  • 如果你使用的是PostMan,清空当前标签页的 HeadersBody,本提交只需设置提交方式和提交链接。我就是因为之前在当前标签页设置了请求体,然后换链接成 /customer?pretty的时候没有清空它导致报错困扰了我好久

请求结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"error": {
"root_cause": [
{
"type": "illegal_argument_exception",
"reason": "unknown setting [index.password] please check that any required plugins are installed, or check the breaking changes documentation for removed settings"
}
],
"type": "illegal_argument_exception",
"reason": "unknown setting [index.password] please check that any required plugins are installed, or check the breaking changes documentation for removed settings"
},
"status": 400
}

这时我们再次查看 indices/_cat/indices?v

1
2
health status index    uuid                   pri rep docs.count docs.deleted store.size pri.store.size
yellow open customer xN-iWj0iQDSzKFelIQ7H7g 5 1 0 0 955b 955b

更多通过http对es中index的增删改查操作可参阅 官方文档


SpringBoot2.x整合es

Spring Data ElasticSearch文档

官方文档

版本说明

Spring Boot Version (x) Spring Data Elasticsearch Version (y) Elasticsearch Version (z)
x <= 1.3.5 y <= 1.3.4 z <= 1.7.2*
x >= 1.4.x 2.0.0 <=y < 5.0.0** 2.0.0 <= z < 5.0.0**
spring data elasticsearch elasticsearch
3.1.x 6.2.2
3.0.x 5.5.0
2.1.x 2.4.0
2.0.x 2.2.0
1.3.x 1.5.2

这里SpringBoot使用 2.3es使用 5.6

环境准备

引入 starter-data-elasticsearch依赖

1
2
3
4
<dependency>  
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

添加SpringBoot整合es配置属性(application.properties

1
2
3
4
5
6
7
# ELASTICSEARCH (ElasticsearchProperties)
# es集群名称(本例只有一个节点),任意.
spring.data.elasticsearch.cluster-name=elasticsearch
# es节点地址,默认9300,可在es/config/elasticsearch.yml中修改
spring.data.elasticsearch.cluster-nodes=192.168.25.135:9300
# 是否开启es仓库
spring.data.elasticsearch.repositories.enabled=true

数据层搭建

POJO

你需要在你的 pojo类上添加 @Document注解,同时指明 indexName(相当于数据库名),type(相当于表名)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package top.zhenganwen.springbootesdemo.pojo;

import lombok.Data;
import org.springframework.data.elasticsearch.annotations.Document;

import java.io.Serializable;

/**
* Article class
*
* @author zhenganwen
* @date 2018/7/23
*/
@Data
@Document(indexName = "blog", type = "article")
public class Article implements Serializable{
private Long id;
private String title;
private String content;
private String summary;
private int pv;
private String author;
}

DAO

与传统的DAO编写不同的是,你只需创建一个接口 extends ElasticsearchRepository<T,ID>,其中 T是该DAO操作的 pojo类,ID是作为主键的属性类型(必须是可序列化的)。你还需要在该接口上添加 @Repository(或 @Component注解),Spring在扫描该组件时会根据你传入的 T动态创建一个实现该接口的 bean并注册到容器中。

ElasticsearchRepositorySpring Data ElasticSearch为我们封装好的可进行 crud、分页、排序的接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package top.zhenganwen.springbootesdemo.repository;

import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;
import top.zhenganwen.springbootesdemo.pojo.Article;

/**
* ArticleEsRepository class
* 实现Article在es中的crud、分页、排序、高亮关键字
*
* @author zhenganwen
* @date 2018/7/23
*/
@Repository
public interface ArticleEsRepository extends ElasticsearchRepository<Article,Long> {

}

测试

插入记录

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
package top.zhenganwen.springbootesdemo;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import top.zhenganwen.springbootesdemo.pojo.Article;
import top.zhenganwen.springbootesdemo.repository.ArticleEsRepository;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootEsDemoApplicationTests {

@Test
public void contextLoads() {
}
@Autowired
private ArticleEsRepository articleEsRepository;

@Test
public void testSave() {
Article article = new Article();
article.setAuthor("Alice");
article.setContent("spring boot data es");
article.setId(1l);
article.setPv(100);
article.setSummary("spring boot es");
articleEsRepository.save(article);
}
}

如果你的DAO接口在 extends ElasticsearchRepository<T,ID>时没有指定 TID没有实现 Serializable,在加载Spring容器时会报错,因为它无法根据你传入的泛型动态创建DAO实现类。

如果测试显示绿条,可以使用PostMan查看索引列表 /_cat/indices?v

1
2
3
health status index    uuid                   pri rep docs.count docs.deleted store.size pri.store.size
yellow open customer xN-iWj0iQDSzKFelIQ7H7g 5 1 0 0 955b 955b
yellow open blog aA45XC7vSOyURbl-Z8twXw 5 1 1 0 5.7kb 5.7kb

查看blog的结构/blog

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
{
"blog": {
"aliases": {},
"mappings": {
"article": {
"properties": {
"author": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"content": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"id": {
"type": "long"
},
"pv": {
"type": "long"
},
"summary": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
},
"settings": {
"index": {
"refresh_interval": "1s",
"number_of_shards": "5",
"provided_name": "blog",
"creation_date": "1532326112557",
"store": {
"type": "fs"
},
"number_of_replicas": "1",
"uuid": "aA45XC7vSOyURbl-Z8twXw",
"version": {
"created": "5060899"
}
}
}
}
}

按主键查记录(/blog/article/1):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"_index": "blog",
"_type": "article",
"_id": "1",
"_version": 1,
"found": true,
"_source": {
"id": 1,
"title": null,
"content": "spring boot data es",
"summary": "spring boot es",
"pv": 100,
"author": "Alice"
}
}

查询记录

使用Spring Data ElasticSearch查询记录的关键接口是 QueryBuilders,其中封装了各种 丰富的crud查询条件,这里使用它做一个关键字查询的实例:

1
2
3
4
5
6
@RequestMapping("queryForTitle")
public Object queryForTitle(String content) {
QueryBuilder queryBuilder = QueryBuilders.matchQuery("content", "spring");
Iterable<Article> articles = articleEsRepository.search(queryBuilder);
return articles;
}

请求http://localhost:8080/article/queryForTitle?content=spring响应结果如下:

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
36
37
{
"content": [
{
"id": 1,
"title": null,
"content": "spring boot data es",
"summary": "spring boot es",
"pv": 100,
"author": "Alice"
}
],
"pageable": {
"sort": {
"sorted": false,
"unsorted": true
},
"offset": 0,
"pageSize": 1,
"pageNumber": 0,
"paged": true,
"unpaged": false
},
"facets": [],
"aggregations": null,
"scrollId": null,
"totalElements": 1,
"totalPages": 1,
"number": 0,
"size": 1,
"sort": {
"sorted": false,
"unsorted": true
},
"first": true,
"numberOfElements": 1,
"last": true
}

根据响应结果的 json 结构,前端可以按需取值。

Query APIs

由于 es查询不是本文重点,大家可以参考官网学习查询API:官方文档


es全套教程

链接 密码:vrw0

教程来源于互联网,若有侵权行为请联系 872852458@qq.com

鼓励一下~