Руководство по архитектуре push-уведомлений

Сегодня предприятия постепенно переходят к созданию мобильных приложений для конечных пользователей (внешних) или сотрудников (внутренних). У них есть серверные системы, размещенные на мэйнфреймах, или бизнес-приложения, которые нужно интегрировать в архитектуру приложений для мобильных устройств. В этом руководстве рассказывается об оптимальном подходе к такой интеграции и излагаются рекомендации относительно возможных решений для распространенных сценариев.

Одно из наиболее распространенных требований — возможность отправки push-уведомлений пользователям с помощью мобильных приложений при определенных событиях в серверной системе. Например, клиент банка, у которого есть банковское приложение на iPhone, хочет получать уведомления при списании со счета сумм, превышающих определенное значение, или сотрудник из финансового отдела, у которого есть приложение для утверждения бюджета на устройстве Windows Phone, хочет получать по интрасети уведомления при получении запроса на утверждение.

Обслуживание банковского счета и обработка утверждений, как правило, осуществляются в серверной системе, которая должна инициировать отправку push-уведомлений пользователю. Таких серверных систем может быть несколько, а для реализации push-уведомлений, отправка которых инициируется наступлением определенных событий, все они должны быть созданы на основе одной и той же логики. Сложность в том, как интегрировать несколько серверных систем с одной системой push-уведомлений, где конечные пользователи могут подписываться на различные уведомления и даже использовать различные мобильные приложения. Например, в случае интрасети, где одно мобильное приложение может получать уведомления из нескольких таких серверных систем. Серверным системам неизвестна или не должна быть известна семантика или технология системы отправки push-уведомлений, поэтому в таких случаях традиционным решением было внедрение компонента, который опрашивает серверные системы, чтобы определить, не произошло ли то или иное событие, и отвечает за отправку push-уведомлений клиенту.

Лучшим решением является использование служебной шины Azure — модели разделов и подписок, которая обеспечивает простоту и масштабируемость.

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

Архитектура

Diagram of the enterprise architecture showing the flow through Events, Subscriptions, and Push Messages.

Основной элемент на этой схеме архитектуры — это служебная шина Azure, которая предоставляет модель программирования разделов и подписок (подробнее об этом написано в статье Как использовать разделы и подписки служебной шины). Получатель (в данном случае это серверная часть мобильной службы, обычно — мобильная служба Azure, которая будет инициировать отправку push-уведомлений для мобильных приложений) не получает сообщения напрямую от серверных систем. Эти уведомления поступают к нему с промежуточного уровня абстракции, предоставляемого Служебной шиной Azure, что позволяет серверной части мобильной службы получать сообщения из одной или нескольких серверных систем. Раздел "Служебная шина" необходимо создать для каждой из серверных систем, например "Бухгалтерия", "Отдел кадров" и "Финансы", по сути являющихся "разделами", которые инициируют отправку сообщений в виде push-уведомлений. Серверные системы отправляют сообщения в эти разделы. Мобильный внутренний сервер может подписаться на один или несколько таких разделов, создав подписку на служебную шину. Это дает серверной части мобильной службы возможность получать уведомления от соответствующей серверной системы. Этот сервер продолжает прослушивать сообщения по своим подпискам, и как только сообщение поступает, он отправляет его в виде уведомления в свой центр уведомлений. В конечном итоге центры уведомлений доставляют сообщение в мобильное приложение. Ниже приведен список основных компонентов.

  1. Серверные системы (бизнес-системы и устаревшие системы)
    • Создает раздел Service Bus
    • Отправляет сообщение
  2. Серверная часть мобильных устройств
    • Создает подписку на службу
    • Получает сообщение (из серверной системы)
    • Отправляет уведомление клиентам (через центр уведомлений Azure)
  3. Мобильное приложение
    • Получает и отображает уведомление

Льготы

  1. Разделение на получателя (мобильное приложение или служба через центр уведомлений) и отправителя (серверные системы) позволяет интегрировать дополнительные серверные системы с минимальными изменениями.
  2. Упрощается ситуация, когда необходимо, чтобы уведомления о событиях в одной или нескольких серверных системах получало несколько мобильных приложений.

Пример

Необходимые компоненты

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

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

Пример кода

Полный пример кода доступен в коллекции примеров центра уведомлений. Он состоит из трех компонентов:

  1. EnterprisePushBackendSystem

    a. Этот проект использует пакет NuGet Azure.Messaging.ServiceBus и основан на материале, изложенном в статье Начало работы с разделами служебной шины.

    b. Это простое консольное приложение на C# для моделирования бизнес-систем, которое инициирует доставку сообщений мобильному приложению.

    static async Task Main(string[] args)
    {
        string connectionString =
            ConfigurationManager.AppSettings.Get("Azure.ServiceBus.ConnectionString");
    
        // Create the topic
        await CreateTopicAsync(connectionString);
    
        // Send message
        await SendMessageAsync(connectionString);
    }
    

    c. CreateTopicAsync используется для создания раздела служебной шины.

    public static async Task CreateTopicAsync(string connectionString)
    {
        // Create the topic if it does not exist already
        ServiceBusAdministrationClient client = new ServiceBusAdministrationClient(connectionString);
    
        if (!await client.TopicExistsAsync(topicName))
        {
            await client.CreateTopicAsync(topicName);
        }
    }
    

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

    public static sync Task SendMessageAsync(string connectionString)
    {
        await using var client = new ServiceBusClient(connectionString);
        ServiceBusSender sender = client.CreateSender(topicName);
    
        // Sends random messages every 10 seconds to the topic
        string[] messages =
        {
            "Employee Id '{0}' has joined.",
            "Employee Id '{0}' has left.",
            "Employee Id '{0}' has switched to a different team."
        };
    
        while (true)
        {
            Random rnd = new Random();
            string employeeId = rnd.Next(10000, 99999).ToString();
            string notification = String.Format(messages[rnd.Next(0,messages.Length)], employeeId);
    
            // Send Notification
            ServiceBusMessage message = new ServiceBusMessage(notification);
            await sender.SendMessageAsync(message);
    
            Console.WriteLine("{0} Message sent - '{1}'", DateTime.Now, notification);
    
            System.Threading.Thread.Sleep(new TimeSpan(0, 0, 10));
        }
    }
    
  2. ReceiveAndSendNotification

    a. Этот проект использует пакеты NuGet WindowsAzure.ServiceBus и Microsoft.Web.WebJobs.Publish и основан на материале, изложенном в статье Начало работы с разделами служебной шины.

    b. Приведенное ниже консольное приложение запускается в качестве веб-задания Azure, так как оно должно выполняться непрерывно для ожидания передачи сообщений из бизнес-систем и серверных систем. Это приложение входит в состав серверной части мобильного приложения.

    static async Task Main(string[] args)
    {
        string connectionString =
                 ConfigurationManager.AppSettings.Get("Azure.ServiceBus.ConnectionString");
    
        // Create the subscription that receives messages
        await CreateSubscriptionAsync(connectionString);
    
        // Receive message
        await ReceiveMessageAndSendNotificationAsync(connectionString);
    }
    

    c. CreateSubscriptionAsync используется для создания подписки служебной шины для раздела, в который серверная система отправляет сообщения. В зависимости от бизнес-сценария этот компонент создает одну или несколько подписок на соответствующие разделы (например, одни будут получать сообщения из системы "Отдел кадров", другие — из системы "Финансы" и т. д.)

    static async Task CreateSubscriptionAsync(string connectionString)
    {
        // Create the subscription if it does not exist already
        ServiceBusAdministrationClient client = new ServiceBusAdministrationClient(connectionString);
    
        if (!await client.SubscriptionExistsAsync(topicName, subscriptionName))
        {
            await client.CreateSubscriptionAsync(topicName, subscriptionName);
        }
    }
    

    d. ReceiveMessageAndSendNotificationAsync используется для чтения сообщения из раздела с использованием подписки. Если операция считывания выполнена успешно, создается уведомление (в этом примере сценария — собственное всплывающее уведомление Windows), которое необходимо отправить в мобильное приложение с помощью Центров уведомлений Azure.

    static async Task ReceiveMessageAndSendNotificationAsync(string connectionString)
    {
        // Initialize the Notification Hub
        string hubConnectionString = ConfigurationManager.AppSettings.Get
                ("Microsoft.NotificationHub.ConnectionString");
        hub = NotificationHubClient.CreateClientFromConnectionString
                (hubConnectionString, "enterprisepushservicehub");
    
        ServiceBusClient Client = new ServiceBusClient(connectionString);
        ServiceBusReceiver receiver = Client.CreateReceiver(topicName, subscriptionName);
    
        // Continuously process messages received from the subscription
        while (true)
        {
            ServiceBusReceivedMessage message = await receiver.ReceiveMessageAsync();
            var toastMessage = @"<toast><visual><binding template=""ToastText01""><text id=""1"">{messagepayload}</text></binding></visual></toast>";
    
            if (message != null)
            {
                try
                {
                    Console.WriteLine(message.MessageId);
                    Console.WriteLine(message.SequenceNumber);
                    string messageBody = message.Body.ToString();
                    Console.WriteLine("Body: " + messageBody + "\n");
    
                    toastMessage = toastMessage.Replace("{messagepayload}", messageBody);
                    SendNotificationAsync(toastMessage);
    
                    // Remove message from subscription
                    await receiver.CompleteMessageAsync(message);
                }
                catch (Exception)
                {
                    // Indicate a problem, unlock message in subscription
                    await receiver.AbandonMessageAsync(message);
                }
            }
        }
    }
    static async void SendNotificationAsync(string message)
    {
        await hub.SendWindowsNativeNotificationAsync(message);
    }
    

    д) Чтобы опубликовать это приложение в виде веб-задания, щелкните правой кнопкой мыши решение в Visual Studio и выберите Publish as WebJob (Опубликовать как веб-задание).

    Screenshot of the right-click options being displayed with Publish as Azure WebJob outlined in red.

    f. Выберите профиль публикации и создайте новый веб-сайт Azure для размещения веб-задания (если он еще не существует), а когда веб-сайт будет создан, щелкните Опубликовать.

    Screenshot showing the workflow to create a site on Azure.

    Снимок экрана. Диалоговое окно "Публикация веб-сайта" с выбранным параметром “Веб-сайты Microsoft Azure”. Зеленая стрелка указывает на диалоговое окно "Выбрать существующий веб-сайт" с параметром “Новый”, выделенным красным цветом. Другая зеленая стрелка указывает на диалоговое окно "Создание сайта в Microsoft Azure" с параметрами “Имя сайта” и “Создать”, выделенными красным цветом.

    ж. Настройте непрерывное выполнение задания, чтобы при входе на портал Azure отображалось примерно следующее:

    Screenshot of the Azure Portal with the enterprise push backend webjobs displayed and the Name, Schedule, and Logs values outlined in red.

  3. EnterprisePushMobileApp

    a. Это приложение Магазина Windows, которое будет получать всплывающие уведомления из задания WebJob, выполняющегося в составе серверной части мобильной службы, и отображать это уведомление. Оно основано на материалах руководства по центрам уведомлений для Windows Universal.

    b. Убедитесь, что в приложении включено получение всплывающих уведомлений.

    c. Убедитесь, что при запуске приложения вызывается следующий код регистрации Центров уведомлений (после замены значений HubName и DefaultListenSharedAccessSignature).

    private async void InitNotificationsAsync()
    {
        var channel = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();
    
        var hub = new NotificationHub("[HubName]", "[DefaultListenSharedAccessSignature]");
        var result = await hub.RegisterNativeAsync(channel.Uri);
    
        // Displays the registration ID so you know it was successful
        if (result.RegistrationId != null)
        {
            var dialog = new MessageDialog("Registration successful: " + result.RegistrationId);
            dialog.Commands.Add(new UICommand("OK"));
            await dialog.ShowAsync();
        }
    }
    

Выполнение примера

  1. Убедитесь, что веб-задание успешно запущено и для него запланировано непрерывное выполнение.

  2. Запустите EnterprisePushMobileApp, чтобы запустить приложение Магазина Windows.

  3. Запустите консольное приложение EnterprisePushBackendSystem, которое имитирует серверную часть бизнес-системы. Оно начнет отправлять сообщения. Вы должны увидеть всплывающие уведомления, как на рисунке ниже.

    Screenshot of a console running the Enterprise Push Backend System app and the message that is sent by the app.

  4. Первоначально сообщения были отправлены в разделы служебной шины, которые отслеживаются по подпискам служебной шины в веб-задании. При получении сообщения было создано уведомление, которое затем было отправлено в мобильное приложение. Чтобы убедиться в правильности выполнения обработки, можно просмотреть журналы веб-заданий, перейдя по ссылке "Журналы" на портале Azure для соответствующего веб-задания:

    Screenshot of the Continuous WebJob Details dialog box with the message that is sent outlined in red.