servlet3.0异步处理-导出、第三方调用等耗时操作

一、前言

在我们的实际生产中,常常会遇到下面的这种情况(例如JMS,定时任务,队列、下载等),某个请求非常耗时(大约5s返回),当大量的访问该请求的时候,再请求其他服务时,会造成没有连接使用的情况,造成这种现象的主要原因是,我们的容器(tomcat)中线程的数量是一定的,例如500个,当这500个线程都用来请求服务的时候,再有请求进来,就没有多余的连接可用了,只能拒绝连接。

二、环境要求

servlet3.0(Tomcat7及以上)

三、响应步骤

  1. 客户端发送一个请求
  2. Servlet容器分配一个线程来处理容器中的一个servlet
  3. Servlet调用request.startAsync(),保存AsyncContext, 然后返回
  4. 任何方式存在的容器线程都将退出,但是response仍然保持开放
  5. 其他线程使用保存的AsyncContext来完成响应
  6. 客户端收到响应

四、配置类

package com.example.demo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

@Configuration
@EnableAsync
public class AsyncExecutorPoolConfiguration {
    
    

    private static final int MAX_POOL_SIZE = 50;

    private static final int CORE_POOL_SIZE = 10;

    @Bean("asyncTaskExecutor")
    public AsyncTaskExecutor asyncTaskExecutor() {
    
    
		ThreadPoolTaskExecutor asyncTaskExecutor = new ThreadPoolTaskExecutor();
		asyncTaskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
		asyncTaskExecutor.setCorePoolSize(CORE_POOL_SIZE);
		asyncTaskExecutor.setQueueCapacity(Integer.MAX_VALUE);
		asyncTaskExecutor.setAllowCoreThreadTimeOut(true);
		asyncTaskExecutor.setKeepAliveSeconds(60);
		asyncTaskExecutor.setThreadNamePrefix("async-task-thread-pool-");
		asyncTaskExecutor.initialize();
    }

}

五、示例

package com.example.demo.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.WebAsyncTask;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

@RestController
public class AsyncController {
    
    

    @RequestMapping("/asyncUpload")
    public WebAsyncTask<String> asyncUpload(){
    
    
        // 模拟上传文件
        WebAsyncTask result = new WebAsyncTask(30000L, "asyncTaskExecutor" , () -> {
    
    
            System.out.println(Thread.currentThread().getName());

            try {
    
    
                TimeUnit.SECONDS.sleep(20);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            return "success";
        });
        result.onTimeout(() -> {
    
    
            throw new TimeoutException("调用超时!");
        });
        return result;
    }


}

六、典型应用场景

使用方法:

Callable<String>、DefferredResult<String>、WebAsyncTask<String>及手动创建线程

1.使用场景:导出报表使用独立线程,不影响业务系统正常使用

// springMVC开启一个Callable新线程,利用TaskExecutor管理新产生的线程。
@RequestMapping(method=RequestMethod.POST)
public Callable<String> processUpload(final MultipartFile file) {
    
    
  return new Callable<String>() {
    
    
    public Object call() throws Exception {
    
    
      // ...
      return "someView";
    }
  };
}

2.订单创建:调用其它模块任务,使用中间件消息队列。

@RequestMapping("/quotes")
@ResponseBody
public DeferredResult quotes() {
    
    
  DeferredResult deferredResult = new DeferredResult();
  // Add deferredResult to a Queue or a Map...
  return deferredResult;
}

// In some other thread..
// Set the return value on the deferredResult

deferredResult.set(data);

七、与shiro认证整合

@Bean
    public FilterRegistrationBean shiroFilterRegistration() {
    
    
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new DelegatingFilterProxy("shiroFilter"));
        //该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理
        registration.addInitParameter("targetFilterLifecycle", "true");
        registration.setEnabled(true);
        registration.setOrder(Integer.MAX_VALUE - 1);
        registration.addUrlPatterns("/*");

        //支持异步
        registration.setAsyncSupported(true);
        registration.setDispatcherTypes(DispatcherType.REQUEST,DispatcherType.ASYNC);
        return registration;
    }

猜你喜欢

转载自blog.csdn.net/ory001/article/details/108547869
今日推荐