生產環境 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

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