使用RestHighLevelClient请求Es抛错listener timeout after waiting for [30000] ms

前言:本文的解决方案其实不是根本解决方式,因为默认时长30s,操作es都没有返回,这个需要考虑到在使用es的时候,怎么样才能更快返回,去提高es的读写性能,建议阅读有关提高es读写性能的博客,会很有用,但是如果只是想暂时不抛这个错,可以看一下这个解决方式

使用RestClient请求Es抛错listener timeout after waiting for [30000] ms
异常如下:

java.io.IOException: listener timeout after waiting for [30000] ms
	at org.elasticsearch.client.RestClient$SyncResponseListener.get(RestClient.java:899)
	at org.elasticsearch.client.RestClient.performRequest(RestClient.java:227)
	at org.elasticsearch.client.RestHighLevelClient.performRequest(RestHighLevelClient.java:1256)
	at org.elasticsearch.client.RestHighLevelClient.performRequestAndParseEntity(RestHighLevelClient.java:1231)
	at org.elasticsearch.client.RestHighLevelClient.bulk(RestHighLevelClient.java:329)
	at com.changjia.commodity.task.util.EsUtil.saveAll(EsUtil.java:146)


或者抛错:java.net.SocketTimeoutException: 30,000 milliseconds timeout on connection http-outgoing-3 [ACTIVE]

aused by: java.net.SocketTimeoutException: 30,000 milliseconds timeout on connection http-outgoing-3 [ACTIVE]
	at org.elasticsearch.client.RestClient$SyncResponseListener.get(RestClient.java:928)
	at org.elasticsearch.client.RestClient.performRequest(RestClient.java:227)
	at org.elasticsearch.client.RestHighLevelClient.performRequest(RestHighLevelClient.java:1256)
	at org.elasticsearch.client.RestHighLevelClient.performRequestAndParseEntity(RestHighLevelClient.java:1231)
	at org.elasticsearch.client.RestHighLevelClient.search(RestHighLevelClient.java:730)
	at com.changjia.commodity.task.es.impl.EsSearchServiceImpl.search(EsSearchServiceImpl.java:32)
	... 85 common frames omitted
Caused by: java.net.SocketTimeoutException: 30,000 milliseconds timeout on connection http-outgoing-3 [ACTIVE]
	at org.apache.http.nio.protocol.HttpAsyncRequestExecutor.timeout(HttpAsyncRequestExecutor.java:387)
	at org.apache.http.impl.nio.client.InternalIODispatch.onTimeout(InternalIODispatch.java:92)
	at org.apache.http.impl.nio.client.InternalIODispatch.onTimeout(InternalIODispatch.java:39)
	at org.apache.http.impl.nio.reactor.AbstractIODispatch.timeout(AbstractIODispatch.java:175)
	at org.apache.http.impl.nio.reactor.BaseIOReactor.sessionTimedOut(BaseIOReactor.java:261)
	at org.apache.http.impl.nio.reactor.AbstractIOReactor.timeoutCheck(AbstractIOReactor.java:502)
	at org.apache.http.impl.nio.reactor.BaseIOReactor.validate(BaseIOReactor.java:211)
	at org.apache.http.impl.nio.reactor.AbstractIOReactor.execute(AbstractIOReactor.java:280)
	at org.apache.http.impl.nio.reactor.BaseIOReactor.execute(BaseIOReactor.java:104)
	at org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor$Worker.run(AbstractMultiworkerIOReactor.java:591)
	... 1 common frames omitted


解决方案:实现RestClientBuilderCustomizer接口,设置过期时间更长:

@Service
public class EsClientConfiguration implements RestClientBuilderCustomizer {

    @Override  
    public void customize(RestClientBuilder builder) {
        //第一个异常解决方案
        builder.setMaxRetryTimeoutMillis(300 * 1000);
        //第二个异常解决方案
          RestClientBuilder.RequestConfigCallback configCallback = new RestClientBuilder.RequestConfigCallback() {
            @Override
            public org.apache.http.client.config.RequestConfig.Builder customizeRequestConfig(org.apache.http.client.config.RequestConfig.Builder requestConfigBuilder) {
                return requestConfigBuilder
                        .setConnectTimeout(30000)
                        .setSocketTimeout(300 * 1000);//更改客户端的超时限制默认30秒现在改为5分钟
            }
        };
        builder.setRequestConfigCallback(configCallback);
    }
}

这样就可以了。

下面是第一个异常找到解决方案的过程:
看到异常后打开
RestClient.java 看到899行
看来看去被绕晕了,于是用xmind记录了一下看的过程,完成以后觉得这是一次不错的经历,并且笔记完整,于是记录一下:
在这里插入图片描述
这是899行抛错的经过,因为CountDownLatch等待了 timeout毫秒还没等到,于是就抛出了这个异常,
于是就找这个timeout怎么来的,是RestClient的静态内部类SyncResponseListener的这个构造方法设置了这个过期时间,gou
然后这个构造函数是被下面这个方法调用了,参数maxRetryTimeoutMillis是RestClient的一个私有属性

在这里插入图片描述
在这里插入图片描述
然后它的值是在执行RestClient的构造函数的时候传进来的。
在这里插入图片描述
RestClient的构造函数是RestClientBuilder的build()执行的,过期时间maxRetryTimeout是RestClientBuilder的私有属性,并且默认值是在这里插入图片描述在这里插入图片描述
在这里插入图片描述
这个时间可以被RestClientBuilder的这个方法设置的。在这里插入图片描述
那RestClientBuilder和我们使用的RestHighLevelClient有什么关系尼,从上面的异常可以看出
RestHighLevelClient调用RestClient的performRequest()方法出现了这次异常。
在这里插入图片描述
从抛错的地方看出RestClient是RestHighLevelClient的一个私有属性,并且被设置的最终原因是:下面第二张图中的第一个方法。
在这里插入图片描述
在这里插入图片描述
所以问题变成了,这个RestHighLevelClient的构造函数是在什么时候被调用的,RestClientBuilder是什么时候生成的,我怎么才能调用RestClientBuilder的设置时间的这个方法,对RestClientBuilder这个实例操作,设置这个时间。
于是从自己的代码出发,我使用的 RestHighLevelClient是被@Resource注入的,于是我找到了下面这个java配置
在这里插入图片描述
这个RestHighLevelClient实例被传入了RestClientBuilder。
RestClientBuilder实例是怎么来的尼
在这里插入图片描述
customizer.customize(builder)方法,点进去,发现这是一个接口,我们看一下这个接口的名字,好像感觉到了点什么,我们再看一下这个接口的注释。破案了。
在这里插入图片描述

好的,下面是一个操作 Elasticsearch 的工具类: ```java import org.apache.http.HttpHost; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.DocWriteResponse; import org.elasticsearch.action.delete.DeleteRequest; import org.elasticsearch.action.delete.DeleteResponse; import org.elasticsearch.action.get.GetRequest; import org.elasticsearch.action.get.GetResponse; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClientBuilder; import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.client.indices.CreateIndexRequest; import org.elasticsearch.client.indices.CreateIndexResponse; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.builder.SearchSourceBuilder; import java.io.IOException; import java.util.Map; public class EsUtil { private static RestHighLevelClient client; static { RestClientBuilder builder = RestClient.builder( new HttpHost("localhost", 9200, "http") ); client = new RestHighLevelClient(builder); } /** * 创建索引 * * @param indexName 索引名称 * @param settings 索引配置 * @param mappings 索引映射 * @return 是否创建成功 */ public static boolean createIndex(String indexName, Settings settings, String mappings) { try { CreateIndexRequest request = new CreateIndexRequest(indexName); request.settings(settings); request.mapping(mappings, XContentType.JSON); CreateIndexResponse response = client.indices().create(request); return response.isAcknowledged(); } catch (IOException e) { e.printStackTrace(); return false; } } /** * 判断索引是否存在 * * @param indexName 索引名称 * @return 是否存在 */ public static boolean existsIndex(String indexName) { try { return client.indices().exists(new GetIndexRequest(indexName), RequestOptions.DEFAULT); } catch (IOException e) { e.printStackTrace(); return false; } } /** * 删除索引 * * @param indexName 索引名称 * @return 是否删除成功 */ public static boolean deleteIndex(String indexName) { try { AcknowledgedResponse response = client.indices().delete(new DeleteIndexRequest(indexName), RequestOptions.DEFAULT); return response.isAcknowledged(); } catch (IOException e) { e.printStackTrace(); return false; } } /** * 新增文档 * * @param indexName 索引名称 * @param id 文档id * @param source 文档内容 * @return 是否新增成功 */ public static boolean indexDocument(String indexName, String id, Map<String, Object> source) { try { IndexRequest request = new IndexRequest(indexName); request.id(id); request.source(source); IndexResponse response = client.index(request, RequestOptions.DEFAULT); return response.getResult() == DocWriteResponse.Result.CREATED || response.getResult() == DocWriteResponse.Result.UPDATED; } catch (IOException e) { e.printStackTrace(); return false; } } /** * 删除文档 * * @param indexName 索引名称 * @param id 文档id * @return 是否删除成功 */ public static boolean deleteDocument(String indexName, String id) { try { DeleteRequest request = new DeleteRequest(indexName, id); DeleteResponse response = client.delete(request, RequestOptions.DEFAULT); return response.getResult() == DocWriteResponse.Result.DELETED; } catch (IOException e) { e.printStackTrace(); return false; } } /** * 获取文档 * * @param indexName 索引名称 * @param id 文档id * @return 文档内容 */ public static Map<String, Object> getDocument(String indexName, String id) { try { GetRequest request = new GetRequest(indexName, id); GetResponse response = client.get(request, RequestOptions.DEFAULT); if (response.isExists()) { return response.getSourceAsMap(); } else { return null; } } catch (IOException e) { e.printStackTrace(); return null; } } /** * 搜索文档 * * @param indexName 索引名称 * @param query 查询条件 * @param size 返回数量 * @return 搜索结果 */ public static SearchHit[] search(String indexName, String query, int size) { try { SearchRequest request = new SearchRequest(indexName); SearchSourceBuilder builder = new SearchSourceBuilder(); builder.query(QueryBuilders.queryStringQuery(query)); builder.size(size); request.source(builder); SearchResponse response = client.search(request, RequestOptions.DEFAULT); if (response.status() == RestStatus.OK) { return response.getHits().getHits(); } else { return new SearchHit[0]; } } catch (IOException e) { e.printStackTrace(); return new SearchHit[0]; } } /** * 异步搜索文档 * * @param indexName 索引名称 * @param query 查询条件 * @param size 返回数量 * @param listener 异步回调 */ public static void searchAsync(String indexName, String query, int size, ActionListener<SearchResponse> listener) { SearchRequest request = new SearchRequest(indexName); SearchSourceBuilder builder = new SearchSourceBuilder(); builder.query(QueryBuilders.queryStringQuery(query)); builder.size(size); request.source(builder); client.searchAsync(request, RequestOptions.DEFAULT, listener); } /** * 关闭客户端连接 */ public static void close() { try { client.close(); } catch (IOException e) { e.printStackTrace(); } } } ``` 这个工具类提供了创建索引、删除索引、新增文档、删除文档、获取文档、搜索文档等基本操作。其中,`RestHighLevelClient` 用于操作 Elasticsearch,`RestClientBuilder` 用于创建 `RestHighLevelClient` 实例,`HttpHost` 表示 Elasticsearch 的主机和端口,可以根据实际情况修改。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值