Windows Azure 驱动器:第 1 部分:在 Web Role 生命周期启动时配置和挂载






4.97/5 (7投票s)
通过 RoleEntryPoint 回调方法为任何基于云的 Web 应用程序提供 Windows Azure 驱动器(又称 XDrive),并通过派生自 HttpApplication 基类的 Global.asax 回调方法在环境变量中公开成功挂载的结果。
Doxygen 生成的 CodePlex 下载视图:源代码文档
引言
本文介绍了一种通过 RoleEntryPoint 回调方法为任何基于云的 Web 应用程序提供 Windows Azure 驱动器(又称 XDrive)的方法,并通过派生自 HttpApplication 基类的 Global.asax 回调方法在环境变量中公开成功挂载的结果。本文将演示如何在 .NET (C#) 云 Web 应用程序运行之前使用此方法挂载 XDrive。
下一篇文章将采用此方法并将其应用于基于 PHP 的云 Web 应用程序。
第 1 部分:WebRole 在 RoleEntryPoint.OnStart() 中挂载 XDrive:C#
本文实现了 RoleEntryPoint.OnStart() 回调来执行以下操作
- 读取配置为挂载零个(如果未提供)或更多 XDrive 的 ServiceConfiguration.cscfg 和 ServiceDefinition.csdef 文件。
- 创建 LocalStorage,它将在挂载 XDrive 时用作本地读取缓存。
- 以写入或只读访问模式挂载 XDrive。
- 通过填充环境变量 X_DRIVES 来公开成功的 XDrive 挂载,并提供以下信息
- 友好的 XDrive 标签
- 驱动器盘符
- 驱动器访问模式
此外
- 如何在 ServiceConfiguration.cscfg 和 ServiceDefinition.csdef 文件中配置 XDrive 挂载。
- 演示如何在 Web 角色应用程序中使用创建的环境变量 X_DRIVES。
第 2 部分:使用 XDrive 的基于云的 PHP Web 应用程序
下一篇文章将介绍如何创建和部署基于云的 PHP Web 应用程序到 Windows Azure 平台,并能够利用 Windows Azure 驱动器。
它将包括讨论如何使用以下与 Windows Azure 平台支持集成的 PHP 开发工具之一添加此功能
Windows Azure 驱动器概述
大多数(如果不是全部)应用程序都需要持久数据存储。Windows Azure 驱动器是一种基础设施解决方案,可在 Windows Azure 平台中提供持久的 NTFS 驱动器。
挑战之一是将现有 Windows 应用程序迁移到云中运行,同时确保其数据持久,并使用标准 Windows NTFS API。借助 Windows Azure 驱动器,在云中运行的 Windows Azure 应用程序可以使用现有的 NTFS API 访问持久驱动器。这可以显著简化现有 Windows 应用程序向云的迁移。Windows Azure 应用程序可以从驱动器盘符(例如 X:\)读取或写入,该驱动器盘符代表一个持久的 NTFS 卷,用于存储和访问数据。
持久驱动器被实现为包含 NTFS 格式的 虚拟硬盘 (VHD) 的 Windows Azure Page Blob。这种方法的另一个优点是 VHD 可以随时从 Blob 存储下载,从而为客户提供对其数据的轻松访问。
有关 Windows Azure 驱动器的更多信息,请参阅 Windows Azure 驱动器白皮书。
关于需要持久卷的 Web 应用程序
现有或新的 Web 应用程序,无论是否在 Windows 中运行,许多都需要一个持久的位置来存放运行站点所必需的内容。这些内容可能包括任何类型的文件:站点配置、图像、样式表、日志、数据库等... 对这些文件的访问模式将是只读或可写,访问位置将是未缓冲的磁盘上或缓存的。
将上述 Web 应用程序迁移到云中并运行的挑战之一是确保其数据需求是持久的。Windows Azure 驱动器简化了在 Windows 平台上运行的 Web 应用程序到云的迁移,因为它们的持久需求仍然可以访问,因为它们的任何文件操作仍然使用标准的 Windows NTFS API。
配置 Web 应用程序访问持久卷
使用 XDrive 的实现问题在于,它们如何集成到已迁移到云中且期望持久位置的 Web 应用程序中。
当 Web 应用程序安装在本地 Windows 主机上时,安装管理员会根据可用的磁盘空间选择一个硬盘驱动器(例如 HOMEDRIVE C:\),该硬盘驱动器预计将保存持久数据,并分配该驱动器数据的本地磁盘缓存量。选择此硬盘驱动器后,管理员会根据其盘符构建目录、设置访问权限,并复制 Web 应用程序及其数据。
当将 XDrive 建立为迁移到云的 Web 应用程序的持久存储时,安装管理员仍然可以选择分配所需的磁盘空间和驱动器数据的本地磁盘缓存量。但是,与标准本地主机安装不同的是,驱动器的位置直到在云中运行时才可知。换句话说,Web 应用程序在创建和挂载 XDrive 时定义其持久需求,然后 Windows Azure 为此挂载的 XDrive 分配一个驱动器盘符给 Web 应用程序。有了这个运行时分配给 XDrive 的驱动器盘符,Web 应用程序现在在云中拥有一个已知位置,可以访问持久的 NTFS 卷来存储和访问基于文件的数据。
因此,在云中为 Web 应用程序提供持久卷的关键在于,它必须能够灵活地通过驱动器盘符接收 XDrive 的位置,这只会在运行时发生。
挂载 XDrive 的方法
挂载 XDrive 并将其位置提供给基于云的 Web 应用程序有两种方法。这两种方法都要求 Web 应用程序已作为进程在 Windows Azure Web 实例主机中启动。不同之处在于,Web 应用程序在运行时(即已启动)如何被告知所有成功挂载的 XDrive 的状态:卷位置或驱动器盘符,以及已建立的访问模式。
- 在 Web 应用程序的程序集加载之前挂载 XDrive。
成功挂载的 XDrive 的位置(驱动器盘符)必须仅在 Web 应用程序启动后才能间接提供。 - 在 Web 应用程序启动后及其运行时随时挂载 XDrive。
Web 应用程序可以直接获取成功挂载的 XDrive 的位置(驱动器盘符),因为它是通过在运行时执行的。
向任何基于云的 Web 应用程序提供 XDrive
如果 Web 应用程序在运行时挂载 XDrive,则该 Web 应用程序必须使用 .NET 支持的语言(VB、C#、C++、F# 和 JScript)编写,才能访问 Microsoft.WindowsAzure.StorageClient 命名空间中的 Windows Azure 云功能。
但是,如果 XDrive 在 Web 应用程序启动之前挂载,那么 Web 应用程序可以是任何编程语言,包括 PHP 和 Java。必须提供的是在加载 Web 应用程序程序集期间使用的共享二进制文件 (DLL)。此 DLL 必须按以下顺序执行以下功能,例如为 PHP Web 应用程序提供服务,这将是本文建立的方法
- 当 Windows Azure 初始化 Web 应用程序的 WebRole 实例时
- 读取一个或多个 XDrive 的配置预期,其中包括
- 驱动器大小
- 驱动器数据的本地磁盘缓存大小
- 访问模式
- 挂载 Windows Azure 驱动器
- 为每个成功挂载的 XDrive 提供以下信息到可由任何 Web 应用程序访问的位置。例如,通过设置环境变量
- 友好标签
- 驱动器盘符
- 已建立的访问模式
- 读取一个或多个 XDrive 的配置预期,其中包括
- 在 Web 应用程序运行时(启动后)
- 读取在 WebRole 启动期间设置的环境变量。
- Web 应用程序将定义任何预期通过其驱动器盘符引用 XDrive 的目录路径,而是通过与友好标签的关联进行引用。
- 持久 NTFS 卷提供给 Web 应用程序。
- 当 Windows Azure 停止 PHP Web 应用程序的 WebRole 实例时
- 卸载 Windows Azure 驱动器
Windows Azure 中的 Web 角色生命周期
为了填补 XDrive 和 PHP 应用程序之间的空白,本文的方法是创建一个 C# WebRole DLL,用于在 PHP Web 应用程序启动之前管理所有 XDrive 的挂载。要实现这一点,首先需要了解 Web 角色的生命周期。
Web 角色
托管在 Windows Azure 中的服务架构基于使用托管代码构建的离散可伸缩组件。这些组件被称为角色。
Web 角色是为 Web 应用程序编程定制的角色。
生命周期管理
Web 角色的生命周期由处理其启动和停止序列的回调方法管理。
- WaWebHost.exe(Windows Azure Web 实例主机)启动进程及其中的 Web 角色实例。
- Internet Information Services (IIS) 7.0 托管 Web 核心被激活,并且 Web 角色在其上以集成管道模式运行。
- 加载 Web 角色应用程序的程序集并调用 RoleEntryPoint.OnStart()。
- 在 Web 服务启动时调用 Global.Application_Start()。
- 当 HttpApplication 类的第一个实例创建时触发。
- 它允许您创建所有 HttpApplication 实例都可以访问的对象。
- Web 角色应用程序运行。
- Global.Application_End() 作为 Web 应用程序的结束事件被调用。
- 当 HttpApplication 类的最后一个实例销毁时触发。
- 它在应用程序的生命周期中只触发一次。
- 调用 RoleEntryPoint.OnStop()。
- Internet Information Services (IIS) 7.0 托管 Web 核心被禁用。
- WaWebHost.exe 进程已停止。
使用 Windows Azure 驱动器的基于云的 C# Web 应用程序示例
设置先决条件
- Windows 7、Windows Server 2008 或 Windows Vista SP1
- Windows Azure 软件开发工具包(2010 年 2 月或更高版本):下载
- Visual Studio 2008 或更高版本
CodePlex 下载内容
此下载站点来自托管在 CodePlex 上的开源项目:适用于 PHP 开发人员的 Windows Azure 命令行工具。
适用于 PHP 的 Windows Azure 命令行工具使开发人员能够使用简单的命令行工具轻松地将 PHP 应用程序打包和部署到 Windows Azure。这些工具允许创建新应用程序或将现有 PHP 应用程序转换为 Windows Azure,并创建部署包 (.cspkg) 和配置文件 (.cscfg)。
此页面中的 zip 文件包含处理 Windows Azure 驱动器挂载的 WebRole 源:下载
下载并解压缩 azurephptools-*.zip 后,本文讨论的是子目录 \azurephptools-*\web_role_dll_zip。其内容包含一个 Visual Studio 2008 项目,该项目创建了一个 WebRole DLL 和一个根据提供的服务配置挂载 XDrive 的 C# Web 应用程序。
Doxygen 生成的 CodePlex 中这些 C# 源的视图可在此处获取:文档
下载包含以下文件
Web 角色 DLL
- WebRole.cs - 使用 RoleEntryPoint 事件处理程序来挂载和卸载 XDrive。
- XDrives.cs - XDrives 类读取请求的 XDrive 配置并挂载它们。
- 全局应用程序类 (Global.asax & Global.asax.cs) - 收集成功挂载的 XDrive 的信息,并使用此信息建立 Windows 环境变量 X_DRIVES。
示例服务配置
- ServiceDefinition.csdef - 声明 LocalStorage 并定义 XDrive 的设置
- ServiceConfiguration.cscfg - 提供关于如何通过上述 Web 角色 DLL 在运行时挂载 XDrive 的示例配置设置。
示例 Windows Azure C# Web 应用程序
- XDrivesTest.cs - 从 X_DRIVES 中收集挂载的 XDrive 信息,并根据每个挂载的 XDrive 的访问模式,创建和写入文件或读取文件。
- 默认页面 (Default.asap, Default.asap.cs) - 显示由 XDrivesTest 类执行的测试结果。
添加引用
已添加到解决方案引用中的是必需的 Microsoft.WindowsAzure.CloudDrive
工作原理
实现 Web 角色事件处理程序
在实现此 Web 角色时,仅实现了以下 Web 角色生命周期事件处理程序
- RoleEntryPoint.OnStart() - 读取配置和挂载 XDrive。
- RoleEntryPoint.OnStop() - 卸载已挂载的 XDrive。
- Global.Application_Start() - 在环境变量 X_DRIVES 中公开已挂载的 XDrive 信息。
WebRole.cs
public class WebRole : RoleEntryPoint
{ private XDrives m_XDrives = new XDrives();
public override bool OnStart()
{
DiagnosticMonitor.Start("DiagnosticsConnectionString");
// Mount XDrives
m_XDrives.MountXDrives();
// For information on handling configuration changes
// see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.
RoleEnvironment.Changing += RoleEnvironmentChanging;
return base.OnStart();
}
public override void OnStop()
{
// Unmount XDrives
m_XDrives.UnmountXDrives();
base.OnStop();
}
// Continue...
}<}
Global.asax.cs
public class Global : System.Web.HttpApplication
{
private XDrives m_XDrives = new XDrives();
protected void Application_Start(object sender, EventArgs e)
{
m_XDrives.SetXDrivesEnvironmentVariable();
}
// Continue
}
RoleEntryPoint.OnStart()
事件处理程序通过调用 XDrives.MountXDrives(),根据提供的配置设置挂载 XDrive。
代码:XDrives.MountXDrives()
- 从 ServiceConfiguration.cscfg 读取配置。稍后会有更多说明。RoleEnvironment.GetConfigurationSettingValue()
- 如果 ServiceConfiguration.cscfg 中没有任何 XDrive 配置,则退出。
- 将 XDrive 配置解析为字典。
- 从 ServiceDefinition.csdef 读取本地存储配置:RoleEnvironment.GetLocalResource()
- 获取云存储帐户。
- 初始化此 VM 中 XDrives 将使用的挂载读取缓存:CloudDrive.InitializeCache()
- 收集当前已挂载的 XDrive 列表:CloudDrive.GetMountedDrives()
- 创建新的 Blob 服务客户端:CloudStorageAccountStorageClientExtensions.CreateCloudBlobClient()
- 遍历配置并挂载 XDrive:XDrives.MountXDrive()
/// <summary>
/// Mount VHDs based upon settings within ServiceConfiguration.cscfg file.
/// </summary>
public void MountXDrives()
{
try
{
if (!GetXDrivesConfig())
{
return;
}
Trace.TraceInformation("Mounting X-Drives");
if (!InitializeXDrives())
{
return;
}
/*
* Interate over each X-Drive configuration and
* perform setup as requested.
*/
Trace.TraceInformation("Mounting {0} X-Drives Configurations",
m_aXDriveConfigs.Count
);
foreach (IDictionary<string, string> dicXDriveConfig in m_aXDriveConfigs)
{
MountXDrive(dicXDriveConfig);
}
}
catch (Exception ex)
{
Trace.TraceError("Unexpected Exception: Failed to mount X-drives: {0): {1)",
ex.GetBaseException(),
ex.Message
);
}
}
代码:XDrives.GetXDrivesConfig()
/// <summary>
/// Gather from ServiceConfiguration.cscfg, XDrives' configuration
/// </summary>
/// <returns>True if XDrives configuration is found and has contents.</returns>
private bool GetXDrivesConfig()
{
/*
* X-Drives configuration string
* from ServiceConfiguration.cscfg file.
*/
string sXDrivesConfig = ReadXDrivesConfig();
if (null == sXDrivesConfig || 0 == sXDrivesConfig.Length)
{
return false;
}
/*
* Parse provide XDrives string from Service Configuration file.
*/
m_aXDriveConfigs = ParseXDriveConfigs(sXDrivesConfig);
if (null == m_aXDriveConfigs || 0 == m_aXDriveConfigs.Count)
{
return false;
}
return true;
}
代码:XDrives.ReadXDrivesConfig()
/// <summary>
/// Read X-Drives configuration settings from ServiceConfiguration.cscfg
/// </summary>
/// <returns>X-Drives Configuration String</returns>
public string ReadXDrivesConfig()
{
string sXDrivesConfig = string.Empty;
try
{
sXDrivesConfig = RoleEnvironment.GetConfigurationSettingValue(XDRIVES_CONFIG_SETTING);
if (null != sXDrivesConfig || string.Empty != sXDrivesConfig)
{
sXDrivesConfig = sXDrivesConfig.Trim();
}
}
catch (Exception ex)
{
Trace.TraceError(
"X-Drives: Unexpected Exception: Failed Read X-Drives Config: {0}",
ex.Message
);
}
return sXDrivesConfig;
}
代码:XDrives.InitializeXDrives()
///
/// Initializes for mounting X-Drives
///
private bool InitializeXDrives()
{
Trace.TraceInformation("Initializing for mounting X-Drives");
/*
* Cloud Storage Account
*/
if (!GetCloudStorageAccount() || (null == m_oCloudStorageAccount) )
{
return false;
}
/*
* Configuration: Get name of LocalStorage that will be used
* for initializing X-Drives' mounts read cache size.
*/
string sXDrivesLocalCacheName
= RoleEnvironment.GetConfigurationSettingValue(XDRIVES_CONFIG_LOCAL_CACHE_SETTING).Trim();
if (null == sXDrivesLocalCacheName || 0 == sXDrivesLocalCacheName.Length)
{
Trace.TraceWarning("No Local Cache for X-Drives was defined in Service Configuration file.");
return false;
}
/*
* Windows Azure Drive environment has to be initialized.
*
* Create local storage which will act as the local cache
* for all X-Drive mounts.
*/
LocalResource localCache = RoleEnvironment.GetLocalResource(sXDrivesLocalCacheName);
Char[] backSlash = { '\\' };
String localCachePath = localCache.RootPath.TrimEnd(backSlash);
CloudDrive.InitializeCache(localCachePath, localCache.MaximumSizeInMegabytes);
m_iMaximumSizeInMegabytes = localCache.MaximumSizeInMegabytes;
/*
* Validate the sum of all request mount read cache size does
* not exceed requested Local Cache size.
*/
int iSumAllMountReadCacheSizeMB = XDrives.GetSumXDrivesMountCache(m_aXDriveConfigs);
if (iSumAllMountReadCacheSizeMB > localCache.MaximumSizeInMegabytes)
{
Trace.TraceWarning(
"Sum of all X-Drives Mount Read Cache size {0} MB exceeds requested Local Cache size {1} MB",
iSumAllMountReadCacheSizeMB,
localCache.MaximumSizeInMegabytes
);
return false;
}
/*
* Get URI paths for all currently mounted X-Drives.
*/
m_aXDrivesPathMounted = GetMountedDrivesPaths();
/*
* Validate: Maximum number of X-Drives has not been exceeded
*/
if (MAX_XDRIVES_MOUNTS_PER_INSTANCE < m_aXDriveConfigs.Count)
{
Trace.TraceWarning(
"Number of X-Drives Mount request exceeds maximum {0}",
MAX_XDRIVES_MOUNTS_PER_INSTANCE
);
return false;
}
/*
* Create a new Blob service client
*/
m_blobClient
= m_oCloudStorageAccount.CreateCloudBlobClient();
return true;
}
代码:XDrives.MountXDrive()
- 验证提供的 XDrive 配置
- 使用之前创建的 Blob 服务客户端,创建 Blob 容器,它将保存页面 Blob VHD:CloudBlobClient.GetContainerReference()
- 从存储帐户创建 CloudDrive 对象的新实例:CloudStorageAccountCloudDriveExtensions.CreateCloudDrive()
- 如果 XDrive 配置请求,则创建 NTFS 格式的 Windows Azure 驱动器及其关联的页面 Blob:CloudDrive.Create()
- 尝试挂载 XDrive:XDrives.DoCloudDriveMount()
- 如果首次尝试以访问模式 write 挂载 XDrive 失败,但决定尝试以访问模式 readonly 重新挂载 XDrive,则执行第二次尝试:XDrives.DoCloudDriveMount()
/// <summary>
/// Setup X-Drive Configuration.
/// </summary>
/// <param name="dicXDriveConfig"></param>
private bool MountXDrive(
IDictionary<string, string> dicXDriveConfig
)
{
try
{
string sXDriveLabel = string.Empty;
string sBlobContainerName = string.Empty;
string sPageBlobVhdName = string.Empty;
AccessMode eAccessMode = AccessMode.None;
bool bXDriveCreateIfNotExist = false;
int iXDriveSizeMB = 0;
int iXDriveMountCacheSizeMB = 0;
if (!GetXDriveConfig(
dicXDriveConfig,
ref sXDriveLabel,
ref sBlobContainerName,
ref sPageBlobVhdName,
ref eAccessMode,
ref bXDriveCreateIfNotExist,
ref iXDriveSizeMB,
ref iXDriveMountCacheSizeMB
)
) {
return false;
}
/*
* Create a blob storage container
*/
if (!CreateXDriveBlobContainer(
sXDriveLabel,
sBlobContainerName
)
) {
return false;
}
/*
* Get a reference to a Windows Azure Drive
*/
CloudDrive oCloudDrive
= m_oCloudStorageAccount.CreateCloudDrive(
m_blobClient
.GetContainerReference(sBlobContainerName)
.GetPageBlobReference(sPageBlobVhdName)
.Uri.ToString()
);
/*
* If requested, try creating X-Drive
* if not listed as mounted.
*/
if (bXDriveCreateIfNotExist)
{
if (!CreateXDrive(
oCloudDrive,
sXDriveLabel,
sPageBlobVhdName,
iXDriveSizeMB
)
)
{
return false;
}
}
/*
* If a mount fails to get Write access because another
* already as write access (ERROR_LEASE_LOCKED),
* then redo mount as ReadOnly.
*/
bool bReDo = false;
if (!DoCloudDriveMount(oCloudDrive, sXDriveLabel, iXDriveMountCacheSizeMB, eAccessMode, ref bReDo))
{
return false;
}
if (bReDo && !DoCloudDriveMount(oCloudDrive, sXDriveLabel, iXDriveMountCacheSizeMB, AccessMode.ReadOnly, ref bReDo))
{
return false;
}
}
catch (NullReferenceException ex)
{
Trace.TraceError("NullReferenceException: Failed to mount X-Drive: {0}",
ex.Message
);
return false;
}
catch (Exception ex)
{
Trace.TraceError("Unexpected Exception: Failed to mount X-Drive: {0}",
ex.Message
);
return false;
}
return true;
}
代码:XDrives.CreateXDriveBlobContainer()
/// <summary>
/// Create Blob Container that will hold Page Blob VHDs
/// </summary>
/// <param name="sXDriveLabel">Friendly X-Drive Label</param>
/// <param name="sBlobContainerName">Blob Container Name</param>
/// <returns>True upon success, else False</returns>
private bool CreateXDriveBlobContainer(
string sXDriveLabel,
string sBlobContainerName
) {
try
{
m_blobClient
.GetContainerReference(sBlobContainerName)
.CreateIfNotExist();
}
catch (StorageClientException ex)
{
Trace.TraceError(
"X-Drive {0}: StorageClientException: Failed to create Page Blob Container {1}: {2}",
sXDriveLabel,
sBlobContainerName,
ex.Message
);
return false;
}
catch (Exception ex)
{
Trace.TraceError(
"X-Drive {0}: Unexpected Exception: Failed to create Page Blob Container {1}: {2}",
sXDriveLabel,
sBlobContainerName,
ex.Message
);
return false;
}
return true;
}
代码:XDrives.CreateXDrive()
/// <summary>
/// Create an X-Drive
/// </summary>
/// <param name="oCloudDrive">Cloud Drive reference</param>
/// <param name="sXDriveLabel">Friendly X-Drive Label</param>
/// <param name="sPageBlobVhdName">Page Blob VHD name</param>
/// <param name="iXDriveSizeMB">Size of X-Drive in MBs</param>
/// <returns>True upon success, else False</returns>
private bool CreateXDrive(
CloudDrive oCloudDrive,
string sXDriveLabel,
string sPageBlobVhdName,
int iXDriveSizeMB
) {
bool bSuccess = false;
try
{
Trace.TraceInformation(
"X-Drive {0}: Creating drive and its associated page blob {1}",
sXDriveLabel,
sPageBlobVhdName
);
oCloudDrive.Create(iXDriveSizeMB);
bSuccess = true;
}
catch (CloudDriveException)
{
/*
* Ignore: Throws this exception if drive already exists.
*/
bSuccess = true;
}
catch (Exception ex)
{
Trace.TraceError(
"X-Drive {0}: Unexpected Exception: Failed to create drive: {1}",
sXDriveLabel,
ex.Message
);
}
return bSuccess;
}
代码:XDrives.DoCloudDriveMount()
- 如果 XDrive 的挂载请求是只读的,则创建一个只读快照:CloudDrive.Snapshot(),并使用此快照创建一个新的 CloudDrive 对象实例。
- 执行 XDrive 的挂载,并按如下方式评估结果:CloudDrive.Mount()
- 如果 access_mode 为 none(默认),则首先尝试使用 DriveMountOptions.None 进行 write 挂载
- 如果挂载成功,则以成功条件退出。
- 如果挂载失败是因为抛出 CloudDriveException 并且其错误消息是 ERROR_LEASED_LOCKED,那么页面 Blob VHD 当前有一个被另一个具有写入访问模式的 Web 角色实例锁定的租约,那么它将建议下次挂载尝试应该是 readonly。
- 如果挂载因任何其他条件失败,则不会再进行挂载尝试。
- 如果 access_mode 为 write,则首先尝试使用 DriveMountOptions.Force 进行 write 挂载
- 如何进行下去的条款将与 access_mode 为 none 时相同,此选项将尝试破坏页面 blob 上的租约锁(如果存在)。
- 如果 access_mode 为 readonly,则只有一次使用创建的快照进行挂载的尝试。
- 如果 access_mode 为 none(默认),则首先尝试使用 DriveMountOptions.None 进行 write 挂载
/// <summary>
/// Attempt to perform mount of X-Drive
/// </summary>
/// <param name="oCloudDrive">Cloud Drive reference</param>
/// <param name="sXDriveLabel">X-Drive Label</param>
/// <param name="iXDriveMountCacheSizeMB">X-Drive mount cache size MB</param>
/// <param name="eAccessMode"></param>
/// <param name="bReDo">Reference Redo mount attempt</param>
/// <returns>True if no error has occurred, else False</returns>
private bool DoCloudDriveMount(
CloudDrive oCloudDrive,
string sXDriveLabel,
int iXDriveMountCacheSizeMB,
AccessMode eAccessMode,
ref bool bReDo
)
{
/*
* A mount is made readonly if requested by X-Drive
* configuration or X-Drive is already mounted.
*/
if (AccessMode.ReadOnly == eAccessMode)
{
CloudDrive oCloudDriveSnapshot = null;
if (!CreateXDriveSnapshot(
oCloudDrive,
sXDriveLabel,
ref oCloudDriveSnapshot
))
{
return false;
}
oCloudDrive = oCloudDriveSnapshot;
}
if (!DoAttemptXDriveMount(
oCloudDrive,
sXDriveLabel,
iXDriveMountCacheSizeMB,
eAccessMode,
ref bReDo
)
) {
return false;
}
return true;
}
代码:XDrives.CreateXDriveSnapshot()
/// <summary>
/// Create snapshot of X-Drive
/// </summary>
/// <param name="oCloudDrive">Cloud Drive reference</param>
/// <param name="sXDriveLabel">X-Drive Label</param>
/// <param name="oCloudDriveSnapshot">Snapshot of Cloud Drive</param>
/// <returns>True if no error has occurred, else False</returns>
private bool CreateXDriveSnapshot(
CloudDrive oCloudDrive,
string sXDriveLabel,
ref CloudDrive oCloudDriveSnapshot
) {
try
{
Trace.TraceInformation(
"X-Drive {0}: Creating snapshot",
sXDriveLabel
);
Uri uriSnapshot = oCloudDrive.Snapshot();
oCloudDriveSnapshot = m_oCloudStorageAccount.CreateCloudDrive(
uriSnapshot.ToString()
);
}
catch (CloudDriveException ex)
{
Trace.TraceError(
"X-Drive {0}: CloudDriveException: Failed to create snapshot: {1}",
sXDriveLabel,
ex.Message
);
return false;
}
catch (Exception ex)
{
Trace.TraceError(
"X-Drive {0}: Unexpected Exception: Failed to create snapshot: {1}",
sXDriveLabel,
ex.Message
);
return false;
}
return true;
}
代码:XDrives.DoAttemptXDriveMount()
/// <summary>
/// Attempt to perform mount of X-Drive
/// </summary>
/// <param name="oCloudDrive">Cloud Drive reference</param>
/// <param name="sXDriveLabel">X-Drive Label</param>
/// <param name="iXDriveMountCacheSizeMB">X-Drive mount cache size MB</param>
/// <param name="eAccessMode"></param>
/// <param name="bReDo">Reference Redo mount attempt</param>
/// <returns>True if no error has occurred, else False</returns>
private bool DoAttemptXDriveMount(
CloudDrive oCloudDrive,
string sXDriveLabel,
int iXDriveMountCacheSizeMB,
AccessMode eAccessMode,
ref bool bReDo
) {
try
{
string sAccessMode = string.Empty;
DriveMountOptions option = DriveMountOptions.None;
switch (eAccessMode)
{
case AccessMode.ReadOnly:
sAccessMode = "ReadOnly";
break;
case AccessMode.Write:
sAccessMode = "Write";
option = DriveMountOptions.Force;
break;
case AccessMode.None:
default:
sAccessMode = "Default";
break;
}
Trace.TraceInformation("X-Drive {0}: Mounting: Mode \"{1}\"",
sXDriveLabel,
sAccessMode
);
string sDriveLetter
= oCloudDrive.Mount(iXDriveMountCacheSizeMB, option);
Trace.TraceInformation(
"X-Drive {0}: Mounted: Letter \"{1}\": Mode \"{2}\", Uri \"{3}\"",
sXDriveLabel,
sDriveLetter,
sAccessMode,
oCloudDrive.Uri.ToString()
);
}
catch (CloudDriveException ex)
{
if (ex.Message.Equals("ERROR_LEASE_LOCKED"))
{
if (AccessMode.ReadOnly != eAccessMode)
{
Trace.TraceInformation("X-Drive {0}: Mount attempt had lease locked. Redo in mode ReadOnly.",
sXDriveLabel
);
bReDo = true;
return true;
}
return false;
}
else
{
Trace.TraceError(
"X-Drive {0}: CloudDriveException: Failed to mount: {1}",
sXDriveLabel,
ex.Message
);
return false;
}
}
catch (Exception ex)
{
Trace.TraceError(
"X-Drive {0}: Unexpected Exception: Failed to mount: {1}",
sXDriveLabel,
ex.Message
);
return false;
}
return true;
}
Global.Application_Start()
在事件处理程序 RoleEntryPoint.OnStart() 完成 XDrive 的挂载后,事件处理程序 Global.Application_Start() 通过调用 XDrives.SetXDrivesEnvironmentVariable() 继续执行。- 如果 ServiceConfiguration.cscfg 中没有任何 XDrive 配置,则退出。
- 将 XDrive 配置解析为字典。
- 获取所有成功挂载的 XDrive:CloudDrive.GetMountedDrives()。如果没有,则退出。
- 将 XDrive 配置的请求路径(Blob 容器名称 + 页面 Blob VHD 名称)与其中一个已挂载的 XDrive 匹配。
- 为环境变量 X_DRIVES 创建设置。
代码:XDrives.SetXDrivesEnvironmentVariable()
/// <summary>
/// Sets Environment variable X_Drives with currently mounted
/// X-Drives
/// </summary>
public void SetXDrivesEnvironmentVariable()
{
/*
* X-Drives configuration string
* from ServiceConfiguration.cscfg file.
*/
string sXDrivesConfig = ReadXDrivesConfig();
if (null == sXDrivesConfig || 0 == sXDrivesConfig.Length)
{
return;
}
/*
* Parse provide XDrives string from Service Configuration file.
*/
ICollection<IDictionary<string, string>> aXDriveConfigs = ParseXDriveConfigs(sXDrivesConfig);
if (null == aXDriveConfigs || 0 == aXDriveConfigs.Count)
{
Trace.TraceInformation("X-Drives: None are currently defined to be mounted.");
return;
}
IDictionary<String, Uri> listDrives = CloudDrive.GetMountedDrives();
if (0 == listDrives.Count)
{
Trace.TraceInformation("X-Drives: None are currently mounted");
return;
}
string[] aXDrives = new string[listDrives.Count];
int i = 0;
foreach (KeyValuePair<String, Uri> kvpDrive in listDrives)
{
string sXDriveLetter = XDrives.GetDriveLetter(kvpDrive.Key);
Uri uriXDrivePath = kvpDrive.Value;
string sXDriveLabel;
string sPageBlobContainer = String.Empty;
string sPageBlobVHD = String.Empty;
bool bReadOnly = false;
XDrives.GetPathPageBlobVHD(
uriXDrivePath,
ref sPageBlobContainer,
ref sPageBlobVHD,
ref bReadOnly
);
sXDriveLabel
= XDrives.GetXDriveLabel(
aXDriveConfigs,
sPageBlobContainer,
sPageBlobVHD
);
string sAccess = bReadOnly ? "r" : "w";
aXDrives[i] = String.Format("{0}=[{1},{2}]", sXDriveLabel, sXDriveLetter, sAccess);
i++;
}
if (0 < aXDrives.Length)
{
string sXDriveEnv = String.Join(";", aXDrives);
Trace.TraceInformation("{0} = {1}", XDRIVES_ENV_VAR, sXDriveEnv);
Environment.SetEnvironmentVariable(XDRIVES_ENV_VAR, sXDriveEnv);
}
}
代码:XDrives.GetPathPageBlobVHD()
/// <summary>
/// Get Page Blob VHD Path Parts.
/// </summary>
/// <param name="uri">Page Blob VHD URI</param>
/// <param name="sPageBlobContainer">OUT Page Blob Container name</param>
/// <param name="sPageBlobVHD">Page Blob VHD name</param>
public static void GetPathPageBlobVHD(
Uri uri,
ref string sPageBlobContainer,
ref string sPageBlobVHD,
ref bool bReadOnly
)
{
sPageBlobContainer = String.Empty;
sPageBlobVHD = String.Empty;
string sPath;
string sPathAndQuery = uri.PathAndQuery;
string sQuery = uri.Query;
if (null != sQuery && sQuery.Length > 0)
{
sPath = sPathAndQuery.Substring(0, sPathAndQuery.Length - sQuery.Length);
bReadOnly = sQuery.StartsWith("?snapshot");
}
else
{
sPath = sPathAndQuery;
bReadOnly = false;
}
char[] aPathDelims = { '/' };
sPath = sPath.TrimStart(aPathDelims);
if (sPath.StartsWith(DEV_STORAGE_ACCT_PREFIX))
{
sPath = sPath.Substring(DEV_STORAGE_ACCT_PREFIX.Length);
sPath = sPath.TrimStart(aPathDelims);
}
int iPageBlobContainerDelim = sPath.IndexOf('/');
sPageBlobContainer = sPath.Substring(0, iPageBlobContainerDelim);
sPageBlobVHD = sPath.Substring(iPageBlobContainerDelim + 1);
}
RoleEntryPoint.OnStop()
最后,当角色实例将停止时,Windows Azure 会调用 XDrives.UnmountXDrives()- 如果 ServiceConfiguration.cscfg 中没有任何 XDrive 配置,则退出。
- 获取所有成功挂载的 XDrive:CloudDrive.GetMountedDrives()。如果没有,则退出。
- 遍历每个已挂载的 XDrive 的页面 Blob VHD 路径,并创建 CloudDrive 对象:CloudStorageAccountCloudDriveExtensions.CreateCloudDrive()
- 卸载每个:CloudDrive.Unmount()
/// <summary>
/// Unmount a VHDs based upon settings within ServiceConfiguration.cscfg file.
/// </summary>
public void UnmountXDrives()
{
try
{
/*
* X-Drives configuration string
* from ServiceConfiguration.cscfg file.
*/
string sXDrivesConfig = ReadXDrivesConfig();
if (null == sXDrivesConfig || 0 == sXDrivesConfig.Length)
{
return;
}
if (null == m_oCloudStorageAccount)
{
return;
}
/*
* Get a listing of all X-Drives currently mounted.
*/
IDictionary<String, Uri> listDrives = CloudDrive.GetMountedDrives();
if (0 == listDrives.Count)
{
return;
}
Trace.TraceInformation("Unmounting X-Drives");
/*
* Iterate over each mounted X-Drive and
* unmount them.
*/
foreach (KeyValuePair<String, Uri> kvpDrive in listDrives)
{
string sXDriveLetter = GetDriveLetter(kvpDrive.Key);
Uri uriXDrivePath = kvpDrive.Value;
try
{
CloudDrive myCloudDrive = m_oCloudStorageAccount.CreateCloudDrive(
uriXDrivePath.ToString()
);
myCloudDrive.Unmount();
Trace.TraceInformation(
"X-Drive {0}: Unmounted {1}",
sXDriveLetter,
uriXDrivePath.ToString()
);
}
catch (CloudDriveException ex)
{
Trace.TraceError(
"X-Drive {0}: CloudDriveException: Failed to unmount {1}: {2}",
sXDriveLetter,
uriXDrivePath.ToString(),
ex.Message
);
}
catch (Exception ex)
{
Trace.TraceError(
"X-Drive {0}: Unexpected Exception: Failed to unmount {1}: {2}",
sXDriveLetter,
uriXDrivePath.ToString(),
ex.Message
);
}
}
}
catch (Exception ex)
{
Trace.TraceError(
"Unexpected Exception: Failed to unmount X-Drives: {0): {1)",
ex.GetBaseException(),
ex.Message
);
}
}
服务配置设置
要通过 XDrives.cs 访问 XDrive 功能,需要修改 ServiceConfiguration.cscfg 和 ServiceDefinition.csdef 文件中的三个必需配置- 为 XDrives 定义一个 LocalStorage。
- 定义 XDrives 将使用哪个 LocalStorage。
- 为每个要挂载的 XDrive 定义配置。
XDrive 挂载读取缓存的本地存储
在一个新的或现有的云服务中,请确保在 ServiceDefinition.csdef 中定义了一个 LocalStorage。此本地存储将由 Windows Azure 驱动器 API 用于在运行的虚拟机上缓存虚拟硬盘,从而实现更快的访问时间。对于 XDrive,仅提供一个对 LocalStorage 的引用,以定义与角色实例关联的所有后续挂载驱动器的读取缓存。例如,添加了一个名为 MyXDrivesLocalCache 且大小为 1000 MB 的 LocalStorage。
由于可以向 LocalResources 添加多个 LocalStorage,因此 class XDrives 需要知道专门为 XDrive 的本地缓存创建的 LocalStorage 的名称。对于此示例,配置设置 XDrivesLocalCache 将引用 LocalStorage MyXDrivesLocalCache 的名称。
新服务设置:“XDrives”
此下一设置用于定义如何配置所有 XDrive 应该被挂载。它的值包含与此 Web 角色实例关联的所有已挂载 XDrive 的列表。以下是单个 XDrive 配置的示例,解释将在本文后面给出[xdrive_label=MyDriveA,blob_container=MyDrives,page_blob_vhd=MyDriveA.vhd,xdrive_sizeMB=40,mount_cache_sizeMB=10,create=Yes];
ServiceDefinition.csdef 和 ServiceConfiguration.cscfg 的示例修改
以下设置将按如下方式修改 ServiceDefinition.csdef<ServiceDefinition >
<WebRole name="WebRole">
<ConfigurationSettings>
</ConfigurationSettings>
<LocalResources>
<LocalStorage name="MyXDrivesLocalCache"
cleanOnRoleRecycle="false"
sizeInMB="1000" />
</LocalResources>
</WebRole>
</ServiceDefinition>
最后两个设置将如下修改 ServiceConfiguration.cscfg <ConfigurationSettings>
<!-- Windows Azure XDrive Configuration Settings -->
<Setting name="XDrives" value="
[xdrive_label=MyDriveA,blob_container=MyDrives,page_blob_vhd=MyDriveA.vhd,xdrive_sizeMB=40,mount_cache_sizeMB=10,create=Yes];"/>
<Setting name="XDrivesLocalCache" value="MyXDrivesLocalCache" />
<ConfigurationSettings>
配置 Windows Azure 驱动器
至此,定义处理 XDrive 挂载的 Web 角色已完成。接下来需要了解如何在设置 XDrives 的值中配置 XDrive 挂载。配置结构
设置 XDrives 的值是一个以分号分隔的字符串,每个值都是一个 XDrive 配置请求。“[x_drive_config];[x_drive_config];[x_drive_config];...” 每个 x_drive_config 请求都是一个带括号的逗号分隔配置设置集,每个设置都是一个键值对。[key=value,key=value,key=value,...]配置说明
这是所有可用键及其预期值的说明,可用于定义 XDrive 配置请求。- xdrive_label
- 必填:用于在挂载后标识 XDrive 配置及其分配的驱动器盘符的友好标签。
- blob_container
- 必填:Blob 容器名称。
- 值:3 到 63 个小写字符。参见 命名容器、Blob 和元数据。
- page_blob_vhd
- 必填:页面 Blob 名称。
- 值:1 到 1024 个任意字符组合。参见 命名容器、Blob 和元数据。
- xdrive_sizeMB
- 创建 XDrive 时必需
- 值:16 到 1000000 MB 的整数
- mount_cache_sizeMB
- 可选:挂载读取缓存大小。
- 值:0 到 xdrive_sizeMB 的整数
- 默认值:0 - 挂载期间不使用读取缓存。
- 创建
- 可选:如果 XDrive 不存在则创建。
- 值:Yes|No
- 默认值:否
- access_mode
- 可选:强制 XDrive 挂载特定的访问模式,而不是获取可用的访问模式。
- 值
- none - 分配当前可用的访问。如果是首次挂载,则为 XDrive 分配 write 访问模式,否则为 read-only。
- write - 强制破坏另一个具有 write 访问模式的 XDrive 挂载的租约锁,并为此次挂载分配 write 访问模式。注意:目前不工作。请参阅下面的当前错误部分。
- readonly - 为此挂载分配只读访问模式,无论挂载是否具有 write 访问模式。
- 默认值:none
配置示例
为了说明源代码 XDrives.cs 如何服务 XDrive,将通过各种 XDrive 配置进行介绍。- [xdrive_label=MyDriveA,blob_container=mydrives,page_blob_vhd=MyDriveA.vhd]
-
- XDrive 预计挂载到现有的页面 Blob 路径 mydrives/MyDriveA.vhd 上,如果它存在,则挂载将成功。
- 通过不提供 mount_cache_sizeMB 值,从而将挂载设置为对驱动器进行无缓冲访问。
- 如果此页面 blob 存在且未被其他角色实例挂载,则 XDrive 将具有写入访问权限,否则为只读。
- [xdrive_label=MyDriveA,blob_container=mydrives,page_blob_vhd=MyDriveA.vhd,xdrive_sizeMB=10,create=Yes]
-
- XDrive 期望挂载到页面 blob 路径 mydrives/MyDriveA.vhd 上,如果它不存在,则在挂载之前创建它。
- 当请求创建与 XDrive 关联的页面 blob 时,键 xdrive_sizeMB 是必需的。
- 要创建的 XDrive 大小为 10 MB。允许的最小大小为 16 MB。
- 如果此页面 blob 存在且未被其他角色实例挂载,则 XDrive 将具有写入访问权限,否则为只读。
- 如果此页面 blob 不存在,则 XDrive 将创建此页面 blob 并具有写入访问权限。
- [xdrive_label=MyDriveA,blob_container=mydrives,page_blob_vhd=MyDriveA.vhd,mount_cache_sizeMB=10,readonly=Yes]
-
- XDrive 预计挂载到现有的页面 Blob 路径 mydrives/MyDriveA.vhd 上,如果它存在,则挂载将成功。
- 如果此页面 Blob 存在,则 XDrive 将具有只读访问权限,无论它是否已被其他角色实例挂载。
- 此已挂载 XDrive 的读取缓存大小为 10 MB。
代码:XDrives.ParseXDriveConfigs()
以下代码用于解析 ServiceConfiguration.cscfg 中的 XDrives 设置。
/// <summary>
/// Parse returned mounting configuration requests for XDrives
/// </summary>
/// <param name="sXDrivesConfigs">string</param>
/// <returns>Collection defining each X-Drive configuration</returns>
public ICollection<IDictionary<string, string>> ParseXDriveConfigs(string sXDrivesConfigs)
{
ICollection<IDictionary<string, string>> aXDrivesConfigs = new Collection<IDictionary<string, string>>();
char[] aCloudDrivesConfigsDelims = { ';' };
string[] aXDrivesConfigsSplit = sXDrivesConfigs.Split(aCloudDrivesConfigsDelims);
foreach (string sXDriveConfigRaw in aXDrivesConfigsSplit)
{
string sXDriveConfigTrim = " []";
char[] aXDriveConfigTrim = sXDriveConfigTrim.ToCharArray();
string sXDriveConfig = sXDriveConfigRaw.Trim(aXDriveConfigTrim);
if (0 == sXDriveConfig.Length)
{
continue;
}
char[] aXDriveConfigKeyValuesDelims = { ',' };
string[] aXDriveConfigKeyValues = sXDriveConfig.Trim().Split(aXDriveConfigKeyValuesDelims);
IDictionary<string, string> dicXDriveConfigKeyValue = new Dictionary<string, string>();
foreach (string sXDriveConfigKeyValue in aXDriveConfigKeyValues)
{
string sXDriveConfigKeyValueDelimChars = "=";
char[] aXDriveConfigKeyValueDelims = sXDriveConfigKeyValueDelimChars.ToCharArray();
string[] aXDriveConfigKeyValue = sXDriveConfigKeyValue.Trim().Split(aXDriveConfigKeyValueDelims);
string sXDriveConfigKey = aXDriveConfigKeyValue[0].Trim();
string sXDriveConfigValue = aXDriveConfigKeyValue[1].Trim();
if (0 == sXDriveConfigKey.Length)
{
Trace.TraceWarning("XDrive configuration has an undefined key",
sXDriveConfigKey
);
continue;
}
if (dicXDriveConfigKeyValue.ContainsKey(sXDriveConfigKey))
{
Trace.TraceWarning("XDrive configuration has a duplicate key {0}",
sXDriveConfigKey
);
continue;
}
if (0 == sXDriveConfigValue.Length)
{
Trace.TraceWarning("XDrive configuration has undefined value for key {0}",
sXDriveConfigKey
);
continue;
}
dicXDriveConfigKeyValue.Add(sXDriveConfigKey, sXDriveConfigValue);
}
aXDrivesConfigs.Add(dicXDriveConfigKeyValue);
}
return 0 == aXDrivesConfigs.Count ? null : aXDrivesConfigs;
}
打包 Web 应用程序以访问 Windows Azure 驱动器
为了使此 XDrive 访问解决方案正常工作,部署需要打包以下文件- WebRole.dll - 使用 VS 2008 解决方案 WindowsAzure_WebRole_XDrives 构建,用于挂载和卸载 XDrive。
- ServiceDefinition.csdef - 定义 XDrives 将使用的 LocalStorage。
- ServiceConfiguration.cscfg - 为所有要由 WebRole.dll 挂载的 XDrive 设置配置
- Global.asax - Global.Application_Start() 事件处理程序在此文件中指定,它被调用以在此 VM 中设置环境变量 X_DRIVES,这是此部署中的 Web 应用程序在启动之前的 XDrives 状态链接。
- Web 应用程序 - 下载提供两个 Web 应用程序(PHP 和 C#),演示如何根据环境变量 X_DRIVES 中的值访问已挂载的 XDrive。
测试对 Windows Azure 驱动器的访问
遵循此解决方案的打包指南来挂载和访问 XDrive,本节将展示一个使用相同服务配置设置来挂载 XDrive 的两个 Web 应用程序部署的示例运行。访问 XDrive 的示例 Web 应用程序
如前所述,下载中包含两个 Web 应用程序(C# 和 PHP),用于验证 XDrive 是否在 Web 角色 DLL 启动时挂载。这两个 Web 应用程序都执行以下操作- 读取应该由 Web 角色 DLL 设置的 X_DRIVES 环境变量。
- 将其解析为单独的 XDrive 挂载信息
- 如果提供的 XDrive 被声明为可写入,则在提供的驱动器盘符的根目录中创建并写入文件。
- 读取提供的驱动器盘符根目录中所有文件的内容。
XDrive 的示例配置
在这里,我们将尝试使用相同的配置设置在两个不同的 Web 应用程序部署中挂载 XDrive。ServiceConfiguration.cscfg 中的示例设置 XDrives 是挂载两个名为 MyDriveA 和 MyDriveB 的 XDrive[xdrive_label=MyDriveA,blob_container=mydrives,page_blob_vhd=MyDriveA.vhd,xdrive_sizeMB=40,create=Yes];
[xdrive_label=MyDriveB,blob_container=mydrives,page_blob_vhd=MyDriveB.vhd,xdrive_sizeMB=40,mount_cache_sizeMB=20,create=Yes];
首次部署
打包的 Web 应用程序部署到开发结构中运行,使用上述 XDrive 配置值挂载两个 XDrive。在 开发结构 UI 中查看首次部署的进度,部署的日志控制台显示,在 RoleEntryPoint.OnStart() 期间,两个 XDrive 的挂载都成功了[runtime] Role entrypoint . CALLING OnStart() [WaWebHost.exe] RoleEntryPoint.OnStart() [WaWebHost.exe] Get XDrives Configuration [WaWebHost.exe] Mounting XDrives [WaWebHost.exe] XDrive MyDriveA: Reading configuration [WaWebHost.exe] XDrive MyDriveA: Mounted: Letter "a:": Mode "Default", Uri "http://127.0.0.1:10000/devstoreaccount1/myxdrives/MyDriveA.vhd" [WaWebHost.exe] XDrive MyDriveB: Reading configuration [WaWebHost.exe] XDrive MyDriveB: Mounted: Letter "b:": Mode "Default", Uri "http://127.0.0.1:10000/devstoreaccount1/myxdrives/MyDriveB.vhd" [runtime] Role entrypoint . COMPLETED OnStart() [runtime] Role entrypoint . CALLING Run() [WaWebHost.exe] X_DRIVES = MyDriveA=[a,w];MyDriveB=[b,w] [fabric] Role state Started并且 Global.Application_Start() 在首次部署的 VM 中发布以下环境变量设置:X_DRIVES = MyDriveA=[a,w];MyDriveB=[b,w] 解释是,由于请求的页面 blob VHD 路径(mydrives/MyDriveA.vhd 和 mydrives/MyDriveB.vhd)都不存在,因此它们都被创建并以可写访问模式挂载。
- MyDriveA - 驱动器盘符:a,访问模式:可写 (w)
- MyDriveB - 驱动器盘符:b,访问模式:可写 (w)
第二次部署
在首次部署仍在运行时,相同的打包 Web 应用程序再次部署到开发结构中运行,使用相同的 XDrive 配置值挂载两个 XDrive。在开发结构 UI 中查看首次部署的进度,部署的日志控制台显示,在 RoleEntryPoint.OnStart() 期间,两个 XDrive 的挂载都成功了,但挂载结果与首次部署不同[runtime] Role entrypoint . CALLING OnStart() [WaWebHost.exe] Get XDrives Configuration [WaWebHost.exe] Mounting XDrives [WaWebHost.exe] XDrive MyDriveA: Reading configuration [WaWebHost.exe] XDrive MyDriveA: Mount attempt had lease locked. Redo in mode ReadOnly. [WaWebHost.exe] XDrive MyDriveA: Creating snapshot [WaWebHost.exe] XDrive MyDriveA: Mounting: Mode "ReadOnly" [WaWebHost.exe] XDrive MyDriveA: Mounted: Letter "e:": Mode "ReadOnly", Uri "http://127.0.0.1:10000/devstoreaccount1/myxdrives/MyDriveA.vhd?snapshot=2010-04-17T03:33:08.8070000Z" [WaWebHost.exe] XDrive MyDriveB: Reading configuration [WaWebHost.exe] XDrive MyDriveB: Mount attempt had lease locked. Redo in mode ReadOnly. [WaWebHost.exe] XDrive MyDriveB: Creating snapshot [WaWebHost.exe] XDrive MyDriveB: Mounting: Mode "ReadOnly" [WaWebHost.exe] XDrive MyDriveB: Mounted: Letter "f:": Mode "ReadOnly", Uri "http://127.0.0.1:10000/devstoreaccount1/myxdrives/MyDriveB.vhd?snapshot=2010-04-17T03:33:11.9620000Z" [runtime] Role entrypoint . COMPLETED OnStart() [runtime] Role entrypoint . CALLING Run() [WaWebHost.exe] X_DRIVES = MyDriveA=[e,r];MyDriveB=[f,r] [fabric] Role state Started并且 Global.Application_Start() 在第二次部署的 VM 中发布以下环境变量设置,该设置与首次部署不同:X_DRIVES = MyDriveA=[f,r];MyDriveB=[g,r] 解释是,请求的页面 blob VHD 路径(mydrives/MyDriveA.vhd 和 mydrives/MyDriveB.vhd)都存在,并且都已被前一个仍在运行的部署以租约锁定(写入访问)挂载,因此此部署将通过创建页面 blob VHD 路径的快照然后挂载它们来获得对两个 XDrive 的只读访问。
- MyDriveA - 驱动器盘符:f,访问模式:只读 (r)
- MyDriveB - 驱动器盘符:g,访问模式:只读 (r)
两次部署的最终结果
最终,存在两个并发部署
- 首次部署可以写入由 MyDriveA 和 MyDriveB 表示的页面 blob VHD。
- 第二次部署可以读取首次部署在 MyDriveA 和 MyDriveB 表示的页面 blob VHD 中写入的快照,直到其只读挂载点。此后,由于它是快照,无论首次部署在这些页面 blob VHD 中写入什么,第二次部署都不会看到这些更改,除非它执行另一个快照。