生产环境 InputStream.available() = 0 导致的一次血案

1、问题现象

          InputStream is = connection.getInputStream();
            String reqData = "";
            if (is != null && is.available()!=0) {
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                byte[] receiveBuffer = new byte[2048];//缓冲区长度
                int readBytesSize = is.read(receiveBuffer);//读取数据长度,InputStream要读取的数据长度一定要小于等于缓冲区中的字节数
                while (readBytesSize != -1) {//判断流是否位于文件末尾而没有可用的字节
                    bos.write(receiveBuffer, 0, readBytesSize);//从receiveBuffer内存处的0偏移开始写,写与readBytesSize长度相等的字节
                    readBytesSize = is.read(receiveBuffer);
                }
                reqData = new String(bos.toByteArray(), "UTF-8");//编码后的tr2报文
                String reqDataEncrypt=Base64Utils.base64Encode(reqData,"UTF-8");
                logger.info("请求返回报文:{}", reqDataEncrypt);
            }

在生产环境数据请求量的时候经常无法进入读取数据流的逻辑, 导致业务逻辑报空指针异常,联系了接口提供方,排查数据是有正常通过数据流回写,联系运维排查了网络,网络没有异常。既然数据回写与网络都没有问题, 我们就着研究上面的业务代码。

2、问题原因

进过对上面代码进行添加日志,发现“is.available()” 是等于0,导致无法进入读取数据流的逻辑。
为什么接口服务方已经返回了数据 但 is.available()=0呢 ? 为什么测试环境压测也没能复现?
带着这两个疑问,在google上需求答案。 终于在一些博客上发现 is.available() 判断数据流是否为0,是非阻塞操作,此操作不会等待数据流全部返回以后才执行,在业务数量大的时候,服务方数据同步返回时间比较长,还未等到数据流返回,程序已经开始执行 is.available(),从而导致服务方有返回数据,而is.available()=0 ;

3、问题解决方案

InputStream的available()方法的作用是返回此输入流在不受阻塞情况下能读取的字节数。网络流与文件流不同的关键就在于是否“受阻”二字,网络socket流在读取时如果没有内容read()方法是会受阻的,所以从socket初始化的输入流的available也是为零的,所以要read一字节后再使用,这样可用的字节数就等于 available + 1。但文件读取时read()一般是不会受阻的,因为文件流的可用字节数 available = file.length(),而文件的内容长度在创建File对象时就已知了。
所以调用网络流(socket)的available()方法前,一定记得要先调用read()方法,这样才能避免获取为0的不正确情况。

      InputStream is = connection.getInputStream();
            String reqData = "";
            is.read();  //read()为阻塞操作,会等待数据全部返回后执行
            if (is != null && is.available()!=0) {
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                byte[] receiveBuffer = new byte[2048];//缓冲区长度
                int readBytesSize = is.read(receiveBuffer);//读取数据长度,InputStream要读取的数据长度一定要小于等于缓冲区中的字节数
                while (readBytesSize != -1) {//判断流是否位于文件末尾而没有可用的字节
                    bos.write(receiveBuffer, 0, readBytesSize);//从receiveBuffer内存处的0偏移开始写,写与readBytesSize长度相等的字节
                    readBytesSize = is.read(receiveBuffer);
                }
                reqData = new String(bos.toByteArray(), "UTF-8");//编码后的tr2报文
                String reqDataEncrypt=Base64Utils.base64Encode(reqData,"UTF-8");
                logger.info("请求返回报文:{}", reqDataEncrypt);
            }

感谢:
https://stackoverflow.com/questions/5826198/inputstream-available-is-0-always
https://www.cnblogs.com/zjfjava/p/10829241.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章