原文連接:www.cnblogs.com/baixianlong…html
特色:nginx
能夠先釋放容器分配給請求的線程與相關資源,減輕系統負擔,釋放了容器所分配線程的請求,其響應將被延後,能夠在耗時處理完成(例如長時間的運算)時再對客戶端進行響應。程序員
@RequestMapping(value = "/email/servletReq", method = GET)
public void servletReq (HttpServletRequest request, HttpServletResponse response) {
AsyncContext asyncContext = request.startAsync();
//設置監聽器:可設置其開始、完成、異常、超時等事件的回調處理
asyncContext.addListener(new AsyncListener() {
@Override
public void onTimeout(AsyncEvent event) throws IOException {
System.out.println("超時了...");
//作一些超時後的相關操做...
}
@Override
public void onStartAsync(AsyncEvent event) throws IOException {
System.out.println("線程開始");
}
@Override
public void onError(AsyncEvent event) throws IOException {
System.out.println("發生錯誤:"+event.getThrowable());
}
@Override
public void onComplete(AsyncEvent event) throws IOException {
System.out.println("執行完成");
//這裏能夠作一些清理資源的操做...
}
});
//設置超時時間
asyncContext.setTimeout(20000);
asyncContext.start(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(10000);
System.out.println("內部線程:" + Thread.currentThread().getName());
asyncContext.getResponse().setCharacterEncoding("utf-8");
asyncContext.getResponse().setContentType("text/html;charset=UTF-8");
asyncContext.getResponse().getWriter().println("這是異步的請求返回");
} catch (Exception e) {
System.out.println("異常:"+e);
}
//異步請求完成通知
//此時整個請求才完成
asyncContext.complete();
}
});
//此時之類 request的線程鏈接已經釋放了
System.out.println("主線程:" + Thread.currentThread().getName());
}複製代碼
@RequestMapping(value = "/email/callableReq", method = GET)
@ResponseBody
public Callable<String> callableReq () {
System.out.println("外部線程:" + Thread.currentThread().getName());
return new Callable<String>() {
@Override
public String call() throws Exception {
Thread.sleep(10000);
System.out.println("內部線程:" + Thread.currentThread().getName());
return "callable!";
}
};
}
@Configuration
public class RequestAsyncPoolConfig extends WebMvcConfigurerAdapter {
@Resource
private ThreadPoolTaskExecutor myThreadPoolTaskExecutor;
@Override
public void configureAsyncSupport(final AsyncSupportConfigurer configurer) {
//處理 callable超時
configurer.setDefaultTimeout(60*1000);
configurer.setTaskExecutor(myThreadPoolTaskExecutor);
configurer.registerCallableInterceptors(timeoutCallableProcessingInterceptor());
}
@Bean
public TimeoutCallableProcessingInterceptor timeoutCallableProcessingInterceptor() {
return new TimeoutCallableProcessingInterceptor();
}複製代碼
}web
@RequestMapping(value = "/email/webAsyncReq", method = GET)
@ResponseBody
public WebAsyncTask<String> webAsyncReq () {
System.out.println("外部線程:" + Thread.currentThread().getName());
Callable<String> result = () -> {
System.out.println("內部線程開始:" + Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(4);
} catch (Exception e) {
// TODO: handle exception
}
logger.info("副線程返回");
System.out.println("內部線程返回:" + Thread.currentThread().getName());
return "success";
};
WebAsyncTask<String> wat = new WebAsyncTask<String>(3000L, result);
wat.onTimeout(new Callable<String>() {
@Override
public String call() throws Exception {
// TODO Auto-generated method stub
return "超時";
}
});
return wat;
}複製代碼
@RequestMapping(value = "/email/deferredResultReq", method = GET)
@ResponseBody
public DeferredResult<String> deferredResultReq () {
System.out.println("外部線程:" + Thread.currentThread().getName());
//設置超時時間
DeferredResult<String> result = new DeferredResult<String>(60*1000L);
//處理超時事件 採用委託機制
result.onTimeout(new Runnable() {
@Override
public void run() {
System.out.println("DeferredResult超時");
result.setResult("超時了!");
}
});
result.onCompletion(new Runnable() {
@Override
public void run() {
//完成後
System.out.println("調用完成");
}
});
myThreadPoolTaskExecutor.execute(new Runnable() {
@Override
public void run() {
//處理業務邏輯
System.out.println("內部線程:" + Thread.currentThread().getName());
//返回結果
result.setResult("DeferredResult!!");
}
});
return result;
}複製代碼
異步請求的處理。除了異步請求,通常上咱們用的比較多的應該是異步調用。一般在開發過程當中,會遇到一個方法是和實際業務無關的,沒有緊密性的。好比記錄日誌信息等業務。這個時候正常就是啓一個新線程去作一些業務處理,讓主線程異步的執行其餘業務。spring
代碼略。。。就倆標籤,本身試一把就能夠了bash
調用的異步方法,不能爲同一個類的方法(包括同一個類的內部類),簡單來講,由於Spring在啓動掃描時會爲其建立一個代理類,而同類調用時,仍是調用自己的代理類的,因此和日常調用是同樣的。其餘的註解如@Cache等也是同樣的道理,說白了,就是Spring的代理機制形成的。因此在開發中,最好把異步服務單獨抽出一個類來管理。下面會重點講述。。服務器
其實咱們的注入對象都是從Spring容器中給當前Spring組件進行成員變量的賦值,因爲某些類使用了AOP註解,那麼實際上在Spring容器中實際存在的是它的代理對象。那麼咱們就能夠微信
@Controller
@RequestMapping("/app")
public class EmailController {
//獲取ApplicationContext對象方式有多種,這種最簡單,其它的你們自行了解一下
@Autowired
private ApplicationContext applicationContext;
@RequestMapping(value = "/email/asyncCall", method = GET)
@ResponseBody
public Map<String, Object> asyncCall () {
Map<String, Object> resMap = new HashMap<String, Object>();
try{
//這樣調用同類下的異步方法是不起做用的
//this.testAsyncTask();
//經過上下文獲取本身的代理對象調用異步方法
EmailController emailController = (EmailController)applicationContext.getBean(EmailController.class);
emailController.testAsyncTask();
resMap.put("code",200);
}catch (Exception e) {
resMap.put("code",400);
logger.error("error!",e);
}
return resMap;
}
//注意必定是public,且是非static方法
@Async
public void testAsyncTask() throws InterruptedException {
Thread.sleep(10000);
System.out.println("異步任務執行完成!");
}
}複製代碼
代碼實現,以下:併發
@Service
@Transactional(value = "transactionManager", readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = Throwable.class)
public class EmailService {
@Autowired
private ApplicationContext applicationContext;
@Async
public void testSyncTask() throws InterruptedException {
Thread.sleep(10000);
System.out.println("異步任務執行完成!");
}
public void asyncCallTwo() throws InterruptedException {
//this.testSyncTask();
// EmailService emailService = (EmailService)applicationContext.getBean(EmailService.class);
// emailService.testSyncTask();
boolean isAop = AopUtils.isAopProxy(EmailController.class);//是不是代理對象;
boolean isCglib = AopUtils.isCglibProxy(EmailController.class); //是不是CGLIB方式的代理對象;
boolean isJdk = AopUtils.isJdkDynamicProxy(EmailController.class); //是不是JDK動態代理方式的代理對象;
//如下才是重點!!!
EmailService emailService = (EmailService)applicationContext.getBean(EmailService.class);
EmailService proxy = (EmailService) AopContext.currentProxy();
System.out.println(emailService == proxy ? true : false);
proxy.testSyncTask();
System.out.println("end!!!");
}
}複製代碼
更多技術文章請關注微信公衆號:Java程序員彙集地app