Java 测试容器 - 无法连接到暴露的端口

Java Testcontainers - Cannot connect to exposed port

我使用 javax.mail 实现了一个 POP3 服务器和客户端,只是为了尝试使用 Docker 进行集成测试。所以我基于 openjdk:8-jre 图像创建了两个 Docker 图像并将我的罐子复制到它们并启动它。根据我的配置(见下文),它正在工作。他们正在互相交谈。

但是由于想要进行多个集成测试,因此为每个测试构建一个映像并启动它们将会很乏味。我也不知道如何自动化结果。 但后来我偶然发现了 TestContainers,这似乎对实施这些测试有很大帮助。

所以我开始将这些测试移植到 TestContainers,使用我的 POP3 服务器映像作为 GenericContainer,并在 JUnit 测试方法中启动我的 POP3 客户端 类。我公开了我的 POP3 服务器正在侦听的端口 24999。但是当我尝试连接到服务器时,出现以下错误:

com.sun.mail.util.MailConnectException: Couldn't connect to host, port: localhost, 32782; timeout -1;
  nested exception is:
    java.net.ConnectException: Connection refused
...

我可能在 TestContainers 中缺少某些设置。你能帮帮我吗

这是我使用的代码:

public class DockerPop3AutocryptKeyProvidingAndReceivingTest {
    @Test
    public void test() throws InterruptedException {
        GenericContainer container = new GenericContainer<>("immerfroehlich/emailfilter:latest")
                .withExposedPorts(24999);
        
        container.start();
        
        String host = container.getContainerIpAddress();
        String port = container.getFirstMappedPort().toString();

        //The following is simplified, but copied from the working jar used in the Docker Client image/container
        MyPOP3Client client = new MyPOP3Client(host, port);
        client.connect();
        
        container.stop();
    }
}

这就是我创建 Docker 图片的方式:

FROM openjdk:8-jre

ADD build/distributions/MyPOP3Server.tar . #This is where I have packed all the needed files to. It gets unpacked by Docker.
#EXPOSE 24999 #I tried both with and without this expose
WORKDIR /MyPOP3Server/bin
ENTRYPOINT ["sh","MyPOP3Server"] #Executes the shell script which runs java with my jar

这是服务器 Jar 中 运行 代码的简化版本:

MyPOP3Server server = new MyPOP3Server();
server.listenToPort(24999);

请告诉我我错过了什么。这里有什么问题?

谢谢和亲切的问候。

由于您的邮件服务器和客户端在容器中运行,我认为您应该连接到端口 24999 而不是映射端口

尝试 container.getMappedPort(24999) 而不是 getFirstMappedPort。可能您的 docker 图像暴露了几个端口。

尝试添加一个 http 检查。

 new GenericContainer<>("immerfroehlich/emailfilter:latest")
 .withExposedPorts(24999)
 .waitingFor(new HttpWaitStrategy().forPort(24999)
 .withStartupTimeout(Duration.ofMinutes(5)));

有可能您的容器已启动,但您在服务器初始化之前尝试连接。

此外,注册一个日志附加程序以查看容器内的服务器发生了什么。

 .withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger(
              DockerPop3AutocryptKeyProvidingAndReceivingTest.class)))

其他答案中有一些很好的建议;我将用其他一些技巧来补充这些:

如前所述:

  • 绝对要添加 LogConsumer 以便您可以看到容器的日志输出 - 也许有用的东西会出现在那里,现在或将来。拥有总是好的。

  • 在容器启动后设置一个断点,就在您启动客户端之前。

此外,我希望以下内容有所作为。在断点处暂停时:

  • 运行 docker ps -a 在终端
  • 首先,检查您的容器是否 运行ning 并且没有退出。如果它已经退出,请从终端查看容器的日志。
  • 其次,检查 docker ps 输出中的端口映射。您应该会看到类似 0.0.0.0:32768->24999/tcp 的内容(不过第一个端口号是随机的)。
  • 在您的 IDE 中评估 container.getFirstMappedPort() 并检查您返回的端口号是否与随机公开的端口号相同。除非您在本地计算机上安装了非常不寻常的 Docker,否则应该可以通过 localhost: + 此端口访问此容器。
  • 如果你已经走到这一步,那么很可能是容器或客户端代码出了问题。您可以尝试将不同的客户端连接到 运行ning 容器 - 如果您手边没有另一个 POP3 客户端,甚至 nc 之类的东西也会有所帮助。

另一件事是手动 运行 容器,只是为了减少发生的间接访问量。您提供的 Testcontainers 代码片段相当于:

docker run -p 24999 immerfroehlich/emailfilter:latest

您可能会发现这可以帮助您将问题 space 分成更小的部分。

感谢您的帮助。这使我找到了解决方案。它是缺少 WaitStrategy 和端口映射问题的结合。

这是我所做的: 1) 在 MyPop3Server.listenToPort(String port) 方法中我添加了一个 System.out.println:

public class MyPop3Server {
  public void listenToPort(String port) {
     //simplified: do initialization and listenToPort
     System.out.println("Awaiting Connection...");
  }
}

在我的测试中,我添加了一个监听 "Awaiting Connection"

的 LogMessageWaitStrategy
GenericContainer container = new GenericContainer<>("immerfroehlich/emailfilter:latest")
   .waitingFor(Wait.forLogMessage("Awaiting Connection.*", 1))
   .withExposedPorts(24999);

2) 我从 container.getFirstMappedPort() 切换到

container.getMappedPort(24999);

这里是整个更改后的工作测试代码:

public class DockerPop3AutocryptKeyProvidingAndReceivingTest {
    @Test
    public void test() throws InterruptedException {
        GenericContainer container = new GenericContainer<>("immerfroehlich/emailfilter:latest")
                .waitingFor(Wait.forLogMessage("Awaiting Connection.*", 1))
                .withExposedPorts(24999);

        container.start();

        String host = container.getContainerIpAddress();
        String port = container.getMappedPort(24999).toString();

        //The following is simplified, but copied from the working jar used in the Docker Client image/container
        MyPOP3Client client = new MyPOP3Client(host, port);
        client.connect();

        container.stop();
    }
}

谢谢大家