org.apache.catalina.connector.ClientAbortException: java.io.IOException: APR error: -32


       項目里面有比較多的導入導出,錄音文件加載等功能,但是有個問題是:導入導出客戶端可能會頻繁的點擊,錄音文件一次性可能會加載多個。如果導出的excel文件過大,或者加載的錄音文件過多,或者一個音頻文件過長,可能會出現 這個異常:

   

org.apache.catalina.connector.ClientAbortException: java.io.IOException: APR error: -32
	at org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:396)
	at org.apache.tomcat.util.buf.ByteChunk.append(ByteChunk.java:344)
	at org.apache.catalina.connector.OutputBuffer.writeBytes(OutputBuffer.java:421)
	at org.apache.catalina.connector.OutputBuffer.write(OutputBuffer.java:409)
	at org.apache.catalina.connector.CoyoteOutputStream.write(CoyoteOutputStream.java:97)
	at java.io.BufferedOutputStream.write(BufferedOutputStream.java:122)
	at java.io.FilterOutputStream.write(FilterOutputStream.java:97)
	at com.zhuanche.serv.punish.DriverPunishClientService.renderVideo(DriverPunishClientService.java:181)
	at com.zhuanche.controller.driver.DriverPunishController.render(DriverPunishController.java:138)
	at com.zhuanche.controller.driver.DriverPunishController$$FastClassBySpringCGLIB$$1182fa42.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
	at org.apache.shiro.spring.security.interceptor.AopAllianceAnnotationsAuthorizingMethodInterceptor$1.proceed(AopAllianceAnnotationsAuthorizingMethodInterceptor.java:82)
	at org.apache.shiro.authz.aop.AuthorizingMethodInterceptor.invoke(AuthorizingMethodInterceptor.java:39)
	at org.apache.shiro.spring.security.interceptor.AopAllianceAnnotationsAuthorizingMethodInterceptor.invoke(AopAllianceAnnotationsAuthorizingMethodInterceptor.java:115)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673)
	at com.zhuanche.controller.driver.DriverPunishController$$EnhancerBySpringCGLIB$$fbb8c231.render(<generated>)
	at sun.reflect.GeneratedMethodAccessor2068.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)

          這個問題困擾了好久,解決方法有限制過前端的頻繁點擊的按鈕,比如沒分鍾只允許點擊一次,后端有加緩存的限制,比如以用戶id+請求條件+請求ip作為key,防止頻繁的請求。如果請求頻繁,則導出一個空Excel文件以及提示語。但是由於太多地方,仍然會時不時的拋出這個 arp -32的問題。今天想到用異步線程池的方式,參考:

  https://blog.csdn.net/Muscleheng/article/details/81409672  。

  我是采用的注解+@Async方式,但是發現仍然不管用。特別是加載錄音文件,還拋出了空指針異常。自己的代碼:Controller

  

    @RequiresPermissions(value = {"punishTripVideo"})
    @RequestMapping("/renderVideo")
    @ResponseBody
    public AjaxResponse render(@RequestParam(name = "filePath") String filePath, HttpServletResponse response) {
        if (StringUtils.isBlank(filePath)) {
            return AjaxResponse.failMsg(RestErrorCode.HTTP_FORBIDDEN, "缺失路徑");
        }
        log.info("渲染錄音文件, filePath:{}", filePath);
        try {
            driverPunishService.renderVideo(filePath, response);
        } catch (Exception e) {
            log.error("渲染失敗",e);
            return AjaxResponse.failMsg(RestErrorCode.HTTP_FORBIDDEN, "渲染失敗");
        }
        return AjaxResponse.success(null);
    }

  service:

/**
     * 渲染行程錄音
     * @param filePath
     * @param response
     * @throws IOException
     */
    public void renderVideo(String filePath,HttpServletResponse response) throws IOException {
        log.info("====threadName==== 異步線程池執行該service方法===");
        OutputStream outputStream = null;
        try {
            byte[] fileBytes = OkHttpStreamUtil.executeForBytes(filePath);
            if (Objects.nonNull(fileBytes)) {
                //支持范圍請求
                int fileLength = fileBytes.length;
                response.addHeader("Accept-Ranges", "bytes");
                response.addHeader("Content-Length", "" + fileLength);
                response.addHeader("Content-Range", "bytes 0-" + fileLength);
                response.addHeader("Content-Type", "audio/mpeg");
                response.addHeader("Content-Length", "" + fileLength);
                outputStream = new BufferedOutputStream(response.getOutputStream());
                outputStream.write(fileBytes);
                outputStream.flush();
            }
        } catch (IOException io) {
            log.warn("渲染行程錄音IOException", io);
        } catch (Exception e) {
            log.error("渲染行程錄音失敗", e);
            throw e;
        } finally {
            IoUtil.close(outputStream);
        }
    }

 

  使用異步線程池類:

 

package com.zhuanche.serv.sync;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ThreadPoolExecutor;

/**
 * @author fanht
 * @description 異步線程池
 * @date 2021/1/5 上午9:48
 * @version 1.0
 */
@Configuration
@EnableAsync
public class SyncThreadPool {

    /**核心線程數*/
    private static final Integer CORE_POOL_SIZE = 20;

    /**最大線程數*/
    private static final Integer MAX_POOL_SIZE = 100;

    /**允許線程空閑時間 (單位 秒)*/
    private static final Integer KEEP_ALIVE_TIME  = 10;

    /**緩沖隊列大小*/
    private static final Integer QUEUE_CAPACITY  = 200;

    /**線程名前綴  主要是在service層使用*/
    private static final  String THREADNAME_PREFIX  = "Async-service-";


    @Bean("syncTaskExecutor")
    public ThreadPoolTaskExecutor  syncTaskExecutor(){
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(CORE_POOL_SIZE);
        taskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
        taskExecutor.setKeepAliveSeconds(KEEP_ALIVE_TIME);
        taskExecutor.setQueueCapacity(QUEUE_CAPACITY);
        taskExecutor.setThreadNamePrefix(THREADNAME_PREFIX);
        // 線程池對拒絕任務的處理策略
        // CallerRunsPolicy:由調用線程(提交任務的線程)處理該任務
        taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        taskExecutor.initialize();
        return taskExecutor;
    }

}

 

   然后在service的方法前面加上@Async("syncTaskExecutor"),發現還是不行。而且拋異常了,問題是由於使用了異步線程池后,controller的線程是 tomcat提供的,而server的是使用的異步線程池。傳的outputStream 為空了(詳見: https://cloud.tencent.com/developer/article/1511820)

    不過這也給自己了提示:既然是tomcat超時了,那是不是可以通過調整tomcat的超時時間來延長呢? 我們知道 tomcat的 server.xml的 默認配置是如下:

    

<Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />

    會不會是這個默認的超時時間太短了呢?我把時間改成60000試下。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM