3

クライアントアプリがファイルを開くクライアント/サーバーアプリケーションがあります。これらのファイルはチャンクに分割され、サーバーに送信されます。

クライアントはファイル チャンクを送信するだけでなく、他のデータも送信します。各メッセージ (データまたはファイルチャンク) には優先度レベルがあります。

すべてのメッセージはバッファリングされ、優先度に従って TCP ストリームに書き込まれます。現在、入力ストリームと出力ストリームは、ファイルが完全に送受信されるたびに両側 (クライアント/サーバー) で閉じられます。これは、ファイルの送信にかかる限り、ストリームが開いたままになることを意味します。

TCP 接続は再接続され、メッセージの送信が再開されるため、失敗しても構いません。そのため、ストリームはある時点で閉じられます。

ただし、たとえば JVM が強制終了された場合、ストリームはクリーンアップされません。

これを解決するために最初に考えたのは、ファイナライザーにクリーンアップ コードを追加することでしたが、JVM が強制終了された場合 (または System.exit が呼び出された場合) にこれらが実行されない可能性があることは理解しています。

2 番目に考えたのは、アプリケーションの一部を書き直して、1 つのチャンクの読み取り/書き込みにかかる時間だけストリームを使用することです。したがって、チャンクの数だけファイルを開いたり閉じたりすることになります。この方法には、try-catch-finally コンストラクトを使用できるという利点がありますが、ファイルを開いたり閉じたりするのにかなりのオーバーヘッドがかかることがよくあります。

では、デザインでfinally{}ブロックが許可されていない場合、リソースをクリーンアップするにはどうすればよいでしょうか? それとも、おそらく私が説明したのと同様の方法で、そのような設計を避けるべきですか?

また、優先度 (理論的には無制限) と同じ数のファイルが開かれている可能性についても懸念しています。

通常、ほとんどのファイルは数 KiB の大きさですが、場合によっては数 GB になることもあります。

ご意見をお寄せいただきありがとうございます。

編集:画像を追加

そのままファイル転送

4

2 に答える 2

1

をご覧くださいjava.lang.Runtime.addShutdownHook()。開いているすべてのストリームを閉じるフックを追加してみます。その場合、そのリストを維持する必要があります。ただし、次の点に注意してください。

まれに、仮想マシンが異常終了する場合があります。つまり、正常にシャットダウンせずに実行が停止する場合があります。これは、たとえば、Unix の SIGKILL シグナルまたは Microsoft Windows の TerminateProcess 呼び出しで、仮想マシンが外部で終了したときに発生します。たとえば、内部データ構造が破損したり、存在しないメモリにアクセスしようとしたりして、ネイティブ メソッドが失敗した場合にも、仮想マシンは中止される可能性があります。仮想マシンが異常終了した場合、シャットダウン フックが実行されるかどうかは保証されません。

于 2012-03-24T18:38:55.953 に答える
1

私が質問を正しく理解していれば、JVM が制御不能な方法で終了した場合に適切にクリーンアップしないことを心配しています。

これは一般的な問題ですが、あなたの特定のケースでは心配する必要はないと思います。読み取り用にファイルを開くだけなので、アプリケーションが壊れる可能性のある永続的な状態はありません (私が理解しているように、TCP 接続の反対側は切断を適切に処理できます)。開いているファイルは、アプリケーション内部の一種の状態です。アプリが強制終了された場合、オペレーティング システムは、そのファイルの操作を処理するために内部で使用しているすべてのロックまたはデータ構造をクリーンアップします。これは、OS カーネルに「ガベージ」が残らないことを意味します。クリーンアップのエレガントな方法でも推奨される方法でもありませんが、機能します (もちろん、これは通常の方法としてではなく、緊急時にのみ使用してください)。物を扱うこと)。

もちろん、アプリが強制終了された場合、実行していた操作は終了しません。これにより、アプリケーション レベルの状態の一貫性が失われたり、一部のアプリケーション ロジックで他の問題が発生したりする可能性がありますが、OS レベルの構造に影響を与えることはありません (OS にバグがないと仮定すると)。データが失われたり、アプリの状態が壊れたりする可能性がありますが、カーネル内のファイル システムやデータを壊すことはできません。

そのため、トランザクション スタイルの操作 (完全に実行するか、まったく実行しないかのいずれかで、中間状態が外部から見えるようになってはならない操作) があるアプリケーションを強制終了すると、問題が発生する可能性があります。例として、ファイルがあり、それを新しいバージョンに置き換える必要がある場合があります。最初に古いファイルを切り詰めてから新しい内容を書き込んだ場合 (これは明らかな方法です)、ファイルを切り詰めた後、新しい内容を書き込む前にアプリが強制終了された場合、問題が発生します。ただし、そのようなリスクを引き起こすには、変更可能な状態、つまり何かを書き込む必要があります。読むだけなら、ほぼ確実に安全です。

そのような場合に遭遇した場合、いくつかの道をたどることができます。1 つは、アプリを完全に保護し、常に適切にクリーンアップされるようにすることです。実際には、これは非常に困難です。killJava アプリにシャットダウン フックを追加できます。これは、アプリケーションの終了時に実行されますが、制御されたシャットダウン ( Linux の通常の (SIGTERM)など) に対してのみ機能します。しかし、これはアプリが管理者によって強制的に殺されるのを防ぐことはできません (kill -9もちろん、他のオペレーティングシステムには、これらの状況に相当するものや、制御できない方法でアプリが閉じられる他のケースがあります。制御されたハードウェアおよびソフトウェア環境で実行されるリアルタイム アプリを作成していない場合、アプリが強制的に終了され、クリーンアップ手順の実行が妨げられる可能性があるすべての方法を防ぐことはほとんど不可能です。

そのため、合理的な妥協案は、多くの場合、アプリで単純な予防措置 (シャットダウン フックなど) のみを講じることですが、すべてを防ぐことはできないことを念頭に置いて、手動によるクリーンアップを可能かつ簡単にすることをお勧めします。たとえば、ファイルを上書きする場合の解決策は、操作を分割して、最初に古いファイルを新しい名前に移動してバックアップとして保持することです (この操作は通常、OS レベルでアトミックであるため安全です)。ファイルの新しい内容を古い名前に変更し、新しいファイルが正しく書き込まれたことを確認した後、バックアップを削除します。このようにして、操作の間にアプリが強制終了された場合、単純なクリーンアップ手順が存在します。バックアップ ファイルを元の名前に移動し、古いが一貫した状態に戻します。これは手動で行うことができます (ただし、文書化する必要があります)。または、クリーンアップ スクリプトまたは特別な「クリーンアップ」コマンドをアプリに追加して、この操作を簡単にして目に見えるようにし、手順中の人的エラーの可能性を排除することもできます。アプリがめったに強制終了されないと仮定すると、これは通常、アプリを防弾にするために多くの時間を費やして、それが実際には不可能であることに気付くよりも効果的です。多くの場合、失敗を受け入れることは、それを防止しようとするよりも優れています (プログラミングに限らず)。

OS およびハードウェア レベルでやけどを負う可能性はありますが、コモディティ ハードウェアおよび OS でそれを防ぐのは非常に困難です。アプリが新しいファイルを正しい場所に見つけたとしても、ディスクに物理的に書き込まれていない可能性があり、キャッシュにのみ存在する場合、アプリが強制終了されても失われませんが、誰かが電源を切ると失われますマシンに接続します。しかし、この種の失敗に対処することは、まったく別の話です。

簡単に言うと、ファイルを読み取るだけの特定のケースでは、アプリが強制終了された場合、OS はすべてのクリーンアップを実行する必要があります。その他のケースでは、いくつかのヒントが上記に記載されています。

于 2012-03-24T19:32:07.500 に答える