Azure - 403错误 - 将大量存储帐户blob复制到另一个存储帐户,并按现收现付帐户

时间:2017-09-13 13:00:58

标签: azure azure-storage azure-storage-blobs

我正在

  

403 Forbidden

将大量块blob从一个存储帐户复制到另一个存储帐户(在另一个区域中作为备份)时出现

错误。复制了100,000多个后,我收到403 Forbidden错误。

我看到有关配额的答案,但我相信这是免费帐户。我有一个578,000个文件的客户端,我从本地移动到Azure,工作正常,但我无法复制到我设置为备份的另一个存储帐户(如果主要是删除)。

我正在使用StartCopyAsync,然后检查Copystate状态以验证复制是否成功并在我的代码中重试,但它似乎在StartCopyAsync上失败。

副本工作正常,直到我复制了超过100,000个文件,然后发生错误。我不确定是什么原因造成的,因为相同的代码首先适用于这么多blob。我添加了一个日志文件,告诉我哪个文件失败了,我可以在Azure资源管理器中打开该文件。

我可以发布代码但是现在,我想知道我是否遇到了一些我不知道的引用/带宽问题。

    namespace BackupCloudContainers
    {

    class Program
    {
        static string privateconnectionstring = ConfigurationManager.AppSettings["StorageConnectionString"];
        static string privatebackupconnectionstring = ConfigurationManager.AppSettings["BackupStorageConnectionString"];
        static DateTime testdate = new DateTime(2017, 8, 28, 0, 0, 0);
        static string destContainerName = "";
        static void Main(string[] args)
        {
            try
            {
                //Console.WriteLine("Starting Backup at " + DateTime.Now.ToString("hh:mm:ss.ffff"));
                Log("Starting Incremental Backup (everything since " + testdate.ToString("f") + ") at " + DateTime.Now.ToString("hh:mm:ss.ffff"));
                Backup().GetAwaiter().GetResult();
                // Console.WriteLine("Backup Created as " + destContainerName);
                Log("Backup Created as " + destContainerName);
                //Console.WriteLine("Backup ended at " + DateTime.Now.ToString("hh:mm:ss.ffff"));
                Log("Backup ended at " + DateTime.Now.ToString("hh:mm:ss.ffff"));
                Console.WriteLine("\n\nPress Enter to close. ");
                Console.ReadLine();
            }
            catch (Exception e)
            {
                //Console.WriteLine("Exception - " + e.Message);
                Log("Exception - " + e.Message);
                if (e.InnerException != null)
                {
                    //Console.WriteLine("Inner Exception - " + e.InnerException.Message);
                    Log("Inner Exception - " + e.InnerException.Message);
                }
            }
        }
        static async Task Backup()
        {

            CloudStorageAccount _storageAccount = CloudStorageAccount.Parse(privateconnectionstring);
            CloudStorageAccount _storageBackupAccount = CloudStorageAccount.Parse(privatebackupconnectionstring);

            CloudBlobClient blobClient = _storageAccount.CreateCloudBlobClient();
            CloudBlobClient blobBackupClient = _storageBackupAccount.CreateCloudBlobClient();

            foreach (var srcContainer in blobClient.ListContainers())
            {
                // skip any containers with a backup name
                if (srcContainer.Name.IndexOf("-backup-") > -1)
                {
                    continue;
                }
                var backupTimeInTicks = DateTime.UtcNow.Ticks;
                //var destContainerName = srcContainer.Name + "-" + backupTimeInTicks;
                var backupDateTime = DateTime.UtcNow.ToString("yyyyMMdd-hhmmssfff");
                destContainerName = srcContainer.Name + "-backup-" + backupDateTime;

                var destContainer = blobBackupClient.GetContainerReference(destContainerName);
//                var destContainer = blobClient.GetContainerReference(destContainerName);

                // assume it does not exist already,
                // as that wouldn't make sense.
                await destContainer.CreateAsync();

                // ensure that the container is not accessible
                // to the outside world,
                // as we want all the backups to be internal.
                BlobContainerPermissions destContainerPermissions = destContainer.GetPermissions();
                if (destContainerPermissions.PublicAccess != BlobContainerPublicAccessType.Off)
                {
                    destContainerPermissions.PublicAccess = BlobContainerPublicAccessType.Off;
                    await destContainer.SetPermissionsAsync(destContainerPermissions);
                }

                // copy src container to dest container,
                // note that this is synchronous operation in reality,
                // as I want to only add real metadata to container
                // once all the blobs have been copied successfully.
                await CopyContainers(srcContainer, destContainer);
                await EnsureCopySucceeded(destContainer);

                // ensure we have some metadata for the container
                // as this will helps us to delete older containers
                // on a later date.
                await destContainer.FetchAttributesAsync();

                var destContainerMetadata = destContainer.Metadata;
                if (!destContainerMetadata.ContainsKey("BackupOf"))
                {
                    string cname = srcContainer.Name.ToLowerInvariant();
                    destContainerMetadata.Add("BackupOf", cname);
                    destContainerMetadata.Add("CreatedAt", backupTimeInTicks.ToString());
                    destContainerMetadata.Add("CreatedDate", backupDateTime);
                    await destContainer.SetMetadataAsync();
                    //destContainer.SetMetadata();
                }
            }

            // let's purge the older containers,
            // if we already have multiple newer backups of them.
            // why keep them around.
            // just asking for trouble.
            //var blobGroupedContainers = blobBackupClient.ListContainers()
            //    .Where(container => container.Metadata.ContainsKey("Backup-Of"))
            //    .Select(container => new
            //    {
            //        Container = container,
            //        BackupOf = container.Metadata["Backup-Of"],
            //        CreatedAt = new DateTime(long.Parse(container.Metadata["Created-At"]))
            //    }).GroupBy(arg => arg.BackupOf);

            var blobGroupedContainers = blobClient.ListContainers()
                .Where(container => container.Metadata.ContainsKey("BackupOf"))
                .Select(container => new
                {
                    Container = container,
                    BackupOf = container.Metadata["BackupOf"],
                    CreatedAt = new DateTime(long.Parse(container.Metadata["CreatedAt"]))
                }).GroupBy(arg => arg.BackupOf);

            // Remove the Delete for now
      //      foreach (var blobGroupedContainer in blobGroupedContainers)
      //      {
      //          var containersToDelete = blobGroupedContainer.Select(arg => new
      //          {
      //              Container = arg.Container,
      //              CreatedAt = new DateTime(arg.CreatedAt.Year, arg.CreatedAt.Month, arg.CreatedAt.Day)
      //          })
      //              .GroupBy(arg => arg.CreatedAt)
      //              .OrderByDescending(grouping => grouping.Key)
      //              .Skip(7) /* skip last 7 days worth of data */
      //              .SelectMany(grouping => grouping)
      //              .Select(arg => arg.Container);

      //// Remove the Delete for now
      //          //foreach (var containerToDelete in containersToDelete)
      //          //{
      //          //    await containerToDelete.DeleteIfExistsAsync();
      //          //}
      //      }
        }

        static async Task EnsureCopySucceeded(CloudBlobContainer destContainer)
        {
            bool pendingCopy = true;
            var retryCountLookup = new Dictionary<string, int>();

            while (pendingCopy)
            {
                pendingCopy = false;

                var destBlobList = destContainer.ListBlobs(null, true, BlobListingDetails.Copy);

                foreach (var dest in destBlobList)
                {
                    var destBlob = dest as CloudBlob;
                    if (destBlob == null)
                    {
                        continue;
                    }

                    var blobIdentifier = destBlob.Name;

                    if (destBlob.CopyState.Status == CopyStatus.Aborted ||
                        destBlob.CopyState.Status == CopyStatus.Failed)
                    {
                        int retryCount;
                        if (retryCountLookup.TryGetValue(blobIdentifier, out retryCount))
                        {
                            if (retryCount > 4)
                            {
                                throw new Exception("[CRITICAL] Failed to copy '"
                                                        + destBlob.CopyState.Source.AbsolutePath + "' to '"
                                                        + destBlob.StorageUri + "' due to reason of: " +
                                                        destBlob.CopyState.StatusDescription);
                            }

                            retryCountLookup[blobIdentifier] = retryCount + 1;
                        }
                        else
                        {
                            retryCountLookup[blobIdentifier] = 1;
                        }

                        pendingCopy = true;

                        // restart the copy process for src and dest blobs.
                        // note we also have retry count protection,
                        // so if any of the blobs fail too much,
                        // we'll give up.
                        await destBlob.StartCopyAsync(destBlob.CopyState.Source);
                    }
                    else if (destBlob.CopyState.Status == CopyStatus.Pending)
                    {
                        pendingCopy = true;
                    }
                }

                Thread.Sleep(1000);
            }
        }

        static async Task CopyContainers(
                CloudBlobContainer srcContainer,
                CloudBlobContainer destContainer)
        {
            // get the SAS token to use for all blobs
            string blobToken = srcContainer.GetSharedAccessSignature(new SharedAccessBlobPolicy()
            {
                Permissions = SharedAccessBlobPermissions.Read,
                SharedAccessStartTime = DateTime.Now.AddMinutes(-5),
                SharedAccessExpiryTime = DateTime.Now.AddHours(3)
            });
            int ii = 0;
            int cntr = 0;
            int waitcntr = 0;
            string sourceuri = "";
            int datecntr = 0;
            try
            {

                //Console.WriteLine("  container contains " + srcContainer.ListBlobs(null, true).Count().ToString());
                Log("  container contains " + srcContainer.ListBlobs(null, true).Count().ToString());
                foreach (var srcBlob in srcContainer.ListBlobs(null, true))
                {
                    ii++;

                    //THIS IS FOR COUNTING Blobs that would be on the Incremental Backup
                    CloudBlob blob = (CloudBlob)srcBlob;
                    if (blob.Properties.LastModified > testdate)
                    {
                        datecntr++;
                    }
                    else
                    {
                        // We are only doing an Incremental Backup this time - so skip all other files 
                        continue;
                    }


                    //if (ii > 2000)
                    //{
                    //    //Console.WriteLine("   test run ended ");
                    //    Log("   test run ended ");
                    //    break;
                    //}


                    cntr++;
                    if (cntr > 999)
                    {
                        //Console.WriteLine("    " + ii.ToString() + " processed at " + DateTime.Now.ToString("hh:mm:ss"));
                        Log("    " + ii.ToString() + " processed at " + DateTime.Now.ToString("hh:mm:ss"));

                        //Log("   EnsureCopySucceeded - finished at " + DateTime.Now.ToString("hh:mm:ss"));
                        //await EnsureCopySucceeded(destContainer);
                        //Log("   EnsureCopySucceeded - finished at " + DateTime.Now.ToString("hh:mm:ss"));

                        cntr = 0;

                    }

                    waitcntr++;
                    if (waitcntr > 29999)
                    {
                        Log("   EnsureCopySucceeded (ii=" + ii.ToString() + "- started at " + DateTime.Now.ToString("hh:mm:ss"));
                        await EnsureCopySucceeded(destContainer);
                        Log("   EnsureCopySucceeded - finished at " + DateTime.Now.ToString("hh:mm:ss"));
                        waitcntr = 0;
                    }


                    var srcCloudBlob = srcBlob as CloudBlob;
                    if (srcCloudBlob == null)
                    {
                        continue;
                    }

                    CloudBlob destCloudBlob;

                    if (srcCloudBlob.Properties.BlobType == BlobType.BlockBlob)
                    {
                        destCloudBlob = destContainer.GetBlockBlobReference(srcCloudBlob.Name);
                    }
                    else
                    {
                        destCloudBlob = destContainer.GetPageBlobReference(srcCloudBlob.Name);
                    }
                    sourceuri = srcCloudBlob.Uri.AbsoluteUri + blobToken;

                    try
                    {
                        await destCloudBlob.StartCopyAsync(new Uri(srcCloudBlob.Uri.AbsoluteUri + blobToken));
                    }
                    catch (Exception e)
                    {
                        Log("Error at item " + ii.ToString() + "      Source = " + sourceuri + "      Message = " + e.Message + "      Time = " + DateTime.Now.ToString("F") + "\r\n");
                    }
                }
                Log("Total Items checked = " + ii.ToString() + "    backed up files = " + datecntr.ToString());
                Log("TestDate = " + testdate.ToString("F") + "     datecntr = " + datecntr.ToString());
            }
            catch (Exception e)
            {
                Log("Error at item " + ii.ToString());
                Log("      Source = " + sourceuri);
                Log("      Message = " + e.Message);
                Log("      Time = " + DateTime.Now.ToString("F") + "\r\n");
                //throw e; 
            }
        }


        static void Log(string logdata)
        {
            Console.WriteLine(logdata);
            File.AppendAllText("c:\\junk\\dwlog.txt", logdata + "\r\n");
        }
    }
    }

1 个答案:

答案 0 :(得分:1)

您提到您的代码在3小时后开始失败。那么,以下代码行是罪魁祸首:

    string blobToken = srcContainer.GetSharedAccessSignature(new SharedAccessBlobPolicy()
    {
        Permissions = SharedAccessBlobPermissions.Read,
        SharedAccessStartTime = DateTime.Now.AddMinutes(-5),
        SharedAccessExpiryTime = DateTime.Now.AddHours(3)
    });

如果您注意到,您正在创建一个有效期为3小时的共享访问签名(SAS),并且您正在为所有blob使用此SAS。只要SAS有效,即您的代码尚未过期,您的代码就可以运行。 SAS过期后,由于现在SAS令牌无权执行操作,因此您开始收到403 (Not Authorized)错误。

我的建议是创建一个有效期较长的SAS令牌。我建议使用对15 days有效的SAS令牌,因为这是Azure存储尝试将blob从一个帐户复制到另一个帐户的最长时间。