【问题标题】:how to Throw an exception from an @Async annotated method如何从 @Async 带注释的方法中抛出异常
【发布时间】:2019-04-05 14:13:09
【问题描述】:

我有两个模块,一个从休息模板调用另一个。 (管理员调用notifServer) notifServer 有一个用 @Async 注释的方法。我想在那个方法中抛出一个异常,但是管理员得到响应太快了,并且无法在管理员处捕获异常方法。

我是 spring 和 @Async 流程的新手。我尝试将响应正文从 NotifServer 映射到 CCompletableFuture.class 。 但我仍然没有收到错误响应。

此代码来自 admin

ResponseEntity response = fcmRestTemplate.exchange(nsUrl + "/fcm/admin/" + bulkFcmId, HttpMethod.POST,
                    HttpEntityUtils.getHttpEntity(moduleCode), CompletableFuture.class);
            if (response.getStatusCode() != HttpStatus.CREATED && response.getStatusCode() != HttpStatus.ACCEPTED) {
                String errorMessage = ErrorResourceUtil.getErrorMessage((HashMap) response.getBody(),"Unable to send fcm");
                setStatusToFailedByBulkFcmId(bulkFcmId);
                throw new ClientException(errorMessage);
            }

现在这是来自 NotifServer

JobExecution jobExecution = jobLauncher
                    .run(importJob, new JobParametersBuilder()
                    .addString("fullPathFileName", TMP_DIR)
                    .addString("batch_fcm_id", String.valueOf(id))
                    .addLong("time",System.currentTimeMillis())
                    .toJobParameters());
            if(jobExecution.getStepExecutions().stream().map(StepExecution::getStatus).findFirst().get().equals(BatchStatus.ABANDONED)){
             throw new ClientException("INVALID CSV");

这是用@Async 注释的。

那么有没有办法让我在 Admin 的响应正文中捕获客户端异常?

编辑 这是来自 notifServer 的 API

@ResponseStatus(HttpStatus.CREATED)
    @PostMapping(value = "/admin/{bulkFcmId}")
    public void pushFCMByAdmin(@PathVariable Long bulkFcmId) {
        fcmService.sendFcmByAdmin(bulkFcmId, AuthUtil.getCurrentUser());
    }

那么 sendFcmByAdmin 就有了@Async 注解。

【问题讨论】:

  • 我猜 NotifServer 上的 Rest API 不会等待异步调用完成并立即返回。你能用来自 NotifServer 的完整 API 代码更新帖子吗?
  • 我用notifServer的控制器进行了编辑,sendFcmByAdmin是一个调用批处理作业和pushFcm方法的void方法。
  • 和 sendFcmByAdmin 有 @Async?
  • 是的,先生!该方法有@Async

标签: spring-boot asynchronous spring-async


【解决方案1】:

在下面的代码中,您是否可以将返回类型提供为业务对象而不是 CompletableFuture.class。由于您将 CompletableFuture.class 作为参数传递给交换它期望响应返回值 CompletableFuture.class 类型的 em>。

ResponseEntity response = fcmRestTemplate.exchange(nsUrl + "/fcm/admin/" + bulkFcmId, HttpMethod.POST,
                        HttpEntityUtils.getHttpEntity(moduleCode), CompletableFuture.class);
                if (response.getStatusCode() != HttpStatus.CREATED && response.getStatusCode() != HttpStatus.ACCEPTED) {
                    String errorMessage = ErrorResourceUtil.getErrorMessage((HashMap) response.getBody(),"Unable to send fcm");
                    setStatusToFailedByBulkFcmId(bulkFcmId);
                    throw new ClientException(errorMessage);
                }

您可以尝试按如下方式创建它,而不是传递 Completable Future :

使用异步方法在admin中调用rest模板:

@Async
public CompletableFuture<List<BusinessObject>> getResponseAsynchronously(String value) {
    String url = "https://restendpoint.eu/rest/v2/lang/" + value + "?fields=name";
    BusinessObject[] response = restTemplate.getForObject(url, Country[].class);
    return CompletableFuture.completedFuture(Arrays.asList(response));
}

然后在控制器中读取 CompletableFuture 之类的:

@GetMapping("")
public List<String> getAllDataFromRestCall() throws Throwable {

    CompletableFuture<List<BusinessObject>> businessObjectsFuture = countryClient.getResponseAsynchronously("fr");
    List<String> europeanFrenchSpeakingCountries;

            try {
                europeanFrenchSpeakingCountries = new ArrayList<>(businessObjectsFuture
                .get()
                .stream()
                .map(Country::getName)
                .collect(Collectors.toList()));

     } catch (Throwable e) {
       throw e.getCause();
     }

   return europeanFrenchSpeakingCountries;
}

【讨论】:

  • 我试过了,但它基本上是同步的,那么使用@Async有什么意义
  • 能否提供更多详细信息,例如stacktrace,以便我们进一步分析问题。因此,即使在使用可完成的未来调用方法之后,请求也会同步触发?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-07-04
  • 2012-08-05
  • 1970-01-01
  • 2021-09-27
  • 2014-05-29
  • 2022-01-20
  • 1970-01-01
相关资源
最近更新 更多