Este artigo foi traduzido por máquina.

Armazenamento em nuvem

Impulsiona o mecanismo do seu aplicativo com o Windows Storage Azure

Kevin Hoffman

Baixe o código de exemplo

Os desenvolvedores tendem a cling à sua infra-estrutura física tangível como uma ampla de segurança. Eles sabem como utilizá-lo, eles sabem como operá-lo e quando algo dá errado, eles saber onde ele está. Geralmente, isso cria uma barreira lento desenvolvedor adoção de novas tecnologias, tais como a computação em nuvem.

Uma das maiores perguntas dúvidas que os desenvolvedores perguntar é como eles ainda podem executar em segundo plano processa na nuvem — como seu mecanismo de continuarão a funcionar. Este artigo tem como objetivo difundir mitos sobre falta de processamento na nuvem mostrando como você pode criar um mecanismo de aplicativo, bem como implementar mensagens assíncronas e processamento usando o Windows Azure armazenamento em segundo plano.

Para provar que os desenvolvedores podem lançar a ampla de segurança da infra-estrutura física e colocar seus mecanismos de aplicativo na nuvem, vamos percorrer a implementação de um pequeno subconjunto de um aplicativo de e-commerce, Hackers Hollywood, onde você pode comprar toda a tecnologia de mágica Hollywood usa para ignorar completamente a leis da física e senso antiquado.

Os dois cenários principais, abordaremos são:

  • Enviar mensagens de texto assíncrona (“ brindes ”) para usuários do aplicativo para notificá-los de eventos importantes, como seu carrinho de envio ou para enviar mensagens entre os funcionários. Esse cenário usa Windows Azure fila, tabela de Azure Windows e uma função de trabalho do Windows Azure.
  • Enviando um carrinho de compras para um mecanismo de preenchimento usando Windows Azure Queue e uma função de trabalho do Windows Azure.

Intra-Application com o armazenamento de fila de mensagens

Antes de entrarmos em cenários específicos, precisamos abordar algumas noções básicas sobre Windows Azure fila. Filas na nuvem Don funcionam bem como filas em seu aplicativo .NET de baunilha sem formatação. Ao trabalhar com dados em um AppDomain, você saberá que há apenas uma cópia dos dados e está colocada confortavelmente em um único processo gerenciado.

Na nuvem, uma parte dos seus dados pode estar na Califórnia e outro pode estar em Nova York e talvez você tenha uma função de trabalho fazendo o processamento de dados no Texas e a outra função de trabalho fazendo processamento na Dakota do Norte.

Ajustando a esse tipo de dados distribuídos e de computação distribuída traz muitos desenvolvedores não estiver familiarizado com, como a codificação para falha potencial, criando no conceito de várias tentativas para confirmações de dados e, finalmente, a idéia de idempotence problemas.

A maneira como trabalha Windows Azure filas é relativamente simples, desde que você Don os tratará como regulares filas CLR em processo. Primeiro, seu aplicativo solicitará a fila um número de mensagens (embora nunca mais de 20 por vez; Lembre-se de que tenha) e fornecer um tempo limite. Esse tempo limite controla quanto tempo essas mensagens serão ocultos de outros clientes de processamento de filas. Quando o aplicativo foi concluída com êxito qualquer processamento precisa ser feito na fila de mensagem, ele deve excluir a mensagem.

Se seu aplicativo lança uma exceção ou falha processar a mensagem da fila, a mensagem ficará visível para outros clientes novamente após o período de tempo limite. Isso permite que funções de trabalho adicional para continuar processando quando uma falha. Enviar uma mensagem para uma fila é muito simples: seu aplicativo de formulários a mensagem HTTP POST apropriada (diretamente ou com o auxílio de uma biblioteca de cliente) e envia a seqüência de caracteres ou uma matriz de bytes. Filas foram desenvolvidas especificamente para mensagens intra-application e armazenamento permanente não, para que as mensagens precisam ser mantidas bem pequeno.

Como mencionado anteriormente, você poderia perfeitamente ter várias funções de operador todas tentando processar as mesmas mensagens. O tempo limite de invisibility oculta as mensagens que estão sendo processadas atualmente é útil, mas não é uma garantia. Para evitar completamente o conflito, você deve projetar seu mecanismo de processamento para que ele seja idempotentes . Em outras palavras, a mesma mensagem da fila deve poderá ser processada várias vezes por uma ou mais funções de trabalho sem colocar o aplicativo em um estado inconsistente.

Idealmente, que deseja que a função de trabalho para poder detectar se o trabalho já foi concluído em uma determinada mensagem. À medida que você escrever suas funções de trabalho para processar mensagens de fila, lembre-se de que há uma chance de seu código pode estar tentando processar uma mensagem já foi processada, porém fino que talvez chance.

O trecho de código no 1 Figura mostra como criar e enviar uma mensagem para uma fila de Azure Windows usando o assembly StorageClient fornecida com o SDK do Windows Azure. A biblioteca StorageClient é realmente apenas um wrapper em torno de interface do Windows Azure armazenamento HTTP.

Figura 1 de fila Criando e enviando uma mensagem para um Azure Windows

string accountName;
string accountSharedKey;
string queueBaseUri;
string StorageCredentialsAccountAndKey credentials;

if (RoleEnvironment.IsAvailable)
{
// We are running in a cloud - INCLUDING LOCAL!
  accountName = 
  RoleEnvironment.GetConfigurationSettingValue("AccountName");  
  accountSharedKey =   
  RoleEnvironment.GetConfigurationSettingValue("AccountSharedKey");
  queueBaseUri = RoleEnvironment.GetConfigurationSettingValue
 ("QueueStorageEndpoint");
}
else
{
  accountName = ConfigurationManager.AppSettings["AccountName"];
  accountSharedKey = 
  ConfigurationManager.AppSettings["AccountSharedKey"];
  queueBaseUri = 
  ConfigurationManager.AppSettings["QueueStorageEndpoint"];
}
credentials = 
new StorageCredentialsAccountAndKey(accountName, accountSharedKey);
CloudQueueClient client = 
new CloudQueueClient(queueBaseUri, credentials);
CloudQueue queue = client.GetQueueReference(queueName);
CloudQueueMessage m = new CloudQueueMessage(
  /* string or byte[] representing message to enqueue */);
Queue.AddMessage(m);

Outros exemplos neste artigo, usamos algumas classes de invólucro (disponível no site da CodePlex de Hollywood Hackers: hollywoodhackers.codeplex.com/SourceControl/ListDownloadableCommits.aspx) que simplificar esse processo.

Mensagens assíncronas (brindes)

Sites da Web interativos não são apenas a fúria hoje em dia, eles são um requisito. Os usuários tornaram tão acostumados a sites totalmente interativos que eles acham que algo está errado quando encontram uma página estática não interativa. Com isso em mente, queremos ser capaz de enviar notificações aos nossos usuários que estiverem usando o site.

Para fazer isso, podemos usará mecanismos de armazenamento Windows Azure Queue e Table para criar uma estrutura de entrega de mensagem. Lado do cliente usará o jQuery combinado com o plug-in do jQuery Gritter para exibir notificações no navegador do usuário como brindar, semelhante às mensagens de fade in acima da bandeja de sistema do Windows quando você recebe um novo email do Outlook, mensagens instantâneas ou tweet.

Quando um usuário precisa ser enviada uma notificação, ele será inserido na fila. Como a função de trabalho processa cada item na fila, ele dinamicamente determinará como lidar com cada um deles. No nosso caso, há apenas uma coisa para o mecanismo fazer, mas em um site do CRM complexo ou site de suporte técnico, as possibilidades são infinitas.

Quando a função de trabalho em uma notificação de usuário na fila, ele irá armazenar a notificação no armazenamento de tabela e exclua-o da fila. Isso permite que mensagens para ser mantidos a longo prazo e aguarde o usuário efetuar logon. Mensagens na fila de armazenamento têm um curto tempo de vida máximo e nunca irão durar mais de alguns dias. Quando o usuário acessa o site, nosso script jQuery assincronamente irá receber quaisquer mensagens de fora da tabela e exiba-os no navegador invocando um método em um controlador que retorna o JavaScript Object Notation (JSON) em uma forma bastante conhecida.

Embora a fila manipula somente seqüências de caracteres ou matrizes de bytes, pode armazenar qualquer tipo de dados estruturados na fila por ele serialização em binário e convertê-lo novamente quando precisamos para usá-lo. Isso se torna uma técnica poderosa para passar objetos com rigidez de tipos para a fila. Criaremos este na classe base para nossas mensagens de fila. Em seguida, nossa classe de mensagem do sistema pode conter nossos dados e o objeto inteiro pode ser enviado à fila e utilizado conforme necessário (consulte do Figura 2).

Figura 2 de armazenar dados estruturados na fila

namespace HollywoodHackers.Storage.Queue
{
    [Serializable]
    public class QueueMessageBase
    {
        public byte[] ToBinary()
        {
            BinaryFormatter bf = new BinaryFormatter();
            MemoryStream ms = new MemoryStream();
            ms.Position = 0;
            bf.Serialize(ms, this);
            byte[] output = ms.GetBuffer();
            ms.Close();
            return output;
        }
        public static T FromMessage<T>(CloudQueueMessage m)
        {
            byte[] buffer = m.AsBytes();
            MemoryStream ms = new MemoryStream(buffer);
            ms.Position = 0;
            BinaryFormatter bf = new BinaryFormatter();
            return (T)bf.Deserialize(ms);
        }
    }
    [Serializable]
    public class ToastQueueMessage : QueueMessageBase
    {
      public ToastQueueMessage()
            : base()
        {
        }
        public string TargetUserName { get; set; }        
        public string MessageText { get; set; }
        public string Title { get; set; }
        public DateTime CreatedOn { get; set; }       
   }

Tenha em mente que, para usar a classe BinaryFormatter, sua função de trabalho do Windows Azure precisa estar sendo executado no modo de confiança total (você pode ativar isso através do arquivo de configuração do serviço).

Agora precisamos um wrapper simples para interagir com nossa fila. Em seu núcleo, precisam ser capazes de inserir uma mensagem na fila, obter todas as mensagens pendentes e limpar a fila (consulte do Figura 3).

Figura 3 wrapper interaja com a fila

namespace HollywoodHackers.Storage.Queue
{
    public class StdQueue<T> : 
    StorageBase where T : QueueMessageBase, new()
    {
        protected CloudQueue queue;
        protected CloudQueueClient client;

        public StdQueue(string queueName)
        {
            client = new CloudQueueClient
            (StorageBase.QueueBaseUri, StorageBase.Credentials);
            queue = client.GetQueueReference(queueName);
            queue.CreateIfNotExist();
        }
        public void AddMessage(T message)
        {
            CloudQueueMessage msg = 
            new CloudQueueMessage(message.ToBinary());
            queue.AddMessage(msg);
        }
        public void DeleteMessage(CloudQueueMessage msg)
        {
            queue.DeleteMessage(msg);
        }
        public CloudQueueMessage GetMessage()
        {
            return queue.GetMessage(TimeSpan.FromSeconds(60));
        }
    }
    public class ToastQueue : StdQueue<ToastQueueMessage>
    {
        public ToastQueue()
            : base("toasts")
        {
        }
    }
}

Também precisamos definir um wrapper para nossa tabela de armazenamento para que notificações de usuário podem ser armazenadas até que efetuam login no site. Tabela de dados é organizada usando um PartitionKey, que é o identificador para uma coleção de de linhas e uma RowKey, que identifica com exclusividade cada linha individual em uma determinada partição. A escolha de quais dados você usa para um PartitionKey e uma RowKey pode ser uma das mais importantes decisões de design que você fazer ao usar armazenamento de tabela.

Esses recursos permitem o balanceamento de carga em nós de armazenamento e fornecem opções de escalabilidade interna em seu aplicativo. Independentemente da afinidade do data center dos seus dados, linhas na tabela de armazenamento com a mesma chave de partição serão mantidas no armazenamento de dados físicos do mesmo. Como as mensagens são armazenadas para cada usuário, a chave de partição será o nome de usuário e a RowKey será um GUID que identifica cada linha (consulte a Figura 4 ).

Figura 4 do wrapper para o armazenamento de tabela

namespace HollywoodHackers.Storage.Repositories
{
    public class UserTextNotificationRepository : StorageBase
    {
        public const string EntitySetName = 
        "UserTextNotifications";
        CloudTableClient tableClient;
        UserTextNotificationContext notificationContext;
        public UserTextNotificationRepository()
            : base()
        {
            tableClient = new CloudTableClient
            (StorageBase.TableBaseUri, StorageBase.Credentials);
            notificationContext = new UserTextNotificationContext 
            (StorageBase.TableBaseUri,StorageBase.Credentials);

            tableClient.CreateTableIfNotExist(EntitySetName);
        }
        public UserTextNotification[] 
        GetNotificationsForUser(string userName)
        {
            var q = from notification in 
                    notificationContext.UserNotifications
                    where notification.TargetUserName == 
                    userName select notification;
            return q.ToArray();
        }
        public void AddNotification 
       (UserTextNotification notification)
        {
            notification.RowKey = Guid.NewGuid().ToString();
            notificationContext.AddObject
           (EntitySetName, notification);
            notificationContext.SaveChanges();
        }
    }
}

Agora que nossos mecanismos de armazenamento estão no lugar, precisamos uma função de trabalho que atua como nosso mecanismo; processamento de mensagens no plano de fundo de nosso site de comércio eletrônico. Para fazer isso, podemos definir uma classe que herda da classe Microsoft.ServiceHosting.ServiceRuntime.RoleEntryPoint e associá-lo com a função de trabalho em nosso projeto de serviço em nuvem (consulte do Figura 5).

Figura 5 trabalho função atuando como mecanismo

public class WorkerRole : RoleEntryPoint
{
    ShoppingCartQueue cartQueue;
    ToastQueue toastQueue;
    UserTextNotificationRepository toastRepository;

    public override void Run()
    {
        // This is a sample worker implementation. 
        //Replace with your logic.
        Trace.WriteLine("WorkerRole1 entry point called", 
        "Information");
        toastRepository = new UserTextNotificationRepository();
        InitQueue();
        while (true)
        {
            Thread.Sleep(10000);
            Trace.WriteLine("Working", "Information");

            ProcessNewTextNotifications();
            ProcessShoppingCarts();
        }
    }
    private void InitQueue()
    {
        cartQueue = new ShoppingCartQueue();
        toastQueue = new ToastQueue();
    }
    private void ProcessNewTextNotifications()
    {
        CloudQueueMessage cqm = toastQueue.GetMessage();
        while (cqm != null)
        {
            ToastQueueMessage message = 
            QueueMessageBase.FromMessage<ToastQueueMessage>(cqm);

            toastRepository.AddNotification(new 
            UserTextNotification()
            {
                MessageText = message.MessageText,
                MessageDate = DateTime.Now,
                TargetUserName = message.TargetUserName,
                Title = message.Title
            });
            toastQueue.DeleteMessage(cqm);
            cqm = toastQueue.GetMessage();
        }
    }
    private void ProcessShoppingCarts()
    {
        // We will add this later in the article!
    }
    public override bool OnStart()
    {
        // Set the maximum number of concurrent connections 
        ServicePointManager.DefaultConnectionLimit = 12;

        DiagnosticMonitor.Start("DiagnosticsConnectionString");
        // For information on handling configuration changes
        // see the MSDN topic at 
        //http://go.microsoft.com/fwlink/?LinkId=166357.
        RoleEnvironment.Changing += RoleEnvironmentChanging;
        return base.OnStart();
    }
    private void RoleEnvironmentChanging(object sender, RoleEnvironmentChangingEventArgs e)
    {
        // If a configuration setting is changing
        if (e.Changes.Any(change => change is RoleEnvironmentConfigurationSettingChange))
        {
            // Set e.Cancel to true to restart this role instance
            e.Cancel = true;
        }
    }
}

Let’s mostre o código de função de trabalho. Depois de Inicializando e configurando a fila necessária e o armazenamento de tabela, o código irá inserir um loop. A cada 10 segundos, ele processará as mensagens na fila. Cada vez, passamos através do loop de processamento, nós obterá mensagens da fila até que nós finalmente retornar nulo, indicando que nós ter esvaziado a fila.

Vale a pena repetir que você pode nunca examinar mais de 20 mensagens da fila. Tudo o que o processamento em uma fila tem uma quantidade limitada de tempo para fazer algo significativo com cada mensagem da fila antes da mensagem da fila é considerada o tempo limite esgotado e aparece novamente na fila — tornando-se disponíveis para o processamento por outros funcionários. Cada mensagem é adicionada como uma notificação de usuário no armazenamento de tabela. Um importante lembrar-se sobre as funções de operador é quando termina o método de ponto de entrada, essa função de trabalho é feita. É por isso que você precisa manter sua lógica em execução dentro de um loop.

Do lado cliente, é necessário retornar as mensagens como JSON e jQuery assincronamente pode pesquisar e exibir novas notificações de usuário. Para fazer isso, adicionaremos alguns códigos a controlador de mensagem para que possa acessar as notificações (consulte do Figura 6).

Figura 6 retornando mensagens como JSON

public JsonResult GetMessages()
{
     if (User.Identity.IsAuthenticated)
     {
UserTextNotification[] userToasts = 
        toastRepository.GetNotifications(User.Identity.Name);
object[] data = 
(from UserTextNotification toast in userToasts
            select new { title = toast.Title ?? "Notification",
 text = toast.MessageText }).ToArray();
            return Json(data, JsonRequestBehavior.AllowGet);
     }
     else
         return Json(null);
}

2 De MVC do ASP.NET em Visual Studio 2010 beta 2 (o ambiente são usados para gravar neste artigo), não é possível retornar dados JSON jQuery ou qualquer outro cliente sem a opção JsonRequestBehavior.AllowGet. No ASP.NET MVC 1, esta opção não é necessária. Agora podemos escrever o JavaScript, que chamará o método GetMessages cada 15 segundos e exibir as notificações como estilo brindar mensagens (consulte do Figura 7).

Figura 7 do notificações como estilo Toast mensagens

$(document).ready(function() {

    setInterval(function() {
        $.ajax({
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            url: "/SystemMessage/GetMessages",
            success: function(data) {
                for (msg in data) {
                    $.gritter.add({
                        title: data[msg].title,
                        text: data[msg].text,
                        sticky: false
                    });
                }
            }
        })
    }, 15000)
});

Enviando e processando um carrinho de compras

Nosso aplicativo de exemplo, outro cenário de chave que queríamos habilitar usando o armazenamento de fila foi enviando carrinhos de compra. Hollywood hackers possui um sistema de cumprimento de terceiros (que não é possível manter todos os gadgets em seu warehouse pouco) para que o mecanismo precisa fazer algum processamento sobre o carrinho. Quando termina o mecanismo de fazer seu processamento, ele irá enviar uma mensagem para a fila de notificação do usuário para permitir que o usuário sabe que o carrinho de compras foi processado (ou que algo saiu errado). Se o usuário está on-line quando o carrinho é processado, ele receberá uma mensagem pop-up brindar do sistema. Se ele não estiver on-line, ele receberá essa mensagem pop-up na próxima vez que ele fizer ao site, como mostra a do Figura 8.

Figura 8 notificação de usuário do exemplo

image: Sample User Notification

O que precisamos primeiro são algumas classes de wrapper para permitir a interagir com a fila de carrinho de compras. Esses invólucros são bastante simples e se você desejar ver o código-fonte para eles, você pode fazer check-no site da CodePlex out.

Ao contrário de um padrão CRUD (criar, ler, atualizar e excluir) repositório, operações em uma fila de leitura não são operações de leitura simples. Lembre-se de que sempre que receber uma mensagem de uma fila, você tem uma quantidade limitada de tempo durante o qual processo que mensagem e um falhar a operação ou excluir a mensagem para indicar o processamento concluído. Esse padrão não traduzir bem em repositório padrão para que deixamos essa abstração desativar a classe de wrapper.

Agora que temos o código para interagir com a fila de carrinho de compras, pode colocar alguns código no controlador de carrinho para enviar o conteúdo do carrinho para a fila (consulte do Figura 9).

Figura 9 de remetente carrinho para a fila de compras

public ActionResult Submit()
    {
        ShoppingCartMessage cart = new ShoppingCartMessage();
        cart.UserName = User.Identity.Name;
        cart.Discounts = 12.50f;
        cart.CartID = Guid.NewGuid().ToString();
        List<ShoppingCartItem> items = new List<ShoppingCartItem>();
        items.Add(new ShoppingCartItem() 
             { Quantity = 12, SKU = "10000101010", 
             UnitPrice = 15.75f });
        items.Add(new ShoppingCartItem() 
             { Quantity = 27, SKU = "12390123j213", 
             UnitPrice = 99.92f });
        cart.CartItems = items.ToArray();
        cartQueue.AddMessage(cart);
        return View();
    }

Em um cenário de mundo real, você obteria o carrinho de compras de algum estado fora de processo como uma sessão de armazenar, um cache ou de um formulário de postagem. Para simplificar o código de artigo, nós estiver fabricating apenas o conteúdo de um carrinho.

Finalmente, com o conteúdo do carrinho de compras colocada na fila, podemos modificar nossa trabalho função para que ele verifica periodicamente a fila para pendente carrinhos. Será Puxe cada carrinho da fila, um por vez, permitir que ela própria um minuto para processamento e, em seguida, envie uma mensagem para a fila de notificação do usuário informando o usuário que o carrinho de compras foi processado (consulte Figura 10 ).

Figura 10 de verificação da fila para pendente carrinhos de compras

private void ProcessShoppingCarts()
{
    CloudQueueMessage cqm = cartQueue.GetMessage();            

    while (cqm != null)
    {             
        ShoppingCartMessage cart =  
        QueueMessageBase.FromMessage<ShoppingCartMessage>(cqm);

        toastRepository.AddNotification(new UserTextNotification()
        {
            MessageText = String.Format
            ("Your shopping cart containing {0} items has been processed.",   
            cart.CartItems.Length),
            MessageDate = DateTime.Now,             
            TargetUserName = cart.UserName
        });
        cartQueue.DeleteMessage(cqm);
         cqm = cartQueue.GetMessage();
    }
}

Com a mensagem da fila que está sendo puxado e colocar na tabela de notificação de usuário, o código de Gritter jQuery colocada na página mestra irá detectar a nova mensagem no próximo ciclo de apuração de 15 segundos e exibir a notificação de brindar carrinho de compras para o usuário, em seguida.

Resumo e próximos passos

O objetivo deste artigo é obter os desenvolvedores de lançar a ampla de segurança de seus centros de dados físicos e perceber que você pode fazer mais com o Windows Azure de criar sites de “ Hello World ” simples. Com o poder do Windows Azure filas e Windows Azure armazenamento de tabela e usando esse poder para mensagens assíncronas entre o aplicativo e suas funções de operador, você pode realmente combustível Windows Azure o mecanismo do seu aplicativo.

Para manter o artigo claro e fácil de ler, podemos deixado um pouco de código como está, sem refatoração. Como um exercício flexionar seus músculos Windows Azure novos, tente Refatorar algum código neste artigo para tornar o uso de filas mais genérica e até mesmo criam um assembly independente que contém o código necessário fazer mensagens assíncronas e notificações para qualquer site da Web do ASP.NET MVC.

O principal é as mangas, criar alguns sites e ver o que você pode fazer. O código deste artigo pode ser encontrado no site da CodePlex de Hollywood Hackers: hollywoodhackers.codeplex.com.

Você pode localizar os autores de blogs e ininterruptas ranting sobre nova tecnologia em exclaimcomputing.com .Kevin Hoffmane**Nate Dudek  **são co-fundadores da Exclaim computação, uma empresa especializada no desenvolvimento de soluções para a nuvem.