前言
很多人在Spring boot项目中都已经习惯采用Spring家族封装的spring-data-elasticsearch来操作elasticsearch,而官方更推荐采用rest-client。
今天给大家介绍下在spring boot中如何整合rest-client操作elasticsearch。
一、为什么不使用Spring家族封装的spring-data-elasticsearch?
主要是灵活性和更新速度。
spring将elasticsearch过度封装,让开发者很难跟ES的DSL查询语句进行关联。再者就是更新速度,ES的更新速度是非常快的,但是spring-data-elasticsearch更新速度比较缓慢。
另外,spring-data-elasticsearch底层依赖的是TransportClient。而TransportClient在ES7中已被弃用,取而代之的是 Java 高级 REST 客户端,并将在 Elasticsearch 8.0 中删除。
ElasticsearchRepository的优缺点:
优点: 简单,SpringBoot无缝对接,配置简单
缺点: 基于即将废弃的TransportClient, 不能支持复杂的业务
基于此,强烈建议采用官方推出的java客户端elasticsearch-rest-high-level-client,它的代码写法跟DSL语句很相似,懂ES查询的使用其上手很快。
二、低级REST客户端和高级REST客户端的关系
使用Elasticsearch服务,则要先获取一个Elasticsearch客户端。获取Elasticsearch客户端的方法很简单,最常见的就是创建一个可以连接到集群的传输客户端对象。
在Elasticsearch中,客户端有初级客户端和高级客户端两种,它们均使用Elasticsearch提供了RESTful风格的API,在使用RESTful API时,一般通过9200端口与Elasticsearch进行通信。
初级客户端是Elasticsearch为用户提供的官方版初级客户端。初级客户端允许通过HTTP与Elasticsearch集群进行通信,它将请求封装发给Elasticsearch集群,将Elasticsearch集群的响应封装返回给用户。初级客户端与所有Elasticsearch版本都兼容。
高级客户端是用于弹性搜索的高级客户端,它基于初级客户端。高级客户端公开了API特定的方法,并负责处理未编组的请求和响应。
官网对java-rest客户端的特性介绍:https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-low.html
java low-level client特性:
最小依赖
针对可用节点,负载均衡
针对错误和故障节点可以进行故障转移
针对某个节点连接失败次数越多,客户端就会等待更长的时间,才会再次尝试这个节点
持久连接
请求日志追踪
原子性操作
java High-level client特性:
在 Java 低级 REST 客户端之上运行
主要目标是公开 API 特定的方法
每个API都有同步和异步调用
Java High Level REST Client 依赖于 Elasticsearch 核心项目
简单来说:
low-level client 最小依赖,兼容性更好,使用更灵活。
High-level client基于low-level client ,它的主要目标是公开 API 特定的方法,封装性更好,使用更方便。
三、实战
示例主要演示low-level client 和High-level client的基本用法,所以只包含客户端的获取和简单的查询示例。
1、添加maven依赖
<properties> <java.version>1.8</java.version> <es.version>7.10.2</es.version> <monitor.version>1.2.7.5.RELEASE</monitor.version> <skipTests>true</skipTests> </properties> <!-- es的starter, 主要是为了实现自动化配置,方便快捷的获取rest client --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> <exclusions> <exclusion> <groupId>org.springframework.data</groupId> <artifactId>spring-data-elasticsearch</artifactId> </exclusion> </exclusions> </dependency> <!-- low-level client --> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-client</artifactId> <version>${es.version}</version> </dependency> <!-- high-level client ,默认依赖的elasticsearch存在版本差异,排除后添加统一的es版本--> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> <version>${es.version}</version> <exclusions> <exclusion> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> </exclusion> </exclusions> </dependency> <!-- 使用low-level client不需要,但是high-level client需要依赖--> <dependency> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> <version>${es.version}</version> </dependency>
2、配置ES连接属性
#es配置 spring.elasticsearch.rest.uris = http://localhost:9200 spring.elasticsearch.rest.username = elastic spring.elasticsearch.rest.password = 123456 spring.elasticsearch.rest.connection-timeout = 10s spring.elasticsearch.rest.read-timeout = 30s
3、RestClient、RestHighLevelClient使用
@SpringBootTest @Slf4j public class EsTest { @Autowired private RestClient restClient; @Autowired private RestHighLevelClient highLevelClient; /** * low-rest client测试类 * 说明:根据DSL语句发起请求 * 同步请求 performRequest * 异步请求 performRequestAsync */ @Test public void lowClientTest() { try{ //DSL查询语句 String queryString = "{\"query\":{\"match_all\":{}},\"sort\":{\"crawler_time\":{\"order\":\"desc\"}},\"from\":0,\"size\":2}"; HttpEntity entity = new NStringEntity(queryString, ContentType.APPLICATION_JSON); //指定请求方法和索引 Request request = new Request("GET", "/user_info_*/_search"); request.setEntity(entity); Map<String, String> params = Collections.emptyMap(); request.addParameters(params); //发起请求 Response response = restClient.performRequest(request); if(response!=null && HttpStatus.OK.value() == response.getStatusLine().getStatusCode()){ String responseBody = EntityUtils.toString(response.getEntity()); JSONObject jsonObject = JSON.parseObject(responseBody); JSONObject jsonObject1 = (JSONObject)((JSONObject)jsonObject.get("hits")).get("total"); if(jsonObject1.get("value")!=null){ Integer total = (Integer) jsonObject1.get("value"); System.out.println("总记录数:" + total); } //处理返回结果 JSONArray jsonArray = (JSONArray)((JSONObject)jsonObject.get("hits")).get("hits"); List<UserInfo> infoList = jsonArray.stream().map(e->{ JSONObject source = (JSONObject) ((JSONObject)e).get("_source"); return JSON.parseObject(JSON.toJSONString(source), UserInfo.class); }).collect(Collectors.toList()); infoList.forEach(e->{ System.out.println(e.toString()); }); } }catch (Exception e){ log.error("lowClientTest fail",e); } } /** * high-rest Client测试类 * 说明:通过SearchRequest、SearchSourceBuilder、QueryBuilders构建请求,API封装的更丰富 */ @Test public void highRestClientTest(){ String indice = "user_info_*"; SearchRequest searchRequest = new SearchRequest(indice); SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); sourceBuilder.query(QueryBuilders.matchAllQuery()); sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS)); log.info("ES查询DSL语句:\nGET {}\n{}", String.format("/%s/_search",searchRequest.indices()[0]),sourceBuilder); searchRequest.source(sourceBuilder); try { SearchResponse response = highLevelClient.search(searchRequest, RequestOptions.DEFAULT); Arrays.stream(response.getHits().getHits()) .forEach(i -> { //索引名称 System.out.println(i.getIndex()); System.out.println(i.getSourceAsString()); }); System.out.println("记录总数:" + response.getHits().getTotalHits().value); } catch (Exception e) { log.error("highRestClientTest fail",e); } } }
4、spring-boot-starter-data-elasticsearch说明
RestClientAutoConfiguration自动化配置类:
RestClientConfigurations核心源码
class RestClientConfigurations { /** * 声明RestClient的bean */ @Bean @ConditionalOnMissingBean RestClient elasticsearchRestClient(RestClientBuilder builder) { return builder.build(); } } @Configuration( proxyBeanMethods = false ) @ConditionalOnClass({RestHighLevelClient.class}) static class RestHighLevelClientConfiguration { RestHighLevelClientConfiguration() { } /** * 声明RestHighLevelClient */ @Bean @ConditionalOnMissingBean RestHighLevelClient elasticsearchRestHighLevelClient(RestClientBuilder restClientBuilder) { return new RestHighLevelClient(restClientBuilder); } /** * 声明RestClient的bean */ @Bean @ConditionalOnMissingBean RestClient elasticsearchRestClient(RestClientBuilder builder, ObjectProvider<RestHighLevelClient> restHighLevelClient) { RestHighLevelClient client = (RestHighLevelClient)restHighLevelClient.getIfUnique(); return client != null ? client.getLowLevelClient() : builder.build(); } }
说明:
引入spring-boot-starter-data-elasticsearch自动化配置类,主要是简化配置,更方便的获取RestClient和RestHighLevelClient。
总结
本文主要介绍类如下内容:
1、为什么弃用spring-data-elasticsearch?
2、es中低级RestClient和高级RestHighLevelClient的特性和区别
3、spring boot中如何通过RestClient和RestHighLevelClient访问elasticsearch