我将在Java中实现一个(简单的)下载器应用程序作为个人练习。它将在不同的线程中运行多个作业,这样我将在执行期间的所有时间同时下载几个文件。
我希望能够定义所有下载作业之间共享的下载速率限制,但即使是单个下载任务,我也不知道该怎么做。我该怎么做呢?我应该尝试实施哪些解决方案?
感谢。
答案 0 :(得分:5)
答案 1 :(得分:2)
这个问题很高,所以我希望你不要期待低水平的答案。通常,您首先需要定义/决定将使用哪些网络实用程序。例如,你打算打开一个标准的Java Socket吗?您将使用某些第三方网络库吗?你是否熟悉了任何可用的选项?
在最一般意义上,您可以通过您决定的网络库来控制带宽。它应该是一个相对简单的公式。
您将拥有一些设置带宽限制的对象(称为套接字)。您将套接字的带宽限制(通常)设置为总带宽/活动连接数。如果某些连接未使用其完全分配的带宽,您可以持续优化此数字。当你到达那里时,请求有关该算法的帮助,如果你愿意的话......
等式的第二部分是,OS /网络库是否已经通过给出速率限制数量来控制带宽,或者您是否需要通过限制读/写速率来自己控制此过程?这并不像看起来那么简单,因为操作系统可以使用TCP套接字缓冲区来读取数据直到满。假设您有一个2Mb的套接字缓冲区用于入站流量。如果您依赖远程端仅在2Mb缓冲区已满时停止发送数据,则必须等待2Mb数据传输才能通过从队列中删除速率限制,您将始终有一个巨大的突发在每个套接字上你可以限速之前。
此时你开始谈论编写一个将通过tcp(或UDP)运行的协议,以便一方可以告诉另一方,“确定发送更多数据”,或“等待,我的带宽限制已被暂时命中”。简而言之,开始使用,然后在实施实施后提出问题,并希望改进它......
答案 2 :(得分:2)
我从一个管理所有下载的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秒)内可以读取多少字节的计数器。每次读取调用都会检查计数器并使用它来限制读取的实际字节数。计时器用于重置计数器。
在实践中,在多个流中分配带宽可能会变得相当复杂,特别是为了避免饥饿和促进公平,但这应该会给你一个良好的开端。
答案 3 :(得分:1)
这基本上是大多数限制器的工作方式(就像wget
)