首先要明确一点,同步请求和异步请求对于客户端用户来讲是一样的,都是需客户端等待返回结果。不同之处在于请求到达服务器之后的处理方式,下面用两张图解释一下同步请求和异步请求在服务端处理方式的不同:
两个流程中客户端对Web容器的请求,都是同步的。因为它们在请求客户端时都处于阻塞等待状态,并没有进行异步处理。在Web容器部分,第一个流程采用同步请求,第二个流程采用异步回调的形式。通过异步处理,可以先释放容器分配给请求的线程与相关资源,减轻系统负担,从而增加了服务器对客户端请求的吞吐量。但并发请求量较大时,通常会通过负载均衡的方案来解决,而不是异步。
使用AsyncContext执行异步请求
package com.example.async;
import java.io.IOException;
import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AsyncContextController {
@GetMapping("/asyncContext")
@ResponseBody
public String asyncTask(HttpServletRequest request) {
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().getMessage());
}
@Override
public void onComplete(AsyncEvent event) throws IOException {
System.out.println("执行完成,释放资源");
}
});
asyncContext.setTimeout(6000);
asyncContext.start(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000);
System.out.println("内部线程:" + Thread.currentThread().getName());
asyncContext.getResponse().getWriter().println("async processing");
} catch (Exception e) {
System.out.println("异步处理发生异常:" + e.getMessage());
}
asyncContext.complete(); // 异步请求完成通知,整个请求完成
}
});
System.out.println("主线程:" + Thread.currentThread().getName());
return "OK";
}
}
使用Callable执行异步请求
package com.example.async;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CallableController {
@GetMapping(path = "/callable")
@ResponseBody
public Callable<String> asyncRequest() {
return () -> {
TimeUnit.SECONDS.sleep(10);
return "OK";
};
}
}
使用WebAsyncTask执行异步请求
package com.example.async;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.WebAsyncTask;
@RestController
public class WebAsyncTaskController {
@GetMapping("/webAsyncTask")
@ResponseBody
public WebAsyncTask<String> asyncTask() {
WebAsyncTask<String> webAsyncTask = new WebAsyncTask<String>(1000l * 10, new Callable<String>() {
@Override
public String call() throws Exception {
TimeUnit.SECONDS.sleep(5);
return "OK";
}
});
webAsyncTask.onCompletion(new Runnable() {
@Override
public void run() {
System.out.println("调用完成");
}
});
webAsyncTask.onTimeout(new Callable<String>() {
@Override
public String call() throws Exception {
return "Time Out";
}
});
return webAsyncTask;
}
}
使用DeferredResult执行异步请求
package com.example.async;
import java.util.concurrent.TimeUnit;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;
@RestController
public class DeferredResultController {
@GetMapping(path = "/deferredResult")
@ResponseBody
public DeferredResult<String> asyncRequest() {
DeferredResult<String> deferredResult = new DeferredResult<>(1000L * 5, "失败");
deferredResult.onTimeout(() -> {
System.out.println("调用超时");
deferredResult.setResult("调用超时");
});
deferredResult.onCompletion(() -> {
System.out.println("调用完成");
});
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(10);
deferredResult.setResult("OK");
} catch (Exception e) {
e.printStackTrace();
}
}).start();
return deferredResult;
}
}
另外:Spring Boot中使用注解@Async处理异步任务
@Async注解的异步操作和上文所诉的四种异步请求不同之处在于,使用@Async处理异步任务时没有异步回调响应客户端的流程:
使用@EnableAsync开启@Async
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@EnableAsync
@SpringBootApplication
public class ExampleApplication {
public static void main(String[] args) {
SpringApplication.run(ExampleApplication.class, args);
}
}
如果将@Async加在Controller上或是 Controller 的方法上
package com.example.async;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AsyncController {
@Autowired
private TestService testService;
@GetMapping("/async")
@ResponseBody
@Async
public String asyncTask() {
testService.doSomeThing();
System.out.println("处理完成");
return "OK";
}
}
控制器立即会给客户端空响应,但是控制器方法依旧执行
如果将@Async加在Service上或是 Service 的方法上
package com.example.async;
import java.util.concurrent.TimeUnit;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class TestService {
@Async
public void doSomeThing() {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
控制器不再等待Service方法执行完毕就响应客户端