zoukankan      html  css  js  c++  java
  • 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试下。

  • 相关阅读:
    前端工程师须知pc电脑端分辨率
    移动前端的坑
    07.01工作笔记
    缓存
    word-wrap,white-space和text-overflow属性
    页面结构
    Spring Bean的作用域和自动装配
    Spring配置文件
    初识Spring和IOC理解
    MyBatis缓存
  • 原文地址:https://www.cnblogs.com/thinkingandworkinghard/p/14237455.html
Copyright © 2011-2022 走看看