Follow Me Widget

четверг, 8 сентября 2011 г.

Windows Azure CDN. Доставка статического контента.

Все мы с вами прекрасно знакомы с предназначением такой технологии как CDN. Согласно определению из Википедии Content Delivery Network – это географически распределенная сетевая инфраструктура, позволяющая оптимизировать доставку и дистрибуцию контента конечным пользователям в сети Интернет. Сегодня я начинаю серию постов, посвященных CDN от Microsoft. В одной из моих предыдущих публикаций я кратко упоминал о возможности использования Windows Azure CDN для получения различного статического содержимого из хранилища блобов; сегодня же мы с вами подробнее познакомимся с тем как и при помощи каких средств это можно сделать.
В качестве примера, использующего CDN будет продемонстрировано простое Web-приложение с единственной страничкой, содержащей таблицу стилей и движок JQuery. Для начала весь статический контент будет храниться на сервере рядом с приложением, в дальнейшем мы перенесем его в хранилище блобов и в конце концов получим доступ к нему посредством Azure CDN. 
Итак, в первую очередь создадим стандартную заготовку ASP.NET MVC 3 приложения и слегка подредактируем вьюшку, которая используется на главной странице. Давайте посмотрим на код представления
@using MvcApplication1.Models

@{
    ViewBag.Title = "Home Page";
}

@section Header
{
   <link rel="Stylesheet" href="@Url.StaticContent("/content/site.css")" />
   <script type="text/javascript" src="@Url.StaticContent("/content/jquery-1.5.1.min.js")"></script>
}

<h2>@ViewBag.Message</h2>
<p>
    To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website">http://asp.net/mvc</a>.
</p>

Как видим я воспользовался extension-методом StaticContent для получения ссылок на статическое содержимое и для упрощения генерации урлов, в дальнейшем это очень пригодится. Сам код расширяющего метода представлен листингом ниже.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.WebPages.Html;
using System.Web.Mvc;

namespace MvcApplication1.Models
{
    public static class UrlHelperExtensions
    {
        public static string StaticContent(this UrlHelper helper, string resource)
        {
            return helper.Content(resource);
        }
    }
}

На данный момент вся статика находится на нашем сервере. Но мы хотим получать ее из облака. Для загрузки контента в хранилище блобов я воспользовался командлетом PowerShell, свободно распространяемом в Интернете. Командлет поставляется совместно с конфигурационным файлом, в котором необходимо прописать идентификатор подписки, имя storage-аккаунта, ключ доступа к хранилищу, название целевого контейнера и путь к исходной папке, в которой хранятся загружаемые файлы. Для наглядности давайте посмотрим на этот файл.

Blobs Upload Config

Если Вы знакомы с PowerShell сам командлет также ничем особенным не удивит. Вот его код.
function UploadBlobs($container, $directory, $virtualPath)
{  
    Write-Output "Uploading $directory files..."
    Foreach ($file in Get-ChildItem $directory)
    {
        if($file.PSIsContainer)
        {
            UploadBlobs $container $file.fullname ($virtualPath + $file.name + '/')
        }
        else
        {
            Write-Output "Uploading $file"
            $blob = $container.GetBlobReference($virtualPath + $file.name)
            $blob.Properties.ContentType = GetContentTypeFromExtension $file.extension
            $blob.Properties.CacheControl = "public, max-age=86400"
            $blob.UploadFile($file.fullName)
        }
    }
}

function SetCacheControlNoCache($container, $resource)
{
    $blob = $container.GetBlobReference($resource)
    $blob.Properties.CacheControl = "public, max-age=0";
    $blob.SetProperties()
}

function GetContentTypeFromExtension([string]$extension)
{   
    switch ($extension)
    {
        ".png" { return "image/png" }
        ".htm" { return "text/html" }
        ".pfx" { return "application/x-pkcs12" }
        ".xml" { return "text/xml" }
		".css" { return "text/css" }
		".jpg" { return "image/jpeg" }
		".jpeg" { return "image/jpeg" }
		".bmp" { return "image/bmp" }
		".js" { return "text/x-javascript" }
		".zip" { return "application/zip" }
    }
	
    Write-Output "application/octet-stream"
}

$scriptDir = (split-path $myinvocation.mycommand.path -parent)
Set-Location $scriptDir

$sdkPath = resolve-path "$Env:ProgramFiles\Windows Azure SDK\v1.?\ref"

write-host $sdkPath

if ($sdkPath -is [Array]) 
  { $refFolder = $sdkPath[-1] }
else 
  {$refFolder = [string]$sdkPath}

[Reflection.Assembly]::LoadFile($refFolder + ‘\Microsoft.WindowsAzure.StorageClient.dll’)

[xml] $xml = get-Content "./configuration.xml"
$subId = $xml.settings.subscriptionId
$storageAccount = $xml.settings.storageAccount.name
$storageAccountKey = $xml.settings.storageAccount.key
$containerName = $xml.settings.containerName
$sourceFolder = $xml.settings.sourceFolder

Write-Host "Uploading files..."

$credentials = New-Object Microsoft.WindowsAzure.StorageCredentialsAccountAndKey -ArgumentList $storageAccount, $storageAccountKey
$account = New-Object Microsoft.WindowsAzure.CloudStorageAccount -ArgumentList $credentials, TRUE
$client = [Microsoft.WindowsAzure.StorageClient.CloudStorageAccountStorageClientExtensions]::CreateCloudBlobClient($account)

$timeout = New-Object System.TimeSpan -ArgumentList 0, 10, 0
#set the timeout to 5 minutes. this allows us to upload large blobs.
$client.Timeout = $timeout

$container = $client.GetContainerReference($containerName)
$container.CreateIfNotExist()
# set public permissions, only if necessary
$publicPermission = New-Object Microsoft.WindowsAzure.StorageClient.BlobContainerPermissions
$publicPermission.PublicAccess = [Microsoft.WindowsAzure.StorageClient.BlobContainerPublicAccessType]::Blob
$container.SetPermissions($publicPermission)
UploadBlobs $container $sourceFolder ''
SetCacheControlNoCache $container "images/Destination.jpg"          

Write-Host "Done!"

Как только файлы будут успешно загружены, незначительно изменим код нашего расширяющего метода, а именно перенаправим все ссылки на хранилище блобов вместо локального сервера.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.WebPages.Html;
using System.Web.Mvc;

namespace MvcApplication1.Models
{
    public static class UrlHelperExtensions
    {
        public static string StaticContent(this UrlHelper helper, string resource)
        {
            return helper.Content(String.Format("https://feschenkoalex.blob.core.windows.net{0}", resource));
        }
    }
}

После запуска убедимся в том, что сайт все также успешно подгружает статику, но уже из хранилища блобов.
На данный момент мы переместили все статическое содержимое в удаленное хранилище , но цель перед нами стояла получать доступ к файлам посредством Windows Azure CDN. Для этого необходимо выполнить следующую последовательность шагов. В первую очередь необходимо активировать точку доступа, делается это в разделе CDN.

CDN New Endpoint

В открывшемся модальном окне необходимо выбрать аккаунт в том случае если у нас их несколько и нажать кнопку ОК (при этом не забыть оставить включенной галочку Enable CDN).

Create a New CDN Endpoint

После этого нас грозно предупредят о том, что изменения вступят в силу в течение 60 минут. Подождем пока точка перейдет в статус Enabled и скопируем полученный адрес из панельки свойств CDN.

Windows Azure CDN Domain

Когда все подготовительные процедуры будут завершены можно снова вернуться к коду приложения и снова изменить тело нашего расширяющего метода. В этот раз мы заменим домен хранилища блобов на CDN-домен.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.WebPages.Html;
using System.Web.Mvc;

namespace MvcApplication1.Models
{
    public static class UrlHelperExtensions
    {
        public static string StaticContent(this UrlHelper helper, string resource)
        {
            return helper.Content(String.Format("https://az32162.vo.msecnd.net{0}", resource));
        }
    }
}

Если к этому моменту наши изменения распространились по всему миру )) то мы получим контент с ближайшего CDN-узла, иначе придется немного подождать. В завершение продемонстрирую результат выполнения утилиты tracert для домена источника данных и для CDN-домена.

TraceRT With Blobs Domain

TraceRT On the Windows Azure CDN Domain

Как видим количество задействованных сетевых устройств в случае использования CDN значительно сокращается, что позволяет получать контент значительно быстрее.

Комментариев нет: