如何干净地关闭嵌入式 ActiveMQ Artemis

How to cleanly close embedded ActiveMQ Artemis

我是 JavaLite open source project. One part of it is called Async 的维护者,它是 Apache ActiveMQ Artemis 代理的简化前端。它的存在是为了更容易将 Artemis 嵌入到进程的内存中,同时也为处理“命令”增加了一层便利。我们已经在生产中使用它多年,几乎没有任何问题。

但是,JavaLite 项目本身有多次测试 start/stop 代理,并使用不同的实例进行不同的测试。 Here's the source code of Async and the source code of tests.

如您所见,测试创建了一个新的代理实例,使用它,然后停止它。

这是 start and stop 方法。

现在进入正题。通常,构建 运行 测试在我的笔记本电脑和较旧的 CI 环境中都可以成功,没有问题。我的笔记本电脑和以前的 CI 服务器从来没有遇到过任何问题。但是,我们正在构建一个新的 CI 服务器,并且该测试在那里失败并出现随机数量的逻辑错误(测试条件)。有时它也会成功。除了硬件之外,成功和失败的机器之间的一切都是相同的。它失败的盒子只有两个 CPU 核心(我的笔记本电脑有 12 个核心)。

因此,在测试 AsyncSpec 中,我们创建、启动和停止代理,似乎某些数据在代理的不同实例之间随机流失。

在同一个 VM 中创建/start/stop/销毁 Artemis 嵌入式服务器而不会跨多个实例发生冲突的best/cleanest方法是什么?

我将 JavaLite 加载到我的 IDE 中并为 AsyncSpec test you linked 重现了一些失败。这是我的观察...

我注意到的一件事是您的某些测试可能会泄漏代理。如果断言失败,则在代理停止之前具有断言的任何测试都可能泄漏,因为测试将在不停止代理的情况下终止。这将对随后的任何测试产生负面影响。您应该在 finally 块或 @After 方法中停止您的经纪人。在任何情况下,您都需要绝对确保无论测试中发生什么情况,代理都会停止。

您的测试也会泄漏日志。您使用此创建嵌入式代理的日志目录:

Files.createTempDirectory("async").toFile().getCanonicalPath();

但是,您永远不会清理该目录。我在一个循环中 运行 数千次测试迭代,它填满了超过 200GB 的磁盘 space。

我认为最重要的是,当您在测试中发送消息时,您将它们作为非持久性发送,这意味着它们将被 异步发送 。但是,您的测试没有考虑到这一点,这可能会导致竞争条件并最终 assertion/test 失败。我看到有几个选项可以解决这个问题。您可以将消息作为持久消息发送,这将同步完成。您可以在嵌入式客户端的 URL(在 the documentation 中讨论)上设置 blockOnNonDurableSend=true,这也将使发送消息同步。或者您可以添加一些其他类型的 Wait.waitFor() 以确保在继续测试之前满足有意义的条件(尽管您无法在大多数测试中检查消息计数,因为您使用的是异步消息侦听器).

我还认为如果您在检查 HelloCommand.counter() 时使用 Wait.waitFor() 并且如果您在 之前 关闭了代理,那么您的测试会更加可靠.考虑到消息侦听器的异步特性,计数器的值和队列的消息计数之间可能会出现短时间的差异。此外,在 Wait.waitFor() 期间让代理保持运行将意味着消息侦听器不会过早停止。

当我第一次开始 运行 测试时,我可以在不到 25 运行 秒左右的时间内始终如一地重现故障。通过上面列出的更改,我能够 运行 超过 12,000 次而没有失败。

最终我没有发现代理有任何问题,只是测试有问题。