SpringMVC异步化请求初探

    xiaoxiao2021-03-25  73

    SpringMVC异步化请求初探

    同步请求

    在servlet3.0之前,servlet在同一个线程中解析,处理,响应http请求

    Created with Raphaël 2.1.0 客户端 客户端 服务器 服务器 http request 解析,处理,生成响应 http response

    异步化请求

    servlet3.0之后,servlet提供asyncContext支持异步请求,是的解析,返回请求的线程和处理请求的线程资源分离。

    Created with Raphaël 2.1.0 客户端 客户端 服务器 服务器 业务线程池 业务线程池 http request 解析请求 submit 处理请求 complete 返回响应 http response

    异步化使得原本同步调用时阻塞的线程资源释放出来,可以提高服务器并发性能(有利有弊,同时增加了平均响应时长)。

    同时因为处理请求的线程池交由自己管理,就可以根据业务重要性创建多个线程池,特别是在一个项目中有多个业务在运行时:

    把业务分为核心业务和非核心业务为不同级别的业务定义不同的线程池,线程池之间是隔离的根据不同业务量设置线程池大小

    此时在一个池中的业务发生接口或者数据库访问慢的情况,并不会对其他线程池产生影响。

    我们还可以(作死)在线上动态调整线程池的大小,或者在服务器线程满的情况下reject一部分请求,保证服务的持续性。

    SpringMVC异步化请求

    SpringMVC已经对servlet async context使用进行了封装,方便我们使用异步请求。

    我们需要改写Controller中handler方法的返回值类型为DeferredResult, 然后在其他线程中调用deferredResult.setResult(result)完成响应。

    简单的代码示例:

    private ExecutorService executorService = Executors.newFixedThreadPool(10); @ResponseBody @RequestMapping("/async") public DeferredResult<Map<String ,Object>> test(){ DeferredResult<Map<String, Object>> deferredResult = new DeferredResult<>(); executorService.submit(()->asyncTest(deferredResult)); return deferredResult; } public void asyncTest(DeferredResult<Map<String, Object>> deferredResult) { Map<String, Object> result = Maps.newHashMap(); result.put("key","value"); deferredResult.setResult(result); }

    使用RxJava2 进行异步化

    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<Object> deferredResult = new DeferredResult<>(); single.subscribe(deferredResult::setResult, deferredResult::setErrorResult); WebAsyncUtils.getAsyncManager(webRequest) .startDeferredResultProcessing(deferredResult, mavContainer); } }

    然后在mvc中注册一下:

    @Bean @ConditionalOnMissingBean @ConditionalOnClass(Single.class) public SingleReturnValueHandler singleReturnValueHandler() { return new SingleReturnValueHandler(); } @Configuration public static class RxJavaWebConfiguration { @Autowired private List<AsyncHandlerMethodReturnValueHandler> handlers = Collections.emptyList(); @Bean public WebMvcConfigurer rxJavaWebMvcConfiguration() { return new WebMvcConfigurerAdapter() { @Override public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) { returnValueHandlers.addAll(handlers); } }; } }

    现在在Controller中返回Single对象,SpringMVC就可以异步处理请求了。

    @GetMapping("/sayHello") Single<String> sayHello() { return observerService.sayHello(); } @Profile("dev") @Service public class ObserverService { Single<String> sayHello() { return Single .create((SingleOnSubscribe<String>) 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

    什么情况,并发量没有上去啊,我是不是用了假的异步请求呀…

    转载请注明原文地址: https://ju.6miu.com/read-15997.html

    最新回复(0)