Spring boot、ElasticSearch 和 TestContainers 集成测试。拒绝连接

Posted

技术标签:

【中文标题】Spring boot、ElasticSearch 和 TestContainers 集成测试。拒绝连接【英文标题】:Spring boot, ElasticSearch and TestContainers integration tests. Connection refused 【发布时间】:2019-05-30 09:11:08 【问题描述】:

我为从 elasticsearch 检索数据创建了一个集成测试。

我正在使用 testContainer 的默认值,因此我的 RestHighLevelClient 应该可以访问测试容器,但是当我尝试索引数据时,我总是得到相同的异常 (java.net.ConnecteException: Connection refused),但是当我在本地运行时通过命令获取 docker 镜像

docker run -d --rm -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e "transport.host=127.0.0.1"  --name elastic docker.elastic.co/elasticsearch/elasticsearch:6.5.4

我的测试工作正常。

问题出在哪里,因为端口映射是一样的? 这个异常的原因是什么?

我的测试:

@ExtendWith(SpringExtension.class)
@Testcontainers
@WebMvcTest
class FlowerResourceTest 

    @Container
    private ElasticsearchContainer esContainer = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:6.5.4");

    @Autowired
    private ElasticsearchConfiguration esConfig;

    @Autowired
    private FlowerService flowerService;

    private RestHighLevelClient client;


    @Test
    void test() throws IOException, InterruptedException 
        client = esConfig.client();

        var jsonFlower = "\n" +
                "    \"name\": \"XXX\",\n" +
                "  \"color\" : \"red\"\n" +
                "";
        IndexRequest indexRequest = new IndexRequest("flowers", "doc", "1")
                .source(jsonFlower, XContentType.JSON);


        assertTrue(esContainer.isRunning());
        client.index(indexRequest, RequestOptions.DEFAULT);


        var flowers = flowerService.findAll();


        assertTrue(flowers.size() > 0);

        DeleteRequest deleteRequest = new DeleteRequest("flowers", "doc", "1");

        client.delete(deleteRequest, RequestOptions.DEFAULT);
    

【问题讨论】:

【参考方案1】:

如果我没记错的话,你可以使用以下命令请求暴露的端口:

esContainer.getMappedPort(ELASTICSEARCH_PORT);

Docker 容器会公开一个随机可用端口,因此请按照上述命令检查该端口。为客户端使用该端口。如果您有兴趣,我的一位同事写了一篇关于此的博客文章,其中包含一些示例代码: https://amsterdam.luminis.eu/2018/08/20/elasticsearch-instances-for-integration-testing/

【讨论】:

是否可以将此端口设置为常数? 他们认为使用固定端口是一种不好的做法。如果你想要一个固定的端口,你可以使用特定的容器 FixedHostPortGenericContainer。如果您希望使用弹性容器来实现这一点,您可以子类化弹性容器并添加此行为。【参考方案2】:
    除非您配置不同,否则默认端口为 9200。因此,如果您的客户端尝试连接到默认端口,则需要确保 9200 可用,以便可以在该端口上启动 Testcontainers 中的 Elasticsearch。

    我猜 Elasticsearch 没有足够的时间启动。这是一个 Java 进程,可能需要一些时间——在我的机器上大约 20 多岁。您可能需要设置等待策略;像这样:

    esContainer.setWaitStrategy(
            Wait.forHttp("/")
                    .forPort(9200)
                    .forStatusCode(200)
                    .withStartupTimeout(Duration.ofSeconds(60)));
    

【讨论】:

如果您查看 ElasticseachContainer 的构造函数实现,您可以看到 StartupTime 设置为 120 秒。【参考方案3】:

试试这个方法

    @Container
    private static ElasticsearchContainer elasticsearchContainer = new ElasticTestContainer();
    private RestHighLevelClient client;

    @BeforeClass
    public static void init()
       elasticsearchContainer.start();
    

    @Test
    void test() throws IOException, InterruptedException 
        RestClientBuilder restClientBuilder = RestClient.builder(new HttpHost(elasticsearchContainer.getHost(), 9200, "http"));
        client = new RestHighLevelClient(restClientBuilder);
        

        var jsonFlower = "\n" +
                "    \"name\": \"XXX\",\n" +
                "  \"color\" : \"red\"\n" +
                "";
        IndexRequest indexRequest = new IndexRequest("flowers", "doc", "1")
                .source(jsonFlower, XContentType.JSON);


        assertTrue(elasticsearchContainer.isRunning());
        client.index(indexRequest, RequestOptions.DEFAULT);


        var flowers = flowerService.findAll();


        assertTrue(flowers.size() > 0);

        DeleteRequest deleteRequest = new DeleteRequest("flowers", "doc", "1");

        client.delete(deleteRequest, RequestOptions.DEFAULT);
    

ElasticTestContainer 类

public class ElasticTestContainer extends ElasticsearchContainer 

    private static final String DOCKER_ELASTIC = "docker.elastic.co/elasticsearch/elasticsearch:6.5.4";

    private static final String CLUSTER_NAME = "sample-cluster";

    private static final String ELASTIC_SEARCH = "elasticsearch";

    public ElasticTestContainer() 
        super(DOCKER_ELASTIC);
        this.addFixedExposedPort(9200, 9200);
        this.addFixedExposedPort(9300, 9300);
        this.addEnv(CLUSTER_NAME, ELASTIC_SEARCH);
    

【讨论】:

以上是关于Spring boot、ElasticSearch 和 TestContainers 集成测试。拒绝连接的主要内容,如果未能解决你的问题,请参考以下文章

Spring BootSpring Boot之使用 Spring Data Elasticsearch 整合elasticsearch

ElasticSearch 副本-04Spring Boot 集成 ElasticSearch

spring boot 整合Elasticsearch

Spring Boot 与ElasticSearch

es 实战 —— spring boot 中使用 Elasticsearch

Spring-Boot整合Elasticsearch