【问题标题】:In Spring Integration app, Is it possible to test a Spring Retry mechanism outside of chain?在 Spring Integration 应用程序中,是否可以在链外测试 Spring Retry 机制?
【发布时间】:2016-09-20 22:53:25
【问题描述】:

我继承了一个包含 Spring Retry 的 Spring Integration 项目。我不确定它是否已经过测试,也没有单独的测试。所以我试图用一个简单的场景来练习它。

通过模拟 RestTemplate exchange 方法,我希望能够测试重试逻辑。我可以让我想要抛出的异常发生,但它只发生一次 - 没有重试发生。

重试建议的 XML 在这里(retry-advice-context.xml):

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:int="http://www.springframework.org/schema/integration"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

    <bean id="retryAdvice" class="org.springframework.integration.handler.advice.RequestHandlerRetryAdvice" >
        <property name="retryTemplate">
            <bean class="org.springframework.retry.support.RetryTemplate">
                <property name="backOffPolicy">
                    <bean class="org.springframework.retry.backoff.ExponentialBackOffPolicy">
                        <property name="initialInterval" value="${retry.initialInterval}"/>
                        <property name="maxInterval" value="${retry.maxInterval}"/>
                        <property name="multiplier" value="${retry.multiplier}"/>
                    </bean>
                </property>

                <property name="retryPolicy">
                    <bean class="com.reachlocal.mediapublishing.shim.integration.retry.CustomRetryPolicy">
                        <constructor-arg name="maxAttempts" value="${retry.maxAttempts}" />
                        <constructor-arg name="retryableExceptions" ref="retryableExceptions" />
                    </bean>
                </property>
            </bean>
        </property>

        <property name="recoveryCallback">
            <bean class="org.springframework.integration.handler.advice.ErrorMessageSendingRecoverer">
                <constructor-arg ref="errorChannel" />
            </bean>
        </property>
    </bean>

    <util:map id="retryableExceptions"  map-class="java.util.HashMap" >
        <entry key="java.net.SocketException" value="true" />
        <entry key="com.examplel.ConnectionException" value="true" />
        <entry key="com.example.CustomException" value="true" />
    </util:map>

</beans>

这是一个 SI 处理文件的一部分:

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:int="http://www.springframework.org/schema/integration"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                  http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd">

    <import resource="retry-advice-context.xml"/>

    <int:channel id="channel1">
    </int:channel>

    <int:header-value-router id="commandTypeRouter" input-channel="commandChannel"  <---DEFINED IN MASTER FILE
                             header-name="commandType" resolution-required="true">
        <int:mapping value="COMMAND_1" channel="channel1"/>
    </int:header-value-router>

    <int:chain id="command1Chain" input-channel="channel1" output-channel="commandProcessed">
        <int:header-enricher>
            <int:error-channel ref="errorChannel" />
        </int:header-enricher>
        <int:service-activator ref="eventDataWriter" method = "addEventStart"/>

        <int:service-activator ref="accountProcessor" method="processAccount">
            <int:request-handler-advice-chain><ref bean="retryAdvice" /></int:request-handler-advice-chain>
        </int:service-activator>
    </int:chain>
</beans>

所以重试 bean retryAdvice 是不同链的一部分。链还有很多,所以我只想能够从服务层检查重试逻辑。代码中的任何地方都没有Retry注解(不知道是否需要)。

几个问题:

  1. 我可以从服务层测试重试功能还是需要执行整个链?
  2. 是否缺少重试机制所需的任何内容(注释、其他 XML)?

顺便说一句,这是使用 SI 4.1.3。

谢谢。

更新 1:

设法让 Gary 的项目在我的环境中运行。之后,我将retry-advice-context.xml 文件添加到主 SI xml 中。我将地图更改为只有RuntimeException。日志语句显示ExponentialBackoffPolicy 语句。我还收到了RetryTemplate 调试日志语句。

有了更多的了解,我将那里的内容转换为我正在使用的真实代码,并取得了更大的成功。我收到了我的异常发生的日志语句,最多将重试 3 次。

不幸的是,我得到的是:

17:29:26.154 DEBUG [task-scheduler-2][org.springframework.retry.support.RetryTemplate] Checking for rethrow: count=1
17:29:26.155 DEBUG [task-scheduler-2][org.springframework.retry.support.RetryTemplate] Retry failed last attempt: count=1

所以它最初知道它应该重试最多 3 次。但随后它表示它在 1 次重试后进行了最后一次尝试。

在 Gary 的工作代码中,调试语句将显示 Retry: count=2 等...以进行下一次连续尝试。

在应该进行重试期间,Spock 测试代码中有一个sleep 语句。我把时间加长和缩短都没有改变。

通过重试代码继续尝试和调试,看看为什么它在第一次重试时停止。

【问题讨论】:

  • 您能否澄清问题 1 中的“服务层”是什么意思?
  • 我的意思是在对外部系统进行 REST 调用的服务类中。可能必须重试的请求。它使用 Spring RestTemplate 调用代码。

标签: java groovy spring-integration spring-retry


【解决方案1】:

您不需要任何额外的注释或 XML。

如果你给链和服务id属性你可以独立测试处理程序...

<int:chain id="myChain" input-channel="foo">
    <int:transformer expression="payload" />
    <int:service-activator id="myService" ref="bar">
        <int:request-handler-advice-chain>
            <int:ref bean="retry" />
        </int:request-handler-advice-chain>
    </int:service-activator>
</int:chain>

<bean id="retry" class="org.springframework.integration.handler.advice.RequestHandlerRetryAdvice" />

<bean id="bar" class="com.example.Bar" />

Bar 是...

public class Bar {

    private int count;

    public void bar(String in) {
        System.out.println(in);
        if (count++ < 2) {
            throw new RuntimeException("foo");
        }
    }

}

测试:

public class So39604931ApplicationTests {

    @Autowired
    @Qualifier("myChain$child.myService.handler")
    public MessageHandler handler;

    @Test
    public void test() {
        handler.handleMessage(new GenericMessage<>("foo"));
    }

}

结果:

foo
foo
foo

您还应该为 org.springframework.retry 打开调试日志记录以查看重试行为。

08:51:16.897 [main] DEBUG o.s.retry.support.RetryTemplate - Retry: count=0
foo
08:51:16.902 [main] DEBUG o.s.retry.support.RetryTemplate - Checking for rethrow: count=1
08:51:16.902 [main] DEBUG o.s.retry.support.RetryTemplate - Retry: count=1
foo
08:51:16.903 [main] DEBUG o.s.retry.support.RetryTemplate - Checking for rethrow: count=2
08:51:16.903 [main] DEBUG o.s.retry.support.RetryTemplate - Retry: count=2
foo

【讨论】:

  • 几个问题:所以您仍然需要通过链进行测试,因为您正在处理消息处理程序,对吗?这就是 GenericMessage 的原因。我的服务激活器 XML 只使用ref,那么@Qualifier 会发生什么?我真的不想将id 属性添加到我所有的服务激活器。基本上我不知道你是怎么想出child.myService的。谢谢。
  • 链是消息处理程序的链;限定符将您带到包装您的 accountProcessor bean 的处理程序实例;所以你只是在锻炼那个单独的处理程序。一般的命名策略是&lt;chainId&gt;$child.&lt;memberId&gt;.handler。如果链成员上没有id,我们不会在应用程序上下文中注册处理程序(包装你的bean),因此无法将其连接到测试中。如果您不想使用 id,另一种方法是注入MessageHandlerChain bean 并使用getHandlers()。但这有点脆弱,因为它是由位置决定的。它是在 4.3 中添加的。
  • idref 可以相同还是需要不同?
  • 它可以相同,因为在链成员中,它不是完整的 bean 名称,它只是 myChain$child.&lt;id&gt;.handler bean 名称的一部分。
  • 谢谢。顺便说一句,这是否意味着在 SI 中 ref 与对 bean 实例的引用具有不同的含义?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-28
  • 2015-12-25
  • 1970-01-01
  • 2015-08-12
  • 1970-01-01
相关资源
最近更新 更多