Blob Lease就是给Blob文件上锁,上锁以后直到lease过期的这段时间,只有拥有lease ID的有权限的访问者,才能删写该Blob文件。
Blob Lease包含以下五个操作:
- Acquire,根据自定义lease ID(或者让系统自动生成),请求一个新的lease,并设置lease的过期时间(15s-60s或者是无限时间);
- Renew,刷新现有的lease,lease过期时间从头计算;
- Change,改变现有lease的ID;
- Release,将lease立即释放,以便其他Client可以请求新的lease;
- Break,将lease终止,在该操作发生以后,必须等lease过期时间过去以后,lease才真正被终止,或者可以自定义一个终止等待时长。
在Blob Lease的整个操作过程中,包含五种状态,如下图所示:
- Available,可以获取新的lease;
- Leased,Acquire操作成功,Blob被锁住;
- Expired,lease过期,Blob锁释放;
- Breaking,Break操作执行以后,当前lease还没到过期时间,Blob仍然被锁;
- Broken,lease终止。
几点需要注意的地方:
- 一旦lease过期,在Blob文件被修改或者再次上锁之前,Blob服务仍然保留着lease Id。Client还能够使用这个lease Id进行renew或者release操作。如果操作成功了,则说明在lease过期以后的这段时间里,Blob文件并没有被修改过。如果操作失败了,则说明Blob文件被修改过或者是拥有过了新的lease,这个时候就需要使用新的lease Id来给Blob上锁。
- 一旦lease被释放(执行了Release操作),则该lease不能被Renew或者Break,强制执行将或者409 Conflict的异常信息。
- Break操作时并不需要提供lease id,一般用来释放已存在的lease,并且不允许再次renew lease。这个操作一般是供管理员使用,用来重置lease状态。
- 对于Acquire方法,早期版本的Blob不提供自定义Lease Id,在这里我们来探讨一下自定义Lease Id的好处。如果acquire操作在服务端成功了,但是在返回客户端响应时失败了,那这时client可以使用相同的lease id进行重试,而不需要先让之前的lease失效(终止)再acquire一个新的Lease;
- 在执行Acquire操作时,如果过期时间设置为-1,则该lease永不过期。当需要自己控制lease的生命周期时,可以如此设置。如果需要设置过期时间,时间范围只能是15s至60s。
好了,了解完一些概念性的内容,我们来通过代码验证一下这些概念。
整个例子是一个简单的web应用程序,访问位于云端的Blob服务,角色为所有者(owner of storage account),当然,也可以通过有权限的匿名用户来操作。
(关于Blob权限设置请参看 Windows Azure: Blob Container的访问权限与策略设置)
页面结构很简单,包含两部分,如图所示:
蓝框内是列出容器内所有的Block Blob文件,红框内是对选定的Blob文件进行lease各种操作。
列出容器内所有Block Blob文件代码:
1: CloudStorageAccount storageAccount = CloudStorageAccount.Parse(StorageConnectionString);
2: CloudBlobClient blobClient = new CloudBlobClient(storageAccount.BlobEndpoint);
3: CloudBlobContainer blobContainer = blobClient.GetContainerReference("mycontainerforlease");
4: blobContainer.CreateIfNotExists();
5:
6: IEnumerable<IListBlobItem> blobItems = blobContainer.ListBlobs(null, true, BlobListingDetails.None, null, null);
7: List<CloudBlockBlob> blockBlobs = new List<CloudBlockBlob>();
8: foreach (IListBlobItem item in blobItems)
9: {
10: if (item.GetType() == typeof(CloudBlockBlob))
11: {
12: blockBlobs.Add((CloudBlockBlob)item);
13: }
14: }
15: BlobItemGridView.DataSource = blockBlobs;
16: BlobItemGridView.DataBind();
上传文件代码:
1: try
2: {
3: CloudStorageAccount storageAccount = CloudStorageAccount.Parse(StorageConnectionString);
4: CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
5: CloudBlobContainer blobContainer = blobClient.GetContainerReference("mycontainerforlease");
6: CloudBlockBlob blockBlob = blobContainer.GetBlockBlobReference(FileUpload1.FileName);
7: blockBlob.UploadFromStream(FileUpload1.FileContent);
8: }
9: catch (Exception ex)
10: {
11: ErrorMsg.Text = ex.Message;
12: }
删除文件代码:
1: try
2: {
3: CloudStorageAccount storageAccount = CloudStorageAccount.Parse(StorageConnectionString);
4: Uri uri = e.Keys[0] as Uri;
5: CloudBlockBlob blob = new CloudBlockBlob(uri, storageAccount.Credentials);
6: blob.Delete();
7: BindGridView();
8: ErrorMsg.Text = "";
9: }
10: catch (Exception ex)
11: {
12: ErrorMsg.Text = ex.Message;
13: }
Acquire lease:
1: string selectValue = Request.Form.Get("selectRadioButton");
2: if (!string.IsNullOrEmpty(selectValue))
3: {
4: try
5: {
6: CloudStorageAccount storageAccount = CloudStorageAccount.Parse(StorageConnectionString);
7: CloudBlockBlob blockBlob = new CloudBlockBlob(new Uri(selectValue), storageAccount.Credentials);
8: string leaseId = "";
9: if (!string.IsNullOrEmpty(LeaseId.Text))
10: {
11: leaseId = LeaseId.Text;
12: }
13: else
14: {
15: leaseId = Guid.NewGuid().ToString();
16: }
17: blockBlob.AcquireLease(TimeSpan.FromSeconds(double.Parse(LeaseTimeSpan.Text)), leaseId);
18: LeaseId.Text = leaseId;
19: ErrorMsg.Text = "";
20: }
21: catch (Exception ex)
22: {
23: ErrorMsg.Text = ex.Message;
24: }
25: }
Release lease:
1: string selectValue = Request.Form.Get("selectRadioButton");
2: if (!string.IsNullOrEmpty(selectValue))
3: {
4: try
5: {
6: CloudStorageAccount storageAccount = CloudStorageAccount.Parse(StorageConnectionString);
7: CloudBlockBlob blockBlob = new CloudBlockBlob(new Uri(selectValue), storageAccount.Credentials);
8:
9: blockBlob.ReleaseLease(AccessCondition.GenerateLeaseCondition(LeaseId.Text));
10: ErrorMsg.Text = "";
11: }
12: catch (Exception ex)
13: {
14: ErrorMsg.Text = ex.Message;
15: }
16: }
Renew Lease:
1: string selectValue = Request.Form.Get("selectRadioButton");
2: if (!string.IsNullOrEmpty(selectValue))
3: {
4: try
5: {
6: CloudStorageAccount storageAccount = CloudStorageAccount.Parse(StorageConnectionString);
7: CloudBlockBlob blockBlob = new CloudBlockBlob(new Uri(selectValue), storageAccount.Credentials);
8: blockBlob.RenewLease(AccessCondition.GenerateLeaseCondition(LeaseId.Text));
9:
10: ErrorMsg.Text = "";
11: }
12: catch (Exception ex)
13: {
14: ErrorMsg.Text = ex.Message;
15: }
16: }
Break Lease:
1: string selectValue = Request.Form.Get("selectRadioButton");
2: if (!string.IsNullOrEmpty(selectValue))
3: {
4: try
5: {
6: CloudStorageAccount storageAccount = CloudStorageAccount.Parse(StorageConnectionString);
7: CloudBlockBlob blockBlob = new CloudBlockBlob(new Uri(selectValue), storageAccount.Credentials);
8: blockBlob.BreakLease();
9: ErrorMsg.Text = "";
10: }
11: catch (Exception ex)
12: {
13: ErrorMsg.Text = ex.Message;
14: }
15: }
首先,我们来给Blob上锁(Acquire lease),如图所示,过期时间设置为60s(如果过期时间设置为-1,即Lease永不过期):
然后我们尝试删除该Blob文件,如图所示,出现了异常:
执行Release lease以后,再来删除文件,OK,文件已经成功删除。
点击 这里 下载源码。有兴趣的朋友可以根据源码自己做其他验证。