Skip to content
October 30, 2012 / ahriman hpc mode

Windows Azure и Java. Руководство по использованию.

Windows Azure и Java. Ч.1. Hello World.

Для разработки на Java для платформы Windows Azure необходимо иметь:

1) Java Developer Kit не ниже 1.6

2) Windows Azure SDK.

3) Необходимые библиотеки Java, которые можно загрузить и подключить к проекту либо вручную, либо с помощью Maven, либо установив плагин к Eclipse. Ссылки: http://msdn.microsoft.com/en-us/library/windowsazure/hh690953(v=vs.103).aspx

4) Аккаунт Windows Azure.

Имея все 4 позиции, вы можете начать разрабатывать ваши проекты либо переносить уже существующие в облако.

В данном руководстве будет использоваться среда разработки Eclipse Juno (http://eclipse.org) с установленным плагином.

1. Установка плагина.

После скачивания и распаковки Eclipse запустите среду разработки. Перед вами появляется рабочая область среды разработки. Вызовите Help=>InstallNewSoftware и введите в строку Work With адрес репозитория – http://dl.msopentech.com/eclipse (рисунок 1). Нажмите Enter.

clip_image002[4]

Рис.1. Рабочая область Eclipse Juno.

В нижнем окне после загрузки списка содержимого репозитория возникнет выпадающая “ветка” Windows Azure Toolkit For Java. В данной ветке содержится несколько проектов – Microsoft JDBC Driver for SQL Server (для работы с хранилищем, в том числе SQL Azure Databases) и другие. Отметьте всю ветку и нажмите Next.

В зависимости от скорости Интернета и доступности репозитория загрузка может занять до нескольких минут.

После загрузки необходимых метаданных появится окно настроек (рисунок 2).

clip_image004[4]

Рис. 2. Установка плагина Windows Azure для Eclipse

Нажмите Next и в следующем окне согласитесь с лицензионным соглашением, после чего нажмите Finish.

После установки плагина вам предложат перезагрузить Eclipse. Согласитесь.

1. Альтернатива. Подключение библиотек вручную.

Для того, чтобы вручную подключить плагин, необходимо скачать соответствующие библиотеки и зависимости со страницы http://msdn.microsoft.com/en-us/library/windowsazure/hh690953(v=vs.103).aspx.

Создайте в Eclipse Java-проект (рисунок 3).

clip_image006[4]

Рис.3. Создание Java-проекта.

Введите необходимые сведения в появившемся диалоговом окне. Достаточно ввести имя проекта (рисунок 4).

clip_image008[4]

Рис.4. Настройки проекта.

Нажмите Next. На странице Java Settings перейдите на вкладку Libraries и нажмите External JARs.

clip_image010[4]

Рис.5. Добавление библиотек.

Выберите загруженные с сайта JAR (рисунок 6).

clip_image012[4]

Рис.6. Выбор библиотек.

Нажмите Finish.

Java-проект с подключенными библиотеками, необходимыми для работы с Windows Azure, создан.

2. Создание Java-проекта.

Для создания проекта нажмите в Eclipse CTRL+N и выберите Dynamic Web Project (рисунок 7).

clip_image014[4]

Рис. 7. Шаблоны проектов

На странице настроек нового проекта введите необходимые данные – выберите веб-сервер (например, Tomcat 7.0), название проекта. Нажмите Finish.

clip_image016[4]

Рис.8. Настройки проекта.

Структура вашего приложения должна выглядеть как на рисунке 9.

clip_image018[4]

Рис. 9. Структура проекта.

Основная папка, которая нас интересует – WebContent. В неё вы поместите основной JSP-файл.

Щелкните правой кнопкой мыши на папке WebContent и нажмите New=>JSP File. В появившемся диалоговом окне введите имя файла Index.jsp и нажмите Next. На странице Select JSP Template выберите шаблон проекта New JSP File (html) и нажмите Finish.

Введите в появившемся окне редактора в контейнере <body> следующий код:

<body>
Hello Azure.
</body>

Нажмите для сохранения изменений.

Теперь необходимо экспортировать пакет Java-проекта. Для этого нажмите правой кнопкой мыши на проекте JavaWindowsAzure и нажмите Export=>WAR File. В появившемся диалоговом окне нажмите Browse и выберите папку approot проекта Windows Azure (рисунок 10).

clip_image020[4]

Рис.10. Экспорт пакета проекта

3. Создание проекта Windows Azure.

Для создания Java-проекта Windows Azure нажмите в Eclipse CTRL+N и найдите вкладку Windows Azure, после чего два раза кликните на Windows Azure Project, чтобы инициировать создание проекта (рисунок 11).

clip_image022[4]

Рис. 11. Создание Windows Azure проекта.

Откроется окно настроек создаваемого проекта. Обратите внимание на уже существующие настройки, после чего нажмите Finish – для первого проекта можно ограничиться настройками по умолчанию (рисунок 12).

clip_image024[4]

Рис. 12. Настройки проекта Windows Azure.

Нажмите Next. В следующем окне отметьте IncludeaJDKfromthislocationи Includeaserverfromthislocationи укажите пути к соответствующим дистрибутивам JDK и Tomcat. Выделите HelloWorld.warи нажмите Remove. Нажмите Addи выберите экспортированный в прошлом пункте WAR (рисунок 13). Нажмите Finish.

clip_image026[4]

Рис. 13. Настройки проекта

Выберите проект WindowsAzureForJavaApp и нажмите на кнопку RunInWindowsAzureEmulator(рисунок 14).

clip_image028[4]

Рис. 14. Запуск приложения в локальном эмуляторе вычислений

После того, как вы нажмете на кнопку, произойдет развертывание проекта в локальный эмулятор вычислений Windows Azure. Обратите внимание на периодически появляющиеся окна – одновременно с запуском в эмуляторе запускается также сервер Tomcat 7.

Откройте графический интерфейс локального эмулятора вычислений (рисунки 15,16).

clip_image030[4]

Рис. 15. Вызов интерфейса эмулятора вычислений

clip_image032[4]

Рис. 16. Интерфейс эмулятора вычислений

Нажмите на WorkerRole1 и выберите 0. Это экземпляр вашей роли (рисунок 17).

clip_image034[4]

Рис. 17. Лог экземпляра роли

После запуска веб-сервера (рисунок 18) зайдите в веб-браузере на http://localhost:8080/ http://localhost:8080/JavaWindowsAzure/, чтобы увидеть вашу страницу с простым сообщением.

clip_image036[4]

Рис. 18. Успешный запуск веб-сервера

4. Развертывание проекта в облако.

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

Щелкните правой кнопкой мыши на облачном проекте Windows Azure и нажмите Properties. В окне свойств перейдите на вкладку Windows Azure (рисунок 19).

clip_image038[4]

Рис. 19. Настройка облачного проекта для развертывания в облако

На вкладке Windows Azure выберите Deployment to cloud. На вкладке Remote Access снимите галочку – таким образом вы отключите удаленный доступ к вашей роли, что сейчас не нужно, так как это связано с загрузкой сертификата безопасности. Закройте окно конфигурации и пересоберите проект (Project => Build All). В этот момент будут созданы необходимые для развертывания в облако файлы, находящиеся в папке deploy вашего проекта.

Войдите на портал управления Windows Azure под своим аккаунтом.

Создайте вычислительный сервис, который будет выполнять код вашего приложения. На левой панели нажмите Newи нажмите Compute => CloudService => QuickCreate (рисунок 20). Введите название вычислительного сервиса. Нажмите CreateCloudService.

clip_image040[4]

Рис. 20. Создание вычислительного сервиса

Перейдите обратно в Eclipse.

Нажмите кнопку Publish to Windows Azure Cloud (рисунок 21).

clip_image042[4]

Рис. 21. Publish to Windows Azure Cloud

Нажмите Download. Введите свои учетные данные для подписки Windows Azure. Сохраните файл настроек подписки (рисунок 22).

clip_image044[4]

Рис. 22. Сохранение файла настроек подписки

Нажмите Importи укажите расположение сохраненного файла настроек подписки. Настройки подписки будут импортированы (рисунок 23). Нажмите Publish.

clip_image046[4]

Рис. 23. Импортированные настройки подписик

Дождитесь окончания развертывания роли. Во время развертывания происходит весь процесс установки вашего приложения: ищется свободное оборудование, сервис загружается на портал, создаётся и разворачивается виртуальная машина, на неё загружается сервис, после чего происходит инициирование событий OnStartup и Run – в событии OnStartup запускается веб-сервер и JDK, необходимые для выполнения нашего приложения, в событии-методе Run происходит основная логика приложения.

Обратите внимание на новое представление в Eclipse – Windows Azure Activity Log (рисунок 24).

clip_image048[4]

Рис. 24. Представление Windows Azure Activity Log

Перейдя на уровень развертывания (azurejavatest), обратите внимание на панель свойств справа. В нем доступно DNS-имя развертывания. URL выглядит как <guid>.cloudapp.net, где <guid> – некоторый случайный идентификатор, отличающийся от адреса, который получит приложение при развертывании в реальной среде. Хотя ячейки развертывания и разделены между собой, физических различий между ними нет – все определяется тем, куда подключен балансировщик нагрузки. Попробуйте перейти по ссылке. В том случае, если вы сконфигурировали все правильно, вам должна быть выведена страница вашего сайта. Если появляется окно администраторской панели Tomcat, попробуйте добавить в ссылку имя вашего приложения и указать непосредственно имя файла, допустим:

http://fe744ac908e24c4dafbc2afce3c7f5da.cloudapp.net/JavaWindowsAzure/Index.jsp

Определив корректность работы приложения с помощью тестового развертывания, можно переводить его в реальное развертывание. При развертывании приложения в реальной среде Windows Azure перенастраивает балансировщики нагрузки, обеспечивая доступ к приложению по URL реального развертывания.
Для Swap VIP нажмите кнопку Swap на панели администрирования вашего вычислительного сервиса (рисунок 25).

clip_image050[4]

Рис. 25. Swap VIP

Нажмите на ссылку DNS name для открытия сайта в реальном развертывании в новом окне браузера и обратите внимание на ссылку в адресной строке браузера.

Примечание: Если вы получаете при входе на сайт DNS-ошибку 404, подождите пару минут и попробуйте еще – возможно, DNS-имя ещё не готово. Windows Azure создает DNS-имена динамически и применение этих изменений может занять несколько минут.

Даже если развертывание находится в статусе suspended, Windows Azure всё ещё держит выделенные для каждого экземпляра виртуальные машины в рабочем состоянии, таким образом начисляя за них счёт. После окончания тестирования приложения вам необходимо удалить развертывание из Windows Azure во избежание дополнительных затрат.

Windows Azure & Java. Использование Service Runtime Library

Windows Azure SDK For Java предоставляет библиотеку Windows Azure Service Runtime, позволяющую взаимодействовать приложению, написанному на Java, со средой Windows Azure (локальным эмуляторм или облаком) и получать информацию о развертывании (информацию о состоянии ролей, экземпляров и т.д.), а также запрашивать и задавать некоторые значения, включая запросы на “очистку” экземпляров роли.

К основным классам данной библиотеки относятся:

* RoleEnvironment

* RoleInstance

* Role

 

Всего в библиотеке гораздо больше классов, которые можно изучить на MSDN.

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

RoleEnvironment

Данный класс является отображением среды выполнения Windows Azure, в которой работает экземпляр вашего приложения. Класс предоставляет следующую функциональность (список неполон):

static void clearStatus()
Очистка статуса экземпляра роли.

static Map<String,String>getConfigurationSettings()
Получение настроек, хранящихся в файле конфигурации роли.

static RoleInstance getCurrentRoleInstance()
Возвращает объект типа RoleInstance, отображающий экземпляр роли, в котором выполняется приложение в данный момент.

static String getDeploymentId()
Возвращает идентификатор развертывания, уникальное идентифицирующий развертывание, в котором выполняется экземпляр. Возвращает ID в форуме deployment(nn), где nn – integer в случае выполнения в локальном эмуляторе и GUID в случае выполнения в облаке.

static Map<String,LocalResource>getLocalResources()
Возвращает набор именованных ресурсов локального хранилища, зарезервированных в файловой системой виртуальной машиной, в котором выполняются экземпляр роли. Возвращаемая коллекция содержит набор объектов LocalResources. Список локальных ресурсов определяется в файле определения сервиса.

static Map<String,Role>getRoles()
Возвращает набор объектов типа Role, определенных в вашем приложении.

static boolean isAvailable()

Определяет, запущено ли приложение в облаке или нет. Возвращает true, если да. Если возвращается false, запрещено использовать любые части класса RoleEnvironment (будет выброшено исключение RoleEnvironmentAvailableException).

static boolean isEmulated()
Определяет, запущено ли приложение в локальном эмуляторе или нет. Возвращает true, если да.

static void requestRecycle()
Запрашивает перезапуск экземпляра роли.

static void setStatus(RoleInstanceStatus status, Date expiration_utc)
Устанавливает статус экземпляра роли.

RoleInstance

Класс RoleInstance является отображением конкретного экземпляра роли и содержит в себе следующую функциональность:

getFaultDomain()
Возвращает значение типа Integer, отображающее домен неисправности, в котором расположен экземпляр.

String getId()
Возвращает идентификатор экземпляра.

Map<String,RoleInstanceEndpoint> getInstanceEndpoints()
Возвращает набор точек входа экземпляра роли.

Role getRole()
Возвращает объект типа Role для данного экземпляра.

Int getUpdateDomain()
Возвращает значение типа Integer, отображающее домен обновления, в котором находится экземпляр.

Role

Класс Role является отображением роли в вашем приложении. Роли (веб-роль, воркер-роль) определяются в файле определения сервиса. Класс содержит всего два метода:

Map<String,RoleInstance>getInstances()
Возвращает набор экземпляров для данной роли.

String getName()
Возвращает имя роли, прописанное в файле определения сервиса.

Межклассовые отношения RoleEnvironment, Role, RoleInstance

· Метод RoleEnvironment.getCurrentRoleInstance возвращает экземпляр роли типа RoleInstance, с помощью которого можно получить различные данные об экземпляре, такие как домены неисправностей и обновления, точки входа и так далее.

· Метод RoleEnvironment.getRoles возвращает набор ролей вашего сервиса.

· Метод RoleInstance.getRole возвращает объект типа Role, ассоциированный с ролью экземпляра, с помощью которого вы можете получить имя роли для данного экземпляра.

· Метод Role.getRoleInstances возвращает набор экземпляров ролей.

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

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

InetSocketAddress getIpEndPoint()
Возвращает объект типа InetSocketAddress , отображающий точку входа для экземпляра роли.

String getProtocol()
Возвращает протокол точки входа.

RoleInstance getRoleInstance()
Возвращает объект типа RoleInstance, к которому принадлежит данная точка входа.

* RoleEnvironmentChangedEvent. Данный класс отображает событие, которое совершается во время изменения конфигурации сервиса. Содержит один метод:

Collection<RoleEnvironmentChange> getChanges()
Возвращает набор изменений конфигурации, которые были применены к экземпляру роли.

* RoleEnvironmentConfigurationSettingChange . Данный класс является отображением изменения конфигурации и содержит один метод:

String getConfigurationSettingName()
Возвращает имя настройки конфигурации, которая была изменена.

* RoleEnvironmentTopologyChange отображает изменение топологии сервиса. Топология сервиса – количество экземпляров для роли. Содержит один метод:

String getRoleName()
Возвращает имя роли, над которой было совершено изменение.

* RoleInstanceStatus . Данное перечисление (enum) является отображением статуса экземпляра роли и содержит две константы:

Busy
Экземпляр не будет отвечать на запросы.

Ready
Экземпляр готов отвечать на запросы.

В первой части цикла был приведен алгоритм настройки проекта. Для дальнейшей работы можно работать прямо из проекта из первой статьи, редактируя лишь файл .JSP. В практической же части будет выполнен новый код, использующий основные возможности Service Runtime Library, с использованием Tomcat 7.

Вызовите окно Properties вашего Java-проекта (не облачного).

Перейдите на вкладку Java Build Path и нажмите Add Library(рисунок 26).

clip_image052[4]

Рис. 26. Добавление библиотек в проект

Выберите Package for Windows Azure Libraries for Java (by MS Open Tech) (рисунок 27).

clip_image054[4]

Рис. 27. Добавление библиотек Windows Azure в проект

Нажмите Finish.

Java-проект с подключенными библиотеками, необходимыми для работы с Windows Azure, создан.

Откройте JSP-файл, который вы должны отредактировать в соответствии с кодом ниже.

<%@ page language=“java” contentType=“text/html; charset=ISO-8859-1”

      pageEncoding=“ISO-8859-1”

      import=“com.microsoft.windowsazure.serviceruntime.*, java.util.Map, java.util.*”%>

<!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd”&gt;

<html>

<head>

<meta http-equiv=“Content-Type” content=“text/html; charset=ISO-8859-1”>

<title>Hello</title>

</head>

<body>

      <b>Hello Azure!</b>

      <br />

      <p>

            RoleEnvironment.isEmulated() возвратил

            <%=RoleEnvironment.isEmulated()%>.

      </p>

      <p>

            Идентификатор экземпляра роли равен

            <%=RoleEnvironment.getCurrentRoleInstance().getId()%>.

      </p>

      <p>

            Имя роли:

            <%=RoleEnvironment.getCurrentRoleInstance().getRole()

                              .getName()%>.

      </p>

      <%

            Map<String, RoleInstanceEndpoint> endpoints =

            RoleEnvironment.getCurrentRoleInstance().getInstanceEndpoints();

            for (Map.Entry<String, RoleInstanceEndpoint> entry : endpoints

                        .entrySet())

            {

                  out.println(“Точка входа ‘”

                              + entry.getKey()

                              +

                              “‘ : “

                              + entry.getValue().getIpEndPoint().getAddress()

                                         .getHostAddress());

            }

            Map<String, LocalResource> localresources = RoleEnvironment

                        .getLocalResources();

            Set set = localresources.entrySet();

            Iterator iterator = set.iterator();

            LocalResource localResource;

            while (iterator.hasNext())

            {

                  Map.Entry entry = (Map.Entry) iterator.next();

                  localResource = (LocalResource) entry.getValue();

                  out.println(“Локально зарезервированные файловые ресурсы: \n Название: “

                              + localResource.getName()

                              + “Путь: “

                              + localResource.getRootPath()

                              + “\n Максимальный размер (Мб): “

                              + localResource.getMaximumSizeInMegabytes() + “\n”);

            }

            ;

            Map<String, String> mapConfigSettings = RoleEnvironment

                        .getConfigurationSettings();

            String mysetting = mapConfigSettings.get(“name”);

            out.println(mysetting);

      %>

</body>

</html>

Соберите проект – Project => Build Project.

Теперь необходимо экспортировать пакет Java-проекта. Для этого нажмите правой кнопкой мыши на проекте JavaWindowsAzure и нажмите Export=>WAR File. В появившемся диалоговом окне нажмите Browse и выберите папку approot проекта Windows Azure.

Нажмите Run In Windows Azure Emulator.

Произойдет развертывание проекта в локальный эмулятор вычислений Windows Azure. Откройте графический интерфейс локального эмулятора вычислений (рисунок 28).

clip_image056[4]

Рис. 28. Интерфейс эмулятора вычислений, лог роли

Нажмите на WorkerRole1 и выберите 0. Это экземпляр вашей роли.

После запуска веб-сервера зайдите в веб-браузере на http://localhost:8080/JavaWindowsAzure, чтобы увидеть вашу страницу (рисунок 29).

clip_image058[4]

Рис. 29. Страница с результатом выполнения

Остановите развертывание, нажав соответствующую кнопку (рисунок 30).

clip_image060[4]

Рис. 30. Кнопка остановки развертывания в локальном эмуляторе

Таким образом, вы увидели, как можно взаимодействовать со средой выполнения локального эмулятора из Java-приложения. Аналогичным образом осуществляется взаимодействие с облачной средой – достаточно выложить проект в облако.

Windows Azure & Java. Вопросы отладки Java-приложений.

Плагин Windows Azure для интегрированной среды разработки Eclipse позволяет в числе своих возможностей отлаживать приложения как в локальном эмуляторе, так и в облаке. Для того, чтобы сконфигурировать проект для использования отладки, необходимо нажать правой кнопкой мыши на вашей роли (с синей шестеренкой) и вызвать окно Properties (рисунок 31), где необходимо перейти на вкладку Windows Azure, после чего на вкладку Debugging, где отметить опцию Enable remote debugging for this role.

clip_image062[4]

Рис. 31. Свойства роли

Панель позволяет выбрать также точку входа, по которой будет происходить отладка приложения, и другие опции – такие, как ожидание приложения до входа отладчика и т.д. Немного подробнее про Start JVM in suspended mode, waiting for a debugger connection: данная опция предназначена только для некоторых ситуаций, связанных с локальным эмулятором – с помощью ее можно задержать запуск сервера до тех пор, пока отладчик Eclipse не подключится к соответствующей JVM. Откройте меню Windows Azure Debug Configuration и выберите свой проект в Java project to debug, после чего убедитесь, что Configure debugging for указан в значение Windows Azure compute emulator (рисунок 32). Закройте диалог и окно Properties.

clip_image064[4]

Рис.32. Свойства роли.

Теперь можно переходить к непосредственному процессу отладки в локальном эмуляторе. Сначала необходимо отметить точку прерывания (breakpoint). Для этого:

Разверните папку WebContent вашего приложения и откройте Index.jsp.

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

Map<String, LocalResource> localresources = RoleEnvironment.getLocalResources();

Map<String, String> mapConfigSettings = RoleEnvironment.getConfigurationSettings();

clip_image066[4]

Рис. 33. Установка точки прерывания.

После установки точки прерывания появится соответствующий значок (рисунок 34).

clip_image068[4]

Рис.34. Иконка точки прерывания

Запустите проект в локальном эмуляторе.

После окончания процесса запуска необходимо соответствующим образом сконфигурировать конфигурацию отладки. Для этого:

В меню нажмите на иконке стрелки справа от значка жука и выберите Debug Configurations (рисунок 35).

clip_image070[4]

Рис. 35. Открытие окна конфигурации отладки.

Разверните вкладку Remote Java Applications и выберите Windows Azure Emulator (WorkerRole1) (рисунок 36). Убедитесь, что выбран режим Socket Attach. Нажмите Debug.

clip_image071[4]

Рис. 36. Окно конфигураций отладки.

Как только процесс будет завершен, перейдите на страницу JSP в браузере. При открытии обратите внимание, что Eclipse предложит поменять перспективу (рисунок 37). Согласитесь.

clip_image073[4]

Рис. 37. Подтверждение смены перспективы

У вас должна открыться перспектива отладки (рисунок 38).

clip_image075[4]

Рис. 38. Перспектива отладки Java-приложения в Eclipse.

Вам доступна полная функциональность Eclipse по отладке. Нажмите F8 (Resume) и просмотрите данные, которые предоставляет вам среда разработки.

Отладка в облаке Windows Azure

Остановите развертывание.

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

Для настройки проекта для облачной отладки сначала сконфигурируйте сборку проекта для облачной сборки (окно Properties облачного проекта, вкладка Windows Azure, Build for). Нажмите Project->Build all.

Вернитесь к первому абзацу и в окне Properties сконфигурируйте Build for как Deployment to cloud. В этом же диалоге сконфигурируйте значение Host (оно должно быть равно DNS-имени вашего тестового развертывания без http://). DNS-имя развертывания можно получить, зайдя на портал управления Windows Azure и выбрав ваше развертывание.

Закройте диалог и окно Properties вашего проекта и соберите проект (Project –> Build all). Далее загрузите пакет и конфигурационный файл в развертывание в облаке с помощью кнопки Publishв Eclipse. В том случае, если у вас не создано развертывание, обратитесь к первой части.

Дождитесь окончания процесса развертывания вашего проекта. После этого в меню нажмите на иконке стрелки справа от значка жука и выберите Debug Configurations. Разверните вкладку Remote Java Applications и выберите Windows Azure Cloud (WorkerRole1). Убедитесь, что выбран режим Socket Attach. Нажмите Debug.

Как только процесс будет завершен, перейдите на страницу JSP в браузере. Например, http://8eee90deb9394d98bfc4979a9f7fabcd.cloudapp.net/JavaWindowsAzure/NewFile.jsp.

Обратите внимание, что, если вы активировали отладку для развертывания, у которого имеется множество запущенных экземпляров, возможности контролировать, к какому из экземпляров будет подключен отладчик, нет, так как балансировщик нагрузки посылает запрос экземпляру случайным образом. В том случае, если вы не предпринимаете никаких действий по отладке, отладочное подключение будет отключено через 4 минуты со стороны Windows Azure.

Для того, чтобы совершить все те же самые действия по настройке отладки, но с GlassFish, вам необходимо будет сконфигурировать GlassFish вручную, не трогая конфигурацию плагина Windows Azure для Eclipse.

Windows Azure & Java. Дополнительные возможности. Remote Desktop, Session Affinity.

Связка Windows Azure & Java позволяет разработчику подключаться по удаленному рабочему столу (Remote Access, Remote Desktop Protocol (RDP)) к виртуальным машинам, выполняющим приложение. Для конфигурации удаленного доступа необходимо сконфигурировать соответствующим образом вкладку Remote Access в свойствах проекта (рисунок 39).

clip_image077[4]

Рис.39. Окно свойств проекта – настройки удаленного доступа

По умолчанию каждый новый проект Windows Azure имеет включенную возможность удаленного доступа, основанную на использовании самоподписанного сертификата (который идёт в тестовом варианте в комплекте с Windows Azure Plugin для Eclipse, и находится в папке cert в проекте Windows Azure). Данный самоподписанный сертификат имеет две части – файл публичного сертификата (SampleRemoteAccessPublic.cer) и файл сертификата типа Personal Information Exchange (SampleRemoteAccessPrivate.pfx), который содержит в себе приватный ключ для сертификата и пароль по умолчанию (Password1). Данный сертификат не должен быть использован в реальном развертывании (достаточно префикса Sample). Вы можете использовать для целей удаленного доступа либо этот сертификат, либо воспользоваться способом, приведенным в данной статье.

Давайте настроим удаленный доступ для вашего проекта. Для этого:

Нажмите правой кнопкой мыши на облачном проекте в Eclipse и нажмите Properties.

В открывшемся окне раскройте вкладку Windows Azure и выберите Remote Access.

В открывшемся диалоге Remote Access отметьте опцию Enable all roles to accept Remote Desktop connections with these login credentials – таким образом вы активируете удаленный доступ на всех ролях с данными учетными данными. Укажите учетные данные для удаленного подключения в соответствующих текстовых полях – эти учетные даные – не те, которые используются в сертификате.

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

В разделе Certificate to encrypt user credentials нажмите New для создания собственного самоподписанного сертификата, так как мы не хотим использовать тестовый вариант – он слишком небезопасен даже в тестовой среде (либо, если у вас уже есть каким-либо образом созданный самоподписанный сертификат, просто укажите его местоположение с помощью кнопок Workspace и FileSystem).

В диалоге New Certificate введите пароль для файла PFX.

Укажите путь и имя файлов, в которых будут сохранены файлы сертификата и публичный и приватный ключи (.cer, .pfx). В данной статье мы выберем для удобства папку certificates вWorkspace, однако разницы, где вы сохраните ваши сертификаты, абсолютно никакой нет (рисунок 40).

clip_image079[4]

Рис.40. Настройка самоподписанного сертификата.

Нажмите OK. Перейдите на вкладку Windows Azure.

На вкладке Windows Azure выберите Deployment to cloud.

Соберите проект – Project –> Build All (CTRL + B).

Войдите на портал управления Windows Azure. Перейдите на вкладку Settings(рисунок 41).

clip_image081[4]

Рис. 41. Настройки подписки Windows Azure

После этого нажмите на кнопку Uploadдля добавления сертификата (рисунок 42).

clip_image083[4]

Рис. 42. Диалог загрузки сертификата

Разверните сервис в облако.

Убедитесь, что развертывание прошло успешно и имеет статус Ready.

Теперь необходимо подключиться удалённо. После успешного развертывания вы можете подключиться к вашей виртуальной машине приложения. Для этого перейдите на вкладку CloudServices, нажмите на вашем вычислительном сервисе, перейдите на вкладку Configureи нажмите Remote(рисунок 43).

clip_image085[4]

Рис. 43. Кнопка подключения по Remote Desktop

Session Affinity

Windows Azure Plugin для Eclipse with Java позволяет использовать HTTP session affinity (другое название – “липкие сессии для ролей”). Настройки данной функциональности находятся во вкладке Load Balancing в свойствах роли (рисунок 44).

clip_image087[4]

Рис. 44. Настройки Session Affinity.

В появившемся диалоге Properties for WorkerRole1 Load Balancing отметьте опцию Enable HTTP session affinity (sticky sessions) for this role и выберите конечную точку входа для использования (Input endpoint to use). Если у вас еще не сконфигурирована точка входа, плагин предложит вам создать стандартную точку входа http (public:80, private:8080). Естественно, что можно сконфигурировать множество точек входа, но для липких сессий можно указать только одну.

clip_image089[4]

Рис. 45. Настроенная балансировка нагрузки

Соберите ваш проект – Project –> Build All (CTRL + B). Теперь, после включения липких сессий, запросы HTTP, приходящие от клиентов, будут обрабатываться одним и тем же экземпляром ролей. Это позволяет делать гибкую обработку и реализовывать хранение состояния сессий. Необходимо уточнить, что данный механизм реализуется тем, что плагин установливает модуль IIS Application Request Routing  в каждый из экземпляров вашей роли и, таким образом, маршрутизирует HTTP-запросы, сначала заворачивая их на себя, после чего отправляя их по соответствующим экземплярам.

Что необходимо учитывать при работе с липкими сессиями:

Липкие сессии и Session Affinity не работают в локальном эмуляторе вычислений. Так как вычисления только эмулируются, не может быть никаких дополнительных установок модулей.

Сохранение состояния сессий с помощью механизма Session Affinity

Так как у вас уже имеется готовый проект со страницей .JSP, вы можете реализовать сохранение состояния сессий. Нам пригодится еще один файл для просмотра того, как всё это работает – newsession.jsp.

Ваш итоговый файл JSP  должен выглядеть в соответствии с кодом ниже. Обратите внимание, что используется дополнительный код из предыдущей части цикла. Код взят с MSDN.

<%@ page language=“java” contentType=“text/html; charset=ISO-8859-1”

      pageEncoding=“ISO-8859-1”

      import=“com.microsoft.windowsazure.serviceruntime.*, java.util.Map, java.util.*,java.text.DateFormat”%>

<!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd”&gt;

<html>

<head>

<meta http-equiv=“Content-Type” content=“text/html; charset=ISO-8859-1”>

<title>Hello</title>

</head>

<body>

      <b>Hello Azure!</b>

      <br />

      <p>

            RoleEnvironment.isEmulated() возвратил

            <%=RoleEnvironment.isEmulated()%>.

      </p>

      <p>

            Идентификатор экземпляра роли равен

            <%=RoleEnvironment.getCurrentRoleInstance().getId()%>.

      </p>

      <p>

            Имя роли:

            <%=RoleEnvironment.getCurrentRoleInstance().getRole()

                              .getName()%>.

      </p>

      <%

            Map<String, RoleInstanceEndpoint> endpoints =

            RoleEnvironment.getCurrentRoleInstance().getInstanceEndpoints();

            for (Map.Entry<String, RoleInstanceEndpoint> entry : endpoints

                        .entrySet())

            {

                  out.println(“Точка входа ‘”

                              + entry.getKey()

                              +

                              “‘ : “

                              + entry.getValue().getIpEndPoint().getAddress()

                                         .getHostAddress());

            }

            Map<String, LocalResource> localresources = RoleEnvironment

                        .getLocalResources();

            Set set = localresources.entrySet();

            Iterator iterator = set.iterator();

            LocalResource localResource;

            while (iterator.hasNext())

            {

                  Map.Entry entry = (Map.Entry) iterator.next();

                  localResource = (LocalResource) entry.getValue();

                  out.println(“Локально зарезервированные файловые ресурсы: \n Название: “

                              + localResource.getName()

                              + “Путь: “

                              + localResource.getRootPath()

                              + “\n Максимальный размер (Мб): “

                              + localResource.getMaximumSizeInMegabytes() + “\n”);

            }

            ;

            Map<String, String> mapConfigSettings = RoleEnvironment

                        .getConfigurationSettings();

            String mysetting = mapConfigSettings.get(“name”);

            out.println(mysetting);

            // Отслеживание количества визитов в сессии.

            Integer visits = (Integer) session.getAttribute(“visits”);

            if (null == visits)

            {

                  // Визиты в сессии не отслеживаются, при первом визите инициализация.

                  visits = new Integer(1);

            }

            else

            {

                  // Визиты отслеживаются, поэтому инкрементирование счетчика визитов.

                  visits = new Integer(visits.intValue() + 1);

            }

            // Обновление данных сессии для включения текущего количества счетчиков.

            session.setAttribute(“visits”, visits);

            // проверка на наличие логина пользователя в сессии

            String name;

            name = (String) session.getAttribute(“username”);

            if (null == name)

            {

                  // если логина нет, проверяется текстовое поле логина

                  name = request.getParameter(“username”);

                  if (null != name && “” != name)

                  {

                        // сохранение логина в сессии

                        session.setAttribute(“username”, name);

                        // приветствие.

      %>

      <p>

            Welcome

            <%=name%>!

      </p>

      <%

            }

                  else

                  {

                        // логина нет. отображение формы.

      %>

      <form action=<%=request.getRequestURI()%> method=“post”>

            Enter your name: <input type=“text” name=“username” /> (Press the <b>Enter</b>

            key when done)

      </form>

      <%

            }

            }

            else

            {

                  // приветствие возвратившегося пользователя.

      %>

      <p>

            Welcome back

            <%=name%>!

      </p>

      <%

            }

            // отображение инфорамации сессии.

            DateFormat dfDate = DateFormat.getDateInstance(DateFormat.FULL);

            DateFormat dfTime = DateFormat.getTimeInstance();

            long creationTime = session.getCreationTime();

            long lastAccessedTime = session.getLastAccessedTime();

            Date dateCreated = new Date(creationTime);

            Date dateLastAccessed = new Date(lastAccessedTime);

      %>

      <p />

      <table border=1 width=90%>

            <tr>

                  <th>Session information</th>

                  <th>Value</th>

            </tr>

            <tr>

                  <td>Session ID</td>

                  <td><%=session.getId()%></td>

            </tr>

            <tr>

                  <td>New or existing</td>

                  <td><%=(session.isNew() ? “New” : “Existing”)%></td>

            </tr>

            <tr>

                  <td>Created</td>

                  <td><%=dfDate.format(dateCreated)%> <%=dfTime.format(dateCreated)%></td>

            </tr>

            <tr>

                  <td>Last accessed</td>

                  <td><%=dfDate.format(dateLastAccessed)%> <%=dfTime.format(dateLastAccessed)%></td>

            </tr>

            <tr>

                  <td>Maximum inactive interval</td>

                  <td><%=Integer.toString(session.getMaxInactiveInterval())%>

                        seconds</td>

            </tr>

            <tr>

                  <td>Visits or refreshes by you to this session</td>

                  <td><%=visits.intValue()%></td>

            </tr>

            <tr>

                  <td>Windows Azure deployment ID</td>

                  <td><%=RoleEnvironment.getCurrentRoleInstance().getId()%></td>

            </tr>

            <tr>

                  <th>Session attributes</th>

                  <th>Value</th>

            </tr>

            <%

                  // определение атрибутов сессии.

                  Enumeration<String> e = session.getAttributeNames();

                  String attributeName;

                  Object attributeValue;

                  // отображение атрибутов.

                  while (e.hasMoreElements())

                  {

                        attributeName = e.nextElement();

                        attributeValue = session.getAttribute(attributeName);

            %>

            <tr>

                  <td><%=attributeName%></td>

                  <td><%=attributeValue.toString()%></td>

            </tr>

            <%

                  }

            %>

      </table>

      <p />

      <!– Allow the user to create a new session. –>

      <form action=“newsession.jsp” method=“post”>

            <input type=“submit” name=“newSession” value=“Create new session” />

      </form>

      %>

</body>

</html>

Файл newsession.jsp должен иметь следующий код.

<%@ page language=“java” contentType=“text/html; charset=ISO-8859-1”

      pageEncoding=“ISO-8859-1”%>

<!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd”&gt;

<html>

<head>

<meta http-equiv=“Content-Type” content=“text/html; charset=ISO-8859-1”>

<title>New session page</title>

</head>

<body>

      <%

            // удаление текущей сессии.

            session.invalidate();

            // возврат пользователя к главной странице

            response.sendRedirect(“NewFile.jsp”);

      %>

</body>

</html>

Экспортируйте Java-проект в облачный проект.

Разверните проект в облаке и просмотрите результат.

Windows Azure & Java. Что такое Service Bus и как её использовать из Java-приложения. Часть 1.

clip_image091[4]

Рис. 46. Service Bus

Один из дополнительных компонентов платформы Windows Azure, предоставляющий функциональность безопасного, устойчивого к ошибкам и высокомасштабируемого механизма обмена сообщения, является Service Bus. Service Bus позволяет разработчику объединять в единую инфраструктуру обмена сообщениями несколько сущностей, использовать множество протоколов обмена сообщениями, и делать это достаточно красиво и аккуратно – приложение регистрируется в Service Bus Relay, разработчик/администратор выбирает некоторый уникальный адрес конечной точки входа (следуя конвенциям именования –https://[имяточкивхода].accesscontrol.windows.net/), после чего приложение может инициировать создание исходящего подключения к Service Bus Relay. Далее приложение будет держать этот канал открытым до тех пор, пока не будет выключено или перезагружено, постоянно посылая небольшие heartbeat-пакеты (раз в несколько секунд). Весь траффик, попадающий в Service Bus Relay, будет перенаправлен затем на конечную точку входа в облако и локальное приложение-приемник (которых может быть множество одновременно на одну конечную точку входа сервиса – при этом Service Bus Relay случайным образом выбирает, какой из приемников-слушателей получит входящий запрос, обеспечивая таким образом балансировку нагрузки. При этом необходимо учитывать, что нет никаких гарантий, что нагрузка будет ровно “размазана” по всем слушателям).

Service Bus оперирует двумя основными концепциями – Queues (очереди) и Topics (топики).

Очереди

Очереди Service Bus предоставляют модель коммуникаций, называемую brokered messaging communication – при использовании очередей части распределенного приложения осуществляют коммуникации между собой через очередь (что позволяет разрабатывать слабо-связанные системы) – отправитель сообщения кладет сообщение в очередь (очередь следует модели First In, First Out –FIFO), откуда (из начала) его забирает обработчик. Всё происходит асинхронно, поэтому никому ждать, пока кто-то закончит, не требуется. Принцип работы распределенной архитектуры с использованием очереди Service Bus изображен на рисунок 47.

clip_image092[4]

Рис. 47. Принцип работы распределенной архитектуры с использованием очереди Service Bus

Подобную архитектуру можно использовать при разработке самых разных приложений (это является типичным паттерном в “облачных” приложениях), например для обеспечения коммуникаций между веб и воркер-ролями в приложении в Windows Azure, коммуникаций между локальным и облачным приложением в гибридной модели, а также для связывания нескольких частей распределенного локальногоприложения (таким образом, сценарий локального приложения расширяется в облако, но только в контексте хранения и маршрутизации сообщений). Очереди Service Bus позволяют масштабировать приложения и добиваться высокой степени отказоустойчивости за счёт собственных механизмов – если обработчики сообщения не могут какое-то время обработать сообщения, сообщения будут храниться до определенных пор либо до того момента, как обработчики не вернутся обратно в онлайн и не будут готовы к работе.

Топики

По принципу работы топики похожи на очереди, однако их главной особенностью является множество конечных точек для обработчиков – они называются подписками (Subscriptions), и такая модель называется publish/subscribe messaging communication – каждый потребитель-обработчик “подписывается” на подписку. Каждая подписка имеет доступ ко всем добавленным сообщениям, при этом механизм фильтрации позволяет им определить, доступно ли сообщение через подписку или нет, и если доступно, то обработчик забирает сообщение из подписки аналогичным очередям образом. Таким образом, можно реализовать распространение одного и того же сообщения на несколько подписок. Механизм подписок использует принцип работы FIFO и представлен на рисунок 48.

clip_image094[4]

Рис.48. Принцип работы подписок (Subscription) Service Bus

Топики и очереди позволяют разрабатывать более слабо связанные приложения, с временной независимостью (когда отправитель и обработчик сообщения не обязаны быть в он-лайн одновременно), балансировкой нагрузки.

Для того, чтобы начать работу с Service Bus в Windows Azure, необходимо создать именованный сервис (пространство имен), которое будет предоставлять контейнер для Service Bus.

Зайдите на портал управления Windows Azure (http://manage.windowsazure.com)

В левом нижнем углу портала управления нажмите New=>AppServices=>ServiceBusQueue=>QuickCreate(рисунок 49). Введите название очереди и нажмите Createanewqueue.

clip_image096[4]

Рис.49. Интерфейс портала управления Windows Azure, управление Service Bus

Дождитесь создания пространства имен и очереди (рисунок 50).

clip_image098[4]

Рис. 50. Интерфейс управления Service Bus

Нажмите на пространство имён. Перейдя на администраторскую панель пространства имен, нажмите AccessKeyи запишите владельца пространства имен и ключ.

На этом настройка Service Busзакончена.

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

import java.math.BigDecimal;

import java.util.Calendar;

import java.util.List;

import com.microsoft.windowsazure.services.serviceBus.*;

import com.microsoft.windowsazure.services.serviceBus.models.*;

import com.microsoft.windowsazure.services.core.*;

import javax.xml.datatype.*;

import javax.xml.datatype.DatatypeConstants.Field;

public class TestServiceBus {

      // Все операции по управлению вашим контейнером Service Bus выполняются с

      // помощью класса ServiceBusContract, при создании которого конструктору

      // передаётся

      // конфигурация вашего контейнера – название контейнера, имя issuer и ключ.

      // Все эти данные можно получить из панели свойств Properties вашего

      // контейнера Service Bus на портале Windows Azure.

      // Далее можно воспользоваться функциональностью класса ServiceBusService,

      // предоставляющего управление очередями – создание, удаление и т.д. В

      // данном методе создаётся контейнер Service Bus с именем QueueSample.

      private static ServiceBusContract createServiceBus(String issuer, String key) {

            Configuration config = ServiceBusConfiguration

                        .configureWithWrapAuthentication(“testqueue-ns1”, issuer, key,

                                   “.servicebus.windows.net”,

                                   “-sb.accesscontrol.windows.net/WRAPv0.9”);

            ServiceBusContract service = ServiceBusService.create(config);

            return service;

      }

      // В данном методе используется функциональность класса QueueInfo, с помощью

      // которой в данном случае определяется

      // максимальный размер очереди в мегабайтах (указывается максимальный размер

      // в 5Гб). С помощью методов QueueInfo можно настраивать различные параметры

      // ваших очередей, в т.ч. Time To Live для сообщений, максимальный размер и

      // многое другое.

      private static QueueInfo createQueue(String name, ServiceBusContract service) {

            long queueSize = 5120;

            QueueInfo queueInfo = new QueueInfo(“myqueue”);

            try {

                  queueInfo.setMaxSizeInMegabytes(queueSize);

                  CreateQueueResult result = service.createQueue(queueInfo);

            } catch (ServiceException e) {

                  System.out.print(“ServiceException: ” + e.getMessage());

            }

            return queueInfo;

      }

      // Для отображения сообщения в объектной модели существует класс

      // BrokeredMessage, объекты которого содержат набор методов для управления

      // сообщением,

      // набор параметров и набор данных. В набор данных можно передать любой

      // сериализуемый объект. Очереди Service Bus имеют ограничение на размер

      // сообщения в 256 килобайт (заголовок, содержащий свойства – 64 килобайта),

      // однако ограничений как таковых на количество хранимых в очереди сообщений

      // нет, кроме задаваемого вами ограничения-максимального размера очереди.

      private static void putMessageToQueue(String queueName,

      ServiceBusContract service, BrokeredMessage message) {

            try {

                  service.sendQueueMessage(queueName, message);

            } catch (ServiceException e) {

                  System.out.print(“ServiceException encountered: ” + e.getMessage());

            }

      }

      // Аналогично сервису очередей хранилища Windows Azure, вы можете

      // использовать два метода для извлечения сообщений из очередей –

      // получение и удаление (ReceiveAndDelete) и “подсматривание” – получение

      // сообщение, но не удаление его из очереди (PeekLock). При использовании

      // метода ReceiveAndDelete при получении очередью запроса на извлечение

      // сообщения, очередь помечает это сообщение как “потреблённое”.

      // При использовании метода PeekLock процесс получения дробится на два этапа

      // – когда Service Bus получает запрос на извлечение сообщения, он находит

      // это сообщение, помечает его как locked (в этот момент другие обработчики

      // перестают видеть это сообщение) и возвращает его приложению. После

      // окончания

      // обработки приложением сообщения закрывается второй этап процесса с

      // помощью вызова метода Delete полученного сообщения. После этого сообщение

      // помечается как удаленное.

      // Типичным паттерном опроса очереди на наличие новых сообщений является

      // использование бесконечного цикла while. В данном методе очередь

      // опрашивается постоянно. Если вы хотите

      // ограничить выполнение каким-либо количеством полученных сообщений, вам

      // необходимо реализовать логику с использованием break.

      private static void getMessageFromQueue(String queueName,

      ServiceBusContract service) throws ServiceException {

            // ReceiveMessageOptions opts = ReceiveMessageOptions.DEFAULT;

            // opts.setReceiveMode(ReceiveMode.PEEK_LOCK);

            while (true) {

                  ReceiveQueueMessageResult result = service

                  .receiveQueueMessage(queueName);

                  BrokeredMessage message = result.getValue();

                  if (message != null && message.getMessageId() != null) {

                        try {

                              System.out

                              .println(“Сообщение: “

                              + message.toString());

                              System.out.println(“ID сообщения: “

                              + message.getMessageId());

                              System.out

                                         .println(“Если вы задали какое-то свойство, его можно получить с помощью метода getProperty(): “

                                                     + message.getProperty(“CustomProperty”));

                              System.out.println(“Сообщение прочитано – удалено.”);

                              service.deleteMessage(message);

                        } catch (Exception ex) {

                              // если было выброшено исключение, сообщение будет

                              // разблокировано для других обработчиков

                              System.out.println(“Исключение!”);

                              service.unlockMessage(message);

                        }

                  } else {

                        System.out.println(“Больше нет сообщений, прерываем цикл.”);

                        break;

                  }

            }

      }

      public static void main(String args[]) {

            ServiceBusContract service = createServiceBus(“owner”,

            “=”);

            QueueInfo queue = createQueue(“myqueue”, service);

            BrokeredMessage message = new BrokeredMessage(

            “Наше сообщение в очередь.”);

            putMessageToQueue(“testqueue”, service, message);

            try {

                  getMessageFromQueue(“testqueue”, service);

            } catch (ServiceException e) {

                  e.printStackTrace();

            }

      }

}

 

Запустите проект и просмотрите результат в консоли.

Обратите внимание на метод ServiceBusConfiguration.configureWithWrapAuthentication. В стандартном виде он принимает 5 аргументов: название пространства имён Service Bus (testqueuens1), имя для аутентификации (обычно owner), пароль (ключ, key на портале управления), serviceBusRootUri – корневой URI Service Bus, обычно “.servicebus.windows.net”, и wrapRootUri – корневой URI для сервиса аутентификации, который равен “-sb.accesscontrol.windows.net/WRAPv0.9”.

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

04.04.2012 11:57:06 com.microsoft.windowsazure.services.serviceBus.implementation.ServiceBusExceptionProcessor processCatch
WARNING: com.sun.jersey.api.client.UniformInterfaceException: PUT https://ahrimansb.servicebus.windows.net/myqueue returned a response status of 409 Conflict
com.sun.jersey.api.client.UniformInterfaceException: PUT https://ahrimansb.servicebus.windows.net/myqueue returned a response status of 409 Conflict
    at com.sun.jersey.api.client.WebResource.handle(WebResource.java:676)
    at com.sun.jersey.api.client.WebResource.access$200(WebResource.java:74)
    at com.sun.jersey.api.client.WebResource$Builder.put(WebResource.java:533)
    at com.microsoft.windowsazure.services.serviceBus.implementation.ServiceBusRestProxy.createQueue(ServiceBusRestProxy.java:265)
    at com.microsoft.windowsazure.services.serviceBus.implementation.ServiceBusExceptionProcessor.createQueue(ServiceBusExceptionProcessor.java:188)
    at TestServiceBusRelay.createQueue(TestServiceBusRelay.java:46)
    at TestServiceBusRelay.main(TestServiceBusRelay.java:135)
ServiceException: com.sun.jersey.api.client.UniformInterfaceException: PUT https://testqueue-ns1.servicebus.windows.net/myqueue returned a response status of 409 Conflict
Response Body: <Error><Code>409</Code><Detail>Conflict.TrackingId:59992d2d-e47f-40a0-935e-a16ea910d5f2_2,TimeStamp:4/4/2012 4:57:08 AM</Detail></Error>Сообщение: com.microsoft.windowsazure.services.serviceBus.models.BrokeredMessage@878c4c

В этом случае вы можете также просто удалить очередь с помощью портала управления Windows Azure.

Windows Azure & Java. Что такое Service Bus и как ее использовать из Java-приложения. Часть 2. Топики.

Топики

По принципу работы топики похожи на очереди, однако их главной особенностью является множество конечных точек для обработчиков – они называются подписками (Subscriptions), и такая модель называется publish/subscribe messaging communication – каждый потребитель-обработчик “подписывается” на подписку. Каждая подписка имеет доступ ко всем добавленным сообщениям, при этом механизм фильтрации позволяет им определить, доступно ли сообщение через подписку или нет, и если доступно, то обработчик забирает сообщение из подписки аналогичным очередям образом. Таким образом, можно реализовать распространение одного и того же сообщения на несколько подписок. Механизм подписок использует принцип работы FIFO и представлен на рисунок 51.

clip_image100[4]

Рис. 51. Топики и подписки

Топики и очереди позволяют разрабатывать более слабо связанные приложения, с временной независимостью (когда отправитель и обработчик сообщения не обязаны быть в он-лайн одновременно), балансировкой нагрузки.

Для создания топика необходимо нажать на портале управления Windows Azure New=>AppServices=>ServiceBusTopic=>QuickCreateи ввести название топика (рисунок 52).

clip_image102[4]

Рис. 52. Создание топика Service Bus

Ниже приведен Java-код, готовый к использованию, с комментариями. Хочу обратить ваше внимание на использование новой функциональности (в прошлой части всё было гораздо проще). В данном коде используется дополнительный метод convertStreamToString(InputStream is), который преобразовывает поток в строку. Кроме этого, показано два метода добавления контента в сообщение – как напрямую (в конструкторе), так и с помощью метода setBody().

 

import java.io.BufferedReader;

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.IOException;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.io.Reader;

import java.io.StringWriter;

import java.io.Writer;

import java.math.BigDecimal;

import java.util.ArrayList;

import java.util.Calendar;

import java.util.List;

import com.microsoft.windowsazure.services.serviceBus.*;

import com.microsoft.windowsazure.services.serviceBus.models.*;

import com.microsoft.windowsazure.services.core.*;

import javax.xml.datatype.*;

import javax.xml.datatype.DatatypeConstants.Field;

public class TestServiceBus {

      // Все операции по управлению вашим контейнером Service Bus выполняются с

      // помощью класса ServiceBusContract, при создании которого конструктору

      // передаётся

      // конфигурация вашего контейнера – название контейнера, имя issuer и ключ.

      // Все эти данные можно получить из панели свойств Properties вашего

      // контейнера Service Bus на портале Windows Azure.

      // Далее можно воспользоваться функциональностью класса ServiceBusService,

      // предоставляющего управление очередями – создание, удаление и т.д. В

      // данном методе создаётся контейнер Service Bus с именем ahrimansb.

      private static ServiceBusContract createServiceBus(String issuer, String key) {

            Configuration config = ServiceBusConfiguration

                        .configureWithWrapAuthentication(“testqueue-ns1”, issuer, key,

                                   “.servicebus.windows.net”,

                                   “-sb.accesscontrol.windows.net/WRAPv0.9”);

            ServiceBusContract service = ServiceBusService.create(config);

            return service;

      }

      // В данном методе используется функциональность класса TopicInfo, с помощью

      // которой в данном случае определяется

      // максимальный размер очереди в мегабайтах (указывается максимальный размер

      // в 5Гб). С помощью методов TopicInfo можно настраивать различные параметры

      // ваших очередей, в т.ч. Time To Live для сообщений, максимальный размер и

      // многое другое.

      private static void createTopic(String name, ServiceBusContract service) {

            long queueSize = 5120;

            TopicInfo topicInfo = new TopicInfo(name);

            try {

                  topicInfo.setMaxSizeInMegabytes(queueSize);

                  CreateTopicResult result = service.createTopic(topicInfo);

            } catch (ServiceException e) {

                  System.out.print(“ServiceException: ” + e.getMessage());

            }

      }

      // В данном методе используется фильтр по умолчанию MatchAll (поэтому нет

      // никаких дополнительных указаний значения фильтра).

      // При использовании фильтра по умолчанию все сообщения, которые поступают в

      // топик, помещаются в подписку-очередь.

      private static void createSubscriptionWithFilterMatchAll(

      String subscriptionInfoName, String topicName,

      ServiceBusContract service) {

            try {

                  SubscriptionInfo subInfo = new SubscriptionInfo(

                  subscriptionInfoName);

                  CreateSubscriptionResult result = service.createSubscription(

                  topicName, subInfo);

            } catch (ServiceException e) {

                  System.out.print(“ServiceException: ” + e.getMessage());

            }

      }

      // В данном методе создаётся ещё одна подписка с SQL-фильтром сообщений

      // (SqlFilter). При этом

      // в качестве условия используется сравнение некоторого custom-свойства

      // MessageSequenceId.

      // После создания двух подписок сообщения, поступающие в соответствующий

      // топик, буду уходить –

      // все в первую подписку, и только удовлетворяющие условиям фильтра – во

      // вторую, таким образом распределяясь по обработчикам. Естественно,

      // что гораздо оптимальнее создать несколько подписок с разными фильтрами и

      // распределять сообщения по определенному условию.

      private static void createSubscriptionWithFilter(

      String subscriptionInfoName, String topicName,

      ServiceBusContract service) {

            SubscriptionInfo subInfo = new SubscriptionInfo(subscriptionInfoName);

            try {

                  CreateSubscriptionResult result = service.createSubscription(

                  topicName, subInfo);

            } catch (ServiceException e) {

                  e.printStackTrace();

            }

            RuleInfo ruleInfo = new RuleInfo();

            ruleInfo = ruleInfo.withSqlExpressionFilter(“MessageNumber > 4”);

            try {

                  CreateRuleResult ruleResult = service.createRule(topicName,

                  “subscriptioninfoname2”, ruleInfo);

            } catch (ServiceException e) {

                  e.printStackTrace();

            }

      }

      // Для отображения сообщения в объектной модели существует класс

      // BrokeredMessage, объекты которого содержат набор методов для управления

      // сообщением,

      // набор параметров и набор данных. В набор данных можно передать любой

      // сериализуемый объект. Очереди Service Bus имеют ограничение на размер

      // сообщения в 256 мегабайт(заголовок, содержащий свойства – 64 мегабайт),

      // однако ограничений как таковых на количество хранимых в очереди сообщений

      // нет, кроме задаваемого вами ограничения-максимального размера очереди. В

      // данном случае мы также указываем custom-свойство MessageSequenceId,

      // которое будет использоваться для фильтра

      private static void putMessageToTopic(String topicName,

      ServiceBusContract service, BrokeredMessage message) {

            try {

                  message.setProperty(“MessageNumber”, “6”);

                  service.sendTopicMessage(topicName, message);

            } catch (ServiceException e) {

                  System.out.print(“ServiceException: ” + e.getMessage());

            }

      }

      // Метод для добавления множества сообщений в топик.

      private static void putMessagesToTopic(String topicName,

      ServiceBusContract service, List<BrokeredMessage> messages) {

            try {

                  for (BrokeredMessage message : messages) {

                        service.sendTopicMessage(topicName, message);

                  }

            } catch (ServiceException e) {

                  e.printStackTrace();

            }

      }

      // Аналогично сервису очередей хранилища Windows Azure, вы можете

      // использовать два метода для извлечения сообщений из очередей топиков –

      // получение и удаление (ReceiveAndDelete) и “подсматривание” – получение

      // сообщение, но не удаление его из очереди топика (PeekLock). При

      // использовании

      // метода ReceiveAndDelete при получении запроса на извлечение

      // сообщения, очередь помечает это сообщение как “потреблённое”.

      // При использовании метода PeekLock процесс получения дробится на два этапа

      // – когда Service Bus получает запрос на извлечение сообщения, он находит

      // это сообщение, помечает его как locked (в этот момент другие обработчики

      // перестают видеть это сообщение) и возвращает его приложению. После

      // окончания

      // обработки приложением сообщения закрывается второй этап процесса с

      // помощью вызова метода Delete полученного сообщения. После этого сообщение

      // помечается как удаленное.

      // Типичным паттерном опроса очереди на наличие новых сообщений является

      // использование бесконечного цикла while. В данном методе очередь

      // опрашивается постоянно. Если вы хотите

      // ограничить выполнение каким-либо количеством полученных сообщений, вам

      // необходимо реализовать логику с использованием break.

      private static void getMessageFromTopic(String topicName,

      String subscriptionName, ServiceBusContract service)

      throws ServiceException {

            ReceiveMessageOptions opts = ReceiveMessageOptions.DEFAULT;

            opts.setReceiveMode(ReceiveMode.PEEK_LOCK);

            while (true) {

                  ReceiveSubscriptionMessageResult result = service

                  .receiveSubscriptionMessage(topicName, subscriptionName,

                  opts);

                  BrokeredMessage message = result.getValue();

                  if (message != null && message.getMessageId() != null) {

                        try {

                              System.out

                                         .println(“Начало работы по опросу очереди подписки с именем:”

                                                     + subscriptionName);

                              System.out.println(“Сообщение: “

                              + convertStreamToString(message.getBody()));

                              System.out.println(“ID сообщения: “

                              + message.getMessageId());

                              System.out

                                         .println(“Если вы задали какое-то свойство, его можно получить с помощью метода getProperty(): “

                                                     + message.getProperty(“MessageNumber”));

                              System.out.println(“Сообщение прочитано – удалено.”);

                              service.deleteMessage(message);

                        } catch (Exception ex) {

                              // если было выброшено исключение, сообщение будет

                              // разблокировано для других обработчиков

                              System.out.println(“Исключение!”);

                              service.unlockMessage(message);

                        }

                  } else {

                        System.out

                                   .println(“Больше нет сообщений, но топик продолжает опрашиваться.”);

                  }

            }

      }

      private static void deleteTopic(String topicName, ServiceBusContract service) {

            try {

                  service.deleteTopic(topicName);

            } catch (ServiceException e) {

                  e.printStackTrace();

            }

      }

      private static void deleteSubscription(String subscriptionName,

      String subscriptionInfoName, ServiceBusContract service) {

            try {

                  service.deleteSubscription(subscriptionName, subscriptionInfoName);

            } catch (ServiceException e) {

                  e.printStackTrace();

            }

      }

      public static void main(String args[]) throws FileNotFoundException {

            ServiceBusContract service = createServiceBus(“owner”,

            “”);

            createTopic(“mytopic”, service);

            createSubscriptionWithFilterMatchAll(“subscriptioninfoname1”,

            “mytopic”, service);

            createSubscriptionWithFilter(“subscriptioninfoname2”, “mytopic”,

            service);

            InputStream input = new FileInputStream(“c:\\temp\\1.txt”);

            BrokeredMessage message = new BrokeredMessage(“Our message.”);

            message.setBody(input);

            ArrayList<BrokeredMessage> messages = new ArrayList<BrokeredMessage>();

            for (int i = 0; i < 5; i++) {

                  BrokeredMessage msg = new BrokeredMessage(“Message text: ” + i);

                  msg.setProperty(“MessageNumber”, i);

                  messages.add(msg);

            }

            putMessageToTopic(“mytopic”, service, message);

            putMessagesToTopic(“mytopic”, service, messages);

            try {

                  getMessageFromTopic(“mytopic”, “subscriptioninfoname1”, service);

                  getMessageFromTopic(“mytopic”, “mysubscriptionwithfilter”, service);

            } catch (ServiceException e) {

                  e.printStackTrace();

            }

      }

      public static String convertStreamToString(InputStream is)

      throws IOException {

            if (is != null) {

                  Writer writer = new StringWriter();

                  char[] buffer = new char[1024];

                  try {

                        Reader reader = new BufferedReader(new InputStreamReader(is,

                        “UTF-8”));

                        int n;

                        while ((n = reader.read(buffer)) != -1) {

                              writer.write(buffer, 0, n);

                        }

                  } finally {

                        is.close();

                  }

                  return writer.toString();

            } else {

                  return “”;

            }

      }

}

Далее просто запустите ваше приложение. Для этого нажмите ALT+Shift+X либо нажмите соответствующую кнопку в меню в Eclipse(рисунок 53).

clip_image104[4]

Рис.53. Запуск простого Java-проекта.

В консоли вы должны увидеть результат.

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

04.04.2012 11:57:06 com.microsoft.windowsazure.services.serviceBus.implementation.ServiceBusExceptionProcessor processCatch
WARNING: com.sun.jersey.api.client.UniformInterfaceException: PUT https://ahrimansb.servicebus.windows.net/myqueue returned a response status of 409 Conflict
com.sun.jersey.api.client.UniformInterfaceException: PUT https://ahrimansb.servicebus.windows.net/myqueue returned a response status of 409 Conflict
    at com.sun.jersey.api.client.WebResource.handle(WebResource.java:676)
    at com.sun.jersey.api.client.WebResource.access$200(WebResource.java:74)
    at com.sun.jersey.api.client.WebResource$Builder.put(WebResource.java:533)
    at com.microsoft.windowsazure.services.serviceBus.implementation.ServiceBusRestProxy.createQueue(ServiceBusRestProxy.java:265)
    at com.microsoft.windowsazure.services.serviceBus.implementation.ServiceBusExceptionProcessor.createQueue(ServiceBusExceptionProcessor.java:188)
    at TestServiceBusRelay.createQueue(TestServiceBusRelay.java:46)
    at TestServiceBusRelay.main(TestServiceBusRelay.java:135)
ServiceException: com.sun.jersey.api.client.UniformInterfaceException: PUT https://ahrimansb.servicebus.windows.net/myqueue returned a response status of 409 Conflict
Response Body: <Error><Code>409</Code><Detail>Conflict.TrackingId:59992d2d-e47f-40a0-935e-a16ea910d5f2_2,TimeStamp:4/4/2012 4:57:08 AM</Detail></Error>Сообщение: com.microsoft.windowsazure.services.serviceBus.models.BrokeredMessage@878c4c

В этом случае вы можете также просто удалить топик или подписку с помощью портала управления Windows Azure.

Разработка на Java для Windows Azure–что нужно учитывать

Итак, что нужно учитывать при разработке на Java для Windows Azure? Есть плагин для Eclipse (что уже очень большой прогресс), есть SDK, есть готовые наработки. Однако просто так взять и разработать приложение, может быть, и не получится – необходимо учитывать ряд особенностей платформы, которые могут быть и неочевидны.

Microsoft рассматривает язык программирования Java как first-class citizen на своей платформе Windows Azure. В целом я согласен с этим заявлением, потому что уже существующие предложения для размещения Java-приложений в облаке Microsoft позволяют разместить любое приложение. Попробуем рассмотреть “за” и “против” размещения Java-приложения в Windows Azure.

Почему вообще необходимо облако для нужд Java-приложения? Уже разработанные приложения могут быть достаточно сложными и иметь распределенную архитектуру, которая использует множество серверов, соответственно, то, что предлагает облачная платформа, подходит для этих нужд и, возможно, сулит экономические выгоды.

Web-приложения на Java и Windows Azure

Обычно веб-приложения на Java пишутся с использованием сервлетов, JSP, JSF и так далее. На мощностях Tomcat, JBoss, Jetty, GlassFish. В плагине Windows Azure для Eclipse есть конфигурационные файлы, подготовленные отдельно для каждогоиз этих серверов приложений и контейнеров. Кроме этого, у Microsoft уже заготовлены полезные готовые проекты с мануалами, так называемые Solution Accelerator – Windows Azure Tomcat Solution Accelerator и Windows Azure Jetty Solution Accelerator, например.

Распространенный вопрос про сохранение сессий и липкие сессии актуален и с Java—приложениями. Плагин позволяет делать липкие сессии. Вторым подходом является использование Atomus TomcatAzureSessionManager (сохраняющего сессию в хранилище таблиц Windows Azure)  и Windows Azure Sticky HTTP Session Router. Разработчику позволяется использовать модуль IIS Application Request Routing.

Что с хранилищем в Java-приложении?

Где хранить данные, если у нас есть Java-приложение? Есть несколько вариантов – это SQL Azure Databases, реляционная СУБД на основе SQL Server 2008 R2 и наследующая большинство функциональности (и, всё же, не всей). JDBC Driver от Microsoft официально поддерживает SQL Azure Databases. В целом, работа с JDBC Driver, SQL Azure Databases и Java-приложением достаточно приятна и несложна. Если же есть уже готовая база данных и ее требуется перенести в облако, у Microsoft есть для этого средство – SQL Azure Migration Wizard. Позволяет переносить готовую базу данных в облако, будь она Oracle, MySQL или Access.

Вторым вариантом размещения данных являются сервисы хранилища Windows Azure. Windows Azure SDK for Java полностью поддерживает взаимодействие хранилища Windows Azure и Java, поэтому и тут не возникнет никаких проблем. Напоминание: сервисы хранилищ состоят из трех основных сервисов – блобы (дешевое и масштабируемое хранилище больших документов, которое может использовать Windows Azure Content Delivery Network для доставки контента ближе к пользователям), таблицы (масштабируемое и дешевое хранение нереляционных данных, аналог реляционных баз данных, но не позволяет хранить сколь-либо связные данные) и очереди (хранилище сообщений для осуществления коммуникации между ролями).

Таким образом, если вы не используете дополнительную функциональность платформы, вы можете легко разместить ваше приложение и управлять его масштабируемостью.

К недостаткам необходимо отнести:

Максимальный размер базы данных в SQL Azure Databases – 150 гигабайт. Для определенной части приложений такого объема будет достаточно, но, если его станет мало, придется придумывать решение по масштабированию. В SQL Azure уже есть федерации, можно реализовывать шардинг – в целом, недостатком это является только потому, что придется делать это самому, а не дать на откуп платформе.

Отсутствие поддержки для Java сервиса кэширования.

Количество внешних портов-конечных точек входа для каждого экземпляра. Сейчас равно 25, раньше было всего 5 и это было действительно плохо, учитывая использование некоторыми функциональностями Java портов, этого может и не хватить (JMX и др.). Например, для Glassfish необходимо по умолчанию открыть 4 порта сразу же.

Java != Microsoft.Несмотря на то, что Java – first-class citizen на платформе, внятной поддержки от вендоров из мира Java можно и не дождаться – всё это только входит в моду.

Отсутствие шаблонов для Java Runtime и серверов приложений.В принципе, можно объединить с предыдущим пунктом. На платформе есть стандартные образа виртуальных машин, которые построены на основе Windows Server 2008 или Windows Server 2008 R2 различных билдов. Естественно, по умолчанию там нет Java Runtime и тем более каких-то серверов приложений. Есть некоторая возможность, и ходят слухи по Интернету, что может и появиться подобный шаблон, но это всего лишь слухи. В целом же развертывание приложения с Tomcat не занимает особо много времени и не вызывает трудностей. С Glassfish и JBoss немного сложнее, но опять же ненамного. Этот недостаток опять же очень сложно назвать недостатком – невозможно “объять необъятное”, невозможно иметь поддержку всех версий Tomcat и Glassfish, и при этом оставаться простой в освоении платформой. Кроме этого, ролевая модель Windows Azure и Startup-задачи позволяют выполнять любые настройки и установки при запуске экземпляра, в том числе производить любые настройки ваших серверов приложений. Microsoft не ставит никаких ограничений в этом контексте – Java-разработчику позволено выбирать, устанавливать и настраивать любые версии Java Runtime и серверов приложений. А с плагином Windows Azure для Eclipse всё вообще становится задачей внесения изменений в текстовый файл конфигурации.

Как видите, недостатков не так много. Надо обязательно упомянуть, что Microsoft активно работает над вопросами interoperability и Windows Azure, вероятно, является наиболее открытой ко всему миру IT платформой.

Выводы

Как уже было сказано выше, недостатков при разработке на Java на платформе Windows Azure не так много, как может показаться на первый взгляд. Да, необходимо пересматривать архитектуру проекта, предусматривать стоимость, масштабируемость, недостатки, преимущества, специфичные для Windows Azure настройки (брандмауэры SQL Azure Databases, балансировщики нагрузки и так далее). Необходимо также четко понимать, нужно ли вообще переносить в облако свое приложение – но это уже универсальная задача, не специфичная для Java-приложений, так как, несмотря на преимущества и экономическую эффективность, для некоторых ситуаций облако может и не подойти. В целом же платформа Microsoft – ещё одна возможность для выполнения ваших приложений. А, учитывая явный интерес корпорации к interoperability с Java, Python… да, на самом деле, со всем, что сейчас реально популярно и не-.NET, эта возможность должна быть рассмотрена всерьёз.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: