我有一个可以轻松实现HTTP Ranged-GET协议的类。本质上,一些代码调用该类,告诉它下载文件。下面的MyClass
负责以偶数大小的块(直到最后一个块,它可以是可变长度)下载文件,并将其发送到另一个服务。调用时,它将根据来自第一个HTTP响应的Content-Range实例长度来设置文件大小。
该类使用线程池大小为1的ExecutorService来控制线程。
下面是相关的实现,其中一些函数处理了GET和PUT。
class MyClass implements Runnable {
private long start;
private long chunkSize;
private int chunkNumber;
private int fileSize = 0;
private static final int MAX_RETRIES = 3;
public static final ExecutorService ES = Executors.newSingleThreadExecutor();
public MyClass(long start, long chunkSize, int chunkNumber) {
this.start = start;
this.chunkSize = chunkSize;
this.chunkNumber = chunkNumber;
}
public void run() {
for (int i = 0; i < MAX_RETRIES; i++) {
long end = start + chunkSize - 1; // inclusive so subtract 1
// doHttpGet() is a private instance function...
// if fileSize == 0 (i.e. first chunk downloaded), this will set the fileSize
doHttpGet(start, end);
// doHttpPost() is a private instance function
// it builds the POST from the GET message, which I'm not bothering to show here
if (!doHttpPost()) {
continue;
} else {
submitNextChunk(this);
break;
}
}
}
// this is the function a client uses to invoke the class
public static void submitWork(long newStartByte, long chunkSize, int chunkNumber) {
MyClass mc = new MyClass(newStartByte, chunkSize, chunkNumber);
if (ES.submit(mc) == null) {
//log error
}
}
// PROBLEM AREA?!?!
private static void submitNextChunk(MyClass mc) {
mc.chunkNumber++;
mc.start += mc.chunkSize;
// LOGGER.debug("start=" + mc.start + "\n" + "fileSize=" + mc.fileSize)
if (mc.start < mc.fileSize) {
if (ES.submit(mc) == null) {
//log error
}
}
}
}
这是调用MyClass
的代码片段。
long chunkSize = //somecalculation
DownloadAction.submitWork(0L, chunkSize, 1));
此代码可以正常运行很长时间了。但是,我现在注意到当要下载的文件非常小时(例如,<50个字节),这可能是不确定的行为。似乎正在发生的事情是submitNextChunk()
函数似乎无法正确评估mc.start < mc.fileSize
。例如,如果我们设置packetSize = 100K并使用一个50字节的文件,那么我通过Wireshark看到的是连续HTTP GET请求,请求字节0-99999、100000-199000和200000-299000,。 ..等。(另一端的代码也稍作中断,因为它继续为我们提供了最初的50个字节,而不是HTTP超出范围的错误代码...但这是另一回事了。)>
我担心的是比赛条件微妙:
如果我在submitNextChunk()
中添加一些日志记录以打印出start
和fileSize
,那么我 only 只能看到一个日志语句,其中start = 100000和fileSize = 100,并且该函数将小于表达式正确评估为false。这是有道理的,因为这将是第一次,也是唯一一次调用submitNextChunk()
,并且由于该表达式的计算结果为false,因此该函数将中止。
我担心这是一个线程问题,因为没有调试语句,小于表达式显然会导致true
,这本不应该发生。