我们有两年前使用gradle构建的旧式XML配置开发的Spring批处理应用程序。简单设置为自定义运行时异常的重试添加了作业。我们没有打算将此应用程序迁移到最新的基于注释的配置,因为它现在正在生产中运行。
最近发现,由于一些神秘的原因,重试根本不起作用。我尝试将Spring批量版本本地升级到最新版本,但它没有帮助。最初在调用@Service类以获取@PostContruct中的一些数据的步骤中发现了这一点,认为这是因为@PostContruct,使用Tasklet创建了一个额外的步骤并尝试了。我尝试了所有选项,最新的更改只是尝试直接抛出java.lang.Exception但不起作用。在类和方法级别试过@Retryable,从来没有工作过,现在我开始怀疑,这次重试会不会有效?我放弃了这一点,但迫切需要一些帮助。任何可以提供解决这个问题的线索或解决方案的人都是我的英雄!谢谢!
这里有一些代码部分(Groovy代码,一些不必要的Java语法)(我已经简化了一些方法来显示实际关注的区域,但随时可以要求更多的配置更改) -
dependencies {
testCompile "org.hamcrest:hamcrest-all:1.3"
testCompile "info.cukes:cucumber-groovy:${cukes.version}"
testCompile "info.cukes:cucumber-junit:${cukes.version}"
testCompile "junit:junit:4.11"
testCompile "org.spockframework:spock-core:${spock.version}"
testCompile "org.spockframework:spock-spring:${spock.version}"
compile "org.codehaus.groovy:groovy-all:${groovy.version}"
compile "org.springframework:spring-test:${spring.version}"
compile "org.springframework:spring-core:${spring.version}"
compile "org.springframework.batch:spring-batch-core:4.0.1.RELEASE"
compile "org.springframework.batch:spring-batch-infrastructure:4.0.1.RELEASE"
compile "org.springframework.retry:spring-retry:1.2.2.RELEASE"
compile "org.springframework.batch:spring-batch-test:4.0.1.RELEASE"
compile "org.springframework.data:spring-data-jpa:1.9.0.RELEASE"
compile "org.springframework.data:spring-data-commons:1.9.0.RELEASE"
compile "org.springframework:spring-web:${spring.version}"
...
}
<batch:job id="facebookPermissionsReminderEmailJob" restartable="true">
<batch:step id="businessPageProcess" parent="faultTolerance" next="facebookPermissionsReminderEmailStep">
<tasklet ref="businessPageProcessor" retry-limit="5">
<retryable-exception-classes>
<include class="java.lang.Exception"/>
<include class="com.reachlocal.data.synchronizer.model.ApiException"/>
</retryable-exception-classes>
<batch:retry-listeners>
<batch:listener ref="retryLoggerListener" />
</batch:retry-listeners>
</tasklet>
</batch:step>
<bean id="faultTolerance" class="org.springframework.batch.core.step.item.FaultTolerantStepFactoryBean" abstract="true">
<property name="backOffPolicy">
<bean class="org.springframework.retry.backoff.ExponentialBackOffPolicy">
<property name="initialInterval" value="180000" />
<property name="multiplier" value="2" />
</bean>
</property>
</bean>
@Slf4j
@Scope("step")
@Component("businessPageProcessor")
class BusinessPageProcessor implements Tasklet {
@Autowired
BusinessPagesService businessPagesService
@Autowired
PermissionReminderDetails reminderDetails
Map<String, BusinessPage> nameToPageMap = [:]
Date currentDate
@Override
RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
log.info("Inside FacebookFirstReminderProcessor.initProcessorDetails() - retrieving facebook Business info")
List<BusinessPage> businessPages = businessPagesService.getAll()
log.info("Business Pages info retrieved from facebook: ${businessPages}")
businessPages.forEach({ page ->
String pageUrl = extractPageName(page.link ? page.link : page.pageResponse?.link)
nameToPageMap.put(pageUrl, page)
})
log.info("Generated Page to Status Map is: ${nameToPageMap}")
currentDate = new Date()
log.info("--------DATE-------- : run date is ${currentDate} for current batch process")
reminderDetails.currentDate = this.currentDate
reminderDetails.nameToPageMap = this.nameToPageMap
RepeatStatus.FINISHED
}
}
@Service
class BusinessPagesService {
@Autowired
PlatformProperty platformProperty
@Autowired
ApiCaller apiCaller
List<BusinessPage> getAll() {
String businessId = platformProperty.facebookReachLocalBusinessId
getAll(businessId)
}
List<BusinessPage> getAll(String businessId) {
List<BusinessPage> businessPages = []
Assert.isTrue(StringUtils.isNotBlank(businessId), EMPTY_BUSINESS_ID_ERROR_MESSAGE)
String approvedPagesEndPoint = enrichApiUrlForGetApprovedPages(businessId)
String pendingPagesEndPoint = enrichApiUrlForGetPendingPages(businessId)
List<BusinessPage> approvedPages = getBusinessPages(businessId, approvedPagesEndPoint)
//FB does not return access info, so manually setting the value for rest of the workflow logic to work as it is.
approvedPages.stream().forEach({page -> page.accessStatus = FacebookPageAccessStatus.CONFIRMED})
List<BusinessPage> pendingPages = getBusinessPages(businessId, pendingPagesEndPoint)
//FB does not return access info, so manually setting the value for rest of the workflow logic to work as it is.
pendingPages.stream().forEach({page -> page.accessStatus = FacebookPageAccessStatus.PENDING})
businessPages.addAll(approvedPages)
businessPages.addAll(pendingPages)
businessPages
}
List<BusinessPage> getBusinessPages(String businessId, String endPointUrl) {
List<BusinessPage> businessPages = []
while (true) {
log.info("Retrieving Business Pages details for Business Id of: ${businessId} using enriched URL of: ${endPointUrl}")
BusinessPagesResponse response = getSubSetOfBusinessPages(endPointUrl)
log.info("Successfully retrieve Business Pages details ${response}")
if(response.businessPages) {
businessPages.addAll(response.businessPages)
endPointUrl = response.paging?.next
} else {
break
}
if (!endPointUrl) {
break
}
}
businessPages
}
BusinessPagesResponse getSubSetOfBusinessPages (String endPointURL) {
BusinessPagesResponse response
try {
response = (BusinessPagesResponse) apiCaller.call(
endPointURL,
new ParameterizedTypeReference<BusinessPagesResponse>() {},
Optional.empty(),
HttpMethod.GET)
throw new Exception("Test Exception")
//log.info("Successfully retrieve Business Pages details ${response}")
} catch (ApiException apiEx){
if (apiEx.message.contains(EXCESS_DATA_REQUESTED_FAILURE_TEXT)) {
log.error("ExcessDataRequestedException occurred - retrying")
String exceptionClass = apiEx.class.name.concat("-")
throw new Exception(exceptionClass.concat(apiEx.message)) // will trigger retry upto defined max.
} else {
throw apiEx
}
}
//response
}
@Component
class RetryLoggerListener extends RetryListenerSupport {
private Logger logger = LoggerFactory.getLogger(this.getClass())
@Override
<T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback,Throwable throwable) {
logger.error("Error occurred during operation {}",throwable.getMessage())
}
}