ElasticSearch客户端使用
SpringCloud集成
环境说明
JDK: 1.8
SpringBoot版本:2.1.5.RELEASE
ElasticSearch版本:6.3.0
ES客户端说明
ElasticSearch默认有两个端口,9200和9300,分别对应rest(http)和netty(tcp)两种连接方式,与之对应的客户端分别是transport client和rest client,根据官方的计划,transport客户端将会在7.x逐步废弃,在8.x不再使用。
在以前老的版本里面,很多项目都是使用transport的方式去集成ES的,也在此基础上衍生出了一些第三方的包,比较常用的就是elasticsearch-sql,这个包提供将sql转换为dsl进行查询的能力,这为老项目集成ES和不熟悉ES语法的同学提供了很大的便利,也因此很多项目会使用transport的方式,然而transport已经逐渐被官方抛弃了,早晚是需要使用rest的方式的,而且ES在6.3之后已经考虑到这种开发的成本,可以支持直接查询SQL(作者的项目因为是两三年前集成的,当时也就到6.3.0,很遗憾没有搭上这趟车,也使用了elasticsearch-sql来转换sql)。
除此之外,如果ES需要做权限控制的话,transport还不支持,必须要使用x-pack-transport,但好巧不巧x-pack-transport在maven中央仓库中只有一部分的版本可以集成,官方的maven库又连不上,实在有点尴尬。 因此,建议尽量使用官方的客户端进行集成,本文也是基于HighLevelClient进行说明。
POM依赖
POM依赖不复杂,但是ES的版本号与客户端的版本号有强关联关系,最好一一对应,尽管高版本的客户端提供了更多的API,但是否能完全兼容,官方也没说,也没找到有什么文章有提及到。
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>${elasticsearch.version}</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>${elasticsearch.version}</version>
</dependency>
HighLevelClient与LowLevelClient
官方参考文档
官方文档里面有使用说明,不过只是比较简单的介绍,建议先了解一下
Java High Level REST Client
Java Low Level REST Client
差异对比
HighLevelClient和LowLevelClient都是官方推荐客户端,前者是在后者的基础上做了一层封装,提供了一些便于使用的API,本质并无差异,但高度的封装也使得HighLevelClient缺少了一些灵活性,下面会有具体例子说明。
客户端初始化
客户端通过注入Bean方式进行初始化,HighLevelClient内置连接池,不需要另外设置连接池,使用的时候直接通过@Autowired注入即可,不需要另外初始化。
@Bean
public RestHighLevelClient restHighLevelHttpClient() {
//新建host
HttpHost host = new HttpHost("ip", 9200, "http");
//如ES设置了帐号密码,则需要提供登录令牌
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username,password));
//创建client实例,host可以有多个,对应集群的情况
RestClientBuilder builder = RestClient.builder(host);
//设置帐号密码、同时使用线程数、同一域名同时使用线程数
builder.setHttpClientConfigCallback(config -> config.setDefaultCredentialsProvider(credentialsProvider).setMaxConnTotal(hosts.size()*500).setMaxConnPerRoute(500));
//最大重试
builder.setMaxRetryTimeoutMillis(timeout*5);
//设置获取连接超时、请求超时、读超时
builder.setRequestConfigCallback(config -> config.setConnectTimeout(timeout/10).setConnectionRequestTimeout(timeout).setSocketTimeout(timeout));
RestHighLevelClient client = new RestHighLevelClient(builder);
return client;
}
基本增删改查
这里提供了Search、Update、Delete、Index、Bulk几种常规操作的具体使用。
Search
根据条件查询
public static List<Map<String, Object>> search() {
List<Map<String, Object>> result = new ArrayList<>();
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//设置查询条件,queryBuilder就是构造DSL,有很多教程细说DSL的语法
QueryBuilder queryBuilder = new BoolQueryBuilder();
searchSourceBuilder.query(queryBuilder);
//分页大小
searchSourceBuilder.size(0);
//开始页
searchSourceBuilder.from(0);
//查询指定的字段
String[] includeFields = new String[]{};
//排除指定字段
String[] excludesFields = new String[]{};
//设置需要查询哪些字段,排除哪些字段,不设置即全部字段返回
searchSourceBuilder.fetchSource(includeFields,excludesFields);
//构造request请求
SearchRequest searchRequest = new SearchRequest();
//指定查询索引,可以同时查询多个索引
String[] indices = new String[]{"index1","index2"};
searchRequest.indices(indices);
//查询模式
searchRequest.searchType(SearchType.DFS_QUERY_THEN_FETCH);
//设置前面设定好的查询设置
searchRequest.source(searchSourceBuilder);
try {
SearchResponse response = restHighLevelClient.search(searchRequest);
if (response==null || response.getHits()==null) {
return result;
}
//组装查询数据
for (SearchHit hit : response.getHits()) {
result.add(hit.getSourceAsMap());
}
} catch (Exception e) {
logger.error(e.getMessage(),e);
}
return result;
}
Get
获取单条数据
public static Map<String, Object> get(String id) {
//创建request请求
GetRequest getRequest = new GetRequest("index1");
//设置查询的文档主键
getRequest.id(id);
//需要返回的字段
String[] includeFields = new String[]{};
//需要排除的字段
String[] excludeFields = new String[]{};
if (ArrayUtils.isNotEmpty(includeFields) || ArrayUtils.isNotEmpty(excludeFields)) {
//设置排除字段、包含字段
FetchSourceContext fetchSource = new FetchSourceContext(true, includeFields, excludeFields);
getRequest.fetchSourceContext(fetchSource);
}
try {
GetResponse response = restHighLevelClient.get(getRequest);
if (response!=null) {
return response.getSourceAsMap();
}
} catch (IOException e) {
logger.error(e.getMessage(),e);
}
return new HashMap<>();
}
Delete
删除某个数据
public static void delete(String id) {
DeleteRequest request = new DeleteRequest();
request.index("index1");
request.id(id);
try {
restHighLevelClient.delete(request);
} catch (Exception e) {
logger.error(e.getMessage(),e);
}
}
Update
更新某条数据
public static void update(String id) {
//创建update请求
UpdateRequest request = new UpdateRequest();
//设置索引
request.index("index1");
//设置主键
request.id(id);
//这是需要更新的数据
Map<String,String> data = new HashMap<>();
request.doc(data);
//设置冲突重试次数,并发的时候有可能存在版本冲突导致update失败
request.retryOnConflict(5);
//设置刷新策略,三种策略:IMMEDIATE(提交后立即刷新,实时,延时低,消耗高)、WAIT_UNTIL(提交后等待数据完成再刷新,实时,延时高,消耗低)、NONE(默认策略,不管是否刷新,不实时,延时低,消耗低)
request.setRefreshPolicy(RefreshPolicy.IMMEDIATE);
try {
restHighLevelClient.update(request);
} catch (IOException e) {
logger.error(e.getMessage(),e);
}
}
Index
插入/更新记录
public static void index() {
//创建index请求
IndexRequest request = new IndexRequest();
//设置索引
request.index("index1");
//设置主键ID,插入记录的话生成id,更新的话设置已有数据的ID
request.id(UUID.randomUUID().toString());
//需要插入/更新的数据
Map<String,String> data = new HashMap<>();
request.source(data);
//刷新策略
request.setRefreshPolicy(RefreshPolicy.IMMEDIATE);
try {
restHighLevelClient.index(request);
} catch (IOException e) {
logger.error(e.getMessage(),e);
}
}
Bulk
批量执行请求
public static void bulk() {
//创建bulk request
BulkRequest bulkRequest = new BulkRequest();
//添加一个update请求
bulkRequest.add(new UpdateRequest().index("index1").id("123").doc(new HashMap()));
//添加一个delete请求
bulkRequest.add(new DeleteRequest().index("index1").id("123"));
//添加一个index请求
bulkRequest.add(new IndexRequest().index("index1").id("123").source(new HashMap()));
try {
//批量执行
restHighLevelClient.bulk(bulkRequest);
} catch (Exception e) {
logger.error(e.getMessage(),e);
}
}
关于异步方法的一些说明
HighLevelClient提供了一些异步的方法,例如searchAsync、deleteAsync、updateAsync等,这些异步方法是不会返回结果的,而是通过监听器来监听结果。
public static void updateAsync() {
UpdateRequest updateRequest = new UpdateRequest();
ActionListener<UpdateResponse> listener = new ActionListener<UpdateResponse>() {
@Override
public void onResponse(UpdateResponse updateResponse) {
//执行成功时进入这里处理response
logger.info("update successfully");
}
@Override
public void onFailure(Exception e) {
//执行失败时进入这里
logger.error("update fail");
}
};
restHighLevelClient.updateAsync(updateRequest,listener);
}
上面是异步的一个调用示例,但是这里会发现一个使用上不太方便问题,一是没有提供等待的方法,不知道什么时候返回结果,如果在后面的逻辑中需要依赖异步中的结果才能进行,官方没有提供解决的思路,二是onResponse里面没法将response传递到外层,也就是说后面想要获取到response的内容,需要自行做一些处理去进行传递,不知道后面的版本里面是否有提供一些API去解决这些问题。