SpringBoot下實現(xiàn)http請求的長輪詢—AsyncContext【推拉結合的配置更新】 在這篇文章中容燕,可以使用AsyncContext實現(xiàn)異步長輪詢衷模。其實SpringMVC的攔截機制也可以實現(xiàn)這種操作。
1. 場景
客戶端調用服務端接口只嚣,服務端這個接口比較耗時。為了優(yōu)化服務端的性能顿膨。
服務端收到servlet請求后钞脂,釋放掉servlet占用的線程資源。開啟一個異步線程去處理耗時的操作梳码。當耗時操作處理完成后,將結果返回給客戶端伍掀。
注意:在此期間掰茶,客戶端和服務端的http鏈接并不會斷開,客戶端依舊苦苦等待響應數(shù)據(jù)蜜笤;
2. 實現(xiàn)
可以使用接口AsyncHandlerInterceptor實現(xiàn)來攔截涉及異步處理的請求濒蒋,而不是使用HandlerInterceptor。
HandlerInterceptorAdapter適配器把兔,適配了AsyncHandlerInterceptor和HandlerInterceptor沪伙,推薦使用這個來實現(xiàn)。
void afterConcurrentHandlingStarted(HttpServletRequest request,
HttpServletResponse response,
Object handler)
throws Exception
2.1 代碼實現(xiàn)
2.1.1 實現(xiàn)異步線程池
上文說到县好,釋放Servlet線程围橡,交由指定的線程池去處理,那么如何去定義指定的線程池缕贡?
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
@Autowired
private MyAsyncHandlerInterceptor myAsyncHandlerInterceptor;
//配置攔截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
//測試異步攔截器
registry.addInterceptor(myAsyncHandlerInterceptor).addPathPatterns("/**");
}
/**
* An Executor is required to handle java.util.concurrent.Callable return values.
* Please, configure a TaskExecutor in the MVC config under "async support".
* The SimpleAsyncTaskExecutor currently in use is not suitable under load.
* <p>
* 配置SpringMVC的支持
*/
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
//核心線程數(shù)
threadPoolTaskExecutor.setCorePoolSize(5);
threadPoolTaskExecutor.setAllowCoreThreadTimeOut(true);
//最大線程數(shù)
threadPoolTaskExecutor.setMaxPoolSize(5);
//配置隊列大小
threadPoolTaskExecutor.setQueueCapacity(50);
//配置線程池前綴
threadPoolTaskExecutor.setThreadNamePrefix("async-service-");
threadPoolTaskExecutor.initialize();
configurer.setTaskExecutor(threadPoolTaskExecutor);
}
}
2.1.2 實現(xiàn)攔截器
@Slf4j
@Service
public class MyAsyncHandlerInterceptor extends HandlerInterceptorAdapter {
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
log.info("interceptor#preHandle called.");
return true;
}
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
log.info("interceptor#postHandle called. ");
}
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler, Exception ex) throws Exception {
log.info("interceptor#afterCompletion called.");
}
/**
* 這個方法執(zhí)行后翁授,會執(zhí)行Controller方法返回的callable方法。
* 這個方法的目的時善绎,當servlet線程被釋放后黔漂,執(zhí)行清除例如ThreadLocal诫尽、MDC等資源的操作禀酱。
*/
public void afterConcurrentHandlingStarted(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
log.info("interceptor#afterConcurrentHandlingStarted. ");
}
}
2.1.3 Controller代碼
注意:方法返回的是Callable。
@Slf4j
@RestController
public class FirstController {
@RequestMapping(value = "/t2")
public Callable<String> t2() {
log.info("controller#handler called. Thread: " +
Thread.currentThread()
.getName());
Callable<String> callable = new Callable<String>() {
public String call() throws Exception {
log.info("controller-callable#async task started. Thread: " +
Thread.currentThread()
.getName());
Thread.sleep(300);
log.info("controller-callable#async task finished");
return "async result";
}
};
log.info("controller#handler finished");
return callable;
}
}
3. 流程
3.1 流程圖
流程圖.png
執(zhí)行效果如下圖所示:
執(zhí)行結果.png