Azure &引用;指定的阻止列表无效";同时并行上传blob
我有一个(相当大的)Azure应用程序,它与Azure blob存储并行上载(相当大的)文件 在几%的上传中,我遇到了一个异常:Azure &引用;指定的阻止列表无效";同时并行上传blob,azure,blobstorage,Azure,Blobstorage,我有一个(相当大的)Azure应用程序,它与Azure blob存储并行上载(相当大的)文件 在几%的上传中,我遇到了一个异常: 指定的阻止列表无效。 System.Net.WebException:远程服务器返回错误:(400)请求错误。 此时,我们运行一段看起来相当无害的代码,将blob并行上载到Azure存储: public static void UploadBlobBlocksInParallel(this CloudBlockBlob blob, FileInfo file)
指定的阻止列表无效。
System.Net.WebException:远程服务器返回错误:(400)请求错误。
此时,我们运行一段看起来相当无害的代码,将blob并行上载到Azure存储:
public static void UploadBlobBlocksInParallel(this CloudBlockBlob blob, FileInfo file)
{
blob.DeleteIfExists();
blob.Properties.ContentType = file.GetContentType();
blob.Metadata["Extension"] = file.Extension;
byte[] data = File.ReadAllBytes(file.FullName);
int numberOfBlocks = (data.Length / BlockLength) + 1;
string[] blockIds = new string[numberOfBlocks];
Parallel.For(
0,
numberOfBlocks,
x =>
{
string blockId = Convert.ToBase64String(Guid.NewGuid().ToByteArray());
int currentLength = Math.Min(BlockLength, data.Length - (x * BlockLength));
using (var memStream = new MemoryStream(data, x * BlockLength, currentLength))
{
var blockData = memStream.ToArray();
var md5Check = System.Security.Cryptography.MD5.Create();
var md5Hash = md5Check.ComputeHash(blockData, 0, blockData.Length);
blob.PutBlock(blockId, memStream, Convert.ToBase64String(md5Hash));
}
blockIds[x] = blockId;
});
byte[] fileHash = _md5Check.ComputeHash(data, 0, data.Length);
blob.Metadata["Checksum"] = BitConverter.ToString(fileHash).Replace("-", string.Empty);
blob.Properties.ContentMD5 = Convert.ToBase64String(fileHash);
data = null;
blob.PutBlockList(blockIds);
blob.SetMetadata();
blob.SetProperties();
}
一切都很神秘;我认为我们用来计算块列表的算法应该产生长度相同的字符串…我们遇到了类似的问题,但是我们没有指定任何块ID,甚至没有在任何地方使用块ID。在我们的案例中,我们使用了:
using (CloudBlobStream stream = blob.OpenWrite(condition))
{
//// [write data to stream]
stream.Flush();
stream.Commit();
}
这将导致指定的块列表无效。在并行加载下出错。将此代码切换为使用方法,同时将数据缓冲到内存中修复了该问题:
using (MemoryStream stream = new MemoryStream())
{
//// [write data to stream]
stream.Seek(0, SeekOrigin.Begin);
blob.UploadFromStream(stream, condition);
}
显然,如果将太多数据缓冲到内存中,这可能会对内存产生负面影响,但这只是一种简化。需要注意的是,
UploadFromStream(…)
在某些情况下使用Commit(),但检查附加条件以确定要使用的最佳方法。当多个线程将流打开到具有相同文件名的blob中并尝试同时写入该blob时,也会发生此异常。注意:此解决方案基于Azure JDK代码,但我认为我们可以安全地假设纯REST版本将具有非常相同的效果(实际上与任何其他语言一样)
因为我花了整整一天的时间来解决这个问题,即使这是一个极端的情况,我也会在这里留下一张便条,也许这会对某些人有所帮助
我做的一切都对。我有顺序正确的块ID,我有相同长度的块ID,我有一个干净的容器,没有以前一些块的剩余物(这三个原因是我能通过谷歌找到的唯一原因)
有一个陷阱:我一直在为commit via构建阻止列表
CloudBlockBlob.commitBlockList(Iterable<BlockEntry> blockList)
通过
BlockSearchMode.COMMITTED
在第二个论点中。事实证明,这是根本原因。有一次我把它改成了
BlockSearchMode.UNCOMMITTED
最终在单参数构造函数上实现
BlockEntry(String id)
默认情况下使用uncommitted,提交阻止列表有效,blob成功保留。您提到过有时候会发生这种情况。这种情况是发生在特定文件中,还是随机发生的,即代码在某个文件中失败,然后在同一个文件中再次工作?有没有可能,街区的数量超过50000座。另外,您是否可以检查失败的文件,文件大小是否可以被块长度整除?如果可能的话,你能运行fiddler并跟踪失败的请求,特别是正在发送的数据吗?好主意,@Gaurav Mantri,谢谢你——不,它是“随机”发生的,对同一个文件重试似乎有效。对块大小(4M)也没有特别的依赖关系,是的,我的同事指出了其中的fencepost错误。不幸的是,我们在这件事上处于云端,所以不能胡闹。想知道你是否找到了解决这个问题的方法?我们有一个适当的重试策略,可以为我们解决这个问题,但我们仍然会得到10万次写入中可能有1次失败(据估计)。你有没有追根究底?@ScottCate-遗憾的是,没有。我们也加入了重试策略,但仍然看到(公认罕见的)失败。我现在已经转到另一个项目,所以我不知道它是否仍然发生。很好,谢谢。我最初的问题来自六年前:代码已经不存在了,唉,我为之编写代码的组织也不存在了。
BlockEntry(String id)