[spring-projects/spring-boot]改进了在 spring.rabbitmq.host 中意外指定多个主机时的错误消息

2024-05-08 876 views
8

在版本 3.0.x 中将 YAML 配置与 Spring Boot AMQP 启动器一起使用时,可以省略addresses配置属性,而是使用hostport

例如:

spring:
  rabbitmq:
    host: "my-rmq-host.net"
    username: "host_username"
    password: "host_password"
    port: 5672

升级到 Spring Boot 3.1.0 后,此配置选项不再起作用。该服务无法以java.lang.ArrayIndexOutOfBoundsException.

Caused by: java.lang.ArrayIndexOutOfBoundsException: Index 1 out of bounds for length 1
    at org.springframework.boot.autoconfigure.amqp.PropertiesRabbitConnectionDetails.getAddresses(PropertiesRabbitConnectionDetails.java:57) ~[spring-boot-autoconfigure-3.1.0.jar:3.1.0]
    at org.springframework.boot.autoconfigure.amqp.RabbitConnectionDetails.getFirstAddress(RabbitConnectionDetails.java:71) ~[spring-boot-autoconfigure-3.1.0.jar:3.1.0]
    at org.springframework.boot.autoconfigure.amqp.RabbitConnectionFactoryBeanConfigurer.configure(RabbitConnectionFactoryBeanConfigurer.java:98) ~[spring-boot-autoconfigure-3.1.0.jar:3.1.0]
    at org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration$RabbitConnectionFactoryCreator.rabbitConnectionFactory(RabbitAutoConfiguration.java:126) ~[spring-boot-autoconfigure-3.1.0.jar:3.1.0]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:139) ~[spring-beans-6.0.9.jar:6.0.9]
    ... 62 common frames omitted

其根本原因似乎在于内部RabbitConnectionFactoryBeanConfigurer,特别是Address address = this.connectionDetails.getFirstAddress();

在我上面描述的情况下,本地RabbitConnectionDetails类已经配置了主机、端口、用户名和密码。这些字段中的值与我上面描述的 YAML 配置匹配。看起来并public void configure(RabbitConnectionFactoryBean factory)没有尝试使用这些值,而是要求将它们设置在Address.由于我的应用程序 yaml 中没有address定义此更改会导致ArrayIndexOutOfBoundsException.

这一改变是有意为之吗?该rabbitmq.host属性现在是否已弃用并被替换addresses?如果是这样,该port属性是否也已弃用?

回答

7

测试通过了。

    @Test
    void test(){
        RabbitProperties properties = new RabbitProperties();
        properties.setHost("test.com");
        properties.setPort(5672);
        PropertiesRabbitConnectionDetails details = new PropertiesRabbitConnectionDetails(properties);
        assertThat(details.getAddresses()).element(0).isEqualTo(new RabbitConnectionDetails.Address(properties.getHost(), properties.getPort()));
    }
4

仔细查看了一下,问题似乎确实出现在PropertiesRabbitConnectionDetails.

    @Override
    public List<Address> getAddresses() {
        List<Address> addresses = new ArrayList<>();
        for (String address : this.properties.determineAddresses().split(",")) {
            String[] components = address.split(":");
            addresses.add(new Address(components[0], Integer.parseInt(components[1])));
        }
        return addresses;
    }

determineAddresses方法返回正确的地址以及字段中设置的值host。但是,由于返回的地址中没有端口,因此Integer.parseInt(components[1]))失败。

看起来被调用的方法determineAddresses()正在主机末尾连接一次端口。

例如,该方法的结果是: host-a,host-b,host-c:5672

调用者似乎期望端口连接到每个主机的末尾

6

我使用属性中的值添加了一个测试:

    @Test
    void name() {
        RabbitProperties properties = new RabbitProperties();
        properties.setHost("my-rmq-host.net");
        properties.setUsername("host_username");
        properties.setPassword("host_password");
        properties.setPort(5672);
        PropertiesRabbitConnectionDetails details = new PropertiesRabbitConnectionDetails(properties);
        assertThat(details.getAddresses()).isEqualTo(List.of(new Address("my-rmq-host.net", 5672)));
        assertThat(details.getVirtualHost()).isNull();
        assertThat(details.getUsername()).isEqualTo("host_username");
        assertThat(details.getPassword()).isEqualTo("host_password");
    }

这过去了。

当将这些属性放入application.yaml.

请提供不起作用的 YAML,或者我们可以用来重现问题的最小示例。谢谢!

5

谢谢,

如果您将properties.setHost("my-rmq-host.net");示例更改为properties.setHost("my-rmq-host.net,my-rmq-host-2.net");您应该会看到相同的问题

9

我认为该spring.rabbitmq.host房产从未打算容纳超过 1 位房东。在这种情况下,addresses应该使用。

回答你的问题:

rabbitmq.host 属性现在是否已弃用并被地址取代?如果是这样,端口属性是否也已弃用?

不,我们没有反对任何东西。他们仍然受到支持。这只是您过去偶然使用它们的结果。如果有超过 1 个主机,您应该更改配置以使用地址。

4

有道理,但环顾四周,似乎之前的行为确实允许这样做。我想知道是否仍然值得在这里提供更好的错误消息,或者甚至恢复到旧的行为。

由于这两个领域本质上似乎做同样的事情,甚至共享一个实现,也许在未来,值得弃用其中一个领域。

8

在这里提供更好的错误消息

是的,那应该是可能的。

3

感谢您的支持 - 我现在很清楚了。

7

我们将,在 in 中host添加一个检查org.springframework.boot.autoconfigure.amqp.RabbitProperties#determineAddresses,如果是这样的话,让它失败并显示更好的错误消息。

0

您好,请问这个问题可以用于工作吗?

0

是的,请随意进行操作。我会将问题分配给您,另请确保您已阅读我们的贡献文档,这里有一些如何开始的提示。如果您遇到任何问题,请随时与我们联系。玩得开心!

7

你好,我相信我已经准备好了解决方案。然而,在开始 PR 之前,我想确认一些要点,因为这是我的第一个贡献。我按照建议执行了对逗号的检查org.springframework.boot.autoconfigure.amqp.RabbitProperties#determineAddresses。我org.springframework.boot.actuate.autoconfigure.wavefront.WavefrontProperties#getApiTokenOrThrow作为示例并使用 anInvalidConfigurationPropertyValueException来发送错误消息。这是预期的吗?另外我想问一下我写的错误信息好不好?该代码可以在以下位置找到:

https://github.com/rafaelrc7/spring-boot/blob/fdfb17fc1f20d3090e61b647855f6e37aec86955/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitProperties.java# L208-L211

我还实现了一个测试用例来验证解决方案:

https://github.com/rafaelrc7/spring-boot/blob/fdfb17fc1f20d3090e61b647855f6e37aec86955/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/amqp/RabbitPropertiesTests.java# L345-L351

谢谢。

5

嘿@rafaelrc7,这看起来不错,非常感谢你!

InvalidConfigurationPropertyValueException是正确使用的例外,并且该消息对我来说看起来也不错。感谢您编写此测试!

更改看起来不错,请创建 PR。

7

被#35684 取代。