我在SpringBoot应用程序的Service中有一个简单的方法.我使用@Retryable为该方法设置了重试机制.
我正在尝试对服务中的方法进行集成测试,并且在方法抛出异常时不会发生重试.该方法只执行一次.
public interface ActionService {
@Retryable(maxAttempts = 3, backoff = @Backoff(delay = 2000))
public void perform() throws Exception;
}
@Service
public class ActionServiceImpl implements ActionService {
@Override
public void perform() throws Exception() {
throw new Exception();
}
}
@SpringBootApplication
@Import(RetryConfig.class)
public class MyApp {
public static void main(String[] args) throws Exception {
SpringApplication.run(MyApp.class, args);
}
}
@Configuration
@EnableRetry
public class RetryConfig {
@Bean
public ActionService actionService() { return new ActionServiceImpl(); }
}
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration( classes= {MyApp.class})
@IntegrationTest({"server.port:0", "management.port:0"})
public …Run Code Online (Sandbox Code Playgroud) 我正在寻找一种使用spring amqp和Rabbit MQ通过后退策略实现重试的好方法,但是要求是不应该阻止侦听器(因此可以自由处理其他消息)。我在此处看到了类似的问题,但其中不包含“退缩”的解决方案:
RabbitMQ和Spring amqp重试时不会阻止使用者
我的问题是:
如果上述假设是正确的,那么这是唯一的实现此方法的方法,即实现一个单独的重试队列(DLQ?),并为每条消息设置一个TTL(假设我们不想在退避间隔内阻塞线程)。
如果我们采用上述方法(DLQ或单独的队列),那么每次重试尝试是否都不需要单独的队列?如果仅使用1个队列进行重试,则同一队列将包含TTL范围从最小重试间隔到最大重试间隔的消息,并且如果队列前面的消息具有最大TTL,则后面的消息将不会即使它具有最小TTL也会拾取。这是根据此处的Rabbit MQ TTL文档(请参见警告):
还有另一种方法来实现无阻塞的重试机制吗?
添加一些配置信息以帮助对@garyrussel进行故障排除:
队列配置:
<rabbit:queue name="regular_requests_queue"/>
<rabbit:queue name="retry_requests_queue">
<rabbit:queue-arguments>
<entry key="x-dead-letter-exchange" value="regular_exchange" />
</rabbit:queue-arguments>
</rabbit:queue>
<rabbit:direct-exchange name="regular_exchange">
<rabbit:bindings>
<rabbit:binding queue="regular_requests_queue" key="regular-request-key"/>
</rabbit:bindings>
</rabbit:direct-exchange>
<rabbit:direct-exchange name="retry_exchange">
<rabbit:bindings>
<rabbit:binding queue="retry_requests_queue"/>
</rabbit:bindings>
</rabbit:direct-exchange>
<bean id="retryRecoverer" class="com.testretry.RetryRecoverer">
<constructor-arg ref="retryTemplate"/>
<constructor-arg value="retry_exchange"/>
</bean>
<rabbit:template id="templateWithOneRetry" connection-factory="connectionFactory" exchange="regular_exchange" retry-template="retryTemplate"/>
<rabbit:template id="retryTemplate" connection-factory="connectionFactory" exchange="retry_exchange"/>
<bean id="retryTemplate" class="org.springframework.retry.support.RetryTemplate">
<property name="retryPolicy">
<bean class="org.springframework.retry.policy.SimpleRetryPolicy">
<property name="maxAttempts" value="1"/>
</bean>
</property>
</bean>
Run Code Online (Sandbox Code Playgroud) 我使用SimpleRetryPolicy/RetryTemplate尝试使用Simple java类的spring retry框架.它工作正常.所以我想用Annotations做同样的事情.注释从来没有对我有用.在网上找不到太多帮助.下面的代码就像普通的Java程序一样,在第一次尝试时抛出异常,这不是预期的行为.在抛出异常或从中恢复之前,它应该至少试过5次.不知道我哪里出错了.我是否需要执行任何XML/spring AOP配置才能工作?怎么会这样
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.annotation.EnableRetry;
@EnableRetry
@Configuration
public class App {
public static void main(String[] args) throws Exception {
Parentservice p = new SpringRetryWithHystrixService();
String status = p.getStatus();
System.out.println(status);
}
}
import org.springframework.retry.annotation.Retryable;
public interface Parentservice {
@Retryable(maxAttempts=5)
String getStatus() throws Exception;
}
import org.springframework.retry.annotation.Recover;
public class SpringRetryWithHystrixService implements Parentservice{
public static int i=0;
public String getStatus() throws Exception{
System.out.println("Attempt:"+i++);
throw new NullPointerException();
}
@Recover
public static String getRecoveryStatus(){
return "Recovered from NullPointer Exception";
}
}
Run Code Online (Sandbox Code Playgroud)
我在界面和实现的顶部尝试了@Retryable(maxAttempts …
我找不到任何有关需要采取行动的信息。我正在使用@Retryable注释和@Recover处理程序方法。像这样:
@Retryable(value = {Exception.class}, maxAttempts = 5, backoff = @Backoff(delay = 10000))
public void update(Integer id)
{
execute(id);
}
@Recover
public void recover(Exception ex)
{
logger.error("Error when updating object with id {}", id);
}
Run Code Online (Sandbox Code Playgroud)
问题是我不知道如何将我的参数“ id”传递给recovery()方法。有任何想法吗?提前致谢。
我创建了示例Spring-boot应用程序,它运行正常,没有任何错误,
现在我想练习spring-retry重试一些方法。如果我不使用,@EnableRetry我的应用程序启动时将没有任何错误,但重试无法正常进行。
如果我@EnableRetry在配置类上使用,我的应用程序无法启动
build.gradle
apply plugin: 'java-library'
repositories {
jcenter()
}
dependencies {
// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web
compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '2.1.0.RELEASE'
// https://mvnrepository.com/artifact/org.springframework.kafka/spring-kafka
compile group: 'org.springframework.kafka', name: 'spring-kafka', version: '2.2.0.RELEASE'
// https://mvnrepository.com/artifact/commons-logging/commons-logging
compile group: 'commons-logging', name: 'commons-logging', version: '1.2'
// https://mvnrepository.com/artifact/org.springframework.retry/spring-retry
compile group: 'org.springframework.retry', name: 'spring-retry', version: '1.2.0.RELEASE'
}
Run Code Online (Sandbox Code Playgroud)
设定档
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.annotation.EnableRetry;
@Configuration
@EnableRetry
public class RetryConfig {
}
Run Code Online (Sandbox Code Playgroud)
重试服务
@Service
public class RetryService {
@Retryable(value = { CustomException.class …Run Code Online (Sandbox Code Playgroud) 我正在针对PostgreSQL v12数据库进行开发。我正在使用SERIALIZABLE交易。一般想法是,当PostgreSQL检测到序列化异常时,应重试完整的事务。
我正在使用Spring的AbstractFallbackSQLExceptionTranslator将数据库异常转换为Spring的异常类。此异常翻译器应将PostgreSQL错误40001/serialization_failure转换为ConcurrencyFailureException。Spring JDBC维护一个映射文件,以将PostgreSQL特定的代码映射到数据库异常40001的通用cannotSerializeTransactionCodes类,该类将转换ConcurrencyFailureException为API用户。
我的想法是依靠Spring Retry项目重试SERIALIZABLE由于序列化错误而暂停的事务,如下所示:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Retryable(include = ConcurrencyFailureException.class, maxAttempts = ..., backoff = ...)
@Transactional(isolation = Isolation.SERIALIZABLE)
public @interface SerializableTransactionRetry {
}
Run Code Online (Sandbox Code Playgroud)
在服务实现,我会简单地替换@Transactional通过@SerializableTransactionRetry,并用它做。
现在,回到PostgreSQL。本质上,可以在两个阶段检测序列化异常:
似乎Spring AbstractFallbackSQLExceptionTranslator正确地转换了在执行语句期间检测到的序列化异常,但在提交阶段未能转换序列化异常。考虑以下堆栈跟踪:
org.springframework.transaction.TransactionSystemException: Could not commit JDBC transaction; nested exception is org.postgresql.util.PSQLException: ERROR: could not serialize access due to read/write dependencies among …Run Code Online (Sandbox Code Playgroud) 我有一个正在尝试测试的方法
@Retryable(value = {SocketTimeoutException.class},
backoff = @Backoff(delay = 10000),
maxAttempts = 4)
public String getNewString(String oldString) throws IOException{
...
}
Run Code Online (Sandbox Code Playgroud)
我已经创建了它的测试用例,如下所示:
@SpringBootTest
@RunWith(SpringRunner.class)
public class TestStrings {
@Test(expected = SocketTimeoutException.class)
public void testGetNewString() throws IOException {
...
}
Run Code Online (Sandbox Code Playgroud)
一切运行良好,测试用例运行 4 次,延迟 10 秒。但我想更改@Retryable的属性,即对于这个特定的测试用例,maxAttempts从4到2,延迟从10s到0.5s。我想这样做,以便在运行测试用例时不应该等待很长时间,并且测试用例应该快速结束,同时还测试重试功能。
我正在尝试配置spring amqp只重试一次消息定义的次数.目前,例如由于a而失败的消息DataIntegrityViolationException 被无限期地重新传递.
@Bean
public StatefulRetryOperationsInterceptor statefulRetryOperationsInterceptor() {
return RetryInterceptorBuilder.stateful()
.backOffOptions(1000, 2.0, 10000) // initialInterval, multiplier, maxInterval
.maxAttempts(3)
.messageKeyGenerator(message -> UUID.randomUUID().toString())
.build();
}
Run Code Online (Sandbox Code Playgroud)
这似乎没有应用 - 消息仍然无限期地尝试.
感觉就像我在这里遗漏了一些东西.
以下是关于AMQP的剩余配置:
@Bean
Queue testEventSubscriberQueue() {
final boolean durable = true;
return new Queue("testEventSubscriberQueue", durable);
}
@Bean
Binding binding(TopicExchange topicExchange) {
return BindingBuilder.bind(testEventSubscriberQueue()).to(topicExchange).with("payload.event-create");
}
@Bean
SimpleMessageListenerContainer messageListenerContainer(ConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setQueueNames(testEventSubscriberQueue().getName());
container.setMessageListener(listenerAdapter);
container.setChannelTransacted(true);
return container;
}
@Bean
MessageListenerAdapter listenerAdapter(MessageConverter messageConverter, SubscriberHandler …Run Code Online (Sandbox Code Playgroud) 我正在测试春季重试,但似乎没有被称为恢复。试图使其工作,但似乎无遗。我没有给@Recover传递任何参数Throwable Exception。更改了重试依赖项版本,似乎它已包含在aop中以用于春季启动并已将其删除。不会通过以下异常消息来调用保持恢复状态。
请求处理失败;嵌套的异常是org.springframework.retry.ExhaustedRetryException:无法找到恢复方法。嵌套异常是java.lang.ArithmeticException:/按零],其根本原因是
任何帮助将非常感激
我的代码如下所示。
配置类
package hello;
import java.util.Arrays;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.retry.annotation.Retryable;
@SpringBootApplication
@EnableRetry
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
return args -> {
System.out.println("Let's inspect the beans provided by `Spring Boot:");`
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
System.out.println(beanName);
}
};
}
}
Run Code Online (Sandbox Code Playgroud)
休息控制器类;
package hello;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Autowired; …Run Code Online (Sandbox Code Playgroud) 我们如何在同一个方法块中捕获两个不同的异常(例如来自.lang和包) 。其一,我们返回一个,另一个我们重试该方法。.io@RetryableIOException
@Retryable(value = {Exception.calss } ,maxAttempts = 3, backoff = @Backoff(delay = 3000))
public String getInfo() {
try {
//here we have an executive code that may have an IOException
} catch(Exception ex) {
//And here i would catch the Exception
throw new Exception();
}
}
Run Code Online (Sandbox Code Playgroud) spring-retry ×10
java ×8
spring ×7
spring-boot ×5
rabbitmq ×2
spring-amqp ×2
aop ×1
aspectj ×1
exception ×1
mockito ×1
postgresql ×1
recover ×1
spring-jdbc ×1
transactions ×1