Перейти к содержимому

Какой компонент управляет распределенным множеством данных приложения

  • автор:

Какой компонент управляет распределенным множеством данных приложения?

Какой компонент архитектуры Android позволяет любому приложению использовать уже реализованные возможности других приложений, к которым разрешен доступ?

В какой папке необходимо разместить XML файлы, которые определяют все меню приложения?

Какой язык разметки используется для описания иерархии компонентов графического пользовательского интерфейса Android-приложения?

Какой движок баз данных используется в ОС Android?

Разработка и использование мобильных приложений

Вот и пришла пора поговорить непосредственно о внутренней организации приложений под Android: обсудить их архитектуру и основные компоненты.

Архитектура Android приложений основана на идее многократного использования компонентов, которые являются основными строительными блоками. Каждый компонент является отдельной сущностью и помогает определить общее поведение приложения.

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

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

Можно выделить четыре различных типа компонентов, каждый тип служит для достижения определенной цели и имеет свой особый жизненный цикл, который определяет способы создания и разрушения соответствующего компонента. Рассмотрим основные компоненты Android-приложений.

Активности (Activities). Активность — это видимая часть приложения (экран, окно, форма), отвечает за отображение графического интерфейса пользователя. При этом приложение может иметь несколько активностей, например, в приложении, предназначенном для работы с электронной почтой, одна активность может использоваться для отображения списка новых писем, другая активность — для написания, и еще одна — для чтения писем. Несмотря на то, что для пользователя приложение представляется единым целым, все активности приложения не зависят друг от друга. В связи с этим любая из этих активностей может быть запущена из другого приложения, имеющего доступ к активностям данного приложения. Например, приложение камеры может запустить активность, создающую новые письма, чтобы отправить только что сделанную фотографию адресату, указанному пользователем.

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

Контент-провайдеры (Content providers). Контент-провайдер управляет распределенным множеством данных приложения. Данные могут храниться в файловой системе, в базе данных SQLite, в сети, в любом другом доступном для приложения месте. Контент-провайдер позволяет другим приложениям при наличии у них соответствующих прав делать запросы или даже менять данные. Например, в системе Android есть контент-провайдер, который управляет информацией о контактах пользователя. В связи с этим, любое приложение с соответствующими правами может сделать запрос на чтение и запись информации какого-либо контакта. Контент-провайдер может быть также полезен для чтения и записи приватных данных приложения, не предназначенных для доступа извне.

Приемники широковещательных сообщений (Broadcast Receivers). Приемник — компонент, который реагирует на широковещательные извещения. Большинство таких извещений порождаются системой, например, извещение о том, что экран отключился или низкий заряд батареи. Приложения также могут инициировать широковещание, например, разослать другим приложениям сообщение о том, что некоторые данные загружены и доступны для использования. Хотя приемники не отображают пользовательского интерфейса, они могут создавать уведомление на панели состояний, чтобы предупредить пользователя о появлении сообщения. Такой приемник служит проводником к другим компонентам и предназначен для выполнения небольшого объема работ, например, он может запустить соответствующий событию сервис.

Все рассмотренные компоненты являются наследниками классов, определенных в Android SDK.

Рис1

Рис. 3.1. Иерархия классов Android SDK

На рис. 3.1 показана иерархия основных классов Android SDK, с которыми обычно имеет дело разработчик. На самом деле классов намного больше, желтым цветом выделены классы, с которыми разработчик работает непосредственно, наследует от них свои классы. Остальные классы не менее важны, но они реже используются напрямую. Для начала рассмотрим классы Intent и View .

Класс View является основным строительным блоком для компонентов пользовательского интерфейса (UI), он определяет прямоугольную область экрана и отвечает за прорисовку и обработку событий. Является базовым классом для виджетов (GUI widgets), которые используются для создания интерактивных компонентов пользовательского интерфейса: кнопок, текстовых полей и т. д. А также является базовым классом для класса ViewGroup , который является невидимым контейнером для других контейнеров и виджетов, определяет свойства расположения компонентов пользовательского интерфейса. Интерфейс Android-приложения представляет собой иерархию UI компонентов (см. рис. 3.2), можно описать эту иерархию программно, но более простым и эффективным способом задать расположение элементов интерфейса является XML файл, который предоставляет удобную для восприятия структуру компоновки (layout file). Во время исполнения XML файл автоматически превращается в дерево соответствующих объектов. Подробнее о классе View , свойствах и методах: http://developer.android.com/reference/android/view/View.html.

Рис2

Рис. 3.2. Иерархия компонентов, определяющая компоновку интерфейса пользователя

Объекты-экземпляры класса Intent используются для передачи сообщений между основными компонентами приложений. Известно, что три из четырех основных компонентов: активности, сервисы и приемники широковещательных сообщений, могут быть активированы с помощью сообщений, которые называются намерениями. Такие сообщения являются инструментом позднего связывания компонентов одного или нескольких приложений. Экземпляр класса Intent представляет собой структуру данных, содержащую описание операции, которая должна быть выполнена, и обычно используется для запуска активности или сервиса. В случае с приемниками широковещательных сообщений объект Intent содержит описание события, которое произошло или было объявлено.

Для каждого типа компонентов существуют свои механизмы передачи намерений.

  • Чтобы запустить активность или вызвать у работающей активности новое действие, необходимо передать объект-намерение в метод Context.startActivity() или Activity.startActivityForResult() .
  • Чтобы запустить сервис или доставить новые инструкции работающему сервису, необходимо передать объект-намерение в метод Context.startService() . Также объект-намерение может быть передан в метод Context.bindService() , чтобы связать между собой вызывающий компонент и сервис.
  • Чтобы доставить объект-намерение всем заинтересованным приемникам широковещательных сообщений, необходимо передать его в любой из широковещательных методов: Context.sendOrderedBroadcast() , Context.sendStickyBroadcast() , Context.sendBroadcast() .

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

Рис3

Рис. 3.3. Передача намерений (Intent)

На рис. 3.3 можно увидеть как происходит передача намерений (Intent), в данном случае одна активность запускает другую. [6] Активность А создает намерение (Intent) с описанием действия и передает его в метод startActivity() . [7] Система Android проверяет все приложения на совпадение с намерением, когда совпадение найдено, [8] система запускает соответствующую активность, для чего вызывает метод onCreate() и передает в него объект-намерение Intent.

Подробнее о классе Intent :

Пришло время более серьезно рассмотреть основные компоненты: активности, сервисы, контент-провайдеры, приемники широковещательных сообщений. В первую очередь нас будет интересовать жизненный цикл этих компонентов. Что же такое этот жизненный цикл? Жизненный цикл можно рассматривать, как процесс функционирования компонента: начиная с момента создания и запуска, включая активный и неактивный периоды работы, и, заканчивая уничтожением и освобождением ресурсов.

Какой компонент управляет распределенным множеством данных приложения

Warning: mysqli_connect(): (HY000/1040): Too many connections in /home/c/co37471/eljob/public_html/core/main/Msql.php on line 49

Warning: mysqli_connect(): (HY000/1040): Too many connections in /home/c/co37471/eljob/public_html/core/main/Msql.php on line 53

Warning: mysqli_connect(): (HY000/1040): Too many connections in /home/c/co37471/eljob/public_html/core/main/Msql.php on line 53

Warning: mysqli_connect(): (HY000/1040): Too many connections in /home/c/co37471/eljob/public_html/core/main/Msql.php on line 53
Some mysqli shit happened: 1040 Too many connections

Распределенное управление данными: проблемы и решения

Это содержимое является фрагментом из электронной книги, архитектуры микрослужб .NET для контейнерных приложений .NET, доступных в документации .NET или в виде бесплатного скачиваемого PDF-файла, который можно читать в автономном режиме.

.NET Microservices Architecture for Containerized .NET Applications eBook cover thumbnail.

Задача #1. Определение границ каждой микрослужбы

Пожалуй, с проблемой определения границ микрослужб каждый разработчик сталкивается в первую очередь. Каждая микрослужба должна быть частью вашего приложения, и каждая микрослужба должна быть автономной — здесь есть свои преимущества и свои недостатки. Но как определить эти границы?

Во-первых, необходимо сосредоточиться на логических моделях предметных областей приложения и связанных данных. Попробуйте выделить группы данных и различные контексты в одном приложении. Каждый контекст должен иметь свой бизнес-язык (свои бизнес-термины). Контексты должны определяться и управляться независимо друг от друга. Термины и объекты, используемые в этих контекстах, могут звучать похоже, но вы увидите, что в одном контексте концепция используется не так, как в другом, и даже может иметь другое название. Например, пользователь может называться пользователем в контексте идентификации или членства, клиентом — в контексте управления клиентами, покупателем — в контексте заказов и т. д.

Как вы определяете границы между контекстами приложения с отдельной предметной областью для каждого контекста, так же вы определяете границы каждой микрослужбы, ее модель предметной области и данные. Всегда старайтесь свести к минимуму взаимозависимость между этими микрослужбами. Далее в главе Определение границ модели предметной области для каждой микрослужбы будет подробно рассматриваться это разграничение и проектирование модели предметной области.

Задача 2. Создание запросов, извлекающих данные из нескольких микрослужб

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

Шлюз API. Для простого объединения данных из нескольких микрослужб с разными базами данных рекомендуется использовать микрослужбу агрегирования — шлюз API. Будьте осторожны при применении этого шаблона, поскольку он может стать слабым местом вашей системы и нарушить принцип автономности микрослужб. Чтобы смягчить негативные последствия, используйте несколько мелких шлюзов API для различных вертикальных срезов или областей системы. Шаблон шлюза API более подробно описан в разделе Использование шлюза API ниже.

GraphQL Federation One можно рассмотреть, если микрослужбы уже используют GraphQL — это Федерация GraphQL. Федерация позволяет определять «подграфы» из других служб и создавать их в агрегатный «суперграф», который выступает в качестве автономной схемы.

CQRS с таблицами запросов/чтения. Еще одно решение для объединения данных из нескольких микрослужб — шаблон материализованного представления. При таком подходе вы заранее создаете (готовите денормализованные данные до фактической отправки запросов) таблицу, доступную только для чтения, с данными, принадлежащими нескольким микрослужбам. Таблица имеет формат, соответствующий потребностям клиентского приложения.

Представьте себе экран мобильного приложения. Если у вас одна база данных, вы можете собрать данные для этого экрана с помощью SQL-запроса, выполняющего сложное соединение с использованием нескольких таблиц. Но если у вас несколько баз данных и каждая база данных принадлежит отдельной микрослужбе, невозможно отправить в них запрос и создать соединение SQL. Такой сложный запрос становится проблемой. Эту проблему можно решить с помощью подхода CQRS — создайте денормализованную таблицу в другой базе данных, которая используется только для запросов. Таблица может предназначаться специально для данных, необходимых в этом сложном запросе, и между полями, необходимыми для экрана приложения, и столбцами в таблице запроса может существовать отношение один к одному. Такой метод также можно использовать для составления отчетов.

Этот подход позволяет не только решить изначальную проблему (как отправлять запросы в несколько микрослужб), но и значительно повысить производительность по сравнению с использованием сложных соединений, поскольку у вас уже есть все необходимые приложению данные в таблице запроса. Конечно, если вы используете принцип разделения ответственности на команды и запросы (Command and Query Responsibility Segregation, CQRS) с таблицами запросов/чтения, придется проделать дополнительную работу и проследить за итоговой согласованностью. Тем не менее мы рекомендуем применять принцип CQRS с несколькими базами данных там, где существуют особые требования к производительности и масштабируемости в ситуации совместной работы (или соперничества, это как посмотреть).

«Холодные данные» в центральных базах данных. Для составления сложных отчетов и выполнения запросов, не требующих немедленного ответа, рекомендуется экспортировать «горячие данные» (данные о транзакциях из микрослужб) как «холодные данные» в большие базы данных, использующиеся только для отчетности. Эта центральная система базы данных может быть системой на основе больших данных, например Hadoop; хранилище данных, например хранилище данных, основанное на хранилище данных SQL Azure; или даже отдельная база данных SQL, используемая только для отчетов (если размер не будет проблемой).

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

Но если в приложении требуется непрерывное агрегирование сведений из нескольких микрослужб для выполнения сложных запросов, это может быть признаком плохой структуры — микрослужбы должны быть максимально изолированы друг от друга. (Мы не учитываем отчеты и аналитику, которые всегда должны использовать центральные базы данных с «холодными данными».) Если такая проблема возникает, возможно, следует объединить микрослужбы. Попробуйте найти баланс между автономностью развития и развертывания каждой микрослужбы и прочными зависимостями, слаженностью и агрегированием данных.

Задача 3. Как добиться согласованности между несколькими микрослужбами

Как мы уже говорили, данные микрослужбы принадлежат только ей, и получить их можно только через API микрослужбы. Поэтому встает вопрос, как реализовать целостные бизнес-процессы, сохраняя согласованность нескольких микрослужб.

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

В гипотетической монолитной версии этого приложения при изменении цены в таблице «Товары» подсистема каталога может просто использовать транзакцию ACID, чтобы обновить текущую цену в таблице «Корзина».

Однако в приложении на базе микрослужб таблицы «Товар» и «Корзина» находятся в соответствующих микрослужбах. Микрослужбы никогда не должны включать таблицы или хранилища других микрослужб в свои транзакции, и в том числе в прямые запросы, как показано на рис. 4-9.

Diagram showing that microservices database data can

Рис. 4-9 Микрослужба не может обратиться к таблице другой микрослужбы напрямую

Микрослужба каталога не должна напрямую изменять таблицу «Корзина», поскольку эта таблица принадлежит микрослужбе корзины. Чтобы обновить сведения в микрослужбе корзины, микрослужба каталога может использовать только итоговую согласованность, возможно на основе асинхронной связи, например событий интеграции (взаимодействие на основе сообщений и событий). Вот как такая итоговая согласованность микрослужб выполняется в примере приложения eShopOnContainers.

Как гласит теорема CAP, вы должны выбирать между доступностью и согласованностью данных по принципу ACID. В большинстве случаев при использовании микрослужб доступность и масштабируемость имеют приоритет над строгой согласованностью. Критически важные приложения должны непрерывно работать, и разработчики могут решить проблему строгой согласованности, используя методы работы со слабой или итоговой согласованностью. Такой подход используется в большинстве архитектур на базе микрослужб.

Кроме того, транзакции в стиле ACID или с двухфазной фиксацией не просто противоречат принципам микрослужб — большинство баз данных NoSQL (например, Azure Cosmos DB, MongoDB и т. д.) не поддерживают транзакции с двухфазной фиксацией, типичные для сценариев распространенных баз данных. Согласованность данных в разных службах и базах данных все же имеет большое значение. Эта проблема также связана с вопросом распространения изменений в нескольких микрослужбах, когда некоторые данные должны быть избыточными — например, когда название или описание товара должно присутствовать в микрослужбе каталога и в микрослужбе корзины.

Хорошим решением этой проблемы может стать использование итоговой согласованности между микрослужбами, выраженной в управляемом событиями взаимодействии и системе публикации и подписки. Эти темы обсуждаются в разделе Асинхронное взаимодействие, управляемое событиями.

Задача 4. Проектирование взаимодействия между границами микрослужбы

Взаимодействие через границы микрослужб является настоящей проблемой. В этом контексте взаимодействие не подразумевает выбор протокола (HTTP и REST, AMQP, обмен сообщениями и так далее). Нужно подумать, какой стиль следует использовать и, особенно, насколько микрослужбы должны зависеть друг от друга. В случае сбоя его последствия для системы будут определяться степенью этой взаимозависимости.

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

Чаще всего используются службы на базе HTTP (REST), поскольку они очень простые. Использовать HTTP можно. Но как именно? Если вы используете запросы и ответы HTTP только для взаимодействия между микрослужбами и клиентскими приложениями или шлюзами API, это нормально. Но если вы создаете длинные цепочки синхронных HTTP-вызовов для взаимодействия через границы микрослужб, как если бы микрослужбы были объектами в монолитном приложении, в конце концов в приложении возникнут проблемы.

Представьте, что клиентское приложение делает вызов HTTP API к отдельной микрослужбе, например микрослужбе заказов. Если микрослужба заказов, в свою очередь, вызывает дополнительные микрослужбы по протоколу HTTP в рамках одного цикла запросов и ответов, вы создадите цепочку HTTP-вызовов. Поначалу это может казаться разумным. Но при таком подходе следует учитывать несколько важных аспектов:

  • Блокировка и низкая производительность. Поскольку HTTP-запросы синхронные по своей природе, изначальный запрос не получит ответ, пока все внутренние HTTP-вызовы не завершатся. Представьте, что число этих вызовов значительно возрастет и при этом один из промежуточных HTTP-вызовов к микрослужбе будет заблокирован. Это негативно отразится на производительности и масштабируемости приложения, ведь количество HTTP-запросов увеличится.
  • Взаимозависимость микрослужб и HTTP. Микрослужбы для бизнеса не следует объединять друг с другом. В идеале они даже не должны «знать» о существовании других микрослужб. Если приложение использует взаимозависимые микрослужбы, как в примере, добиться автономности каждой микрослужбы будет практически невозможно.
  • Сбой в одной микрослужбе. Если вы создали цепочку микрослужб, соединенную HTTP-вызовами, при сбое одной микрослужбы (а сбой неизбежен) вся цепочка перестанет работать. Система на базе микрослужб должна максимально сохранять работоспособность в случае частичных сбоев. Даже если вы применяете логику, которая использует повторные попытки с экспоненциальной задержкой или механизмы размыкания цепи, чем сложнее цепочки HTTP-вызовов, тем сложнее применить стратегию обработки сбоев на базе HTTP.

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

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

Дополнительные ресурсы

  • Теорема CAP
    https://en.wikipedia.org/wiki/CAP_theorem
  • Итоговая согласованность
    https://en.wikipedia.org/wiki/Eventual_consistency
  • Руководство по согласованности данных
    https://learn.microsoft.com/previous-versions/msp-n-p/dn589800(v=pandp.10)
  • Мартин Фоулер (Martin Fowler). CQRS (разделение обязанностей запросов и команд)
    https://martinfowler.com/bliki/CQRS.html
  • Материализованное представление
    https://learn.microsoft.com/azure/architecture/patterns/materialized-view
  • Чарльз Роу (Charles Row). ACID и BASE: сдвиг pH обработки транзакций базы данных
    https://www.dataversity.net/acid-vs-base-the-shifting-ph-of-database-transaction-processing/
  • Компенсирующие транзакции
    https://learn.microsoft.com/azure/architecture/patterns/compensating-transaction
  • Уди Дахан (Udi Dahan). Объединение на основе служб
    https://udidahan.com/2014/07/30/service-oriented-composition-with-video/

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *