在servlet3.0之前,servlet在同一个线程中解析,处理,响应http请求
servlet3.0之后,servlet提供asyncContext支持异步请求,是的解析,返回请求的线程和处理请求的线程资源分离。
异步化使得原本同步调用时阻塞的线程资源释放出来,可以提高服务器并发性能(有利有弊,同时增加了平均响应时长)。
同时因为处理请求的线程池交由自己管理,就可以根据业务重要性创建多个线程池,特别是在一个项目中有多个业务在运行时:
此时在一个池中的业务发生接口或者数据库访问慢的情况,并不会对其他线程池产生影响。
我们还可以(作死)在线上动态调整线程池的大小,或者在服务器线程满的情况下reject一部分请求,保证服务的持续性。
SpringMVC已经对servlet async context使用进行了封装,方便我们使用异步请求。
我们需要改写Controller中handler方法的返回值类型为DeferredResult,
然后在其他线程中调用deferredResult.setResult(result)完成响应。
简单的代码示例:
private ExecutorService executorService = Executors.newFixedThreadPool(10);
@ResponseBody
@RequestMapping("/async")
public DeferredResult
SpringMVC 4.0还不支持Reactive编程…不过提供了不少扩展点,可以让我们使用RxJava方便线程切换。
先实现AsyncHandlerMethodReturnValueHandler,让Controller handler可以处理RxJava2.0的Single对象。
public class SingleReturnValueHandler implements AsyncHandlerMethodReturnValueHandler {
@Override
public boolean isAsyncReturnValue(Object returnValue, MethodParameter returnType) {
return returnValue != null && supportsReturnType(returnType);
}
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return Single.class.isAssignableFrom(returnType.getParameterType());
}
@SuppressWarnings("unchecked")
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue == null) {
mavContainer.setRequestHandled(true);
return;
}
final Single> single = Single.class.cast(returnValue);
DeferredResult
然后在mvc中注册一下:
@Bean
@ConditionalOnMissingBean
@ConditionalOnClass(Single.class)
public SingleReturnValueHandler singleReturnValueHandler() {
return new SingleReturnValueHandler();
}
@Configuration
public static class RxJavaWebConfiguration {
@Autowired
private List handlers = Collections.emptyList();
@Bean
public WebMvcConfigurer rxJavaWebMvcConfiguration() {
return new WebMvcConfigurerAdapter() {
@Override
public void addReturnValueHandlers(List returnValueHandlers) {
returnValueHandlers.addAll(handlers);
}
};
}
}
现在在Controller中返回Single对象,SpringMVC就可以异步处理请求了。
@GetMapping("/sayHello")
Single<String> sayHello() {
return observerService.sayHello();
}
@Profile("dev")
@Service
public class ObserverService {
Single sayHello() {
return Single
.create((SingleOnSubscribe) e -> e.onSuccess(helloWorld()))
.subscribeOn(Schedulers.computation());
}
String helloWorld() {
//throw new IllegalArgumentException();
return "hello world" + Thread.currentThread().getName();
}
}
执行结果为:
先测试同步请求的性能,先把服务器并发线程调整为200:
server.tomcat.max-threads=200
get一下:
sagedeMac-mini:~ SAGE$ siege -g http://192.168.1.102/update/sayHello
[alert] Zip encoding disabled; siege requires zlib support to enable it
HEAD /update/sayHello HTTP/1.0
Host: 192.168.1.102
Accept: */*
User-Agent: Mozilla/5.0 (apple-x86_64-darwin16.0.0) Siege/4.0.2
Connection: close
HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8
Content-Length: 31
Date: Sun, 12 Mar 2017 14:32:40 GMT
Connection: close
Transactions: 1 hits
Availability: 100.00 %
Elapsed time: 0.44 secs
Data transferred: 0.00 MB
Response time: 0.01 secs
Transaction rate: 2.27 trans/sec
Throughput: 0.00 MB/sec
Concurrency: 0.02
Successful transactions: 1
Failed transactions: 0
Longest transaction: 0.01
Shortest transaction: 0.01
Connection close 这个会影响性能…先按这个标准测试吧…
siege -c100 -t60s http://192.168.1.102/update/sayHello
100并发+60秒,注意我这里没有加-b参数
Transactions: 16307 hits
Availability: 100.00 %
Elapsed time: 59.08 secs
Data transferred: 0.47 MB
Response time: 0.01 secs
Transaction rate: 276.02 trans/sec
Throughput: 0.01 MB/sec
Concurrency: 2.13
Successful transactions: 16307
Failed transactions: 0
Longest transaction: 0.08
Shortest transaction: 0.00
siege -c255 -t60s http://192.168.1.102/update/sayHello
255并发+60秒
已经有报错了:
socket: 369811456 connection timed out.: Operation timed out
Transactions: 16874 hits
Availability: 98.51 %
Elapsed time: 59.70 secs
Data transferred: 0.49 MB
Response time: 0.02 secs
Transaction rate: 282.65 trans/sec
Throughput: 0.01 MB/sec
Concurrency: 4.94
Successful transactions: 16874
Failed transactions: 255
Longest transaction: 0.14
Shortest transaction: 0.00
现在再来测测异步请求,先把tomcat并发改成1,线程池为固定200
server.tomcat.max-threads=1
siege -c100 -t60s http://192.168.1.102/update/sayHelloAsync
Transactions: 16355 hits
Availability: 100.00 %
Elapsed time: 59.69 secs
Data transferred: 0.43 MB
Response time: 0.04 secs
Transaction rate: 274.00 trans/sec
Throughput: 0.01 MB/sec
Concurrency: 9.73
Successful transactions: 16355
Failed transactions: 0
Longest transaction: 7.59
Shortest transaction: 0.00
siege -c255 -t60s http://192.168.1.102/update/sayHelloAsync
Transactions: 16787 hits
Availability: 98.50 %
Elapsed time: 59.50 secs
Data transferred: 0.44 MB
Response time: 0.02 secs
Transaction rate: 282.13 trans/sec
Throughput: 0.01 MB/sec
Concurrency: 5.13
Successful transactions: 16787
Failed transactions: 255
Longest transaction: 0.10
Shortest transaction: 0.00
什么情况,并发量没有上去啊,我是不是用了假的异步请求呀…