Java 在MvcAsyncTask中停止可调用
我有一个带有WebAsyncTask的控制器。更进一步,我使用了一个超时回调。 如前所述,我可以选择通知可调用方取消处理。然而,我看不到任何这样做的选择Java 在MvcAsyncTask中停止可调用,java,spring,spring-mvc,timeout,asynccallback,Java,Spring,Spring Mvc,Timeout,Asynccallback,我有一个带有WebAsyncTask的控制器。更进一步,我使用了一个超时回调。 如前所述,我可以选择通知可调用方取消处理。然而,我看不到任何这样做的选择 @Controller public class UserDataProviderController { private static final Logger log = LoggerFactory.getLogger(UserDataProviderController.class.getName()); @Autow
@Controller
public class UserDataProviderController {
private static final Logger log = LoggerFactory.getLogger(UserDataProviderController.class.getName());
@Autowired
private Collection<UserDataService> dataServices;
@RequestMapping(value = "/client/{socialSecurityNumber}", method = RequestMethod.GET)
public @ResponseBody
WebAsyncTask<ResponseEntity<CustomDataResponse>> process(@PathVariable final String socialSecurityNumber) {
final Callable<ResponseEntity<CustomDataResponse>> callable = new Callable<ResponseEntity<CustomDataResponse>>() {
@Override
public ResponseEntity<CustomDataResponse> call() throws Exception {
CustomDataResponse CustomDataResponse = CustomDataResponse.newInstance();
// Find user data
for(UserDataService dataService:dataServices)
{
List<? extends DataClient> clients = dataService.findBySsn(socialSecurityNumber);
CustomDataResponse.put(dataService.getDataSource(), UserDataConverter.convert(clients));
}
// test long execution
Thread.sleep(4000);
log.info("Execution thread continued and shall be terminated:"+Thread.currentThread().getName());
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.setContentType(new MediaType("application", "json", Charset.forName("UTF-8")));
return new ResponseEntity(CustomDataResponse,responseHeaders,HttpStatus.OK);
}
};
final Callable<ResponseEntity<CustomDataResponse>> callableTimeout = new Callable<ResponseEntity<CustomDataResponse>>() {
@Override
public ResponseEntity<CustomDataResponse> call() throws Exception {
// Error response
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.setContentType(new MediaType("application", "json", Charset.forName("UTF-8")));
return new ResponseEntity("Request has timed out!",responseHeaders,HttpStatus.INTERNAL_SERVER_ERROR);
}
};
WebAsyncTask<ResponseEntity<CustomDataResponse>> task = new WebAsyncTask<>(3000,callable);
task.onTimeout(callableTimeout);
return task;
}
}
非常标准的拦截器:
public class TimeoutCallableProcessingInterceptor extends CallableProcessingInterceptorAdapter {
@Override
public <T> Object handleTimeout(NativeWebRequest request, Callable<T> task) {
throw new IllegalStateException("[" + task.getClass().getName() + "] timed out");
}
}
公共类TimeoutCallableProcessingInterceptor扩展CallableProcessingInterceptorAdapter{
@凌驾
公共对象handleTimeout(NativeWebRequest请求,可调用任务){
抛出新的IllegalStateException(“[”+task.getClass().getName()+“]超时”);
}
}
一切正常,但从控制器调用总是完成的,这是显而易见的,但如何停止那里的处理?您可以使用
WebAsyncTask
实现超时控制和Thread
管理来优雅地停止新的异步线程
可调用的
来运行流程线程存储在控制器的局部变量中
Callable
来处理超时事件线程
,并调用中断()
方法中断它TimeoutException
以停止控制器进程thread.currentThread().isInterrupted()
中断,如果是,则回滚引发异常的事务public WebAsyncTask<ResponseEntity<BookingFileDTO>> confirm(@RequestBody final BookingConfirmationRQDTO bookingConfirmationRQDTO)
throws AppException,
ProductException,
ConfirmationException,
BeanValidationException {
final Long startTimestamp = System.currentTimeMillis();
// The compiler obligates to define the local variable shared with the callable as final array
final Thread[] asyncTaskThread = new Thread[1];
/**
* Asynchronous execution of the service's task
* Implemented without ThreadPool, we're using Tomcat's ThreadPool
* To implement an specific ThreadPool take a look at http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#mvc-ann-async-configuration-spring-mvc
*/
Callable<ResponseEntity<BookingFileDTO>> callableTask = () -> {
//Stores the thread of the newly started asynchronous task
asyncTaskThread[0] = Thread.currentThread();
log.debug("Running saveBookingFile task at `{}`thread", asyncTaskThread[0].getName());
BookingFileDTO bookingFileDTO = bookingFileService.saveBookingFile(
bookingConfirmationRQDTO,
MDC.get(HttpHeader.XB3_TRACE_ID))
.getValue();
if (log.isDebugEnabled()) {
log.debug("The saveBookingFile task took {} ms",
System.currentTimeMillis() - startTimestamp);
}
return new ResponseEntity<>(bookingFileDTO, HttpStatus.OK);
};
/**
* This method is executed if a timeout occurs
*/
Callable<ResponseEntity<BookingFileDTO>> callableTimeout = () -> {
String msg = String.format("Timeout detected at %d ms during confirm operation",
System.currentTimeMillis() - startTimestamp);
log.error("Timeout detected at {} ms during confirm operation: informing BookingFileService.", msg);
// Informs the service that the time has ran out
asyncTaskThread[0].interrupt();
// Interrupts the controller call
throw new TimeoutException(msg);
};
WebAsyncTask<ResponseEntity<BookingFileDTO>> webAsyncTask = new WebAsyncTask<>(timeoutMillis, callableTask);
webAsyncTask.onTimeout(callableTimeout);
log.debug("Timeout set to {} ms", timeoutMillis);
return webAsyncTask;
}
public-WebAsyncTask-confirm(@RequestBody-final-BookingConfirmationRQDTO-BookingConfirmationRQDTO)
抛开欲望,
产品例外,
确认例外,
BeanValidationException{
最终长startTimestamp=System.currentTimeMillis();
//编译器有义务将与可调用对象共享的局部变量定义为最终数组
最终线程[]asyncTaskThread=新线程[1];
/**
*异步执行服务的任务
*在没有线程池的情况下实现,我们使用的是Tomcat的线程池
*要实现特定的线程池,请查看http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#mvc-ann异步配置springmvc
*/
可调用可调用任务=()->{
//存储新启动的异步任务的线程
asyncTaskThread[0]=Thread.currentThread();
debug(“在`{}`线程上运行saveBookingFile任务”,asyncTaskThread[0].getName());
BookingFileDTO BookingFileDTO=bookingFileService.saveBookingFile(
预订确认书RQDTO,
MDC.get(HttpHeader.XB3_TRACE_ID))
.getValue();
if(log.isDebugEnabled()){
debug(“saveBookingFile任务花费了{}毫秒”,
System.currentTimeMillis()-startTimestamp);
}
返回新的ResponseEntity(bookingFileDTO,HttpStatus.OK);
};
/**
*如果发生超时,则执行此方法
*/
可调用callableTimeout=()->{
String msg=String.format(“确认操作期间在%d毫秒检测到超时”,
System.currentTimeMillis()-startTimestamp);
错误(“确认操作期间在{}毫秒检测到超时:通知BookingFileService。”,msg);
//通知服务时间已过
asyncTaskThread[0]。中断();
//中断控制器调用
抛出新的TimeoutException(msg);
};
WebAsyncTask WebAsyncTask=新的WebAsyncTask(timeoutMillis,callableTask);
webAsyncTask.onTimeout(callableTimeout);
debug(“超时设置为{}ms”,timeoutMillis);
返回webAsyncTask;
}
服务实施:
/**
* If the service has been informed that the time has ran out
* throws an AsyncRequestTimeoutException to roll-back transactions
*/
private void rollbackOnTimeout() throws TimeoutException {
if(Thread.currentThread().isInterrupted()) {
log.error(TIMEOUT_DETECTED_MSG);
throw new TimeoutException(TIMEOUT_DETECTED_MSG);
}
}
@Transactional(rollbackFor = TimeoutException.class, propagation = Propagation.REQUIRES_NEW)
DTOSimpleWrapper<BookingFileDTO> saveBookingFile(BookingConfirmationRQDTO bookingConfirmationRQDTO, String traceId) {
// Database operations
// ...
return retValue;
}
/**
*如果通知服务时间已过
*抛出AsyncRequestTimeoutException以回滚事务
*/
private void rollbackOnTimeout()引发TimeoutException{
如果(Thread.currentThread().isInterrupted()){
日志错误(检测到超时消息);
抛出新的TimeoutException(检测到超时消息);
}
}
@事务性(rollbackFor=TimeoutException.class,传播=propagation.REQUIRES\u NEW)
DTOSimpleWrapper saveBookingFile(BookingConfirmationRQDTO BookingConfirmationRQDTO,字符串traceId){
//数据库操作
// ...
返回值;
}
您的意思是需要4秒的可调用进程在3秒后没有停止?是的。基本上,我不希望在睡眠后执行任何代码。可能会重复-@Premek就是这样。您必须手动处理中断,即从数据服务返回结果的可调用程序应检查是否收到中断信号,并在您的情况下,通过发送另一个响应对其采取行动。@Magnamag您的意思是类似于调用:Thread.currentThread().interrupt()代码>在CallableTimeout中并在控制器中检查它,有些人认为像'if(Thread.currentThread().isInterrupted()){'我不确定这是正确的方法。。。
/**
* If the service has been informed that the time has ran out
* throws an AsyncRequestTimeoutException to roll-back transactions
*/
private void rollbackOnTimeout() throws TimeoutException {
if(Thread.currentThread().isInterrupted()) {
log.error(TIMEOUT_DETECTED_MSG);
throw new TimeoutException(TIMEOUT_DETECTED_MSG);
}
}
@Transactional(rollbackFor = TimeoutException.class, propagation = Propagation.REQUIRES_NEW)
DTOSimpleWrapper<BookingFileDTO> saveBookingFile(BookingConfirmationRQDTO bookingConfirmationRQDTO, String traceId) {
// Database operations
// ...
return retValue;
}