【问题标题】:2.1.6 Spring Boot - Elasticsearch Healthcheck failure2.1.6 Spring Boot - Elasticsearch Healthcheck 失败
【发布时间】:2020-04-10 06:05:31
【问题描述】:

***更新 - 我发现了一篇有用的 StackOverflow 帖子,其中其他人遇到了类似的问题,即 Elasticsearch 的 Healthcheck 监视器失败 Springboot elastic search health management : ConnectException: Connection refused

Healthcheck 执行器似乎使用 Rest 客户端,然而,用于映射、获取索引等的 Elasticsearch 使用 RestHighLevelClient。我们有一个 @config 文件,其中包含 esPort、esHost 和 esSchema 的变量(即端口 9200、主机 localhost 和模式 http),代码如下,以及“ESClient.java”类的代码:

ESClientConfig.java 类

package com.cat.digital.globalsearch.configuration;

        import com.cat.digital.globalsearch.component.ESClient;
        import org.apache.http.HttpHost;
        import org.springframework.beans.factory.annotation.Value;
        import org.springframework.context.annotation.Bean;
        import org.springframework.context.annotation.Configuration;

@Configuration
public class ESClientConfig {

    @Value("${elasticSearch.host}")
    private String esHost;

    @Value("${elasticSearch.port}")
    private int esPort;

    @Value("${elasticSearch.scheme}")
    private String esScheme;

    @Bean
    public ESClient esClient() {
        return new ESClient( new HttpHost(esHost, esPort, esScheme));
    }
}

ESClient.java类

package com.cat.digital.globalsearch.component;

import com.cat.digital.globalsearch.model.IndexDocument;
import org.apache.http.HttpHost;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
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.GetIndexRequest;
import org.elasticsearch.client.indices.PutMappingRequest;

import java.io.IOException;
import java.util.List;

import static org.elasticsearch.client.RequestOptions.DEFAULT;
import static org.elasticsearch.common.xcontent.XContentType.JSON;

/**
 * Wrapper around {@link RestHighLevelClient}
 */
public class ESClient {

    private final RestClientBuilder builder;
    private final RestHighLevelClient searchClient;

    public ESClient(HttpHost... hosts) {
        this.builder = RestClient.builder(hosts);
        searchClient = new RestHighLevelClient(RestClient.builder(hosts));
    }

/**
     * @param index String represents index name in ES
     * @return true if index exists, false if not
     */
    public boolean hasIndex(String index) throws IOException {
        final GetIndexRequest request = new GetIndexRequest(index);
        try (RestHighLevelClient client = new RestHighLevelClient(builder)) {
            return client.indices().exists(request, DEFAULT);
        }
    }

所以现在我认为与 Elasticsearch 的“连接被拒绝”可能是因为在开发环境中它试图使用 Rest 客户端并且不存在正确的连接参数。但这将如何解释 Healthcheck 监视器在本地正常工作? RestHighLevelClient 是否在本地使用?

我在 spring boot GitHub 上发布了一个问题,并在此处被引用。我会尽量让这件事变得简单,这样我就能得到一些帮助。它实际上很简单。

TL;DR

  • 使用 Spring Boot 执行器为 Elasticsearch 服务创建自定义 Healthcheck 监控器

  • 创建了 1 个名为“IndexExists”的自定义 Java 类(代码如下)

  • 添加了 Application.yml 文件:rest.uri = ['our-dev-url-on-aws'] 属性

我有一个应用程序在本地和远程都可以正常工作,但是当使用 Spring Boot 添加自定义 Healthcheck 监视器来监视我的 Elasticsearch 服务时,我收到 Elasticsearch 的“连接被拒绝”,并且运行状况检查监视器最终失败。 AWS 上的负载均衡器尝试访问此端点,并且由于运行状况检查无法连接到 Elasticsearch,因此它返回状态:“DOWN”,负载均衡器开始创建新容器。查看 CloudWatch 日志时,这种情况会在无限循环中反复发生(负载均衡器尝试创建更多容器)。我想补充一下,这在本地工作得非常好 - 也就是说,当添加健康检查监视器并通过 POSTMAN 在执行器/健康端点上使用 HTTP GET 请求时,我得到了正确的 JSON 响应:

来自 /actuator/health 端点的本地 JSON 响应(GET 请求)

{
    "status": "UP",
    "details": {
        "indexExists": {
            "status": "UP",
            "details": {
                "index": "exists",
                "value": "assets"
            }
        },
        "diskSpace": {
            "status": "UP",
            "details": {
                "total": 250790436864,
                "free": 194987540480,
                "threshold": 10485760
            }
        },
        "elasticsearchRest": {
            "status": "UP",
            "details": {
                "cluster_name": "elasticsearch",
                "status": "yellow",
                "timed_out": false,
                "number_of_nodes": 1,
                "number_of_data_nodes": 1,
                "active_primary_shards": 7,
                "active_shards": 7,
                "relocating_shards": 0,
                "initializing_shards": 0,
                "unassigned_shards": 5,
                "delayed_unassigned_shards": 0,
                "number_of_pending_tasks": 0,
                "number_of_in_flight_fetch": 0,
                "task_max_waiting_in_queue_millis": 0,
                "active_shards_percent_as_number": 58.333333333333336
            }
        }
    }
}

如您所见,Healthcheck 监视器在顶部返回 "details": { "indexExists": etc... } 部分,用于检查我的 Elasticsearch 索引是否映射到字符串 = "assets"。如果是,则返回“status”:“UP”。

但是,当将此代码推送到 Azure 中的构建管道时,我可以在开发环境中进行测试,这是我得到的 JSON 响应:

来自 /actuator/health 端点的 DEV JSON 响应(GET 请求)

{
    "status": "DOWN",
    "details": {
        "indexExists": {
            "status": "UP",
            "details": {
                "index": "exists",
                "value": "assets"
            }
        },
        "diskSpace": {
            "status": "UP",
            "details": {
                "total": 16776032256,
                "free": 9712218112,
                "threshold": 10485760
            }
        },
        "elasticsearchRest": {
            "status": "DOWN",
            "details": {
               "error": "java.net.ConnectException: Connection refused"
            }
        }
    }
}

我可以在我们的 AWS (Amazon Web Services) 集群上查看错误日志,它们看起来像这样:

我创建并添加到我们的代码库中的 java 类是 IndexExists.java。它实现了 HealthIndicator() 接口并使用 Spring Boot 中的执行器:

IndexExists.java 类

package com.cat.digital.globalsearch.component;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
import com.cat.digital.globalsearch.data.Indices;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;


@Component
public class IndexExists implements HealthIndicator {
    private final ESClient esClient;
    private static final Logger LOGGER = LoggerFactory.getLogger(IndexExists.class);
    Map<String,String> map = new HashMap<>();


    @Autowired
    public IndexExists(ESClient esClient) {
        this.esClient = esClient;
    }

    @Override
    public Health health() {
        try {
            if (!esClient.hasIndex(Indices.INDEX_ASSETS)) {
                return Health.down().withDetail("index", Indices.INDEX_ASSETS + " index does not exist").build();
            }
        } catch (IOException e) {
            LOGGER.error("Error checking if Elasticsearch index {} exists , with exception", Indices.INDEX_ASSETS,  e);
        }
        map.put("index","exists");
        map.put("value", Indices.INDEX_ASSETS);
        return Health.up().withDetails(map).build();
    }
}

我不会发布 application.yml 的所有代码,但这里是我添加的部分。对于spring dev profile,我只添加了rest uri,其余代码已经存在:

management:
  endpoint:
    health:
      show-details: always

spring:
  profiles: dev
elasticSearch:
  host: "aws-dev-url"
  port: -1
  scheme: https
  rest:
  uris: ["aws-dev-url"]

我希望信息不要太多!我真的需要帮助...如果有人需要更多信息,请告诉我。谢谢。

【问题讨论】:

    标签: java spring amazon-web-services spring-boot elasticsearch


    【解决方案1】:

    查看您的配置,您的应用程序似乎正在使用 Spring Data Elasticsearch。这允许 Spring Data 存储库由 Elasticsearch 索引支持,您还可以获得ElasticsearchRestTemplate (see reference docs)。 这是应用程序将用于存储库的内容。

    另一方面,Spring Boot 提供的健康指标(在其他环境中失败的指标)使用org.elasticsearch.client.RestClient。 您的自定义运行状况指示器(在同一环境中正常工作)似乎使用了不同的东西,ESClient。也许这个客户端配置了不同的凭据/URI?

    您似乎没有在 application.yml 文件中使用正确的配置命名空间; spring.data.elasticsearch.host 不存在。见the reference documentation。您可以查看configuration properties in the docs 的完整列表,或者您可以使用直接支持属性自动完成的 IDE(其中许多都支持)。

    如果一切正常但仍然失败,我会尝试在其他环境中直接调用您的 elasticsearch 实例,例如使用 curl 命令,以确保该实例允许 Spring Boot 正在使用的运行状况检查请求。比如:

    curl http://<host-in-other-env>:<port>/_cluster/health/<your-index>
    

    编辑:

    在您对配置文件进行最新编辑后,您的应用程序似乎没有使用 Elasticsearch REST 自动配置。您现在可以使用以下内容编辑您的 application.yml 文件:

    spring:
      elasticsearch:
        rest:
          uris: ["aws-dev-url"]
    

    有了这个,我认为你的ESClient 可以直接注入RestHighLevelClient,因为 Spring Boot 已经创建了一个。

    TLDR:运行状况指示器在本地工作,因为它使用默认的“localhost:9200”地址,但在 dev 中不起作用,因为它仍然依赖相同的默认值。使用正确的配置属性和使用 Spring Boot 支持应该会让事情变得更容易。

    【讨论】:

    • 对不起,那个 data: 标题不应该在那里。我正在根据我正在阅读的 GitHub 帖子尝试不同的设置。我已经更新了它的真实外观。不过,我认为您正在做某事。我做了更多的研究,我看到其他人对 Elasticsearch 的 Healthcheck 失败有类似的问题,因为 ElasticSearch 使用“RestHighLevelClient”进行索引、映射等......但使用“Rest”客户端进行 Healthcheck 执行器。这个代码库中的某个人编写了一个包含主机、端口和模式变量的配置文件,我认为它们可能没有被......
    • 接上一句....我不认为开发环境使用了正确的 rest uri,因为如果它使用 Rest 客户端进行 Healthcheck执行器,但都是用于 RestHighLevelClient 的配置变量。我将尝试测试您列出的 CURL 命令并在我链接的堆栈溢出帖子中添加代码,但是我不太确定在哪里添加该代码(我是实习生,其他人设置了我所在的代码库这有点糟糕:()感谢您迄今为止的帮助!
    • 这是我引用的堆栈溢出帖子,我认为 75% 可能是问题:stackoverflow.com/questions/54363302/…
    • 其他问题表明此应用程序同时使用 REST 客户端和传输客户端。看起来您的应用程序没有使用传输客户端。同样,我不知道ESClient 是什么以及它是否持有不同的URI/凭据。此外,您的配置属性仍然错误(主机、端口、方案不存在 - 并且缩进错误)。
    • 我可以更新这个线程并在顶部发布“ESClient.java”代码。并且凭据看起来来自“ESClientConfig.java”类,该类被注释为具有不同变量值的@configuation(我在顶部发布了该代码)。而且我认为这些“主机”、“端口”和“方案”在我上面列出的 ESClientConfig 中使用。如果没有提供,那么在 Application.yml 中,Spring:默认配置文件与主机:localhost,端口:9200 和方案:http 一起使用,但显然在“dev”中应该不同
    猜你喜欢
    • 2021-08-11
    • 1970-01-01
    • 2018-07-11
    • 2021-06-29
    • 2015-03-28
    • 2020-09-11
    • 2017-07-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多