Azure PaaS 快速实践 3 - Azure Caching 优化云服务
前面的实践中,基于Azure PaaS编程模式(role based)的应用已经实现了,但是其运算效率可能不高,比如,第一个用户求取了10000以内的质数之后,第二个用户后续如果求取10100以内的质数,该云服务会重新计算从1到10100之间的所有质数,显然,云服务可以借鉴前面已计算得出的10000以内的质数,然后快速计算10000到10100间的质数,最后一起返回给第二个用户,这样效率会高不少。
在Azure托管服务(即云服务)中,不建议使用asp.net本身的缓存机制,原因有三:
1. Web Role可以包含多个实例虚机,如A和B,虚机A中的缓存不会共享给虚机B,由于外部用户的请求会通过Azure云服务的负载均衡被分发到web role下的所有虚机,很有可能出现:计算10000以内质数的请求被虚机A处理,后续计算10100以内质数的请求被分发给虚机B,此时A中的缓存对B虚机中的计算没有帮助。
2. Web Role属于PaaS(平台即是服务),一旦开发者将服务部署到云端以后,该服务的环境维护完全由Azure平台来负责,因此,当Web Role实例虚机需要进行系统更新、平台更新、物理故障、发布包故障时,Azure平台会对该实例虚机进行自修复,自修复中的一种方式就是重新初始化(recycling),即Azure平台会使用开发者上传的服务包重新部署到一台新的虚拟机上,来取代有问题的虚拟机。在此Recycling的过程中,如果用户使用了虚拟内部的缓存,缓存的内容会丢失。
3.当Web Role实例虚机需要进行系统更新、平台更新、物理故障、发布包故障时,Azure平台可能会重启该实例虚机来恢复/更新服务,重启操作同样会导致虚拟内部的缓存丢失。
因此,对于Azure托管服务开发,有几点重要的要素:
1.每一个Role(如web role或者worker role)都至少有2个实例:这样Azure平台会将这些虚机部署在完全隔离的物理设备上,上游配以负载均衡来对外服务。且对该role进行管理时,会逐一操作,而不会同时操作,如此可以保证该web role的服务可用性(SLA
>99.95%)。
2.不要将永久使用的数据存放在PaaS虚拟上:由于Role对应的虚机可能会被重启甚至Recycling,临时存储在虚机上的数据、文件可能会丢失。因此,对于服务运行过程中需要保留的数据,请使用虚机意外的独立的永久(长久)存储。如Azure Storage, SQL Azure, Azure Caching等。
本节将使用Azure Caching来优化之前的托管服务。
步骤八:优化设计( Azure Caching )
双击Azure项目中的WorkerRole1,然后选择Caching页面,选择(Enable Caching --> Co-located Role),并配置使用已有的Azure Storage帐号。
如上图配置完成后使用 “Ctrl+S”快捷键键保存整个工程。
从Visual Studio中确认已安装的Azure SDK版本,Visual Studio --> Help --> About Microsoft Visual Studio,如图:
若当前开发机安装的是SDK2.0,在worker role项目中通过Library package manager安装Caching需要的库。如下图。
Install-Package Microsoft.WindowsAzure.Caching -Version 2.0.0.0
若当前开发机使用的AzureSDK版本为2.1,请选用以下方法安装Caching库:
右键选择WorkerRole项目,点击Manage Nuget Packages
在线搜索关键字azure,并找到windows azure caching安装包,点击install完成安装。
使用上述方法中的任何一种,安装Caching库完成后,在WorlerRole1项目中的app.config文件中,可以看到自动添加进来的配置。如下图,将identifier对应的内容改为当前worker role的项目名称:WorkerRole1
完成后使用或者 “Ctrl+S”快捷键键保存整个工程。
双击WorkerRole1项目中的WorkerRole.cs文件,添加一个引用。
using Microsoft.ApplicationServer.Caching;
更新WorkerRole.cs文件中的Run函数为:
public override void Run()
{
// This is a sample worker implementation. Replace with your logic.
Trace.TraceInformation("WorkerRole1 entry point called", "Information");
//Azure存储帐号
var storageAccount = CloudStorageAccount.Parse("DefaultEndpointsProtocol=https;AccountName=portalvhds303zv6wrv9j9q;AccountKey=WNydsi2FU6GRg/KfPV7aF0P86NdXt2AS6twm+qo6zhepr580EP2T7V7PGXsXYOxK/SlKHuq5smnHVsR/1R4klQ==");
var queueStorage = storageAccount.CreateCloudQueueClient();
//检查名为 helloworldqueue 的队列是否被创建,如果没有,创建它
var queue = queueStorage.GetQueueReference("helloworldqueue");
queue.CreateIfNotExists();
while (true)
{
try
{ //read message from the queue
var message = queue.GetMessage();
string strNum = message.AsString.Substring(0, message.AsString.IndexOf("@") - 1);
//process message and save result into blob storage
string strResult = getZhishu(Convert.ToInt32(strNum.Trim()));
int count = strResult.Split(';').Count();
count = count - 1;
strResult = "There are " + count.ToString() + " prime numbers no greater than " + strNum + " : " + strResult;
writeBlobStorage(strNum, strResult);
//delete message from the queue
queue.DeleteMessage(message);
}
catch (Exception ee)
{
}
Thread.Sleep(3000);
Trace.TraceInformation("Working", "Information");
}
}
更新WorkerRole.cs文件中求质数对应的函数代码,为:
public string getZhishu(int Input)
{
if (Input < 2)
{
return "invalid input or there is no prime number less than the input number.";
}
int maxCacheInt = getCachingRef(Input);
if (maxCacheInt > 0)
{
System.IO.File.AppendAllText("./workerrole.log", "\r\n" + DateTime.Now.ToString() + " : cache is called. @" + Input.ToString());
string output0 = "";
if (maxCacheInt >= Input)
{
string content = cache.Get(maxCacheInt.ToString()) as string;
string[] numbers = content.Split(';');
for (int m = 0; m < numbers.Count() - 1; m++)
{
if (Convert.ToInt32(numbers[m]) <= Input)
{
output0 += Convert.ToInt32(numbers[m]) + ";";
}
else
{
break;
}
}
return output0;
}
else //maxCacheInt < Input
{
output0 = cache.Get(maxCacheInt.ToString()) as string;
int i = maxCacheInt+1, j;
while (i <= Input && i >= maxCacheInt)
{
j = 2;
while (j <= i / 2)
{
if (i % j == 0)
break;
j++;
}
if (j > i / 2)
{
output0 += i.ToString() + ";";
//Console.Write("{0}\t", i);
}
i++;
}
//cache update
cache.Remove("maxint");
cache.Remove(maxCacheInt.ToString());
cache.Add("maxint", Input.ToString());
cache.Add(Input.ToString(), output0);
return output0;
}
}
else
{
System.IO.File.AppendAllText("./workerrole.log", DateTime.Now.ToString() + " : cache is not called. @" + Input.ToString());
string res = Calculate(Input);
cache.Add("maxint", Input.ToString());
cache.Add(Input.ToString(), res);
return res;
}
}
在WorkerRole.cs文件中添加两个新的函数和定义,如下:
public int getCachingRef(int input)
{
// check if the name specified is in cache
var maxint = (string)cache.Get("maxint");
if (maxint != null)
{
try
{
System.IO.File.AppendAllText("./workerrole.log", "\r\n" + DateTime.Now.ToString() + " : cached max int is " + maxint);
return Convert.ToInt32(maxint);
}
catch (Exception ex)
{
}
}
return -1;
}
public static DataCacheFactory factory = new DataCacheFactory();
public static DataCache cache = factory.GetDefaultCache();
public string Calculate(int Input)
{
int i = 2, j, n = 0;
string output = "";
while (i <= Input)
{
j = 2;
while (j <= i / 2)
{
if (i % j == 0)
break;
j++;
}
if (j > i / 2)
{
n += 1;
output += i.ToString() + ";";
//Console.Write("{0}\t", i);
}
i++;
}
//output = "There are " + n.ToString() + " prime numbers no greater than " + Input.ToString() + " : " + output;
return output;
}
代码更新后,WorkerRole.cs文件结构如图:
完成后本地Rebuild,并F5调试,确认workerrole运行正常。
同步骤五,发布到Azure Cloud中。
发布完成后,登录Azure 管理主页,选中刚刚发布的云服务,同之前的动手实践一样,配置remote连接功能。
配置Remote后,访问刚刚发布的云服务,进行几次测试,确认前端工作正常。
从管理主页上远程连接到刚刚发布的Worker Role虚机上,在E:\approot下可以找到项目code打出的日志文件workerrole.log,如图,日志中记录了之前的请求,并显示cache被调用过。
至此,一个简单的依赖于Azure Caching的实践已经完成了。
此实验对于求素数的业务逻辑没有深入研究,在算法效率和代码重用方面投入的精力较少,开发者可以结合本次整个培训学习到的知识,进一步的改进和完善。