【エラー解決方法】バッチ処理実行時にSSLExceptionが発生!?を解決する方法
目次
こんにちわ。井上です。
バッチ実行中にSSLExceptionが発生するようになりました。
日に3回実行しているのですが、1~2回エラーが起こっています。
このバッチを入れてから1週間くらい経った頃からです。
バッチの内容としては、とあるサイトに接続してCSVファイルを読み込み、データを登録するというものです。
CSVデータはおよそ30, 000行程度になります。
この原因が判明しましたので、その時の対処法をご紹介します。
エラー内容(javax.net.ssl.SSLException: SSL peer shut down incorrectly)
バッチ処理実行中に以下のエラーが発生しました。
javax.net.ssl.SSLException: SSL peer shut down incorrectly
at sun.security.ssl.InputRecord.readV3Record(InputRecord.java:557)
at sun.security.ssl.InputRecord.read(InputRecord.java:509)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:927)
at sun.security.ssl.SSLSocketImpl.readDataRecord(SSLSocketImpl.java:884)
at sun.security.ssl.AppInputStream.read(AppInputStream.java:102)
at java.io.BufferedInputStream.read1(BufferedInputStream.java:273)
at java.io.BufferedInputStream.read(BufferedInputStream.java:334)
at sun.net.www.MeteredStream.read(MeteredStream.java:134)
at java.io.FilterInputStream.read(FilterInputStream.java:133)
at sun.net.www.protocol.http.HttpURLConnection$HttpInputStream.read
(HttpURLConnection.java:3053)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:283)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:325)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:177)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:154)
at java.io.BufferedReader.readLine(BufferedReader.java:317)
at java.io.BufferedReader.readLine(BufferedReader.java:382)
SSLExceptionについては以下のような説明が書かれています。
「SSLサブシステムによって検出されたエラーを示します。このクラスはSSLに関する操作に失敗したときに生成される例外の汎用クラスです。」
ざっくりしていてエラーの原因はすぐには分からないですね……。
エラー発生時の環境
エラー発生時の環境は以下になります。
- 言語
- Java1.7.0_51
- データベース
- PostgreSQL9.4
エラーの原因
エラーの原因を探るにあたり、以下の点に着目しました。
・サイトへの接続エラーになっているわけではない(handshakeエラーではない)
・エラーの発生する確率は50%くらいで、毎度起きるわけではない
・同じサイトから別のデータも同じ方法で取得しているが、そちらでは起きたことがない
(CSVデータおよそ3, 000行程度)
上記のことから、処理の時間がある一定以上になるとエラーが発生するのではという推測ができました。
実際にとある日のログを確認すると、
・処理成功時のログ
2017/01/21 07:20:01.402 [INFO] – START
2017/01/12 07:25:38.700 [INFO] – END
所要時間:5分37秒
・処理失敗時のログ
2017/01/21 01:20:02.023 [INFO] – START
2017/01/21 01:26:57.152 [ERROR]
エラー発生までの所要時間:6分55秒
何日分か確認しましたが、やはり処理時間が6分30秒を超えるとエラーが発生するようです。
(エラーとなる時間は日によって少し異なる)
エラー解決方法
エラーの原因が分かったので、プログラムを確認します。
修正前のプログラム
// URLの作成 URL url = new URL(”サイトのURL”); BufferedReader reader = null; HttpURLConnection con = null; InputStreamReader isr = null; try { // 接続 con = (HttpURLConnection) url.openConnection(); con.connect(); isr = new InputStreamReader(con.getInputStream(), "Shift-JIS"); reader = new BufferedReader(isr); while ((line = reader.readLine()) != null) { // データ登録処理 が入る } } catch (Exception e) { log.error("", e); } finally { // 終了処理 if (reader != null) reader.close(); if (isr != null) isr.close(); }
修正後のプログラム
// URLの作成 URL url = new URL(”サイトのURL”); BufferedReader reader = null; HttpURLConnection con = null; InputStreamReader isr = null; List<String> str_list = new ArrayList<String>(); try { // 接続 con = (HttpURLConnection) url.openConnection(); con.connect(); isr = new InputStreamReader(con.getInputStream(), "Shift-JIS"); reader = new BufferedReader(isr); while ((line = reader.readLine()) != null) { str_list.add(line); } } catch (Exception e) { log.error("", e); } finally { // 終了処理 if (reader != null) reader.close(); if (isr != null) isr.close(); } for(String str : str_list) { // データ登録処理が入る }
修正前:接続しっぱなしで処理をし、処理終了後に接続をクローズ。
修正後:接続して取得した行データを文字列としてリストに格納して、まずは接続をクローズ。その後に処理を行う。
修正後のプログラムでバッチを回していますが、接続時間が短くなったおかげでエラーが出たことはありませんので、やはりエラーの原因は接続時間が長すぎたということになります。
(どのくらいの長さになると接続が切れてしまうのかは、データの提供元には未確認ですが)
まとめ
今回のエラーの原因は、接続した状態で放置していたことでした。
強制的に接続が切れてしまうので接続先が悪いのかもと疑ってしまいましたが、結局はプログラムの組み立て方が悪かったと思います。
接続先のサイトに文句を言わなくてよかったです。
それではまた問題が発生しましたらお会いしましょう。