再遇org.apache.catalina.connector.clientabortexception:java.net.socketexception:断开的管道(writefailed)(代码片

buguge buguge     2022-12-01     383

关键词:

优付商户平台“付款记录”页面,商户操作员点击“下载结算凭证”按钮,系统会将所选条件的交易的回单文件以zip包的形式返回给浏览器页面。

 

 

 

由于程序涉及到复杂计算,同时涉及到读库、网络、磁盘IO,耗时比较长。为了防止重复请求,今天,我用redis分布式锁做了防重复提交控制。

@RequestMapping(value = "/downLoadBill")
public void downLoadBill(HttpServletRequest request, HttpServletResponse response) throws Exception 
    UserVO userVO=(UserVO) request.getSession().getAttribute("userVO");
    log.info("==MERCHANT==结算凭证下载,执行开始==企业id=", userVO.getMERID());
    response.setCharacterEncoding(Constant.CHARSET);

    String lockKey="downLoadBill:"+userVO.getMERID();
    String lockValue = UUID.randomUUID().toString();
    boolean getLock = JedisUtils.tryGetDistributedLock(lockKey, lockValue,15000);
    if (!getLock) 
        log.info("结算凭证下载中,请勿重复提交");
        response.getWriter().write("您似乎进行了重复提交操作。请重新发起请求,因数据量大,希望您耐心等待系统响应!");
        return;
    
    
    ....
    OutputStream responseStream = new BufferedOutputStream(response.getOutputStream());
    response.setContentType("application/octet-stream");
    response.setHeader("Content-Disposition", "attachment;filename=" + new String(file.getName().getBytes("UTF-8"),"ISO-8859-1"));
    responseStream.write(buffer);
    responseStream.flush();
    
    ....
    

 

那么, 当商户操作人员在页面重复点击时,页面交互如下:

 

 

页面交互倒是OK,不过呢,通过监控运行日志,发现程序有报异常:org.apache.catalina.connector.ClientAbortException: java.net.SocketException: 断开的管道 (Write failed)
同样,查看浏览器的网络请求,也发现,重复点击调用了两次接口,不过,第一次的直接爆红,第二次的正常响应。

如下是往response写入字节流的代码

    try (BufferedInputStream fis = new BufferedInputStream(new FileInputStream(file.getPath()));
         OutputStream responseStream = new BufferedOutputStream(response.getOutputStream())) 
        // 以流的形式下载文件。
        byte[] buffer = new byte[fis.available()];
        fis.read(buffer);
        fis.close();
        // 清空response
        response.reset();

        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", "attachment;filename=" + new String(file.getName().getBytes("UTF-8"), "ISO-8859-1"));
        responseStream.write(buffer);
        responseStream.flush();
        responseStream.close();
     catch (IOException ex) 
        ex.printStackTrace();
    

 

如下是两次请求的log:

2021-06-23 18:05:46.966 [ INFO] [downLoadBill_1624442477031S903] [com.yft.controller.SettleController:2125] ==MERCHANT==结算凭证下载,执行开始==企业id=89900000222116027420
2021-06-23 18:05:47.001(Timestamp), com.yft.service.impl.TPlatOrderServiceImpl(String), selectPlatOrderPage(String), selectPlatOrderPage(String), 192.168.40.69(String)
2021-06-23 18:05:47.008 [ INFO] [downLoadBill_1624442477031S903] [com.yft.controller.SettleController:2199] ====MERCHANT==结算凭证下载,要下载的结算凭证共有==19
2021-06-23 18:05:47.013 [ INFO] [downLoadBill_1624442477031S903] [com.yft.controller.SettleController:2202] ====MERCHANT==结算凭证下载,定义临时文件夹==/home/zipTempPath/89900000222116027420/20210623/
2021-06-23 18:05:47.014 [ INFO] [downLoadBill_1624442477031S903] [com.yft.controller.SettleController:2208] ====MERCHANT==结算凭证下载,循环处理订单platOrder.orderid:2021061014474300562655,获取结算凭证==/home/zipTempPath/89900000222116027420/20210623/20210610_2021061014474300562655.pdf
2021-06-23 18:05:47.043 [ INFO] [downLoadBill_1624442477021S655] [com.yft.controller.SettleController:2125] ==MERCHANT==结算凭证下载,执行开始==企业id=89900000222116027420
2021-06-23 18:05:47.044 [ INFO] [downLoadBill_1624442477021S655] [com.yft.controller.SettleController:2132] 结算凭证下载中,请勿重复提交
2021-06-23 18:05:47.109 [ INFO] [downLoadBill_1624442477031S903] [com.yft.controller.SettleController:2208] ====MERCHANT==结算凭证下载,循环处理订单platOrder.orderid:2021061014474300562654,获取结算凭证==/home/zipTempPath/89900000222116027420/20210623/20210610_2021061014474300562654.pdf
2021-06-23 18:05:47.117 [ INFO] [downLoadBill_1624442477031S903] [com.yft.controller.SettleController:2208] ====MERCHANT==结算凭证下载,循环处理订单platOrder.orderid:2021061014093100562651,获取结算凭证==/home/zipTempPath/89900000222116027420/20210623/20210610_2021061014093100562651.pdf
2021-06-23 18:05:47.124 [ INFO] [downLoadBill_1624442477031S903] [com.yft.controller.SettleController:2208] 
====MERCHANT==结算凭证下载,循环处理订单platOrder.orderid:2021060718400100562574,获取结算凭证==/home/zipTempPath/89900000222116027420/20210623/20210607_2021060718400100562574.pdf
....
2021-06-23 18:05:47.649 [ INFO] [downLoadBill_1624442477031S903] [com.yft.controller.SettleController:2208] ====MERCHANT==结算凭证下载,循环处理订单platOrder.orderid:2021060718380400562573,获取结算凭证==/home/zipTempPath/89900000222116027420/20210623/20210607_2021060718380400562573.pdf
2021-06-23 18:05:47.656 [ INFO] [downLoadBill_1624442477031S903] [com.yft.controller.SettleController:2225] ====MERCHANT==结算凭证下载,定义zip压缩文件路径==20210623180547.zip
org.apache.catalina.connector.ClientAbortException: java.net.SocketException: 断开的管道 (Write failed)
    at org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:410)
    at org.apache.tomcat.util.buf.ByteChunk.append(ByteChunk.java:352)
    at org.apache.catalina.connector.OutputBuffer.writeBytes(OutputBuffer.java:435)
    at org.apache.catalina.connector.OutputBuffer.write(OutputBuffer.java:423)
    at org.apache.catalina.connector.CoyoteOutputStream.write(CoyoteOutputStream.java:91)
    at org.springframework.security.web.context.SaveContextOnUpdateOrErrorResponseWrapper$SaveContextServletOutputStream.write(SaveContextOnUpdateOrErrorResponseWrapper.java:457)
    at java.io.BufferedOutputStream.write(BufferedOutputStream.java:122)
    at java.io.FilterOutputStream.write(FilterOutputStream.java:97)
    at com.yft.util.DownloadFileUtil.downloadFile(DownloadFileUtil.java:99)
    at com.yft.controller.SettleController.downLoadBill(SettleController.java:2236)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    。。。
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:318)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)
    Suppressed: org.apache.catalina.connector.ClientAbortException: java.net.SocketException: 断开的管道 (Write failed)
        at org.apache.catalina.connector.OutputBuffer.doFlush(OutputBuffer.java:370)
        at org.apache.catalina.connector.OutputBuffer.flush(OutputBuffer.java:334)
        at org.apache.catalina.connector.CoyoteOutputStream.flush(CoyoteOutputStream.java:101)
        at org.springframework.security.web.context.SaveContextOnUpdateOrErrorResponseWrapper$SaveContextServletOutputStream.flush(SaveContextOnUpdateOrErrorResponseWrapper.java:376)
        at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:141)
        at java.io.FilterOutputStream.close(FilterOutputStream.java:158)
        at com.yft.util.DownloadFileUtil.downloadFile(DownloadFileUtil.java:105)
        ... 77 more
    Caused by: java.net.SocketException: 断开的管道 (Write failed)
        at java.net.SocketOutputStream.socketWrite0(Native Method)
        at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:111)
        at java.net.SocketOutputStream.write(SocketOutputStream.java:155)
        at org.apache.coyote.http11.InternalOutputBuffer.realWriteBytes(InternalOutputBuffer.java:216)
        at org.apache.tomcat.util.buf.ByteChunk.flushBuffer(ByteChunk.java:442)
        at org.apache.coyote.http11.InternalOutputBuffer.flush(InternalOutputBuffer.java:120)
        at org.apache.coyote.http11.AbstractHttp11Processor.action(AbstractHttp11Processor.java:849)
        at org.apache.coyote.Response.action(Response.java:171)
        at org.apache.catalina.connector.OutputBuffer.doFlush(OutputBuffer.java:366)
        ... 83 more
Caused by: java.net.SocketException: 断开的管道 (Write failed)
    at java.net.SocketOutputStream.socketWrite0(Native Method)
    at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:111)
    at java.net.SocketOutputStream.write(SocketOutputStream.java:155)
    at org.apache.coyote.http11.InternalOutputBuffer.realWriteBytes(InternalOutputBuffer.java:216)
    at org.apache.tomcat.util.buf.ByteChunk.flushBuffer(ByteChunk.java:442)
    at org.apache.tomcat.util.buf.ByteChunk.append(ByteChunk.java:347)
    at org.apache.coyote.http11.InternalOutputBuffer$OutputStreamOutputBuffer.doWrite(InternalOutputBuffer.java:239)
    at org.apache.coyote.http11.filters.ChunkedOutputFilter.doWrite(ChunkedOutputFilter.java:119)
    at org.apache.coyote.http11.AbstractOutputBuffer.doWrite(AbstractOutputBuffer.java:192)
    at org.apache.coyote.Response.doWrite(Response.java:495)
    at org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:405)
    ... 85 more
2021-06-23 18:05:48.194 [ INFO] [downLoadBill_1624442477031S903] [com.yft.qrcodeUtil.FileOperateUtil:54] 执行linux命令删除目录,linux cmd=/bin/rm -rf /home/zipTempPath/89900000222116027420/20210623/

 

 

如下是谷歌浏览器F12调试器窗口的网络请求截图:

 

 

 

 

 

 

那么,为什么会出现“ClientAbortException: java.net.SocketException: 断开的管道 (Write failed)”异常呢?

原因是:浏览器重复提交时,由于是同步请求,当第二次的请求到达时,浏览器已经关闭了第一次的请求。而此时呢,server端对第一次请求的处理尚未结束(线程仍处于RUNNABLE状态),等到往响应流里写数据时,由于客户端连接已断开,所以出现“断开的管道 (Write failed)”异常,因为是响应异常,故而异常类型是SocketException。

如下图示:

 

再遇接雨水(代码片段)

又是经典的接雨水看到这个答案我就没有搞懂为什么要这么写会不会超了这样importjava.util.*;publicclassSolution//以312524为例//从左向右扫描,遇到比第一个数大的则构成一个桶,计算盛多少水//然后再从右向左扫描一遍publiclong... 查看详情

再遇接雨水(代码片段)

又是经典的接雨水看到这个答案我就没有搞懂为什么要这么写会不会超了这样importjava.util.*;publicclassSolution//以312524为例//从左向右扫描,遇到比第一个数大的则构成一个桶,计算盛多少水//然后再从右向左扫描一遍publiclong... 查看详情

再遇新冠:阳过留痕

2019底始于武汉的疫情,经过近三年终于病毒致病性下降到了一个可接受的程度,彻底放开,终于避无可避,以风卷残云方式席卷武汉的大街小巷,举目四望,都是阳人,个人的中招变得理所当然,... 查看详情

再遇org.apache.catalina.connector.clientabortexception:java.net.socketexception:断开的管道(writefailed)(代码片

优付商户平台“付款记录”页面,商户操作员点击“下载结算凭证”按钮,系统会将所选条件的交易的回单文件以zip包的形式返回给浏览器页面。   由于程序涉及到复杂计算,同时涉及到读库、网络、磁盘IO,耗时比... 查看详情

java反射基础笔记

...,遇到的问题与学习的东西也没有很好的做过记录,导致再遇到时耗费大量时间上网搜索,所以决定串下基础知识并尽量形成记录,方便自己之后遗忘时查询,也方便各位有需求的伙伴翻阅查看,大家共同探讨、学习。  本次... 查看详情

简单易实现的水平居中垂直居中方法

...方法中找出一种简单易懂的方法,让大家一目了然,以后再遇到也能立即想出来。1、水平居中:使div02在div01中水平居中。```<divid="div01"><divid="div0 查看详情

mysql初始化时所遇到的问题

...心老,不怕路长。文章目录发现问题:问题解决再遇问题彻底解决经验总结发现问题:    近期,在机房的一台电脑上重新安装MySQL(使用解压版),一顿操作猛如虎,然后要初始化的时候出现了一个让我很... 查看详情

2020年总结:携梦而行,无怨无悔

...点2.追梦的由来与团队的组建3.学习与沉淀4.困境5.希望6.再遇困境7.关于梦的思考8.展望未来2020于技术我的技术之旅关于对技术的思考1.巩固基础2.多看书和大佬的博客3.学习思考方式比学习一项技术更重要2020于CSDN1.初识CSDN2.第一... 查看详情

如何做好文旅夜游项目资源的综合开发

...,到国内故宫上元之夜、云门山龙境、大唐不夜城和再遇娘子关等许多具有地域文化特色的知名夜游品牌都获得了良好的口碑和经济效益,吸引了来自世界各地的游客前来体验。夜游项目投资决策分析对于夜游 查看详情

mysql初始化时所遇到的问题

...心老,不怕路长。文章目录发现问题:问题解决再遇问题彻底解决经验总结发现问题:    近期,在机房的一台电脑上重新安装MySQL(使用解压版),一顿操作猛如虎ÿ 查看详情

赢在csdn:我在csdn的成长,“长风破浪会有时”,如何保证自己有持续写作的动力?(代码片段)

...客之路”初探1.2、CSDN一眼看得到的优势是什么?1.3、大学再遇CSDN二、为什么开始在C站输出内容?2.1、“好记性不如烂笔头”2.2、记事本到“鼓励工具”三、C站吸引我的地方是什么?3.1、海量的技术文章与庞大的用户群体... 查看详情

求流浪猫鲍勃2:鲍勃的礼物2020在线免费播放百度云资源

... 流浪猫鲍勃2 / 鲍勃的圣诞礼物 / 街角再遇BOB(港) / 再见街猫BOB(台) / 鲍勃的礼物 / A Gift From Bob流浪猫鲍勃2:鲍勃的礼物的剧情简介詹姆斯(卢克·崔德威 Luke Treadaway&nbs... 查看详情

smart原则制定大学目标

...,多总结这些公司在面试中经常会问到的问题,以后面试再遇 查看详情

那些歌,那些故事

...剑。公元745年,纯阳宫“紫虚子”祁进前往万花谷做客,再遇谷之岚。佳人伫立烟雨,宛若绮丽诗赋。祁进如遭雷击,竭力补偿当年过错,与谷之岚结为伴侣,恩爱非凡,羡煞江湖中人。然好景不长,姬别情为暗杀“气吞长江”... 查看详情

《ctfshow-web入门》08.web71~80(代码片段)

web80,再遇蚁剑。目录web71知识点题解web72知识点题解web73题解web74题解web75知识点题解web76题解web77知识点题解web78知识点题解web79题解web80知识点题解ctf-web入门web71知识点ob_get_contents():得到输出缓冲区的内容。ob_end_clean():清除缓... 查看详情

这几首歌的歌词链接是啥?(我想弄到qq空间里。)

...再好曾祈求留住你或盲从附和你我呆坐在那处整天想方法再遇你天黑特别难耐心花不敢再开今晚终於要与你分开随意地叫一杯白咖啡安心裹哀糖甜是有他在斋啡够味我心六月有天他在大门楼下吻别时先发现其实爱渐渐的巩固永不... 查看详情