Servlet3实现业务隔离(异步上下文)

6 篇文章 0 订阅
5 篇文章 0 订阅

目录

一、Servlet的同步与异步

二、实现实例

1. 创建线程池并处理业务

2. 使用业务线程池

3. 压测结果


一、Servlet的同步与异步

Servlet2.x规范中,所有处理都是同步。即:解析请求、业务处理、响应结果在同一个线程中。如下步骤:

        step1:Tomcat解析请求,返回请求对象、响应对象;

        step2:Servlet处理业务;

        step3:响应处理结果

Servlet3后规范中,可以启动异步上下文(默认同步),业务可以通过线程池处理,达到请求解析与业务处理线程池分离,如下图所示:

二、实现实例

1. 创建线程池并处理业务

 注意问题:

a. 没有用注解@Async,主要是业务进行分级,如:核心业务用一级线程池等

b. 启用异步上下文,需要进行asyncContext.complete()操作,否则直到线程池超时

package com.common.instance.demo.core.serviceLevel;

import com.alibaba.fastjson.JSON;
import com.log.util.LogUtil;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.*;

/**
 * @description 一级业务线程池处理业务
 * @author tcm
 * @version 1.0.0
 * @date 2021/9/29 16:01
 **/
@Component
public class OneLevelAsyncContext implements InitializingBean {

    private final String URI = "uri";
    private final String PARAMS = "params";

    private AsyncListener asyncListener;
    private LinkedBlockingDeque<Runnable> queue;
    private ThreadPoolExecutor executor;

    public Object submitFuture(final HttpServletRequest request, final Callable<Object> task) throws ExecutionException, InterruptedException {
        // 获取请求URI
        final String uri = request.getRequestURI();
        // 获取请求参数
        final Map<String, String[]> params = request.getParameterMap();

        // 开启异步上下文
        final AsyncContext asyncContext = request.startAsync();
        asyncContext.getRequest().setAttribute(URI, uri);
        asyncContext.getRequest().setAttribute(PARAMS, params);
        // 超时设置
        asyncContext.setTimeout(2 * 1000);
        if (Objects.nonNull(asyncListener)) {
            asyncContext.addListener(this.asyncListener);
        }

        // 线程池处理业务
        Future<Object> future = executor.submit(new Callable<Object>() {
            @Override
            public Object call() throws Exception {
                // 业务处理
                Object result = task.call();
                return result;
            }
        });
        // 完成异步上下文,否则报超时等异常
        asyncContext.complete();
        return future.get();
    }



    // 完成初始化配置
    @Override
    public void afterPropertiesSet() throws Exception {
        // 线程池大小
        int corePoolSize = Integer.parseInt("100");
        // 最大线程池大小
        int maxNumPoolSize = Integer.parseInt("200");
        // 任务队列
        queue = new LinkedBlockingDeque<Runnable>();
        // 创建线程池
        executor = new ThreadPoolExecutor(corePoolSize, maxNumPoolSize, 100, TimeUnit.MILLISECONDS, queue);

        executor.allowCoreThreadTimeOut(true);
        // 线程池饱和处理
        executor.setRejectedExecutionHandler(new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                if (r instanceof CanceledCallable) {
                    CanceledCallable cc = ((CanceledCallable) r);
                    AsyncContext asyncContext = cc.asyncContext;
                    try {
                        ServletRequest request = asyncContext.getRequest();
                        String uri = (String) request.getAttribute(URI);
                        Map params = (Map) request.getAttribute(PARAMS);

                        LogUtil.error(String.format("async request %s, uri:%s, params:%s", "rejectedExecution", uri, JSON.toJSONString(params)));
                    } catch (Exception ex) {

                    }
                    try {
                        HttpServletResponse response = (HttpServletResponse) asyncContext.getResponse();
                        response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                    } finally {
                        asyncContext.complete();
                    }
                }
            }

        });

        // 创建监听器
        if (Objects.isNull(asyncListener)) {
            asyncListener = new AsyncListener() {
                @Override
                public void onComplete(AsyncEvent asyncEvent) throws IOException {

                }

                @Override
                public void onTimeout(AsyncEvent asyncEvent) throws IOException {
                    AsyncContext asyncContext = asyncEvent.getAsyncContext();
                    try {
                        ServletRequest request = asyncContext.getRequest();
                        String uri = (String) request.getAttribute(URI);
                        Map params = (Map) request.getAttribute(PARAMS);

                        LogUtil.error(String.format("async request timeout, uri:%s, params:%s", uri, JSON.toJSONString(params)));
                    } catch (Exception e) {}
                    try {
                        HttpServletResponse resp = (HttpServletResponse) asyncContext.getResponse();
                        resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                    } finally {
                        asyncContext.complete();
                    }
                }

                @Override
                public void onError(AsyncEvent asyncEvent) throws IOException {
                    AsyncContext asyncContext = asyncEvent.getAsyncContext();
                    try {
                        ServletRequest request = asyncContext.getRequest();
                        String uri = (String) request.getAttribute(URI);
                        Map params = (Map) request.getAttribute(PARAMS);

                        LogUtil.error(String.format("async request error, uri:%s, params:%s", uri, JSON.toJSONString(params)));
                    } catch (Exception e) {}
                    try {
                        HttpServletResponse resp = (HttpServletResponse) asyncContext.getResponse();
                        resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                    } finally {
                        asyncContext.complete();
                    }
                }

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

                }

            };
        }
    }

}
package com.common.instance.demo.core.serviceLevel;

import javax.servlet.AsyncContext;
import java.util.concurrent.Callable;

/**
 * @description 线程池饱和处理
 * @author tcm
 * @version 1.0.0
 * @date 2021/9/30 10:56
 **/
public class CanceledCallable implements Runnable, Callable<Object> {

    public AsyncContext asyncContext;


    public CanceledCallable(AsyncContext asyncContext){
        this.asyncContext = asyncContext;
    }


    @Override
    public void run() {

    }

    @Override
    public Object call() throws Exception {
        return null;
    }

}

2. 使用业务线程池

package com.common.instance.demo.controller;

import com.common.instance.demo.core.Response;
import com.common.instance.demo.core.serviceLevel.OneLevelAsyncContext;
import com.common.instance.demo.entity.WcPendantTab;
import com.common.instance.demo.service.WcPendantTabService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

/**
 * Tab菜单(WcPendantTab)表控制层
 *
 * @author tcm
 * @since 2021-01-14 15:02:08
 */
@RestController
@RequestMapping("/tab")
@Api(tags = "活动tab测试")
public class WcPendantTabController {

    @Resource
    private WcPendantTabService wcPendantTabService;

    @Resource
    private OneLevelAsyncContext oneLevelAsyncContext;

    @PostMapping("/oneLevelAsyncContext")
    @ApiOperation("测试一级业务线程池")
    public Response<Object> testOneLevelAsyncContext(HttpServletRequest request, WcPendantTab tab) {
        try {
            Object result = oneLevelAsyncContext.submitFuture(request, () -> wcPendantTabService.queryAll(tab));
            return Response.success(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Response.error("请稍后重试");
    }

}

3. 压测结果

异步线程池提高了吞吐量了,但是也增加了响应时间。说明开启异步处理业务并不能降低响应时间。但是如果业务分级,将核心业务处理进行隔离。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值