【问题标题】:Embedded AMQP Java Broker嵌入式 AMQP Java 代理
【发布时间】:2015-06-18 14:42:43
【问题描述】:

我正在尝试为连接到 RabbitMQ 代理的 Scala / Java 应用程序创建集成测试。为了实现这一点,我想要一个能够在每次测试之前启动和停止的 AMQP 嵌入式代理。最初我尝试将 ActiveMQ 作为 AMQP 的嵌入式代理引入,但是该应用程序使用 RabbitMQ,因此只使用 AMQP 0.9.3 版,而 ActiveMQ 需要 AMQP 1.0 版。

我可以使用另一个嵌入式代理来代替 ActiveMQ 吗?

【问题讨论】:

  • RabbitMQ 实现 AMQP 0.8; 0.9.1 和 AMQP 1.0。如果您使用的是 mac,那么启动/停止 rabbitmq 进行测试非常容易。这适用于 PHP,但可能会对您的用例有所帮助 videlalvaro.github.io/2013/04/using-rabbitmq-in-unit-tests.html
  • 嗨@old_sound,感谢您的调查。理想情况下,我想避免在盒子上要求 rabbitmq 进行测试,我们的测试在我们无法轻松安装 RabbitMQ 的 CI 服务器上运行。
  • 那个 CI 服务器是否至少安装了 Erlang?如果是,您可以下载 rabbit tarball,解压缩,然后启动/停止它以进行测试
  • 由于您在下面有一些回答似乎可以回答您的问题,请考虑通过单击投票数下方的标记将其中一个标记为“已接受”(请参阅​​How do you accept an answer?)。这显示了哪个答案对您最有帮助,并将声誉点分配给答案的作者(以及您!)。它是this site's idea to identify good questions and answers through upvotes and acceptance of answers 的一部分。

标签: java rabbitmq automated-tests integration-testing amqp


【解决方案1】:

完全在内存中的解决方案。根据需要替换 spring.* 属性。

<dependency>
  <groupId>org.apache.qpid</groupId>
  <artifactId>qpid-broker</artifactId>
  <version>6.1.1</version>
  <scope>test</scope>
</dependency>
public class EmbeddedBroker {
  public void start() {
    Broker broker = new Broker();
    BrokerOptions brokerOptions = new BrokerOptions();
    brokerOptions.setConfigProperty("qpid.amqp_port", environment.getProperty("spring.rabbitmq.port"));
    brokerOptions.setConfigProperty("qpid.broker.defaultPreferenceStoreAttributes", "{\"type\": \"Noop\"}");
    brokerOptions.setConfigProperty("qpid.vhost", environment.getProperty("spring.rabbitmq.virtual-host"));
    brokerOptions.setConfigurationStoreType("Memory");
    brokerOptions.setStartupLoggedToSystemOut(false);
    broker.startup(brokerOptions);
  }
}

initial-config.json 添加为资源:

{
  "name": "Embedded Test Broker",
  "modelVersion": "6.1",
  "authenticationproviders" : [{
    "name": "password",
    "type": "Plain",
    "secureOnlyMechanisms": [],
    "users": [{"name": "guest", "password": "guest", "type": "managed"}]
  }],
  "ports": [{
    "name": "AMQP",
    "port": "${qpid.amqp_port}",
    "authenticationProvider": "password",
    "protocols": [ "AMQP_0_9_1" ],
    "transports": [ "TCP" ],
    "virtualhostaliases": [{
      "name": "${qpid.vhost}",
      "type": "nameAlias"
    }]
  }],
  "virtualhostnodes" : [{
    "name": "${qpid.vhost}",
    "type": "Memory",
    "virtualHostInitialConfiguration": "{ \"type\": \"Memory\" }"
  }]
}

【讨论】:

  • 请注意,如果您的应用程序在 prod 中连接到 RabbitMQ,如果您绑定到 Apache QPid 进行集成测试,您可能会发现不一致。 RabbitMQ 扩展了 AMQP 以提供您的应用程序可能依赖的附加功能(例如 TTL、DLE/Q、路由等)。更多信息rabbitmq.com/extensions.html
  • QPid 也有大部分,只是名称不同。
  • 我不能使用它,因为他们对记录器类进行了硬编码。真可惜... org.apache.logging.slf4j.Log4jLogger 不能转换为 ch.qos.logback.classic.Logger java.lang.ClassCastException: org.apache.logging.slf4j.Log4jLogger 不能转换为 ch.qos .logback.classic.Logger
  • @JanGoyvaerts 嗯?它使用 slf4j - 任何地方都没有硬编码。如果由于某种原因您需要直接访问记录器实现,您可以将其配置为使用 Logback。
  • @OrangeDog,感谢您指出这一点!您能指出一些使用 7.x 的嵌入式示例吗?整个 API 似乎都发生了变化,我几乎找不到任何使用它的示例。 This 是我能找到的所有东西,但我无法让它与密码文件一起使用。
【解决方案2】:

我围绕下载、提取、启动和管理 RabbitMQ 的过程开发了一个包装器,因此它可以像任何 JVM 项目控制的嵌入式服务一样工作。

查看:https://github.com/AlejandroRivera/embedded-rabbitmq

很简单:

EmbeddedRabbitMqConfig config = new EmbeddedRabbitMqConfig.Builder()
    .version(PredefinedVersion.V3_5_7)
    .build();
EmbeddedRabbitMq rabbitMq = new EmbeddedRabbitMq(config);
rabbitMq.start();
...
rabbitMq.stop();

适用于 Linux、Mac 和 Windows。

【讨论】:

  • 效果很好,但需要预先安装 ERL 对我来说是个问题。
【解决方案3】:

这是 OrangeDog 提出的适用于 Qpid Broker 7.x 的解决方案,灵感来自here

添加 qpid 7.x 作为测试依赖项。在 7.x 中,这些已在核心 + 插件中分离,具体取决于您的需要。对于 RabbitMQ AMQP 版本,您需要 qpid-broker-plugins-amqp-0-8-protocol,而对于在内存中运行(足以进行集成测试)使用 qpid-broker-plugins-memory-store

pom.xml:

...
<properties>
    ...
    <qpid-broker.version>7.0.2</qpid-broker.version>
</properties>

<dependencies>
    ...
    <dependency>
        <groupId>org.apache.qpid</groupId>
        <artifactId>qpid-broker-core</artifactId>
        <version>${qpid-broker.version}</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.qpid</groupId>
        <artifactId>qpid-broker-plugins-amqp-0-8-protocol</artifactId>
        <version>${qpid-broker.version}</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.qpid</groupId>
        <artifactId>qpid-broker-plugins-memory-store</artifactId>
        <version>${qpid-broker.version}</version>
        <scope>test</scope>
    </dependency>
</dependecies>
...

使用硬编码的用户/密码和映射到默认端口 (5672) 的默认内存虚拟主机添加代理配置:

qpid-config.json:

{
  "name": "EmbeddedBroker",
  "modelVersion": "7.0",
  "authenticationproviders": [
    {
      "name": "password",
      "type": "Plain",
      "secureOnlyMechanisms": [],
      "users": [{"name": "guest", "password": "guest", "type": "managed"}]
    }
  ],
  "ports": [
    {
      "name": "AMQP",
      "port": "${qpid.amqp_port}",
      "authenticationProvider": "password",
      "virtualhostaliases": [
        {
          "name": "defaultAlias",
          "type": "defaultAlias"
        }
      ]
    }
  ],
  "virtualhostnodes": [
    {
      "name": "default",
      "defaultVirtualHostNode": "true",
      "type": "Memory",
      "virtualHostInitialConfiguration": "{\"type\": \"Memory\" }"
    }
  ]
}

定义junit ExternalResource 并声明为ClassRule(或在您的IT @BeforeClass@AfterClass 方法中启动和关闭您的嵌入式代理):

EmbeddedAMQPBroker.java:

public class EmbeddedAMQPBroker extends ExternalResource {

    private final SystemLauncher broker = new SystemLauncher();

    @Override
    protected void before() throws Throwable {
        startQpidBroker();
        //createExchange();
    }

    @Override
    protected void after() {
        broker.shutdown();
    }

    private void startQpidBroker() throws Exception {
        Map<String, Object> attributes = new HashMap<>();
        attributes.put("type", "Memory");
        attributes.put("initialConfigurationLocation", findResourcePath("qpid-config.json"));
        broker.startup(attributes);
    }

    private String findResourcePath(final String fileName) {
        return EmbeddedAMQPBroker.class.getClassLoader().getResource(fileName).toExternalForm();
    }
}

集成测试:

public class MessagingIT{
    @ClassRule
    public static EmbeddedAMQPBroker embeddedAMQPBroker = new EmbeddedAMQPBroker();

    ...
}

【讨论】:

    【解决方案4】:

    我不知道有任何嵌入式 RabbitMQ 服务器,因此我认为您有几个选项可以解决此问题:

    1. 您的 RabbitMQ 服务器不需要存在于您的 CI 服务器上,您可以启动一个新服务器,即您的 CI rabbitmq 服务器。如果你不能自己提出一个,你可以查看CloudAMQP。今天的免费套餐提供:每月 100 万条消息、20 个并发连接、100 个队列、10,000 条排队消息。对您的 CI 流程来说已经足够了。

    2. 如果您的测试只是对 RabbitMQ 进行单元测试,您可以模拟您的 RabbitMQ 消息生产。这就是我们在一些单元测试中所做的。我们只是检查某个操作是否调用了方法以产生特定的消息,但是我们将其模拟出来,因此我们实际上并没有发布消息。然后我们通过使用我们创建的特定消息显式调用消费者方法来测试每个消费者。

    【讨论】:

      【解决方案5】:

      你可以试试Apache QPid Java broker。这可以用作嵌入式代理。

      另一个 SO 问题中描述了 Scala 中的设置 - Example of standalone Apache Qpid (amqp) Junit Test

      【讨论】:

        猜你喜欢
        • 2011-05-28
        • 2012-05-13
        • 2013-01-06
        • 2018-07-08
        • 1970-01-01
        • 1970-01-01
        • 2012-06-02
        • 2018-02-16
        • 2017-02-08
        相关资源
        最近更新 更多