Microsoft Azure изнутри

Microsoft Azure Service Bus и Интернет вещей. Часть 2

Бруно Теркали
Рикардо Виллалобос

Исходный код можно скачать по ссылке.

Бруно Теркали и Рикардо ВиллалобосСоздайте бесплатную изолированную среду для разработки/тестирования в облаке. Подписчики на MSDN могут быстро подготовить такую среду в Microsoft Azure безо всякой оплаты. Обращайтесь по адресу aka.ms/msdnmag.

В прошлой статье (msdn.microsoft.com/magazine/dn574801) мы обсудили текущий технологический ландшафт в сфере межмашинных (machine-to-machine, M2M) вычислений. Такие вычисления относятся к технологиям, которые соединяют устройства между собой (обычно для промышленных систем контроля и управления) в виде датчиков или контрольно-измерительных приборов. Распространение недорогих и простых в программировании крошечных компьютеров привело к расширению этой концепции в то, что теперь называют Интернетом вещей (Internet-of-Things, IoT), сделав возможными сценарии, где даже обыкновенную бытовую технику можно контролировать или использовать в качестве источников информации для генерации событий. Благодаря этому нетрудно посылать оповещения, когда нужно пополнить холодильник, автоматически закрыть оконные жалюзи на ночь или отрегулировать температуру в соответствии с привычками конкретной семьи.

Мы также рассмотрели пример использования Microsoft Azure Service Bus для соединения устройств как альтернативу применению VPN, когда нужно решать проблемы адресации, защиты и производительности, связанные с развертыванием большого количества датчиков или контрольно-измерительных приборов (КИП). Это становится все важнее, учитывая, что, согласно новейшему отчету «BI Intelligence» от Business Insider, к 2018 году в мире будет насчитываться более девяти миллиардов соединений, прямо относящихся к IoT (read.bi/18L5cg8).

Используя выделенную очередь Service Bus или Topic для какого-либо устройства, можно обеспечить элегантный способ, позволяющий добиться отказоустойчивости в условиях непостоянства подключений для приложений IoT. В данной статье мы подробно рассмотрим практическую реализацию на основе Microsoft Azure, которая иллюстрирует эти концепции, и разработаем проект Service Bus с очередями устройств, применяя рабочую роль для прослушивания этих очередей в Cloud Services, и запрограммируем устройство Arduino, которое будет выполнять команды, удаленно посылаемые мобильными клиентами, как показано на рис. 1.

Архитектура IoT с применением Microsoft Azure Service Bus
Рис. 1. Архитектура IoT с применением Microsoft Azure Service Bus

Mobile and Desktop Devices Мобильные и настольные устройства
REST Interface/SDKs REST-интерфейс/SDK
Microsoft Azure Service Bus Microsoft Azure Service Bus
Microsoft Azure Cloud Service Облачный сервис Microsoft Azure
TCP Connection TCP-соединение
Arduino Arduino

Если посмотреть на эту схему, то становится очевидным, что компонент Microsoft Azure Service Bus является центральной частью проекта, обеспечивая аутентификацию, распространение сообщений и масштабируемость для поддержки множества устройств, которые будут отправлять данные или принимать удаленные команды. Service Bus доступна во всех информационных центрах Microsoft, которые предлагают сервисы Microsoft Azure, и поддерживается инфраструктурой хранилищ с высокой степенью избыточности. Кроме того, как и остальные компоненты Microsoft Azure, она предоставляет открытый и простой в понимании REST-интерфейс наряду с несколькими SDK (Microsoft .NET Framework, Java, PHP, Ruby и др.), опирающимися на этот интерфейс.

В предлагаемой нами архитектуре устройства «общаются» с .NET-приложением, выполняемым в Microsoft Azure Cloud Services, который действует как шлюз к Service Bus. Это упрощает процесс взаимодействия приложения с назначенной ему очередью. Такой подход обеспечивает полную поддержку любого из четырех шаблонов коммуникации IoT, описанных в нашей предыдущей статье: Telemetry, Inquiry, Command и Notification. Здесь мы реализуем сценарий, в котором мобильное устройство посылает команду другому устройству, чтобы выполнить некое действие; в данном случае включить или выключить LED-индикатор. Одно из преимуществ этого решения в том, что, если устройство временно отсоединено от сети, оно сможет получить команды, как только соединение с Интернетом будет возобновлено. Кроме того, в сообщении можно указать срок его действия, чтобы избежать выполнения задачи в неподходящий момент, или запланировать отправку сообщений в определенное время в будущем.

Для этого примера мы используем хорошо известное и подробно документированное устройство Arduino, о котором мы говорили в прошлой статье. Для подтверждения концепции в части мобильного клиента мы создадим приложение Windows Phone.

Вот наш простой сценарий.

  1. При запуске устройство Arduino посылает идентификационный сигнал шлюзовому приложению, выполняемому в Microsoft Azure Cloud Services. Шлюз создает очередь Service Bus для этого устройства, если таковой еще нет, и устанавливает TCP-соединение, готовое к отправке команд.
  2. Приложение Windows Phone посылает команду в очередь Microsoft Azure Service Bus, назначенную устройству.
  3. Сообщение остается в очереди, пока шлюзовое приложение не извлечет его и не отправит команду устройству Arduino по установленному TCP-соединению.
  4. Устройство Arduino включает или выключает LED-индикатор в зависимости от команды.

Рассмотрим этапы, которые делают возможными перечисленные выше операции.

Этап 1: создание пространства имен Microsoft Azure Service Bus Используя свои удостоверения Microsoft Azure (вы можете запросить пробную учетную запись по ссылке bit.ly/1atsgSa), войдите на веб-портал и выберите раздел SERVICE BUS (рис. 2). Выберите вариант CREATE и введите имя для вашего пространства имен. Затем щелкните CONNECTION INFORMATION и скопируйте текст в поле Connection String, которое потребуется вам позже.

Создание пространства имен Microsoft Azure Service Bus
Рис. 2. Создание пространства имен Microsoft Azure Service Bus

Этап 2: создание шлюзового приложения и его развертывание в Microsoft Azure Cloud Services Код для шлюзового приложения, которое извлекает сообщения из очереди Service Bus и ретранслирует команды устройству Arduino, включен в пакет исходного кода к этой статье (доступен по ссылке msdn.microsoft.com/magazine/msdnmag0314). Он основан на разработках Клеменса Вастерса (Clemens Vasters), который любезно поделился ими для этой статьи. Его исходный проект можно найти по ссылке bit.ly/L0uK0v.

Прежде чем углубиться в этот код, убедитесь, что у вас установлена Visual Studio 2013 наряду с версией 2.2 пакета Microsoft Azure SDK for .NET (bit.ly/JYXx5n). Решение включает три проекта:

  • ArduinoListener — содержит основной код WorkerRole;
  • ConsoleListener — консольная версия ArduinoListener для локального тестирования;
  • MSDNArduinoListener — проект развертывания ArduinoListener в Microsoft Azure.

Если вы изучите файлы ServiceConfiguration.cscfg (для облачного и локального развертывания) в проекте MSDNArduinoListener, то заметите параметр, в котором хранится строка подключения для Service Bus. Замените его значение тем, что вы получили на этапе 1. Остальное уже сконфигурировано, чтобы решение могло работать, в том числе определен порт 10100 для приема соединений от устройств. Затем откройте файл WorkerRole.cs в проекте ArduinoListener, где находится основной код.

Проанализируйте четыре основных раздела.

Сначала создается TcpListener, и соединения от устройств начинают приниматься:

var deviceServer = new TcpListener(deviceEP);
deviceServer.Start(10);
try
{
  do
  {
    TcpClient connection = 
      await deviceServer.AcceptTcpClientAsync();
    if (connection != null)
    {      ...

Как только соединение с устройством установлено, определяется NetworkStream и переводится в режим прослушивания. Переменная readBuffer будет содержать значение идентификатора, передаваемое каждым устройством Arduino:

NetworkStream deviceConnectionStream = connection.GetStream();
var readBuffer = new byte[64];
if (await deviceConnectionStream.ReadAsync(readBuffer, 0, 4) == 4)
{
  int deviceId = 
    IPAddress.NetworkToHostOrder(BitConverter.ToInt32(readBuffer, 0));
  ...

Затем создается очередь на основе значения deviceId (в случае, если ее нет) и определяется объект — приемник сообщений (рис. 3). Далее приемник очереди устройства (device queue receiver) настраивается на асинхронный режим для извлечения сообщений (команд) из очереди. В этой очереди будут храниться команды, отправленные мобильными устройствами, такими как Windows Phone.

Рис. 3. Создание очереди

var namespaceManager = 
  NamespaceManager.CreateFromConnectionString(
    RoleEnvironment.GetConfigurationSettingValue("serviceBusConnectionString"));
if (!namespaceManager.QueueExists(string.Format("dev{0:X8}", deviceId)))
{
  namespaceManager.CreateQueue(string.Format("dev{0:X8}", deviceId));
}
var deviceQueueReceiver = messagingFactory.CreateMessageReceiver(
  string.Format("dev{0:X8}", deviceId), ReceiveMode.PeekLock);
do
{
  BrokeredMessage message = null;
  message = await deviceQueueReceiver.ReceiveAsync();
  ...

Когда в очередь поступает сообщение, его содержимое анализируется и, если оно совпадает с командой «ON» или «OFF», в поток соединения, установленного с устройством, записывается соответствующая информация (рис. 4).

Рис. 4. Запись в поток соединения

if (message != null)
{
  Stream stream = message.GetBody<Stream>();
  StreamReader reader = new StreamReader(stream);
  string command = reader.ReadToEnd();
  if (command != null)
  {
    switch (command.ToUpperInvariant())
    {
      case "ON":
        await deviceConnectionStream.WriteAsync(OnFrame, 0, 
            OnFrame.Length);
        await message.CompleteAsync();
        break;
      case "OFF":
        await deviceConnectionStream.WriteAsync(OffFrame, 0, 
            OffFrame.Length);
        await message.CompleteAsync();
        break;
    }
  }
}

Заметьте, что сообщение не удаляется из очереди (message.CompleteAsync), пока операция записи в поток соединения с устройством не будет успешной. Кроме того, чтобы поддерживать соединение в активном состоянии, ожидается, что устройство будет периодически посылать команду ping для проверки связи (ping heartbeat). В этом прототипе мы не ожидаем подтверждения от устройства при получении сообщения. Однако в производственной системе это было бы обязательным условием для соответствия шаблону Command.

Этап 3: развертывание проекта ArduinoListener в Microsoft Azure Cloud Services Развернуть ArduinoListener в Microsoft Azure крайне просто. В Visual Studio 2013 щелкните правой кнопкой мыши проект MSDNArduinoListener и выберите Publish. Конкретные инструкции по работе с Publish Microsoft Azure Application Wizard вы найдете по ссылке bit.ly/1iP9g2p. По окончании работы мастера вы получите облачный сервис, расположенный в xyz.cloudapp.net. Запишите это имя, так как оно понадобится, когда вы будете создавать клиент Arduino.

Этап 4: программирование устройства Arduino на взаимодействие со шлюзом (слушателем) Устройства Arduino предлагают богатый интерфейс для выполнения сетевых операций, используя простой объект веб-клиента. В нашем прототипе мы решили использовать модель Arduino Uno R3 (bit.ly/18ZlcM8) наряду с соответствующей ей защитой Ethernet (bit.ly/1do6eRD). Для установки, взаимодействия и программирования устройств Arduino в Windows следуйте руководству по ссылке bit.ly/1dNBi9R. В итоге вы получите простую в применении IDE (называемую приложением Arduino), где можно будет писать программы (называемые скетчами) на JavaScript, как показано на рис. 5.

Приложение Arduino
Рис. 5. Приложение Arduino

На рис. 6 приведен скетч для взаимодействия с Arduino Listener, созданным на этапе 3 и теперь развернутым в Microsoft Azure.

Рис. 6. Код для устройства Arduino

#include <SPI.h>#include <Ethernet.h>#include <StopWatch.h>
// Введите MAC-адрес и IP-адрес своего контроллера ниже.
// IP-адрес будет зависеть от вашей локальной сети,
// и он не обязателен, если включен DHCP.
byte mac[] = { 0x90, 0xA2, 0xDA, 0x0D, 0xBC, 0xAE };
static const byte deviceId[] = { 0x00, 0x00, 0x00, 0x01 };
static const uint8_t ACK = 0x01;
static const int LED_PIN = 8;
int connected = 0;
EthernetClient client;
StopWatch stopWatch;
long pingInterval = 200000;
void setup() {  Serial.begin(9600);
  Serial.println("Initialized");
  Ethernet.begin(mac);
  pinMode(LED_PIN, OUTPUT);}
void turnLedOn(){  digitalWrite(LED_PIN, HIGH);}
void turnLedOff(){  digitalWrite(LED_PIN, LOW);}
void loop() {
        if ( connected == 0)  {
    Serial.println("Trying to connect");
    char* host = "xyz.cloudapp.net";
    client.setTimeout(10000);
    connected = client.connect(host, 10100);
    if (connected)     {
      Serial.println(
        "Connected to port, writing deviceId and waiting for commands...");
      client.write(deviceId, sizeof(deviceId));
      stopWatch.start();
    }
    else
    {
      Serial.println("Connection unsuccessful");
      client.stop();
      stopWatch.reset();
    }
  }
  if (connected == 1)
  {
    if (stopWatch.elapsed() > pingInterval)
    {
      Serial.println("Pinging Server to keep connection alive...");
      client.write(deviceId, sizeof(deviceId));
      stopWatch.reset();
       stopWatch.start();
    }
    byte buf[16];
    int readResult = client.read(buf, 1);
    if (readResult == 0)
     {
      Serial.println("Can't find listener, disconnecting...");
      connected = 0;
      stopWatch.reset();
    }
    else if (readResult == 1)
    {
      Serial.println("Data acquired, processing...");
      switch ( buf[0] )
      {
        case 1:
          Serial.println("Command to turn led on received...");
          turnLedOn();
          break;
        case 2:
           Serial.println("Command to turn led off received...");
          turnLedOff();
          break;
      }
      stopWatch.reset();
      stopWatch.start();
    }
  }
}

Скетчи для Arduino имеют два основных раздела: setup и loop. Инструкции в разделе setup выполняются только раз; именно здесь происходит инициализация переменных и установление соединений. В нашем примере определяются Ethernet-клиент и связанные значения, устанавливается последовательное соединение (для целей отладки), а затем разъем (pin), к которому подключен LED-индикатор, инициализируется как порт вывода.

Код в разделе loop выполняется постоянно и включает два основных блока на основе состояния TCP-соединения между устройством Arduino и слушателем, работающим в Microsoft Azure Cloud Services: connected (подключено) или disconnected (отключено). Когда соединение устанавливается впервые, запускается объект stopwatch, который начинает отслеживать время этого соединения. Кроме того, слушателю посылается идентификатор устройства, который используется в качестве имени очереди, где будут храниться сообщения и команды.

Блок кода, обрабатывающий поведение Arduino после установления соединения, отслеживает время, прошедшее с момента создания соединения, и посылает слушателю команду ping каждые 200 000 мс, чтобы поддерживать соединение в активном состоянии в отсутствие принимаемых команд. Этот код также пытается считывать данные от слушателя, помещая их в массив buf, если таковые данные поступили. Если обнаружено значение 1, LED-индикатор включается, а если значение равно 2, LED-индикатор выключается. Объект stopWatch сбрасывается после каждой команды.

Как только скетч загружается в устройство, этот код выполняется контроллером Arduino в бесконечном цикле, пытаясь подключиться к облачному сервису. После подключения он пересылает идентификатор устройства, чтобы облачный сервис знал, с каким устройством он взаимодействует. Затем код начинает считывать входные данные от облачного сервиса, сообщающие устройству включить или выключить LED-индикатор (в данном случае он подключен к цифровому порту 8 на устройстве).

Этап 5: создание клиента Windows Phone для отправки сообщений в очередь устройства Взаимодействие с устройством сводится к простейшей отправке сообщений в очередь устройства. Как упоминалось в начале статьи, Microsoft Azure Service Bus предоставляет REST-интерфейс, который позволяет взаимодействовать с ним из нескольких языков программирования. Поскольку официального SDK для разработчиков под Windows Phone нет, мы воспользовались одним из примеров из сообщества Windows Phone, который показывает, как выполнять аутентификацию и взаимодействовать с Service Bus с помощью HTTP-запросов и объекта WebClient. Соответствующий код тоже включен в пакет исходного кода для этой статьи, а проект Visual Studio 2013 называется MSDNArduinoClient. Основной экран клиента, с которого вы посылаете команды на устройство Arduino, показан на рис. 7.

Интерфейс клиента Windows Phone
Рис. 7. Интерфейс клиента Windows Phone

Создание подобных клиентов для других мобильных устройств (в том числе под управлением iOS и Android) не составит особого труда, поскольку большинство из них предоставляет библиотеки для генерации REST-команд, используя клиенты HTTP-запросов. Более того, можно напрямую взаимодействовать с Microsoft Azure Service Bus, применяя традиционные языки вроде Java, PHP или Ruby, которые упрощают этот процесс. Эти SDK публикуются по лицензии открытого исходного кода, и вы найдете их по ссылке github.com/WindowsAzure.

Заключение

Создание архитектуры Интернета вещей с применением Microsoft Azure Service Bus для управления соединениями с устройствами и сервисами обеспечивает простой способ защиты, масштабирования и адресации к индивидуальным клиентам без издержек VPN-решений, а также позволяет эффективно обрабатывать ситуации, где постоянно связи нет. Очереди действуют как выделенные почтовые ящики, через которые устройства и сервисы обмениваются сообщениями, поддерживая различные сценарии и шаблоны использования, распространенные в полевых условиях. Microsoft Azure предоставляет надежную, географически распределенную и отказоустойчивую инфраструктуру для развертывания необходимых сервисов с большим количеством подключенных между собой датчиков и контрольно-измерительных приборов — этот тренд будет лишь расти в предстоящие годы.


Бруно Теркали (Bruno Terkaly) — разработчик-идеолог в Microsoft. Его глубокие знания обусловлены долголетним опытом работы в различных областях, написанием кода с использованием множества платформ, языков, инфраструктур, SDK, библиотек и API. Основное внимание в своей работе он уделяет написанию кода, ведению блога и проведению презентаций по созданию облачных приложений, в частности на платформе Microsoft Azure. Читайте его блог blogs.msdn.com/b/brunoterkaly.

Рикардо Виллалобос (Ricardo Villalobos) — квалифицированный архитектор ПО более чем с 15-летним опытом проектирования и создания приложений для компаний в сфере управления цепочками поставок. Обладатель множества технических сертификатов, а также диплома магистра в области управления бизнесом от Университета Далласа, работает как идеолог платформ в группе DPE Globally Engaged Partners для Microsoft. Читайте его блог blog.ricardovillalobos.com.

Теркали и Виллалобос вместе выступают на крупных отраслевых конференциях. Вы можете связаться с ними по адресам bterkaly@microsoft.com и Ricardo.Villalobos@microsoft.com.

Выражаем благодарность за рецензирование статьи экспертам Microsoft Абишеку Лалу (Abhishek Lal) и Клеменсу Вастерсу (Clemens Vasters).