Перед каникулами я представил весь рабочий процесс программы Azure Photo Mosaics , и теперь пришло время покопаться немного глубже. Я собираюсь начать с рассмотрения аспектов хранения приложения, а затем связать их все вместе, когда я буду впоследствии решать три роли, которые составляют обработку приложения. Если вы хотите взломать код, просматривая этот пост (и последующие), вы можете скачать исходный код приложения Photo Mosaics из моего аккаунта хранения Azure!
Для приложения Photo Mosaics иерархия доступа к хранилищу может быть представлена следующей диаграммой. Вверху находятся основные возможности хранилища Windows Azure, доступ к которым осуществляется напрямую через API REST, но с некоторыми абстракциями для простоты программирования (а именно API клиента хранилища ), за которыми следуют уровни доступа для моего приложения (выделены зеленым цветом). В этом посте я расскажу о каждом из этих компонентов по очереди.
Windows Azure Storage
Как вы, вероятно, знаете, Windows Azure Storage имеет четыре основных компонента:
- Таблицы — хорошо масштабируемое, схематизированное, но нереляционное хранилище,
- Blobs — неструктурированные данные, организованные в контейнеры,
- Очереди — используются для обмена сообщениями между экземплярами ролей и
- Диски — (в бета-версии), позволяющие монтировать виртуальные жесткие диски NTFS в качестве блобов страниц
Все ваши ресурсы хранения Windows Azure (большие двоичные объекты, очереди, таблицы и диски) попадают под эгиду учетной записи хранения, связанной с вашей подпиской Windows Azure, и каждая учетная запись хранения имеет следующие атрибуты:
- может хранить до 100 ТБ данных
- может вместить неограниченное количество контейнеров BLOB
- с максимальным объемом 200 МБ на блок-блок и
- максимум 1 ТБ на страницу большого двоичного объекта (и / или Windows Azure Drive)
- может содержать неограниченное количество очередей
- максимум 8 КБ на сообщение в очереди
- можно хранить неограниченное количество столов
- максимум 1 МБ на объект («строка») и
- максимум 255 свойств (столбцов) на объект
- три копии всех данных, сохраняемых в любое время
- Доступ на основе REST через HTTP и HTTPS
- сильно (против в конечном итоге ) последовательный
SQL Azure обеспечивает полную семантику реляционных и транзакционных транзакций в облаке и представляет собой предложение, отдельное от основных возможностей хранилища Windows Azure. Как и хранилище Windows Azure, оно строго согласовано, и в нем постоянно хранятся три реплики. Базы данных SQL Azure могут хранить до 50 ГБ данных каждая, при этом
федерация SQL Azure является технологией для масштабирования за пределами этой точки. Мы не будем обсуждать SQL Azure как часть этой серии блогов, поскольку в настоящее время она не является частью архитектуры приложения Photo Mosaics.
Доступ к REST API
Родной API для доступа к хранилищам является REST на основе , которая является удивительной для открытости для других платформ приложений — все , что вам нужно , это HTTP стек. Когда вы создаете учетную запись хранения в Windows Azure, имя учетной записи становится первой частью URL-адреса, используемого для адресации ваших ресурсов хранения, а вторая часть имени определяет тип хранилища. Например, в учетной записи хранения с именем azuremosaics :
- http://azuremosaics.blob.core.windows.net/flickr/001302.jpg уникальным образом идентифицирует ресурс BLOB- объектов (здесь изображение) с именем 001302.jpg, который находится в контейнере с именем flickr . Глаголы HTTP POST, GET, PUT и DELETE при применении к этому URI выполняют традиционную семантику CRUD (создание, чтение, обновление и удаление).
- http://azuremosaics.table.core.windows.net/Tables предоставляет точку входа для перечисления, создания и удаления таблиц; в то время как запрос GET для URI http://azuremosaics.table.core.windows.net/jobs?$filter=id eq ‘F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4’ возвращает объекты в таблице заданий, имеющие свойство id со значением данного GUID.
- POST для http://azuremosaics.queue.core.windows.net/slicerequest/messages добавит сообщение, включенное в полезную нагрузку HTTP, в очередь slicerequest .
Концептуально это довольно просто и даже элегантно, но на практике это немного утомительно для прямого программирования. Кто хочет создавать HTTP-запросы с нуля и анализировать ответные данные? Вот тут-то и появляется API клиента хранилища Windows Azure , предоставляющий объектный уровень .NET поверх основного API. Фактически это всего лишь один из клиентских API, доступных для доступа к хранилищу; если вы используете другие фреймворки для разработки приложений, для вас также есть варианты:
Рубин | WAZ Storage Gem |
PHP | Windows Azure SDK для разработчиков PHP |
Ява | Windows Azure SDK для разработчиков Java |
питон | Оболочка клиента Python для хранилища Windows Azure |
Также обратите внимание, что за исключением выбора общей или общей политики доступа для больших двоичных объектов, каждый запрос к хранилищу Windows Azure должен содержать заголовок авторизации , значение которого содержит хеш-последовательность, вычисленную с использованием алгоритма HMAC-SHA256 над строкой, которая была сформирована объединение различных свойств указанного HTTP-запроса, который должен быть отправлен. Уф! Хорошая новость заключается в том, что Storage Client API позаботится обо всей этой тяжелой работе для вас.
API клиента хранилища
Storage Client API , в частности Microsoft.WindowsAzure.StorageClient пространства имен, где .NET разработчики будут тратить большую часть своего времени взаимодействия с Windows Azure Storage. API клиента хранилища содержит около 40 классов для обеспечения чистого программного интерфейса поверх основного REST API, и эти классы примерно подразделяются на пять категорий:
- Классы BLOB-объектов
- Классы очереди
- Столовые классы
- Драйв классы
- Классы для сквозных задач, таких как обработка исключений и контроль доступа
Каждый из основных типов хранилищ (большие двоичные объекты, очереди, таблицы и диски) имеет то, что я называю классом точки входа, который инициализируется учетными данными учетной записи, а затем используется для создания дополнительных классов для доступа к отдельным элементам, таким как сообщения в очередях и большие двоичные объекты внутри. контейнеры. Для контейнеров больших двоичных объектов это класс CloudBlobClient , а для очередей — CloudQueueClient . Доступ к таблице осуществляется через CloudTableClient и TableServiceContext ; последний расширяет System.Data.Services.Client.DataServiceContext, который должен звонить в колокол для тех, кто работал с LINQ to SQL или Entity Framework. И, наконец, CloudDrive является точкой входа для монтирования Windows Azure Drive в локальной файловой системе, хорошо расположенной в сети или рабочей роли, которая его монтирует.
Хотя API-интерфейс клиента хранилища можно использовать непосредственно во всем коде, я бы порекомендовал инкапсулировать его в слой доступа к данным, который не только способствует тестируемости, но и предоставляет единую точку входа в хранилище Azure и, следовательно, единую точку для обработки. учетные данные, необходимые для доступа к этому хранилищу. В приложении Photo Mosaics эта роль проекта CloudDAL обсуждается далее.
Облако DAL
В рамках CloudDAL я раскрыл три точки входа для каждого из типов хранилищ, используемых в приложении Photo Mosaics; Я назвал их TableAccessor , BlobAccessor и QueueAccessor . Эти классы предлагают интерфейс репозитория для конкретных данных, необходимых для приложения, и теоретически позволили бы мне полностью заменить Windows Azure Storage другим провайдером хранения, не разбирая все приложение целиком (хотя я на самом деле не проверял это :)) Давайте рассмотрим посмотрим на каждый из этих классов ‘accessor’ по очереди.
TableAccessor (в Table.cs)
Класс TableAccessor имеет единственный открытый конструктор, требующий имя учетной записи и учетные данные для учетной записи хранения, за которую отвечает этот конкретный экземпляр TableAccessor . Сам класс по сути является прокси для CloudTableClient и ссылкой на TableServiceContext :
- _tableClient предоставляет точку входа для проверки существования и создания (при необходимости) необходимой таблицы Windows Azure, а также
- _context обеспечивает LINQ-доступ к двум таблицам, заданиям и состоянию , которые являются частью приложения Photo Mosaics. Каждая таблица также имеет объектное представление, реализованное через потомка класса TableServiceEntity , который был создан для представления схемы базовой таблицы Windows Azure; здесь эти классы-потомки являются StatusEntity и JobEntity .
Все методы, определенные в TableAccessor, используют один и тот же шаблон тестирования существования таблицы (через _tableClient ), а затем отправляют запрос (или обновляют) через _context и соответствующие классы TableServiceEntity . В следующем посте мы немного углубимся в реализацию TableAccessor (и некоторые его недостатки!).
BlobAccessor (в Blob.cs)
BlobAccessor аналогичным образом является прокси-сервером для ссылки CloudBlobClient и, как и TableAccessor, создается с использованием строки подключения, которая указывает учетную запись хранения Windows Azure и ее учетные данные. Имена методов достаточно понятны, но в будущем у меня также будет отдельный пост о деталях доступа к BLOB-объектам в приложении Photo Mosaics.
QueueAccessor (в Queue.cs и Messages.cs)
Класс QueueAccessor является наиболее сложным (и на рисунке выше даже не показаны связанные классы QueueMessage !). Как и BlobAccessor и TableAccessor , QueueAccessor является прокси-сервером для ссылки на CloudQueueClient для установления учетных данных учетной записи и управления доступом к отдельным очередям, требуемым приложением.
Однако вы можете заметить, что QueueAccessor немного опирается на методы, и это потому, что функциональность основной очереди инкапсулирована в отдельный класс, ImageProcessingQueue и QueueAccessorсоздает статическую ссылку на экземпляры этого типа для представления каждой из четырех очередей приложений. Именно через ссылки ImageProcessingQueue (например, QueueAccessor.ImageRequestQueue ) веб-и рабочие роли читают и отправляют сообщения в каждую из четырех очередей, участвующих в приложении Photo Mosaics. Конечно, мы посмотрим, как это работает, в следующем посте.
Со слоем CloudDAL у меня есть четко определенная точка входа в три типа хранилищ, используемых приложением, и мы могли бы на этом остановиться; однако при этом есть одна существенная проблема: аутентификация. Клиентскому приложению Windows Forms требуется доступ к BLOB-объектам и таблицам в хранилище Windows Azure, и если бы он сделал это путем непосредственного включения слоя CloudDAL, ему потребовался бы доступ к учетным данным учетной записи, чтобы создать экземпляр TableAccessor и ссылки BlobAccessor . Это может быть целесообразно, если вы ожидаете, что у каждого клиента будет своя собственная учетная запись хранения Windows Azure, и вы тщательно сохраняете учетные данные учетной записи локально (через защищенную конфигурацию в app.config).или какие-то другие средства). Но даже в этом случае, предоставляя клиентам прямой доступ к ключу учетной записи хранения, вы можете дать им веревку, чтобы они могли повеситься. Это равносильно предоставлению учетных данных конечного пользователя базе данных SQL Server традиционного клиент-серверного приложения с доступом администратора к загрузке!
Если вы не жаждете перемены в карьере, такая реализация не в ваших интересах, и вместо этого вы захотите изолировать доступ к учетной записи хранения специально для тех элементов, которые необходимо перемещать назад и вперед к клиенту. Отличным средством изоляции здесь является веб-служба, специально реализованная как веб-роль WCF; это приводит нас к сервису StorageBroker .
Сервис StorageBroker
StorageBroker — это служба C # WCF, реализующая интерфейс IStorageBroker , и каждый метод интерфейса IStorageBroker в конечном итоге обслуживается CloudDAL (для большинства методов соответствие имен является очевидным). Вот, например, код для GetJobsForClient , который по существу оборачивает метод TableAccessor с тем же именем .
public IEnumerable<JobEntry> GetJobsForClient(String clientRegistrationId) { try { String connString = new StorageBrokerInternal(). GetStorageConnectionStringForClient(clientRegistrationId); return new TableAccessor(connString).GetJobsForClient(clientRegistrationId); } catch (Exception e) { Trace.TraceError("Client: {0}{4}Exception: {1}{4}Message: {2}{4}Trace: {3}", clientRegistrationId, e.GetType(), e.Message, e.StackTrace, Environment.NewLine); throw new SystemException("Unable to retrieve jobs for client", e); } }
Поскольку это служба WCF, клиент Windows Forms может просто использовать прокси-ссылку на службу (Добавить ссылку на службу … в Visual Studio) для вызова метода по проводной связи, как показано в JobListWindow.vb ниже . Это делается асинхронно, чтобы обеспечить лучший опыт конечного пользователя в свете внутренней задержки при доступе к ресурсам в облаке.
Private Sub JobListWindow_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load Dim client = New AzureStorageBroker.StorageBrokerClient() AddHandler client.GetJobsForClientCompleted, Sub(s As Object, e2 As GetJobsForClientCompletedEventArgs) ' details elided End Sub client.GetJobsForClientAsync (Me._uniqueUserId) End Sub
Теперь, если вы внимательно посмотрите на методы в StorageBroker , вы заметите вызовы, подобные следующим:
String connString = new StorageBrokerInternal (). GetStorageConnectionStringForClient(clientRegistrationId);
Что с StorageBrokerInternal ? Давай выясним.
StorageBrokerInternal Service
Хотя служба StorageBrokerInternal может не совпадать с границами YAGNI , я включил ее по двум основным причинам:
Мне не нравилось копировать информацию о конфигурации для каждой из (трех) ролей в моей облачной службе . Вырезать и вставлять информацию из файла ServiceConfiguration.cscfg ( это не просто ракетостроение) (и это проще, чем использование пользовательского интерфейса свойств для каждой роли в Visual Studio), но это просто «неправильно» делать это. StorageBrokerInternal позволяет указывать информацию о конфигурации один раз (через раздел конфигурации веб-роли AzureClientInterface ), а затем предоставлять доступ к любым другим ролям в облачном приложении через службу WCF, доступную на внутренней конечной точке HTTP — вы можете проверить мое сообщение в блоге на эта тема приключений также.
Мне нужна была некоторая гибкость с точки зрения того, какие учетные записи хранения используются для различных аспектов приложения, с ориентацией на многопользовательский режим. В приложении часть хранилища связана с самим приложением (например, с очередями, реализующими рабочий процесс), некоторые потребности в хранилище более тесно связаны с клиентом ( таблицы заданий и состояний, а также контейнеры imagerequest и imageresponse ), и отдых сидит где-то посередине.
Вместо того, чтобы «жестко кодировать» информацию учетной записи хранения для данных, связанных с клиентами, я хотел, чтобы эта информация была более настраиваемой. Предположим, я хотел запустить приложение как добросовестное деловое предприятие. У меня могут быть клиенты более высокого класса, которым нужен более высокий уровень производительности и изоляции, поэтому, возможно, у них есть выделенная учетная запись хранения для таблиц и контейнеров больших двоичных объектов, но я также могу предложить бесплатное или пробное предложение и хранить данные для несколько клиентов в одной учетной записи хранения, причем информация о каждом клиенте отличается уникальным идентификатором.
Различные методы в StorageBrokerInternalобеспечить отправную точку для такой реализации, предоставляя методы, которые могут получить доступ к строкам соединения с учетной записью хранения, привязанным к самому приложению, или к конкретному контейнеру больших двоичных объектов, или к конкретному клиенту. В текущей реализации все эти методы возвращают информацию, жестко закодированную в файле ServiceConfiguration.cscfg , но должно быть очевидно, что их реализации можно легко изменить, например, для запроса базы данных SQL Azure по идентификатору клиента и возврата строка подключения хранилища Windows Azure для этого конкретного клиента.
В рамках реализации общедоступной службы StorageBroker вы увидите экземпляры самого класса StorageBrokerInternal для получения информации (поскольку они находятся в одной сборке и роли), но если вы посмотрите на другие роли в облачной службе ( AzureImageProcessor и AzureJobController) вы заметите, что они получают доступ к информации о соединении через клиентские вызовы WCF, реализованные в файле InternalStorageBrokerClient.cs проекта с тем же именем. Например, рассмотрим следующую строку в WorkerRole.cs в AzureJobController
// instantiate storage accessors blobAccessor = new BlobAccessor( StorageBroker.GetStorageConnectionStringForAccount(requestMsg.ImageUri));
Он явно создает экземпляр нового BlobAccessor (часть CloudDAL ), но какая учетная запись используется, определяется следующим образом:
- Рабочая роль обращается к URI ссылки на изображение в сообщении очереди, которое в данный момент обрабатывается.
- Это делает вызов службе StorageBrokerInternal (инкапсулированной статическим классом StorageBroker ), передавая URI изображения
- Метод посредника внутреннего хранилища ( GetStorageConnectionStringForAccount) выполняет поиск, чтобы определить, какая строка подключения подходит для данной ссылки. ( Опять же, текущий поиск более или менее жестко запрограммирован, но методы StorageBrokerInternal предоставляют точку расширяемости для разделения хранилища между дискретными учетными записями практически любым способом, который вам нужен ).
StorageBroker и
StorageBrokerInternal ограничено самим приложением (связь осуществляется через внутреннюю конечную точку, доступную только экземплярам роли Windows Azure в текущем приложении), общедоступный
интерфейс StorageBroker широко открыт, и в настоящее время не реализованы механизмы авторизации или аутентификации. Это означает, что любой может получить доступ к общедоступным службам через клиентское приложение Windows Forms или даже через любой HTTP-клиент, например, браузер!
Я хотел бы сказать, что хотел, чтобы так было, но это явно не полная реализация. Есть причина очевидного недосмотра! Прежде всего, аутентификация домена здесь не будет работать, это общедоступная служба, и в облаке нет Active Directory. Тем не менее, некоторый тип проверки подлинности на основе форм является жизнеспособным, и для этого потребуется настроить проверку подлинности на основе форм в Windows Azure, используя в качестве хранилища табличное хранилище или SQL Azure. Для этого есть
примеры провайдеров (хотя я не думаю, что они официально поддерживаются), но это означает, что нужно управлять пользователями как частью моего приложения и нести дополнительные расходы на хранение, особенно если я добавлю SQL Azure только для поддержки членства ,
Для этого приложения я бы предпочел переложить аутентификацию пользователя на кого-то другого, например, предоставив клиентам доступ через свои учетные данные Twitter или Facebook, и так получилось, что
служба контроля доступа Windows Azure AppFabric позволит мне сделать именно это. Это еще не реализовано в приложении Photo Mosaics, отчасти потому, что я до него не дошел, а отчасти потому, что я не хотел еще больше усложнять пример в процессе объяснения архитектуры. Так что подумайте над тем, чтобы исправить эту дыру в реализации как что-то в нашем общем списке «дел», и мы подойдем к этому к концу серии блогов.
клиент
Наконец мы достигли дна нашего стека доступа к данным: клиент. Здесь клиент представляет собой простое приложение Windows Forms, но поскольку интерфейс общедоступного хранилища происходит через службу WCF через BasicHttpBinding , предоставление веб-клиента или мобильного клиента в .NET или любом другом языке, поддерживающем веб-службы, должно быть простым. В клиенте Windows Forms, распространяемом с примером кода , я использовал механизм Add Service Reference … в Visual Studio, чтобы создать удобный клиентский прокси-сервер (см. Справа) и изолировать себя от деталей каналов, привязок и тому подобного.
Важно отметить, что весь доступ к хранилищу с клиента осуществляется асинхронно, чтобы обеспечить отзывчивый пользовательский интерфейс независимо от задержки доступа к хранилищу Windows Azure.
Ключевые вынос
Я понимаю, что это было немного, чтобы пройтись, и это в основном даже не специфично для самого приложения; до сих пор это была инфраструктура! Это наблюдение, однако, является ключевым моментом, который нужно убрать. Когда вы создаете приложения для облака — для масштабирования и роста — вам нужно сначала заняться ключевыми основополагающими аспектами (а не загонять себя в угол позже!):
Как мне справиться с несколькими арендаторами?
Как открыть и управлять хранилищем и другими учетными данными для доступа?
Как сделать будущее приложение для устройств и форм-факторов, которые я еще не представил?
Не существует единого правильного подхода ко всем этим проблемам, но, надеюсь, рассказав вам, как я справился с ними, я дал вам пищу для размышлений о том, как лучше всего спроектировать свои собственные приложения, когда вы направляетесь в облако.
Далее: мы рассмотрим, как хранилище больших двоичных объектов используется в контексте приложения Azure Photo Mosaics.