商品详情页系统的Servlet3异步化实践

在京东工作的这一年多时间里,我在整个商品详情页系统(后端数据源)及商品详情页统一服务系统(页面中异步加载的很多服务,如库存服务、图书相关服务、延保服务等)中使用了Servlet3请求异步化模型,总结了Servlet3请求异步化的一些经验和想法跟大家分享和交流。

我将从如下几点阐述Servlet3异步化之后的好处:

1、为什么实现请求异步化需要使用Servlet3

2、请求异步化后得到的好处是什么

3、如何使用Servlet3异步化

4、一些Servlet3异步化压测数据

首先说下一般http请求处理的流程:

1、容器负责接收并解析请求为HttpServletRequest;

2、然后交给Servlet进行业务处理;

3、最后通过HttpServletResponse写出响应。

在Servlet2.x规范中,所有这些处理都是同步进行的,也就是说必须在一个线程中完成从接收请求、业务处理到响应。

1、为什么实现请求异步化需要使用Servlet3

此处已Tomcat6举例子,Tomcat6没有实现Servlet3规范,它在处理请求时是通过如下方式实现的:

org.apache.catalina.connector.CoyoteAdapter#service

Java代码  收藏代码
  1. // Recycle the wrapper request and response  
  2.   if (!comet) {  
  3.       request.recycle();    
  4.       response.recycle();  
  5.   } else {  
  6.       // Clear converters so that the minimum amount of memory  
  7.       // is used by this processor  
  8.       request.clearEncoders();  
  9.       response.clearEncoders();  
  10.   }  

在请求结束时会同步进行请求的回收,也就是说请求解析、业务处理和响应必须在一个线程内完成,不能跨越线程界限。

这也就说明了必须使用实现了Servlet规范的容器进行处理,如Tomcat 7.x。

2、请求异步化后得到的好处是什么

2.1、更高的并发能力;

2.2、请求解析和业务处理线程池分离;

2.3、根据业务重要性对业务分级,并分级线程池;

2.4、对业务线程池进行监控、运维、降级等处理。

2.1、更高的并发能力

得益于技术的升级,在JDK7配合Tomcat7压测中获得了不错的性能表现。

2.2、请求解析和业务处理线程池分离

在引入Servlet3之前我们的线程模型是如下样子的:


 
整个请求解析、业务处理、生成响应都是由Tomcat线程池进行处理,而且都是在一个线程中处理;不能分离线程处理;比如接收到请求后交给其他线程处理,这样不能灵活定义业务处理模型。

引入Servlet3之后,我们的线程模型可以改造为如下样子:

 
此处可以看到请求解析使用Tomcat单线程;而解析完成后会扔到业务队列中,由业务线程池进行处理;这种处理方式可以得到如下好处:

1、根据业务重要性对业务进行分级,然后根据分级定义线程池;

2、可以拿到业务线程池,可以进行很多的操作,比如监控、降级等。

2.3、根据业务重要性对业务分级,并分级线程池

在一个系统的发展期间,我们一般把很多服务放到一个系统中进行处理,比如库存服务、图书相关服务、延保服务等等;这些服务中我们可以根据其重要性对业务分级别和做一些限制:

1、可以把业务分为核心业务级别和非核心业务级别;

2、为不同级别的业务定义不同的线程池,线程池之间是隔离的;

3、根据业务量定义各级别线程池大小。

此时假设非核心业务因为数据库连接池或者网络问题抖动造成响应时间慢,不会对我们核心业务产生影响。

2.4、对业务线程池进行监控、运维、降级等处理

因为业务线程池从Tomcat中分离出来,可以进行线程池的监控,比如查看当前处理的请求有多少,是否到了负载瓶颈,到了瓶颈后可以进行业务报警等处理。


上图是我们一个简陋的监控图,可实时查看到当前处理情况:正在处理的任务有多少,队列中等待的任务有多少;可以根据这些数据进行监控和预警。

另外我们还可以进行一些简单的运维:



对业务线程池进行扩容,或者业务出问题时立即清空线程池防止容器崩溃等问题;而不需要等待容器重启(容器重启需要耗费数十秒甚至数几十毫秒、而且启动后会有预热问题,而造成业务产生抖动)。

如果发现请求处理不过来,负载比较高,最简单的办法就是直接清空线程池,将老请求拒绝掉,而没有雪崩效应。

因为业务队列和业务线程池都是自己的,可以对这些基础组件做很多处理,比如定制业务队列,按照用户级别对用户请求排序,高级别用户得到更高优先级的业务处理。

3、如何使用Servlet3异步化

对于Servlet3的使用,可以参考我之前的博客:

  Servlet3.1规范(最终版)中文版下载 

  Servlet3.1学习示例

而在我项目里使用就比较简单:

1、接收请求 

Java代码  收藏代码
  1. @RequestMapping("/book")  
  2. public void getBook(HttpServletRequest request, @RequestParam(value="skuId"final Long skuId,  
  3.                     @RequestParam(value="cat1"final Integer cat1, @RequestParam(value="cat2"final Integer cat2) throws Exception {  
  4.   
  5.     oneLevelAsyncContext.submitFuture(request, () -> bookService.getBook(skuId, cat1, cat2));  
  6. }  
原文地址:http://jinnianshilongnian.iteye.com/blog/2245925
原文地址:https://www.cnblogs.com/jpfss/p/9329769.html