SpringBoot2.x(十二)整合ElasticSearch
本文将介绍Linux7环境下如何安装ElasticSearch、ElasticSearch常见启动异常解决方法、SpringBoot2.x整合ElasticSearch。简介elasticsearch下文简称 es是一个解决大数据搜索(TB/PB级别)的框架。对比数据库,index、type、document的理解:mysqldatabasetablere...
本文将介绍Linux7环境下如何安装ElasticSearch、ElasticSearch常见启动异常解决方法、SpringBoot2.x整合ElasticSearch。
简介
elasticsearch
下文简称es
是一个解决大数据搜索(TB/PB级别)的框架。- 对比数据库,
index
、type
、document
的理解:
mysql | database | table | record |
---|---|---|---|
elasticsearch | index | type | document |
特点
全文检索,结构化检索,数据统计、分析,接近实时处理,分布式搜索(可部署数百台服务器),处理PB级别的数据
,搜索纠错,自动完成
使用场景
日志搜索,数据聚合,数据监控,报表统计分析
国内外使用者
维基百科,Stack Overflow,GitHub
新特性
elasticsearch
6.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
安装
安装配置
- 下载安装包(
es5.6
):官方地址 shell
命令:
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环境变量
# 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
# 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
部分:
# ---------------------------------- 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.host
为0.0.0.0
启动
非root角色启动
经过上面一些列的配置后就可以启动 es
了,但是不能以 root
用户启动,否则会报错(es
防止黑客入侵可获取 root
权限)如下:
[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
):
useradd -m zaw
并给 zaw
用户设置密码:
passwd zaw #回车后输入密码
给 zaw
用户赋予 es
目录所有权限
chmod 777 -R /export/server/elasticsearch-5.6.8
切换到 zaw
用户:
[root@pinyoyougou-docker config]# su zaw
[zaw@pinyoyougou-docker config]$
启动 es
(elasticsearch
是 es/bin
下的一个命令):
[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
max file descriptors [4096] for elasticsearch process is too low, increase to at least [65536]
原因
启动 es
的用户拥有的可创建文件描述的权限太低,至少需要65536。
解决办法
切换到root
用户
[zaw@pinyoyougou-docker config]$ su
密码: #root用户密码
修改/etc/security/limits.conf
,在文件尾添加如下内容:
zaw hard nofile 65536
其中
zaw
是你创建的用来启动es
的用户
切换到 zaw
用户启动 es
[root@pinyoyougou-docker ~]# su zaw
[zaw@pinyoyougou-docker root]$ elasticsearch
异常2
max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
解决办法
切换到 root
用户修改/etc/sysctl.conf
,在文件末尾添加:
vm.max_map_count=655360
执行 sysctl -p
使更改生效,再切换到 zaw
启动 es
异常3
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
中修改如下两处:
# Xms represents the initial size of total heap space
# Xmx represents the maximum size of total heap space
-Xms128M
-Xmx128M
异常4
max number of threads [1024] for user [work] likely too low, increase to at least [2048]
使用 root
修改 /etc/security/limits.d/
下以 -nproc.conf
结尾的文件:
* soft nproc 2048
root soft nproc unlimited
异常5
Exception in thread "main" java.nio.file.AccessDeniedException: /usr/local/software/temp/elasticsearch-6.2.2/config/jvm.options
当前用户权限不够,切换到 root
,赋予普通用户操作 es
目录所有权
chmod 777 -R /export/server/elasticsearch-5.6.8
测试
访问 192.168.25.135:9200
若响应结果如下则 es
部署成功
{
"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.6
http操作es快速入门:官方文档
使用PostMan测试es接口
关于 PostMan的使用可以参考 SpringBoot2.x(二)SpringBoot接口Http这篇文章。下面将根据官方文档提供接口测试
es
的使用
- 查看集群状态:
192.168.25.135:9200/_cat/health?v
(GET提交)
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
,下文将省略es
IP和端口(192.168.25.135:9200
)
- 查看各节点信息:
/_cat/nodes?v
(GET)
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)
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
当前还未创建 index
,因此只显示表头信息
- 创建 index:
/customer?pretty
(PUT)
{
"acknowledged": true,
"shards_acknowledged": true,
"index": "customer"
}
常见异常原因:
- 要使用PUT提交方式
- 如果你使用的是PostMan,清空当前标签页的
Headers
和Body
,本提交只需设置提交方式和提交链接。我就是因为之前在当前标签页设置了请求体,然后换链接成/customer?pretty
的时候没有清空它导致报错困扰了我好久
请求结果如下:
{
"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
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.3
,es
使用 5.6
环境准备
引入 starter-data-elasticsearch
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
添加SpringBoot整合es配置属性(application.properties
)
# 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
(相当于表名)
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
并注册到容器中。
ElasticsearchRepository
是 Spring Data ElasticSearch
为我们封装好的可进行 crud
、分页、排序的接口。
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> {
}
测试
插入记录
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>
时没有指定 T
或 ID
没有实现 Serializable
,在加载Spring容器时会报错,因为它无法根据你传入的泛型动态创建DAO实现类。
如果测试显示绿条,可以使用PostMan查看索引列表 /_cat/indices?v
:
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
:
{
"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
):
{
"_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
查询条件,这里使用它做一个关键字查询的实例:
@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
响应结果如下:
{
"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:官方文档
更多资源搜索请到白玉搜一搜
更多推荐
所有评论(0)