spring boot async exception handling

공부는 혼자하는 거·2021년 10월 5일
1

Spring Tip

목록 보기
17/52

비동기 메서드 호출시 예외를 던졌는데 @ControllerAdvice에서 catch 하지 못했다. 찾아보니.. 오직 동기적 예외만 잡을 수 있단다..

https://stackoverflow.com/questions/61885358/async-will-not-call-by-controlleradvice-for-global-exception

https://stackoverflow.com/questions/44138199/spring-exceptionhandler-and-multi-threading

https://www.baeldung.com/spring-async

비동기 예외처리는 다른 전용 인터페이스가 있단다..

반환 유형이 void 이면 예외가 호출 스레드로 전파되지 않습니다 .

public class CustomAsyncExceptionHandler
  implements AsyncUncaughtExceptionHandler {

    @Override
    public void handleUncaughtException(
      Throwable throwable, Method method, Object... obj) {
 
        System.out.println("Exception message - " + throwable.getMessage());
        System.out.println("Method name - " + method.getName());
        for (Object param : obj) {
            System.out.println("Parameter value - " + param);
        }
    }
    
}
@EnableAsync
@Configuration
public class AsyncConfig extends AsyncConfigurerSupport {

    @Override
    public Executor getAsyncExecutor() {

        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setThreadNamePrefix("kang-");
        executor.initialize();

        return executor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new CustomAsyncExceptionHandler();

    }

    @Bean
    public ThreadPoolTaskScheduler configureTasks() {
        ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();

        threadPoolTaskScheduler.setPoolSize(10);
        threadPoolTaskScheduler.setThreadNamePrefix("kang-scheduled-");
        threadPoolTaskScheduler.initialize();

        return threadPoolTaskScheduler;
    }

    
}
 @Async// 분석요청  aysnc 
    public void mockInsUpload() throws Exception {

        log.info("#### mockCrescomAiService.insUpload() ");

        String apiUrl = "http://34.64.157.45:80/ins_upload";
        Map<String, Object> resultMap = new HashMap<>();

        FileInputStream fis = new FileInputStream("src/main/resources/img/testImg.txt");
        String data = IOUtils.toString(fis, "UTF-8");

        log.info("읽어들인 파일: " + data);

        Map<String, String> header = new HashMap<String, String>();
//		header.put("Content-Type", "multipart/form-data");
        header.put("Content-Type", "application/x-www-form-urlencoded");


        Map<String, Object> params = new HashMap<String, Object>();

        //params.put("id", "AIBAA");                                              // oscID
        params.put("fix_id", "korea_uni_hos");                                          // 고객ID

        HttpResponse<JsonNode> response = Unirest.post(apiUrl)
                .headers(header)
                .queryString(params)
                .asJson();

//		Future<HttpResponse<JsonNode>> future = Unirest.post(apiUrl)
//				.headers(header)
//				.queryString(params)
//				.asJsonAsync(new Callback<JsonNode>() {
//								 @Override
//								 public void completed(HttpResponse<JsonNode> response) {
//									 System.out.println(response.getBody().getObject());
//									 glovar.synchronizedList.remove(glovar.synchronizedList.size()-1);
//									 //glovar.target = glovar.synchronizedList.size();
//									 System.out.println("토탈개수: " + glovar.target);
//									 System.out.println("완료되면 제거: " + glovar.synchronizedList.toString() + "  " + (glovar.target - glovar.synchronizedList.size()));
//
//
//								 }
//
//								 @Override
//								 public void failed(UnirestException e) {
//									 System.out.println("data 통신 실패");
//								 }
//
//								 @Override
//								 public void cancelled() {
//									 System.out.println("data 통신 취소");
//								 }
//
//							 });

        JSONObject responseJson = response.getBody().getObject();
        log.info("## CrescomAiService.mockInsUpload() responseJson={}", responseJson);
//
//		String jsonString = responseJson.toString();
//		Map<String, Object> map = new ObjectMapper().readValue(jsonString, new TypeReference<Map<String, Object>>(){});
//		log.info("## CrescomAiService.mockinsUpload() map={}", map);
//
//		resultMap.put("result", "성공"); //원래는 무슨 응답을 보냈는지 알 수 가 없는데
//		resultMap.put("code", "0000");

        if (responseJson.has("error")) {
            // 중복 로그인 방지
            throw new CallApiException("API call Exception");
        } 

        //return CompletableFuture.completedFuture(resultMap);
    }

아래 방법도 있는듯..

메소드 리턴 유형이 Future 인 경우 예외 처리가 용이합니다. Future.get () 메소드는 예외를 처리 합니다.

https://stackoverflow.com/questions/54847338/get-exception-thrown-within-annotated-async-method-in-restcontrolleradvice

@Controller
public class ApiController {
    private final Service service;
    public ApiController(Service service) {
        this.service = service;
    }
    @GetMapping( "/activate")
    public CompletableFuture<Void> activate(...){
        return service.activateUser(...)
               .exceptionally(throwable -> ... exception handling goes here ...)
    }
}

@Service
public class Service {
    @Async
    public CompletableFuture<Void> activateUser(...) {
        CompletableFuture<Void> future = new CompletableFuture<>();
        ... your code goes here ...
        if(someCondition){
           future.completeExceptionally(new GeneralSecurityException());
        } else {
           future.complete(null);
        }
        return future;
    }
}
profile
시간대비효율

0개의 댓글