要求
我需要能够通过POST调用触发(长期运行)作业并立即返回。
一次只有一个线程可以运行该作业。
这项工作是一项昂贵的工作,如果一项工作已经在进行中,我希望该工作的所有将来触发因素不执行任何操作。
代码
@RestController
public class SomeTask {
private SomeService someService;
@Autowired
public SomeTask(SomeService someService) {
this.someService = someService;
}
@Async // requirement 1
@RequestMapping(method = RequestMethod.POST, path = "/triggerJob")
public void triggerJob() {
expensiveLongRunningJob();
}
/**
* Synchronized in order to restrict multiple invocations. // requirement 2
*
*/
private synchronized void expensiveLongRunningJob() {
someService.executedJob();
}
}
问题
满足上述代码要求1和2。满足要求3的最佳方法是什么(已通过POST调用创建了新线程,跳过了同步方法并在获取锁失败后立即返回)?
答案 0 :(得分:1)
对于要求1 ,如果您只想使用@Async
,则应将其用于服务方法而非控制器方法。但是请注意,通过使其异步,您将失去对作业的控制,并且无法进行故障处理,除非您使用@Async
实现Future
并通过实现AsyncUncaughtExceptionHandler
接口来处理故障。
对于需求3 ,您可以在服务中包含一个易失的布尔字段,该字段在开始作业过程之前就已设置,而在作业过程完成后才被取消。在控制器方法中,您可以检查服务的易失布尔值字段来确定作业是否正在执行,如果作业正在进行,则仅返回适当的消息。另外,在处理AsyncUncaughtExceptionHandler
接口实现失败时,请确保取消设置布尔字段。
服务:
@Service
public class SomeService {
public volatile boolean isJobInProgress = false;
@Async
public Future<String> executeJob() {
isJobInProgress = true;
//Job processing logic
isJobInProgress = false;
}
}
控制器:
@RestController
public class SomeTask {
@Autowired
private SomeService someService;
@RequestMapping(method = RequestMethod.POST, path = "/triggerJob")
public void triggerJob() {
if (!someService.isJobInProgress){
someService.executeJob(); //can have this in a sync block to be on the safer side.
} else {
return;
}
}
}
AsyncUncaughtExceptionHandler的实现:
public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
@Autowired
private SomeService someService;
@Override
public void handleUncaughtException(
Throwable throwable, Method method, Object... obj) {
//Handle failure
if (someService.isJobInProgress){
someService.isJobInProgress = false;
}
}
}
@异步配置:
@Configuration
@EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
return new ThreadPoolTaskExecutor();
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new CustomAsyncExceptionHandler();
}
}
答案 1 :(得分:1)
同步不是完成任务的正确工具。您可以这样做:
@RestController
public class SomeTask {
private SomeService someService;
private final AtomicBoolean isTriggered = new AtomicBoolean();
@Autowired
public SomeTask(SomeService someService) {
this.someService = someService;
}
@Async // requirement 1
@RequestMapping(method = RequestMethod.POST, path = "/triggerJob")
public void triggerJob() {
if (!isTriggered.getAndSet(true)) {
try {
expensiveLongRunningJob();
} finally {
isTriggered.set(false);
}
}
}
/**
* only runs once at a time, in the thread that sets isTriggered to true
*/
private void expensiveLongRunningJob() {
someService.executedJob();
}
}