個人的な演習として、Javaで(単純な)ダウンローダーアプリケーションを実装します。実行中に常にいくつかのファイルを同時にダウンロードするように、異なるスレッドでいくつかのジョブを実行します。
すべてのダウンロードジョブ間で共有されるダウンロードレート制限を定義できるようにしたいのですが、1つのダウンロードタスクでもそれを行う方法がわかりません。どうすればこれを行うことができますか?実装してみるべきソリューションは何ですか?
ありがとう。
個人的な演習として、Javaで(単純な)ダウンローダーアプリケーションを実装します。実行中に常にいくつかのファイルを同時にダウンロードするように、異なるスレッドでいくつかのジョブを実行します。
すべてのダウンロードジョブ間で共有されるダウンロードレート制限を定義できるようにしたいのですが、1つのダウンロードタスクでもそれを行う方法がわかりません。どうすればこれを行うことができますか?実装してみるべきソリューションは何ですか?
ありがとう。
この質問は高レベルですので、低レベルの回答を期待しないでください。一般に、最初に使用するネットワークユーティリティを定義/決定する必要があります。たとえば、標準のJavaソケットを開くだけですか?使用するサードパーティのネットワークライブラリはありますか?利用可能なオプションのいずれかに精通しましたか?
最も一般的な意味では、決定したネットワークライブラリを介して帯域幅を制御できます。比較的単純な式である必要があります。
帯域幅制限を設定するある種のオブジェクト(ソケットと呼びます)があります。ソケットの帯域幅制限(一般的に)を、合計帯域幅/アクティブな接続数に設定します。一部の接続が帯域幅の完全な割り当てを使用していない場合は、この数を継続的に最適化できます。そこに着いたら、そのアルゴリズムについて助けを求めてください。
方程式の2番目の部分は、OS /ネットワークライブラリがレート制限番号を与えるだけですでに帯域幅を制御できるか、それとも読み取り/書き込みレートを制限することによってこのプロセスを自分で制御する必要があるかということです。OSには、データがいっぱいになるまでデータを読み込むTCPソケットバッファが含まれている可能性があるため、これは見た目ほど簡単ではありません。インバウンドトラフィック用に2Mbのソケットバッファがあるとします。2Mbバッファがいっぱいになったときにのみデータの送信を停止するリモート側に依存している場合、キューから削除してレート制限を行う機会を得る前に、2Mbのデータが転送されるのを待つ必要があります。常に大きなバーストが発生します。制限を評価する前に、すべてのソケットで。
その時点で、tcp(またはUDP)を介して実行されるプロトコルの作成について話し始め、一方の側がもう一方の側に「OK、さらにデータを送信します」または「待機し、帯域幅の制限に一時的に達しました」と伝えることができます。簡単に言えば、始めて、実装が整ってそれを改善したい場合は質問をしてください...
まず、すべてのダウンロードを管理するDownloadManagerから始めます。
interface DownloadManager
{
public InputStream registerDownload(InputStream stream);
}
管理された帯域幅に参加したいすべてのコードは、ダウンロードマネージャーからの読み取りを開始する前に、そのストリームをダウンロードマネージャーに登録します。そのregisterDownload()メソッドでは、マネージャーは指定された入力ストリームをでラップしますManagedBandwidthStream
。
public class ManagedBandwidthStream extends InputStream
{
private DownloadManagerImpl owner;
public ManagedBandwidthStream(
InputStream original,
DownloadManagerImpl owner
)
{
super(original);
this.owner = owner;
}
public int read(byte[] b, int offset, int length)
{
owner.read(this, b, offset, length);
}
// used by DownloadManager to actually read from the stream
int actuallyRead(byte[] b, int offset, int length)
{
super.read(b, offset, length);
}
// also override other read() methods to delegate to the read() above
}
ストリームは、read()へのすべての呼び出しがダウンロードマネージャーに戻されることを保証します。
class DownloadManagerImpl implements DownloadManager
{
public InputStream registerDownload(InputStream in)
{
return new ManagedDownloadStream(in);
}
void read(ManagedDownloadStream source, byte[] b, int offset, int len)
{
// all your streams now call this method.
// You can decide how much data to actually read.
int allowed = getAllowedDataRead(source, len);
int read = source.actuallyRead(b, offset, len);
recordBytesRead(read); // update counters for number of bytes read
}
}
帯域幅割り当て戦略は、getAllowedDataRead()を実装する方法に関するものです。
帯域幅を制限する簡単な方法は、特定の期間(たとえば、1秒)に読み取ることができるバイト数のカウンターを保持することです。readを呼び出すたびにカウンターが調べられ、それを使用して実際に読み取られるバイト数が制限されます。タイマーはカウンターをリセットするために使用されます。
実際には、複数のストリーム間での帯域幅の割り当ては、特に飢餓を回避し、公平性を促進するために非常に複雑になる可能性がありますが、これにより、公平なスタートを切ることができます。
それは基本的にほとんどのリミッターがどのように機能するかです(ちょうどのようにwget
)