Servlet3之NIO线程池隔离

Servlet3之NIO线程池隔离

colie_li 2019-12-30 01:49:04  97  收藏

分类专栏: Web

版权

线程隔离主要是针对业务中不同业务场景,按照权重区分使用不同的线程池,以达到某一个业务出现问题,不会将故障扩散到其他的业务线程池,从而达到保证主要业务高可用。
本案例主要讲解基于servlet3的线程隔离术。
首先我们回忆一下在tomcat6,tomcat6只支持BIO,它的处理流程如下:
1).tomcat负责接收servletRequest请求
2).将接收的请求分配给servlet处理业务
3).处理完请求之后,通过servletResponse写会数据
上面这三步都是在一个线程里面完成的,也就是同步进行。
如下图:

 

t6.png

tomcat7之后版本引入了servlet3,它基于NIO能处理更大的并发数。
我们可以将整个请求改造成如下步骤:
1).tomcat单线程进行请求解析
2).解析完之后将task放入队列(可以根据不同业务类型放入不同的队列)
3).每个队列指定相应业务线程池对task进行处理
这样改造以后就可以把业务按照重要性发送到不同线程池,两个线程池分开独立配置,互不干扰。当非核心的业务出现问题之后,不会影响核心的业务。另外由于此线程池是有我们创建的,我们可以对该线程池进行监控,处理,灵活了很多。
如下图:

 

 

下面是实现代码:

接口层调用

 
  1. @RestController

  2. @RequestMapping("/app")

  3. public class NIOCtrl {

  4. @Autowired

  5. private LocalNewsAsyncContext localNewsAsyncContext;

  6. @Autowired

  7. private NewsService newsService;

  8.  
  9. @RequestMapping("/news")

  10. public void getNews(HttpServletRequest request,@RequestParam(value = "type",required = false) String type){

  11. if("1".equals(type)){

  12. localNewsAsyncContext.submit(request, () -> newsService.getNews());

  13. return;

  14. }

  15. localNewsAsyncContext.submit(request, () -> newsService.getNewsMap());

  16. }

  17. }

将请求丢进指定线程池

 
  1. @Service

  2. public class LocalNewsAsyncContext {

  3. private final static Long timeOutSeconds= 5L;

  4. @Autowired

  5. private CustomAsyncListener asyncListener;

  6. @Autowired

  7. private ThreadPoolExecutor executor;

  8.  
  9. public void submit(final HttpServletRequest request,final Callable<Object> task){

  10. final String uri= request.getRequestURI();

  11. final Map<String,String[]> params= request.getParameterMap();

  12. //开启异步上下文

  13. final AsyncContext asyncContext= request.startAsync();

  14. asyncContext.getRequest().setAttribute(Constant.URI,uri);

  15. asyncContext.getRequest().setAttribute(Constant.PARAMS, params);

  16. asyncContext.setTimeout(timeOutSeconds * 1000);

  17. if(asyncContext!=null){

  18. asyncContext.addListener(asyncListener);

  19. }

  20. executor.submit(new CustomCallable(asyncContext, task));

  21.  
  22. }

  23. }

自定义线程处理

 
  1. public class CustomCallable implements Callable{

  2. private static final Logger LOG = LoggerFactory.getLogger(CustomCallable.class);

  3.  
  4. public AsyncContext asyncContext;

  5. private Callable<Object> task;

  6. private String uri;

  7. private Map<String,String[]> params;

  8.  
  9. public CustomCallable(AsyncContext asyncContext, Callable<Object> task){

  10. this.asyncContext= asyncContext;

  11. this.task= task;

  12. this.uri= (String) asyncContext.getRequest().getAttribute(Constant.URI);

  13. this.params= (Map<String, String[]>) asyncContext.getRequest().getAttribute(Constant.PARAMS);

  14. }

  15.  
  16. @Override

  17. public Object call() throws Exception {

  18. Object o= task.call();

  19. if(o==null){

  20. callback(asyncContext,o);

  21. }else if(o instanceof String){

  22. callback(asyncContext, o);

  23. }else if(o instanceof CompletableFuture){

  24. CompletableFuture<Object> future= (CompletableFuture<Object>) o;

  25. future.thenAccept(o1 -> callback(asyncContext, o1))

  26. .exceptionally(throwable -> {

  27. callback(asyncContext,"");

  28. return null;

  29. });

  30. }else {

  31. callback(asyncContext, o);

  32. }

  33. return null;

  34. }

  35.  
  36.  
  37. private void callback(AsyncContext asyncContext,Object result){

  38. HttpServletResponse response= (HttpServletResponse) asyncContext.getResponse();

  39. try{

  40. if(result instanceof String){

  41. write(response, (String) result);

  42. }else {

  43. write(response, JSON.toJSONString(result));

  44. }

  45. }catch (Exception e){

  46. response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);

  47. e.printStackTrace();

  48. try {

  49. LOG.error("get info error for uri:{}, params:{}",uri,JSON.toJSONString(params),e);

  50. }catch (Exception e1){}

  51. }finally {

  52. asyncContext.complete();

  53. }

  54. }

  55.  
  56. private void write(HttpServletResponse response,String result) throws IOException {

  57. response.getOutputStream().write(result.getBytes());

  58. }

  59. }

定义业务线程池

 
  1. @Configuration

  2. public class LocalNewsPoolConfig {

  3. private final static Logger LOG= LoggerFactory.getLogger(LocalNewsPoolConfig.class);

  4.  
  5. @Bean

  6. public ThreadPoolExecutor init(){

  7. int corePoolSize= 10;

  8. int maximumPoolSize= 100;

  9. int queueCapacity= 200;

  10. LinkedBlockingDeque<Runnable> queue= new LinkedBlockingDeque<>(queueCapacity);

  11. ThreadPoolExecutor executor= new ThreadPoolExecutor(corePoolSize,maximumPoolSize,60L, TimeUnit.SECONDS,queue);

  12. executor.allowCoreThreadTimeOut(true);

  13. executor.setRejectedExecutionHandler((r, executor1) -> {

  14. if(r instanceof CustomCallable){

  15. CustomCallable call= (CustomCallable) r;

  16. AsyncContext asyncContext= call.asyncContext;

  17. if(asyncContext!=null){

  18. handler(asyncContext);

  19. }

  20. }

  21. });

  22. return executor;

  23. }

  24.  
  25. private static void handler(AsyncContext asyncContext){

  26. try{

  27. ServletRequest req= asyncContext.getRequest();

  28. String uri= (String) req.getAttribute(Constant.URI);

  29. Map params= (Map) req.getAttribute(Constant.PARAMS);

  30. LOG.error("async req rejected. uri :{},params:{}",uri, JSON.toJSONString(params));

  31. }catch (Exception e){

  32. e.printStackTrace();

  33. try{

  34. HttpServletResponse response= (HttpServletResponse) asyncContext.getResponse();

  35. response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);

  36. }catch (Exception e1){}

  37. }finally {

  38. asyncContext.complete();

  39. }

  40. }

  41.  
  42. }

定义异步请求监听

 
  1. @Component

  2. public class CustomAsyncListener implements AsyncListener {

  3. private Logger LOG= LoggerFactory.getLogger(CustomAsyncListener.class);

  4. @Override public void onComplete(AsyncEvent asyncEvent) throws IOException {

  5.  
  6. }

  7.  
  8. @Override public void onTimeout(AsyncEvent asyncEvent) throws IOException {

  9. AsyncContext asyncContext= asyncEvent.getAsyncContext();

  10. try{

  11. ServletRequest req= asyncContext.getRequest();

  12. String uri= (String) req.getAttribute(Constant.URI);

  13. Map params= (Map) req.getAttribute(Constant.PARAMS);

  14. LOG.error("async req timeOut. uri :{},params:{}",uri, JSON.toJSONString(params));

  15. }catch (Exception e){

  16. e.printStackTrace();

  17. }finally {

  18. try{

  19. HttpServletResponse response= (HttpServletResponse) asyncContext.getResponse();

  20. response.setStatus(HttpServletResponse.SC_REQUEST_TIMEOUT);

  21. }catch (Exception e1){}

  22. asyncContext.complete();

  23. }

  24. }

  25.  
  26. @Override public void onError(AsyncEvent asyncEvent) throws IOException {

  27. AsyncContext asyncContext= asyncEvent.getAsyncContext();

  28. try{

  29. ServletRequest req= asyncContext.getRequest();

  30. String uri= (String) req.getAttribute(Constant.URI);

  31. Map params= (Map) req.getAttribute(Constant.PARAMS);

  32. LOG.error("async req error. uri :{},params:{}",uri, JSON.toJSONString(params));

  33. }catch (Exception e){

  34. e.printStackTrace();

  35. try{

  36. HttpServletResponse response= (HttpServletResponse) asyncContext.getResponse();

  37. response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);

  38. }catch (Exception e1){}

  39. }finally {

  40. asyncContext.complete();

  41. }

  42. }

  43.  
  44. @Override public void onStartAsync(AsyncEvent asyncEvent) throws IOException {

  45.  
  46. }

  47. }

业务处理和常量类

 
  1. @Service

  2. public class NewsService {

  3. public String getNews(){

  4. return "servlet3 nio test.";

  5. }

  6. public StringBuilder getNewsMap(){

  7. return new StringBuilder("I do and i understand.");

  8. }

  9. }

  10. public class Constant {

  11. public static final String URI= "uri";

  12. public static final String PARAMS= "params";

  13. }

作者:离别刀
链接:https://www.jianshu.com/p/0529c126e166
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值