質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Tomcat

TomcatはApache Software Foundation (ASF)で開発されたオープンソースのWebコンテナです。

Q&A

2回答

4016閲覧

サーブレットから動的に作成したCSVを遅延なく記録させたい。

itmsd

総合スコア16

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Tomcat

TomcatはApache Software Foundation (ASF)で開発されたオープンソースのWebコンテナです。

0グッド

3クリップ

投稿2016/09/05 06:42

編集2016/09/06 02:19

###前提・実現したいこと

サーブレットでCSVを作成させて遅延なくハードディスクへ記録させたいと思います。

###発生している問題・エラーメッセージ

サーブレットを実行し、CSVを作成するのですが、実際にハードディスクに記録
されるまでに5秒程度の遅延が発生しています。

上記と同じことを、jdk1.4とtomcat3の組み合わせで実行しているときは、問題
なく運用できておりましたが、jdk8とtomcat8の環境に変えてから上記の問題が
発生するようになりました。

ソースコード問題、tomcat等の設定でお気づきの点がございましたら、
ご教示いただければ、幸甚にぞんじます。

###該当のソースコード

public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doAction(request, response);
}

public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doAction(request, response);
}

private void doAction(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession(true);
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma","no-cache");
request.setCharacterEncoding("UTF-8");

/途中省略/

synchronized (session) {
sc_ = getServletConfig().getServletContext();
connPool_ = (ConnectionPool)sc_.getAttribute("connPool");
loginUser = (SysUserBeans)session.getAttribute("loginUser");

String sceneStr = request.getParameter("scene"); int scene = -1; if (sceneStr != null) scene = Integer.parseInt(sceneStr); switch(scene) { case 1: // 一覧 response.setContentType(CONTENT_TYPE); showRows(request, response, session); break;

/途中省略/

case 22: // CSV作成 downloadCSV(request, response, session); break; default: // 検索条件 response.setContentType(CONTENT_TYPE); sc_.getRequestDispatcher("/view/sales/salesAggregateFrame.jsp").forward(request, response); } rows.setReqPrintUrl(null);

}
}

private void downloadCSV(HttpServletRequest request, HttpServletResponse response, HttpSession session) throws ServletException, IOException {
SalesAggregateBeans rows = (SalesAggregateBeans)session.getAttribute("salesAggregate");
SysUserBeans loginUser = (SysUserBeans)session.getAttribute("loginUser");

String fileName; fileName = sc_.getRealPath("/data/210.csv"); rows.setReqPrintUrl(response.encodeURL("/example/data/.csv")); FileOutputStream fo = new FileOutputStream(fileName); OutputStreamWriter ps = new OutputStreamWriter(fo, "SJIS"); int iMax = rows.getSize(); for (int i = 0; i < iMax; i++) { rows.getRow(i); ps.write("\"" + rows.getCd() + "\",\""); ps.write(rows.getName() + "\",\""); ps.write(rows.getAddress() + "\",\""); ps.write(rows.getReadings() + "\",\""); ps.write(rows.getSalesmanName() + "\","); ps.write(rows.getCarryOver() + ","); ps.write(rows.getSum(0) + ","); ps.write(rows.getSum(1) + ","); ps.write(rows.getSum(2) + ","); ps.write(rows.getBalance()); ps.flush(); } ps.close(); ps = null; fo.close(); fo = null; sc_.getRequestDispatcher("/view/sales/salesAggregate.jsp").forward(request, response);

}
###試したこと

CSVの出力件数は、わずか5行でも、100行程度でも、5秒程度の遅延が発生しています。

###補足情報(言語/FW/ツール等のバージョンなど)

・Windows10
・jdk8
・tomcat8
(サーブレットの設定は、インストール直後の状態です。)

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2016/09/05 07:16

Writer を close したからといって Stream が閉じるわけじゃないんだが
A-pZ

2016/09/05 07:18

サーブレットのクラス宣言やサーブレットの設定を追記すると回答が得られるかもしれません。
A-pZ

2016/09/05 09:25

もう少し詳細に書くとよいでしょうか。具体的にはサーブレットからdownloadCSVメソッドがどのように呼びだされているかわかりません。downloadCSVメソッドを非同期に呼び出されているなどが考えられます。
matobaa

2016/09/05 13:10 編集

> Writer を close したからといって Stream が閉じるわけじゃないんだが あれ、閉じないんでしたっけ……
退会済みユーザー

退会済みユーザー

2016/09/05 13:29 編集

すぐには閉じませんね。いつ閉じるかわからないので明示的に閉じる必要があります。
matobaa

2016/09/05 13:36

え、そうなのか。手元ではWriterを閉じた直後にstreamに書こうとしたら、もう閉じてるぜって怒られたんですが、気のせいだったか?
退会済みユーザー

退会済みユーザー

2016/09/06 01:26

streamを閉じたらそれを 引数にしたWriter は閉じられますので 逆に書いてしまったとか?
itmsd

2016/09/06 01:55

A-pZさん、downloadCSVの呼び出し側のソースコードを追記しましたが、 参考になりますでしょうか?
A-pZ

2016/09/06 02:03

追記ありがとうございます!しかし残念ながら記載していただいた内容には同期しないで処理をしているかどうかを判別するには至りませんでした。できれば、このdownloadCSVを呼び出しているところで、最終的にServletのdoGetやdoPostに辿ると思うのですが、そこまでのメソッドと、サーブレットの設定があるとわかるかも知れません。
itmsd

2016/09/06 02:27

A-pZさん、早速のご返事有難うございます。 ご指摘のように最終的にはServletのdoGetへ辿るのでソースコードへ追記を致しました。 tomcat3のときは問題なく動作していましたので、同期・非同期等は意識しておりませんでしたが、tomcat8へ変更した際にはソースコードに問題があるか、サーブレットの設定に問題があるかもしれません。サーブレットの設定に問題 があるとすれば、まずどの箇所を参照すればよろしいでしょうか。
matobaa

2016/09/06 02:51

itmsdさん、これ現在のデザインだと「サーブレットで210.csvをいったん作ったあとユーザにバトンを返し、それをsalesAggregate.jspから取り出させる」という遷移になってますが「salesAggregate.jsp から取り出そうとしたときに csv をオンザフライで作成する」というデザインに変更することは許容範囲ですか?
itmsd

2016/09/06 03:17

matobaaさん有難うございます。 処理の流れはご指摘の通りです。 一旦、210.CSVへ出力しているのは、バックアップも兼ねているからです。 CSV作成を指示してから、210.CSVへの出力が終わる前に、salesAggregate.jspが表示されてしまい、210.CSVを取り出そうとすると、前回作成した内容だったり、まだ作成されていなかったりする現象に悩まされています。 オンザフライで作成するとは、ファイルへ出力せずにダウンロードさせるの意味でしょうか?
matobaa

2016/09/06 09:27 編集

バックアップも兼ねているのですか、難しい条件ですね……。オンザフライはご認識のとおりです。ところで salesAggregate.jsp からのダウンロードはただのリンクですか、それともDownloadServletのようにJavaで実現していますか? WebブラウザとTomcatサーバ、間に挟まってるキャッシュサーバやProxyサーバの時刻はずれてないでしょうか?
itmsd

2016/09/07 06:01

ご返事が遅くなりまして、申し訳ありません。 salesAggregate.jspからのダウンロードは、作成した210.csvへのリンクです。 今のところサーバは同じ筐体で動いていますので、時刻のずれはないと思うのですが。
matobaa

2016/09/07 08:56

Webブラウザとサーバの時刻ズレはないということでしょうか。だとするとJava以外のところでなにか悪さしているやつがいる可能性も考えないといけないですね。
guest

回答2

0

手元の環境で再現できないため、推測による回答となりますが、、

ps.close();直前に以下の1行を追加してみてはいかがでしょうか?

java

1fo.getFD().sync();

API リファレンスによると、OutputStream#flush()は(おそらくclose()も)
データを OS の書き込みバッファに引き渡すことを保証するだけで、デバイスそのものに書き込むことを保証するものではないようです。
https://docs.oracle.com/javase/jp/8/docs/api/java/io/OutputStream.html#flush--

このストリームの目的の転送先が、ベースとなるオペレーティング・システムによって提供される抽象化オブジェクト(ファイルなど)である場合、ストリームをフラッシュすることで、それまでにストリームに書き込まれたバイトがオペレーティング・システムに渡されて書き込まれることは保証されますが、ディスク・ドライブなどの物理デバイスに実際に書き込まれることは保証されません。

一方、FileDescriptor#sync()は、それを保証してくれるようです。
https://docs.oracle.com/javase/jp/8/docs/api/java/io/FileDescriptor.html#sync--

このFileDescriptorがファイル・システムのファイルのような物理記憶メディアを参照する場合、syncはこのFileDescriptorに関連付けられたバッファのメモリー内部での変更事項がすべて物理メディアに書き込まれるまでは復帰しません。


ちなみに、以下も推測にすぎませんが、

この現象が発生する原因は Java または Tomcat のバージョン違いによるものではなく、
アプリケーションからのファイル出力が多すぎて(例えばTomcat のデバッグログなど)
OS のI/O処理が追いついていない、ということも考えられるかと思います。

投稿2016/09/06 03:29

KiyoshiMotoki

総合スコア4791

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

itmsd

2016/09/06 05:25

KiyoshiMotokiさん 丁寧なご回答をいただき有難うございます。 ご指示にあったように”ps.close();”の前に”fo.getFD().sync();”を挿入して実行 しましたが、残念ながら現象に変わりはありませんでした。
KiyoshiMotoki

2016/09/06 06:39

返信ありがとうございます。 解決しませんでしたか。。 今のところ、私には他にアイデアはありません。 何か思いつきましたら、またご連絡させていただきますね。
itmsd

2016/09/06 07:49

お忙しいところ、恐れ入ります。
guest

0

OutputStreamWriter.html#flush

try (FileOutputStream fo = new FileOutputStream(fileName); OutputStreamWriter ps = new OutputStreamWriter(fo, "SJIS")) { int iMax = rows.getSize(); for (int i = 0; i < iMax; i++) { // 処理中略 } ps.flush(); } finally { // http://docs.oracle.com/javase/jp/7/technotes/guides/language/try-with-resources.html // autocloseable (try-with-resources) // この例で、try-with-resources 文には 2 つの宣言 ZipFile および BufferedWriter が含まれており、セミコロンで区切られています。 // その直後のコードブロックが正常に終了または例外によって終了した場合、BufferedWriter オブジェクトと ZipFile オブジェクトの close メソッドが、この順序で自動的に呼び出されます。リソースの close メソッドは、作成時とは逆の順序で呼び出されます。 }

のように flush したらどうでしょう?

追記

っと Javadoc (close) みると

ストリームを最初にフラッシュして、閉じます。ストリームが閉じられたあとにwrite()またはflush()を呼び出すと、IOExceptionがスローされます。すでに閉じられているストリームを閉じても、何の影響もありません。

で実行が冗長でしたね。

投稿2016/09/05 13:17

編集2016/09/06 02:42
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

itmsd

2016/09/06 01:16

haruka-kanataさん、ご回答有難うございます。 ご指示のように、forループ中にflushを記述しましたが、改善されませんでした。 コメント中にありました、明示的に閉じるとはどのようなコードの記述に なりますでしょうか。
退会済みユーザー

退会済みユーザー

2016/09/06 01:24

やっぱ close で flush 実行されてましたね。
itmsd

2016/09/06 01:54

恐れ入りますが、当方のソースコードのどの箇所が該当しますでしょうか?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問