wzorzec pracy Queue-Centric (tworzenie aplikacji w chmurze Real-World za pomocą platformy Azure)

Autor : Rick Anderson, Tom Dykstra

Pobierz naprawę projektu lub pobierz książkę elektroniczną

Książka elektroniczna Building Real World Cloud Apps with Azure (Tworzenie rzeczywistych aplikacji w chmurze za pomocą platformy Azure ) jest oparta na prezentacji opracowanej przez Scotta Guthrie. Wyjaśniono w nim 13 wzorców i rozwiązań, które mogą pomóc w pomyślnym tworzeniu aplikacji internetowych dla chmury. Aby uzyskać informacje na temat książki e-book, zobacz pierwszy rozdział.

Wcześniej widzieliśmy, że korzystanie z wielu usług może spowodować "złożoną" umowę SLA, w której obowiązująca umowa SLA aplikacji jest produktem poszczególnych umów SLA. Na przykład aplikacja Fix It używa witryn sieci Web, magazynu i SQL Database. Jeśli którakolwiek z tych usług ulegnie awarii, aplikacja zwróci użytkownikowi błąd.

Buforowanie to dobry sposób obsługi błędów przejściowych dla zawartości tylko do odczytu. Ale co zrobić, jeśli aplikacja musi działać? Na przykład gdy użytkownik prześle nowe zadanie Napraw, aplikacja nie może po prostu umieścić zadania w pamięci podręcznej. Aplikacja musi zapisać zadanie Fix It w trwałym magazynie danych, aby można je było przetworzyć.

W tym miejscu pojawia się wzorzec pracy skoncentrowany na kolejce. Ten wzorzec umożliwia luźne sprzęganie między warstwą internetową a usługą zaplecza.

Oto jak działa wzorzec. Gdy aplikacja pobiera żądanie, umieszcza element roboczy w kolejce i natychmiast zwraca odpowiedź. Następnie oddzielny proces zaplecza ściąga elementy robocze z kolejki i wykonuje pracę.

Wzorzec pracy skoncentrowany na kolejce jest przydatny w następujących celach:

  • Praca, która jest czasochłonna (duże opóźnienie).
  • Praca, która wymaga usługi zewnętrznej, która może nie zawsze być dostępna.
  • Praca intensywnie korzystająca z zasobów (wysokie użycie procesora CPU).
  • Praca, która skorzystałaby z bilansowania szybkości (z powodu nagłych wzrostów obciążenia).

Mniejsze opóźnienie

Kolejki są przydatne za każdym razem, gdy wykonujesz czasochłonną pracę. Jeśli zadanie trwa kilka sekund lub dłużej, zamiast blokować użytkownika końcowego, umieść element roboczy w kolejce. Poinformuj użytkownika "Pracujemy nad nim", a następnie użyj odbiornika kolejki, aby przetworzyć zadanie w tle.

Na przykład w przypadku zakupu czegoś w sklepie internetowym witryna internetowa potwierdza twoje zamówienie natychmiast. Ale to nie znaczy, że twoje rzeczy są już w ciężarówce dostarczanej. Umieszczają zadanie w kolejce, a w tle wykonują kontrolę kredytową, przygotowują swoje elementy do wysyłki itd.

W przypadku scenariuszy z krótkim opóźnieniem łączny czas zakończenia może być dłuższy przy użyciu kolejki w porównaniu z wykonywaniem zadania synchronicznie. Ale nawet wtedy, inne korzyści mogą przeważyć nad tym wadą.

Zwiększona niezawodność

W wersji Fix It, na którą patrzyliśmy do tej pory, fronton internetowy jest ściśle powiązany z zapleczem SQL Database. Jeśli usługa bazy danych SQL jest niedostępna, użytkownik otrzymuje błąd. Jeśli ponawianie prób nie zadziała (oznacza to, że niepowodzenie jest czymś więcej niż przejściowym), jedyną czynnością, którą można zrobić, jest wyświetlenie błędu i poproszenie użytkownika o ponowną próbę później.

Diagram przedstawiający niepowodzenie frontonu internetowego w przypadku niepowodzenia SQL Database zaplecza

Za pomocą kolejek, gdy użytkownik przesyła zadanie Napraw, aplikacja zapisuje komunikat w kolejce. Ładunek komunikatu jest reprezentacją JSON zadania. Gdy tylko komunikat zostanie zapisany w kolejce, aplikacja zwróci i natychmiast wyświetli komunikat o powodzeniu dla użytkownika.

Jeśli którekolwiek z usług zaplecza — takich jak baza danych SQL lub odbiornik kolejki — przechodzą w tryb offline, użytkownicy nadal mogą przesyłać nowe zadania naprawy. Komunikaty będą po prostu kolejkowane do momentu ponownego udostępnienia usług zaplecza. W tym momencie usługi zaplecza nadrobią zaległości na liście prac.

Diagram przedstawiający fronton internetowy, który kontynuuje działanie w przypadku wystąpienia błędu SQL Database

Ponadto teraz możesz dodać więcej logiki zaplecza bez obaw o odporność frontonu. Możesz na przykład wysłać wiadomość e-mail lub wiadomość SMS do właściciela za każdym razem, gdy zostanie przypisana nowa poprawka. Jeśli usługa poczty e-mail lub wiadomości SMS stanie się niedostępna, możesz przetworzyć wszystko inne, a następnie umieścić wiadomość w oddzielnej kolejce na potrzeby wysyłania wiadomości e-mail/SMS.

Wcześniej obowiązująca umowa SLA była Web Apps × storage × SQL Database = 99,7%. (Zobacz Design to Survive Failures (Projektowanie, aby przetrwać awarie).

Gdy zmienimy aplikację tak, aby korzystała z kolejki, fronton internetowy zależy tylko od Web Apps i magazynu w przypadku złożonej umowy SLA na 99,8%. (Pamiętaj, że kolejki są częścią usługi Azure Storage, więc są one uwzględnione w tej samej umowie SLA co usługa Blob Storage).

Jeśli potrzebujesz jeszcze więcej niż 99,8%, możesz utworzyć dwie kolejki w dwóch różnych regionach. Wyznaczanie jednego jako podstawowego, a drugiego jako pomocniczego. W aplikacji przełącz tryb failover do kolejki pomocniczej, jeśli kolejka podstawowa nie jest dostępna. Prawdopodobieństwo niedostępności obu w tym samym czasie jest bardzo małe.

Bilansowanie szybkości i niezależne skalowanie

Kolejki są również przydatne w przypadku tzw. bilansowania szybkości lub równoważenia obciążenia.

Aplikacje internetowe są często podatne na nagłe wzrosty ruchu. Chociaż automatyczne skalowanie umożliwia automatyczne dodawanie serwerów internetowych do obsługi zwiększonego ruchu internetowego, skalowanie automatyczne może nie być w stanie zareagować wystarczająco szybko, aby obsłużyć nagłe skoki obciążenia. Jeśli serwery internetowe mogą odciążać część pracy, którą muszą wykonać, pisząc komunikat do kolejki, mogą obsługiwać większy ruch. Usługa zaplecza może następnie odczytywać komunikaty z kolejki i przetwarzać je. Głębokość kolejki będzie rosnąć lub zmniejszać w miarę różnic obciążenia przychodzącego.

Przy dużej części czasochłonnej pracy poza załadowaniem do usługi zaplecza warstwa internetowa może łatwiej reagować na nagłe wzrosty ruchu. Oszczędzasz pieniądze, ponieważ dowolna ilość ruchu może być obsługiwana przez mniej serwerów internetowych.

Warstwę internetową i usługę zaplecza można skalować niezależnie. Na przykład mogą być potrzebne trzy serwery internetowe, ale tylko jeden serwer przetwarza komunikaty kolejek. Jeśli jednak uruchamiasz zadanie intensywnie korzystające z zasobów obliczeniowych w tle, może być konieczne użycie większej liczby serwerów zaplecza.

Diagram przedstawiający reprezentację warstw skalowania podczas przetwarzania zadań w kolejce

Autoskalowanie działa z usługami zaplecza, a także z warstwą internetową. Można skalować w górę lub w dół liczbę maszyn wirtualnych, które przetwarzają zadania w kolejce, na podstawie użycia procesora CPU maszyn wirtualnych zaplecza. Możesz też automatycznie skalować w zależności od liczby elementów w kolejce. Na przykład możesz poinformować autoskalowania, aby spróbować zachować nie więcej niż 10 elementów w kolejce. Jeśli kolejka ma więcej niż 10 elementów, automatyczne skalowanie doda maszyny wirtualne. Gdy nadrobią zaległości, skalowanie automatyczne usunie dodatkowe maszyny wirtualne.

Dodawanie kolejek do aplikacji Fix It

Aby zaimplementować wzorzec kolejki, musimy wprowadzić dwie zmiany w aplikacji Fix It.

  • Gdy użytkownik przesyła nowe zadanie Napraw, umieść zadanie w kolejce, zamiast zapisywać je w bazie danych.
  • Utwórz usługę zaplecza, która przetwarza komunikaty w kolejce.

W przypadku kolejki użyjemy usługi Azure Queue Storage. Inną opcją jest użycie Azure Service Bus.

Aby zdecydować, której usługi kolejki użyć, rozważ sposób wysyłania i odbierania komunikatów w kolejce przez aplikację:

  • Jeśli współpracujesz z producentami i konkurującymi konsumentami, rozważ użycie usługi Azure Queue Storage. "Współpracujący producenci" oznacza, że wiele procesów dodaje komunikaty do kolejki. "Konkurujący konsumenci" oznacza, że wiele procesów ściąga komunikaty z kolejki w celu ich przetwarzania, ale każdy dany komunikat może być przetwarzany tylko przez jednego "konsumenta". Jeśli potrzebujesz większej przepływności niż można uzyskać z jedną kolejką, użyj dodatkowych kolejek i/lub dodatkowych kont magazynu.
  • Jeśli potrzebujesz modelu publikowania/subskrybowania, rozważ użycie kolejek Azure Service Bus.

Aplikacja Fix It pasuje do współpracujących producentów i konkurencyjnych modeli konsumentów.

Inną kwestią jest dostępność aplikacji. Usługa Queue Storage jest częścią tej samej usługi, która jest używana dla magazynu obiektów blob, więc korzystanie z niej nie ma wpływu na naszą umowę SLA. Azure Service Bus jest oddzielną usługą z własną umową SLA. Gdybyśmy użyli kolejek usługi Service Bus, musielibyśmy uwzględnić dodatkowy procent umowy SLA, a złożona umowa SLA byłaby niższa. Podczas wybierania usługi kolejki upewnij się, że rozumiesz wpływ wybranej aplikacji na dostępność aplikacji. Aby uzyskać więcej informacji, zobacz sekcję Zasoby .

Tworzenie komunikatów w kolejce

Aby umieścić zadanie Fix It w kolejce, fronton internetowy wykonuje następujące kroki:

  1. Utwórz wystąpienie klasy CloudQueueClient. Wystąpienie CloudQueueClient jest używane do wykonywania żądań względem usługi Kolejki.
  2. Utwórz kolejkę, jeśli jeszcze nie istnieje.
  3. Serializowanie zadania Fix It.
  4. Wywołaj metodę CloudQueue.AddMessageAsync, aby umieścić komunikat w kolejce.

Wykonamy tę pracę w konstruktorze i SendMessageAsync metodzie nowej FixItQueueManager klasy.

public class FixItQueueManager : IFixItQueueManager
{
    private CloudQueueClient _queueClient;
    private IFixItTaskRepository _repository;

    private static readonly string fixitQueueName = "fixits";

    public FixItQueueManager(IFixItTaskRepository repository)
    {
        _repository = repository;
        CloudStorageAccount storageAccount = StorageUtils.StorageAccount;
        _queueClient = storageAccount.CreateCloudQueueClient();
    }

    // Puts a serialized fixit onto the queue.
    public async Task SendMessageAsync(FixItTask fixIt)
    {
        CloudQueue queue = _queueClient.GetQueueReference(fixitQueueName);
        await queue.CreateIfNotExistsAsync();

        var fixitJson = JsonConvert.SerializeObject(fixIt);
        CloudQueueMessage message = new CloudQueueMessage(fixitJson);

        await queue.AddMessageAsync(message);
    }

    // Processes any messages on the queue.
    public async Task ProcessMessagesAsync()
    {
        CloudQueue queue = _queueClient.GetQueueReference(fixitQueueName);
        await queue.CreateIfNotExistsAsync();

        while (true)
        {
            CloudQueueMessage message = await queue.GetMessageAsync();
            if (message == null)
            {
                break;
            }
            FixItTask fixit = JsonConvert.DeserializeObject<FixItTask>(message.AsString);
            await _repository.CreateAsync(fixit);
            await queue.DeleteMessageAsync(message);
        }
    }
}

W tym miejscu używamy biblioteki Json.NET do serializacji poprawki do formatu JSON. Możesz użyć dowolnego preferowanego podejścia serializacji. Kod JSON ma zaletę bycia czytelnym dla człowieka, a jednocześnie jest mniej pełny niż XML.

Kod jakości produkcyjnej doda logikę obsługi błędów, wstrzymać, jeśli baza danych stała się niedostępna, obsługiwać odzyskiwanie bardziej czysto, tworzyć kolejkę w uruchamianiu aplikacji i zarządzać komunikatami "trucizny". (Komunikat o truciznie jest komunikatem, którego nie można przetworzyć z jakiegoś powodu. Nie chcesz, aby zatrute komunikaty siedziały w kolejce, gdzie rola procesu roboczego będzie stale próbować je przetworzyć, nie powiedzie się, spróbuj ponownie, nie powiedzie się itd.

W aplikacji MVC frontonu musimy zaktualizować kod, który tworzy nowe zadanie. Zamiast umieszczać zadanie w repozytorium, wywołaj metodę pokazaną SendMessageAsync powyżej.

public async Task<ActionResult> Create(FixItTask fixittask, HttpPostedFileBase photo)
{
    if (ModelState.IsValid)
    {
        fixittask.CreatedBy = User.Identity.Name;
        fixittask.PhotoUrl = await photoService.UploadPhotoAsync(photo);
        //previous code:
        //await fixItRepository.CreateAsync(fixittask);
        //new code:
        await queueManager.SendMessageAsync(fixittask);
        return RedirectToAction("Success");
    }
    return View(fixittask);
}

Przetwarzanie komunikatów kolejki

Aby przetworzyć komunikaty w kolejce, utworzymy usługę zaplecza. Usługa zaplecza uruchomi nieskończoną pętlę, która wykonuje następujące kroki:

  1. Pobierz następny komunikat z kolejki.
  2. Deserializacji komunikatu do zadania Napraw.
  3. Napisz zadanie Fix It do bazy danych.

Aby hostować usługę zaplecza, utworzymy usługę w chmurze platformy Azure zawierającą rolę procesu roboczego. Rola procesu roboczego składa się z co najmniej jednej maszyny wirtualnej, która może wykonywać przetwarzanie zaplecza. Kod uruchamiany na tych maszynach wirtualnych będzie pobierać komunikaty z kolejki, gdy staną się dostępne. Dla każdego komunikatu deserializujemy ładunek JSON i napiszemy wystąpienie jednostki Fix It Task do bazy danych przy użyciu tego samego repozytorium, które zostało użyte wcześniej w warstwie internetowej.

W poniższych krokach pokazano, jak dodać projekt roli procesu roboczego do rozwiązania, które ma standardowy projekt internetowy. Te kroki zostały już wykonane w projekcie Fix It, który można pobrać.

Najpierw dodaj projekt usługi w chmurze do rozwiązania programu Visual Studio. Kliknij prawym przyciskiem myszy rozwiązanie i wybierz polecenie Dodaj, a następnie pozycję Nowy projekt. W okienku po lewej stronie rozwiń węzeł Visual C# i wybierz pozycję Chmura.

Zrzut ekranu przedstawiający kroki dodawania nowego menu projektu w programie .Net Framework

W oknie dialogowym Nowa usługa w chmurze platformy Azure rozwiń węzeł Visual C# w okienku po lewej stronie. Wybierz pozycję Rola procesu roboczego i kliknij ikonę strzałki po prawej stronie.

Poniższy zrzut ekranu przedstawia kontynuację poprzedniego obrazu i pokazuje różne opcje dostępne dla usługi Azure Cloud Service z wyróżnioną poprawną.

(Zwróć uwagę, że można również dodać rolę internetową. Możemy uruchomić fronton Fix It w tej samej usłudze w chmurze, zamiast uruchamiać ją w witrynie internetowej platformy Azure. Ma to pewne zalety w tworzeniu połączeń między frontonem a zapleczem łatwiejszym do koordynowania. Jednak aby zachować prostotę tego pokazu, utrzymujemy fronton w aplikacji internetowej Azure App Service i uruchamiamy zaplecze tylko w usłudze w chmurze).

Domyślna nazwa jest przypisywana do roli procesu roboczego. Aby zmienić nazwę, umieść kursor myszy nad rolą procesu roboczego w okienku po prawej stronie, a następnie kliknij ikonę ołówka.

Zrzut ekranu przedstawiający projekt roli procesu roboczego z różnymi przypisaniami i sposobem zmiany nazw.

Kliknij przycisk OK , aby ukończyć okno dialogowe. Spowoduje to dodanie dwóch projektów do rozwiązania programu Visual Studio.

  • projekt platformy Azure, który definiuje usługę w chmurze, w tym informacje o konfiguracji.
  • Projekt roli procesu roboczego, który definiuje rolę procesu roboczego.

Zrzut ekranu przedstawiający rolę procesu roboczego, definiując rolę i pokazując listę opcji rozwiązania projektu

Aby uzyskać więcej informacji, zobacz Tworzenie projektu platformy Azure za pomocą programu Visual Studio.

Wewnątrz roli procesu roboczego FixItQueueManager sondujemy komunikaty, wywołując ProcessMessageAsync metodę klasy, którą widzieliśmy wcześniej.

public class WorkerRole : RoleEntryPoint
{
    public override void Run()
    {
        Task task = RunAsync(tokenSource.Token);
        try
        {
            task.Wait();
        }
        catch (Exception ex)
        {
            logger.Error(ex, "Unhandled exception in FixIt worker role.");
        }
    }

    private async Task RunAsync(CancellationToken token)
    {
        using (var scope = container.BeginLifetimeScope())
        {
            IFixItQueueManager queueManager = scope.Resolve<IFixItQueueManager>();
            while (!token.IsCancellationRequested)
            {
                try
                {
                    await queueManager.ProcessMessagesAsync();
                }
                catch (Exception ex)
                {
                    logger.Error(ex, "Exception in worker role Run loop.");
                }
                await Task.Delay(1000);
            }
        }
    }
    // Other code not shown.
}

Metoda ProcessMessagesAsync sprawdza, czy jest komunikat oczekujące. Jeśli istnieje jeden, deserializuje komunikat do FixItTask jednostki i zapisuje jednostkę w bazie danych. Jest ona w pętli, dopóki kolejka nie będzie pusta.

public async Task ProcessMessagesAsync()
{
    CloudQueue queue = _queueClient.GetQueueReference(fixitQueueName);
    await queue.CreateIfNotExistsAsync();
    while (true)
    {
        CloudQueueMessage message = await queue.GetMessageAsync();
        if (message == null)
        {
            break;
        }
        FixItTask fixit = JsonConvert.DeserializeObject<FixItTask>(message.AsString);
        await _repository.CreateAsync(fixit);
        await queue.DeleteMessageAsync(message);
    }
}

Sondowanie komunikatów w kolejce wiąże się z niewielką opłatą za transakcję, więc gdy nie ma komunikatu oczekującego na przetworzenie, metoda roli RunAsync procesu roboczego czeka sekundę przed ponownym sondowaniem przez wywołanie metody Task.Delay(1000).

W projekcie internetowym dodanie kodu asynchronicznego może automatycznie poprawić wydajność, ponieważ usługi IIS zarządzają ograniczoną pulą wątków. Tak nie jest w projekcie roli procesu roboczego. Aby zwiększyć skalowalność roli procesu roboczego, możesz napisać kod wielowątkowy lub użyć kodu asynchronicznego w celu zaimplementowania programowania równoległego. Przykład nie implementuje programowania równoległego, ale pokazuje, jak utworzyć kod asynchroniczny, aby można było zaimplementować programowanie równoległe.

Podsumowanie

W tym rozdziale pokazano, jak poprawić czas odpowiedzi aplikacji, niezawodność i skalowalność, implementując wzorzec pracy skoncentrowany na kolejce.

Jest to ostatni z 13 wzorców omówionych w tej książce elektronicznej, ale istnieje oczywiście wiele innych wzorców i praktyk, które mogą pomóc w tworzeniu udanych aplikacji w chmurze. Ostatni rozdział zawiera linki do zasobów dotyczących tematów, które nie zostały omówione w tych 13 wzorcach.

Zasoby

Aby uzyskać więcej informacji na temat kolejek, zobacz następujące zasoby.

Dokumentacja:

Wideo:

  • FailSafe: tworzenie skalowalnych, odpornych Cloud Services. Dziewięć części serii wideo Ulrich Homann, Marc Mercuri i Mark Simms. Przedstawia pojęcia wysokiego poziomu i zasady architektury w bardzo dostępny i interesujący sposób, z historiami pochodzącymi z zespołu doradczego klienta firmy Microsoft (CAT) z rzeczywistymi klientami. Aby zapoznać się z wprowadzeniem do usługi Azure Storage i kolejek, zobacz odcinek 5 rozpoczynający się od 35:13.