美文网首页
利用Spring的@Async异步处理改善web应用中耗时操作的

利用Spring的@Async异步处理改善web应用中耗时操作的

作者: __元昊__ | 来源:发表于2019-12-02 09:38 被阅读0次

Web应用中,有时会遇到一些耗时很长的操作(比如:在后台生成100张报表再呈现,或 从ftp下载若干文件,综合处理后再返回给页面下载),用户在网页上点完按钮后,通常会遇到二个问题:页面超时、看不到处理进度。

对于超时,采用异步操作,可以很好的解决这个问题,后台服务收到请求后,执行异步方法不会阻塞线程,因此就不存在超时问题。但是异步处理的进度用户也需要知道,否则不知道后台的异步处理何时完成,用户无法决定接下来应该继续等候? or 关掉页面?

思路:

1、browser -> Spring-MVC Controller -> call 后台服务中的异步方法 -> 将执行进度更新到redis缓存 -> 返回view

2、返回的view页面上,ajax -> 轮询 call 后台服务 -> 查询redis中的进度缓存数据,并实时更新UI进度显示 -> 如果完成 call 后台服务清理缓存

注:这里采用了redis保存异步处理的执行进度,也可以换成session或cookie或缓存来保存。

步骤:

一、spring配置文件中,增加Task支持

<!-- 计划任务配置,用 @Service @Lazy(false)标注类,用@Scheduled(cron = "0 0 2 * * ?")标注方法 -->
    <task:executor id="executor" pool-size="10"/> <task:scheduler id="scheduler" pool-size="10"/>
    <task:annotation-driven scheduler="scheduler" executor="executor" proxy-target-class="true"/>

注解的应用范围:

类:表示这个类中的所有方法都是异步的
方法:表示这个方法是异步的,如果类也注解了,则以这个方法的注解为准
相关的配置:
<task:annotation-driven />配置:
executor:指定一个缺省的executor给@Async使用。
例子:

<task:annotation-driven executor="asyncExecutor" />
<task:executor />配置参数:

id:当配置多个executor时,被@Async(“id”)指定使用;也被作为线程名的前缀。
pool-size:
core size:最小的线程数,缺省:1
max size:最大的线程数,缺省:Integer.MAX_VALUE
queue-capacity:当最小的线程数已经被占用满后,新的任务会被放进queue里面,当这个queue的capacity也被占满之后,pool里面会创建新线程处理这个任务,直到总线程数达到了max size,这时系统会拒绝这个任务并抛出TaskRejectedException异常(缺省配置的情况下,可以通过rejection-policy来决定如何处理这种情况)。缺省值为:Integer.MAX_VALUE
keep-alive:超过core size的那些线程,任务完成后,再经过这个时长(秒)会被结束掉
rejection-policy:当pool已经达到max size的时候,如何处理新任务
ABORT(缺省):抛出TaskRejectedException异常,然后不执行
DISCARD:不执行,也不抛出异常
DISCARD_OLDEST:丢弃queue中最旧的那个任务
CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行

View Code

二、后台Service中,在方法前加上@Async

@Service
public class Async_Service {

    @Async("executor")
    public void select_GP_choose1(String sql, String itinerary, String itinerary_date, String itinerary_end, HttpServletRequest request){
        ...
       // 查询数据库耗时任务
    }

    @Async("executor")
    public void select_GP_choose2(String sql, String itinerary, String itinerary_date, String itinerary_end, HttpServletRequest request){
       ...
// 查询数据库耗时任务
    }

    @Async("executor")
    public void select_GP_choose3(String sql, String itinerary, String itinerary_date, String itinerary_end, HttpServletRequest request){
       ...
// 查询数据库耗时任务
    }

    @Async("executor")
    public void select_GP_choose4(String sql, String itinerary, String itinerary_date, String itinerary_end, HttpServletRequest request){
       ...
// 查询数据库耗时任务
    }
}

Controller的处理

@Controller
@RequestMapping(value = "${adminPath}")
public class DepartureInformationQueryController {
    @Autowired
    RuntimeService runtimeService;
    @Autowired
    QuerytimesService querytimeService;
    @Autowired
    Async_Service async_service;

    @RequestMapping("select_ligang")
    public ModelAndView select_ligang(String itinerary,String itinerary_date,String choose,String itinerary_end,HttpServletRequest request){
        //异步调用  
  async_service.select_GP_choose1(sql,itinerary,itinerary_date,itinerary_end,request);
    }

    @RequestMapping("async_status")
    @ResponseBody
    public Map<String, String> async_status(){
        String async_result=(String)CacheUtils.get("async_result");
        String error=(String)CacheUtils.get("error");
        Map<String, String> map = new HashMap<>();
        map.put("async_result",async_result);
        map.put("error",error);
        return map;
    }
    @RequestMapping("async_clean")
    @ResponseBody
    public Map<String, String> async_clean(){
        CacheUtils.remove("async_result");
        CacheUtils.remove("error");
        Map<String, String> map = new HashMap<>();
        map.put("messge","清理成功");
        return map;
    }
}

view上的ajax处理

<%--    ajax轮询异步请求进度--%>
    <script type="text/javascript">
        var timerId = null;//定时器ID
        $(document).ready(function () {
            // 定时轮询执行进度
            timerId = setInterval(function () {
                  getStatus();
              }, 1000);
               //getStatus();
            });
        // 获取执行进度
        function getStatus() {
            var statusUrl = "${ctx}/async_status";
            $.get(statusUrl, function (data) {
                if (data.async_result==null || data.async_result==="" ) {
                       return;
                }
                var download_url=data.async_result;
                if(download_url==="未查询到数据"){
                    $(".download_a").attr('href', '#');
                    $(".download_a").text("未查询到数据");
                    $("#change_td").text("已发送");
                }else {
                    $(".download_a").attr('href', '${pageContext.request.contextPath}'+download_url);
                    $(".download_a").text("下载");
                    $("#change_td").text("已发送");
                }

                clearInterval(timerId);//停止定时器
                //清理缓存中数据
                $.get("${ctx}/async_clean", function (data) {

                })
            })
        }
    </script>

相关文章

网友评论

      本文标题:利用Spring的@Async异步处理改善web应用中耗时操作的

      本文链接:https://www.haomeiwen.com/subject/lukxgctx.html