如何调用弹性搜索以获取当前队列负载?

时间:2015-07-28 07:30:50

标签: java elasticsearch queue

在广泛查询ES时,我得到了

Failed to execute [org.elasticsearch.action.search.SearchRequest@59e634e2] lastShard [true]
org.elasticsearch.common.util.concurrent.EsRejectedExecutionException: rejected execution (queue capacity 1000) on org.elasticsearch.search.
action.SearchServiceTransportAction$23@75bd024b
        at org.elasticsearch.common.util.concurrent.EsAbortPolicy.rejectedExecution(EsAbortPolicy.java:62)
        at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:823)
        at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1369)
        at org.elasticsearch.common.util.concurrent.EsThreadPoolExecutor.execute(EsThreadPoolExecutor.java:79)
        at org.elasticsearch.search.action.SearchServiceTransportAction.execute(SearchServiceTransportAction.java:551)
        at org.elasticsearch.search.action.SearchServiceTransportAction.sendExecuteQuery(SearchServiceTransportAction.java:228)
        at org.elasticsearch.action.search.type.TransportSearchQueryThenFetchAction$AsyncAction.sendExecuteFirstPhase(TransportSearchQueryThenFetchAction.java:83)

定期。

我的计划现在是pause查询请求,直到队列加载低于x。您可以在客户端查询其stats

client.admin().cluster().threadPool().stats().iterator();

但由于我的客户端不是数据节点(我认为这就是原因),我得到queue=0返回,而服务器节点抛出上述错误。

我知道为什么会被抛出,我知道如何更新设置,但这只是推迟了这个错误,并创建了其他人......

如何询问群集节点队列负载是什么?

PS:我正在使用Java Api

除非另有说明,否则我尝试过的,没有要求的结果,空行表示另一次尝试

//Nodes stats
final NodesStatsResponse nodesStatsResponse = client.admin().cluster().prepareNodesStats().execute().actionGet();
final NodeStats nodeStats = nodesStatsResponse.getNodes()[0];
final String nodeId = nodeStats.getNode().getId(); // need this later on

// same as before, but with explicit NodesStatsRequest (with id)
final NodesStatsResponse response = client.admin().cluster().nodesStats(new NodesStatsRequest(nodeId)).actionGet();
final NodeStats[] nodeStats2 = response.getNodes();
for (NodeStats nodeStats3 : nodeStats2) {
    Stats stats = nodeStats3.getThreadPool().iterator().next();
}

// Cluster?
final ClusterStatsRequest clusterStatsRequest = new ClusterStatsRequestBuilder(client.admin().cluster()).request();
final ClusterStatsResponse clusterStatsResponse = client.admin().cluster().clusterStats(clusterStatsRequest).actionGet();
final ClusterStatsNodes clusterStatsNodes = clusterStatsResponse.getNodesStats();

// Nodes info?
final NodesInfoResponse infoResponse = client.admin().cluster().nodesInfo(new NodesInfoRequest(nodeId)).actionGet();// here
final NodeInfo[] nodeInfos = infoResponse.getNodes();
for (final NodeInfo nodeInfo : nodeInfos) {
    final ThreadPoolInfo info = nodeInfo.getThreadPool();
    final Iterator<Info> infoIterator = info.iterator();
    while (infoIterator.hasNext()) {
        final Info realInfo = infoIterator.next();
        SizeValue sizeValue = realInfo.getQueueSize();
        // is no == null, then (¿happens?, was expecting a nullpointer, but Thread disappeared)
        if (sizeValue == null) 
            continue;
        // normal queue size, no load (oddly found 1000 (expected), and one of 200 in one node?)
        final long queueSize = sizeValue.getSingles(); 
    }
}

问题是某些进程需要立即调用(例如用户请求),而其他进程可能会等待数据库太忙(后台进程)。最好是,我将一定数量的队列分配给立即请求的进程,另一部分分配给后台进程(但我还没有看到此选项)。

更新 看起来,我没想到,当单独搜索的总量超过1000时(当x分片或x索引除以1000 / x时,单个批量查询可以获得查询重载)搜索次数)。所以膨胀,,,不是一个选项,除非你可以进行single查询。因此,当您一次定位700个搜索结果时(考虑到上述声明),您需要知道队列中是否有超过300个项目,因为它会丢弃东西。

总结一下:

假设每个呼叫的负载是最大bulkrequest,因此我无法组合请求。那么,如何在弹性搜索开始抛出上述异常之前开始暂停请求。所以我可以暂停我的应用程序的一部分,但不能停止另一部分?如果我知道队列已填满,比方说,中途,后台进程必须休眠一段时间。我如何知道(近似)队列加载?

2 个答案:

答案 0 :(得分:4)

您尝试查看队列使用情况的方式是错误的,因为您没有查看正确的统计信息。

看一下这段代码:

    final NodesStatsResponse response = client.admin().cluster().prepareNodesStats().setThreadPool(true).execute().actionGet();
    final NodeStats[] nodeStats2 = response.getNodes();

    for (NodeStats nodeStats3 : nodeStats2) {
        ThreadPoolStats stats = nodeStats3.getThreadPool();

        if (stats != null)
            for (ThreadPoolStats.Stats threadPoolStat : stats) {
                System.out.println("node `" + nodeStats3.getNode().getName() + "`" + " has pool `" + threadPoolStat.getName() + "` with current queue size " + threadPoolStat.getQueue());
            }
    }

首先,您需要setThreadPool(true)才能返回线程池统计信息,否则它将是null

其次,您需要ThreadPoolStats而不是ThreadPoolInfo用于线程池设置。

所以,这是你的第二次尝试,但不完整。您看到的1000是设置本身(最大队列大小),而不是实际负载。

答案 1 :(得分:0)

希望这是答案,来源https://www.elastic.co/guide/en/elasticsearch/guide/current/_monitoring_individual_nodes.html#_threadpool_section

  

批量拒绝

     

如果您要遇到队列拒绝,很可能会   由批量索引请求引起的。发送很多批量很容易   通过使用并发导入进程向Elasticsearch请求。更多   更好,对吧?

     

实际上,每个群集都有一定的限制,无法保留   摄入。超过此阈值后,队列将会   快速填满,新的批量将被拒绝。

     

这是一件好事。队列拒绝是一种有用的形式   压力。他们让您知道您的群集处于最大容量,   这比将数据粘贴到内存中队列要好得多。   增加队列大小不会提高性能;它只是隐藏   问题。如果您的群集每秒只能处理10,000个文档,   队列是100还是10,000,000 - 你的集群并不重要   仍然每秒只能处理10,000个文档。

     

队列只是隐藏了性能问题并带来了真正的风险   数据丢失。根据定义,坐在队列中的任何东西都不是   已处理。如果节点发生故障,则所有这些请求都将丢失   永远。此外,队列占用了大量内存,但事实并非如此   理想的。

     

优雅地处理应用程序中的排队要好得多   处理完整队列的背压。当你收到批量   拒绝,您应该采取以下步骤:

     

暂停导入线程3-5秒。提取被拒绝的操作   从批量响应,因为很可能是许多行动   成功了。批量响应将告诉您哪个成功和   哪些被拒绝了。仅拒绝发送新的批量请求   动作。如果再次遇到拒绝,请从步骤1开始重复。运用   在此过程中,您的代码自然会适应您的群集负载   并自然退缩。

     

拒绝不是错误:它们只是意味着您应该稍后再试。

特别是这个When you receive bulk rejections, you should take these steps我不喜欢。我们应该能够正手处理即将到来的问题。