@Scheduled에서 외부 서비스에 데이터를 처리요청할때 데이터를 중복으로 처리하는 문제가 발생할수 있다.
Scheduled는 매번 실행할때마다 빠른 처리를 위해 fixedDelayString=”1000″ 으로 설정하였고, 한번에 실행되는 thread의 수는 5개로 제한하였다. 또한 Scheduled 한번실행에 처리되지 않은 데이터 1000건을 외부 API를통해 가져와 처리하도록 설계하였다.
이때 발생할수 있는 문제는 1000개의 데이터를 처리(Thread가 종료) 하기전에 Scheduled가 실행되면서 1000개의 데이터를 외부API에 다시 요청하게 되고 1000개의 데이터에는 첫번째 가져온 아직 처리되지 않은 데이터가 포함되어 중복 처리되면서 오류가 발생한다.
한번의 Scheduled의 실행으로 1000개의 데이터를 5개의 Thread가 모두 처리(종료)될때까지 대시 시켜 해당 문제를 해결할수 있는데 방법은 아래와 같다.
// Thread로 실행할 Class
public class Job implements Callable<Void> {
@Override
public Void call() throws Exception {
// 외부 API 호출
// 외부 API를 사용한 데이터 처리요청
return null;
}
}
// Scheduled Class
public class MyScheduler {
@Scheduled(fixedDelay = 1000)
public void executeJob() {
// Thread 개수
int threadSize = 5;
// 외부에서 처리 데이터 가져오기
int totalTasks = 1000;
ExecutorService executor = Executors.newFixedThreadPool(threadSize);
List<Callable<Void>> tasks = new ArrayList<>();
// 모든 작업이 완료될때까지 대기
for (int i=0; i < totalTasks; i++) {
Callable<Void> task = new Job();
tasks.add(task);
}
try {
List<Future<Void>> results = executor.invokeAll(tasks);
for (Future<Void> result : results) {
// 작업 결과 처리
result.get();
}
} catch (InterruptedException | ExcutionException e) {
// 예외처리
} finally {
// ExecutorService 종료
executor.shutdown();
}
}
}
위 코드처럼 ExecutorService를 사용하여 Callable에 작업요청하고 결과를 기다린다.
invokeAll을 사용하여 작업이 완료될때까지 대기하고, 작업결과를 얻어 처리할수 있다. 이렇게 하면 모든 스레드가 종료될때까지 대기한 다음 스케줄링을 실행할수 있다.
invokeAll(Collection
여러개의 Callable 타입 작업을 동시에 스레드풀에서 실행하고, 모든 작업이 완료될때까지 기다린다.
invokeAny(Collection
여러개의 Callable 타입 작업중 하나라도 완료되면 결과를 반환하고, 다른작업들은 취소한다.
shutdown()
스레드 풀을 종료하고 스레드 풀에 할당된 리소스를 해제한다. 작업이 완료되지 않은 경우에도 스레드 풀을 종료한다.
shutdownNow()
스레드 풀을 즉시 종료하고 아직 시작되지 않은 작업을 취소한다. 작업이 이미 실행중인경우에는 종료되지 않을수 있다.