Skip to content
March 30, 2012 / ahriman hpc mode

Windows Azure & Java. Основы хранилища. Очереди. Доступ из локального приложения.

О том, что такое очереди, как с ними работать и какими концепциями оперирует данный сервис хранилищ Windows Azure, можно посмотреть здесь – http://www.techdays.ru/videos/4211.html и почитать здесь – http://social.technet.microsoft.com/wiki/contents/articles/8137.windows-azure-ru-ru.aspx .

clip_image001

Рис.1. Основные концепции очереди

В качестве повторения определим основные понятия, которые используются при работе с очередями:

URL очереди: каждая очередь, будучи отдельной сущностью, имеет свой собственный URL (конечную точку входа), с помощью которой можно получить к ней доступ. Формат URL выглядит стандартно для сервисов хранилищ Windows Azure:
http://<storage account>.queue.core.windows.net/<queue>

Account: Аккаунт хранилища Windows Azure необходим для управления вашими данными – все очереди, блобы и прочие сервисы хранилища ассоциируются с вашим аккаунтом с помощью аккаунта хранилища. Максимальный суммарный объем данных, который вы можете хранить в аккаунте хранилища – 100Тб.

Queue: Очередь (queue) необходима для хранения сообщений. 

Message: Сообщение(message) – сущность в любом формате до 64Кб.

Для того, чтобы продолжить разработку, создадим новый Java-проект и внесем в него некоторые изменения в Java-проект:

1. Добавьте новый класс TestJavaStorage в Java-проект. Для этого необходимо щелкнуть правой кнопкой мыши на проекте и нажать New, после чего нажать Class (рис.2).

image

Рис.2. Добавление Java-класса.

2. Добавьте директивы импорта в созданный класс.
import com.microsoft.windowsazure.services.core.storage.*;
import com.microsoft.windowsazure.services.queue.client.*;
3. Настройте строку подключения к хранилищу Windows Azure. Строка подключения к хранилищу следует стандартным конвенциям и синтаксису. Замените значения AccountName и AccountKey на соответствующие значения, взятые с портала управления Windows Azure.
public static final String ConnectionString =
    "DefaultEndpointsProtocol=http;AccountName=[accountName]; AccountKey=[accountKey]";
Либо, если вы хотите использовать правильный подход к архитектуре приложения, добавьте следующий код, получающий строку подключения из файла конфигурации сервиса ServiceConfiguration.cscfg.
String ConnectionString =
    RoleEnvironment.getConfigurationSettings().get("StorageConnectionString");
В этом случае добавьте также одним из двумя способов – вручную либо с помощью XML Editor в Eclipse – соответствующий элемент в файл ServiceConfiguration.cscfg. Ваш файл должен иметь в секции ConfigurationSettings следующий код.

<ConfigurationSettings>

<Setting name="Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString" value="…" />

</ConfigurationSettings>

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

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

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URISyntaxException;
import java.security.InvalidKeyException;
import java.util.EnumSet;

import com.microsoft.windowsazure.services.core.storage.*;
import com.microsoft.windowsazure.services.queue.client.CloudQueue;
import com.microsoft.windowsazure.services.queue.client.CloudQueueClient;
import com.microsoft.windowsazure.services.queue.client.CloudQueueMessage;
import com.microsoft.windowsazure.services.queue.client.MessageUpdateFields;
import com.microsoft.windowsazure.services.blob.client.*;

public class TestJavaStorage {
    public static final String StorageConnectionString = "UseDevelopmentStorage=true";

    public static CloudQueueClient getQueueClient() throws InvalidKeyException,
            URISyntaxException {
        CloudStorageAccount storageAccount = CloudStorageAccount
                .parse(StorageConnectionString);
        return storageAccount.createCloudQueueClient();
    }

    public static CloudQueue createQueue(CloudQueueClient queueClient,
            String name) throws InvalidKeyException, URISyntaxException,
            StorageException {
        CloudQueue queue = queueClient.getQueueReference(name);
        queue.createIfNotExist();
        return queue;
    }

    public static void addMessageToQueue(CloudQueue queue,
            CloudQueueMessage message) throws InvalidKeyException,
            URISyntaxException, StorageException {

        queue.addMessage(message);
    }

    public static CloudQueueMessage peekMessage(CloudQueue queue)
            throws InvalidKeyException, URISyntaxException, StorageException {
        CloudQueueMessage message = queue.peekMessage();
        return message;
    }

    public static void deleteMessage(CloudQueue queue, CloudQueueMessage message) {

        try {
            queue.deleteMessage(message);
            System.out.println("Сообщение с ID "+message.getId() + " удалено");
        } catch (StorageException e) {
            e.printStackTrace();
        }

    }

    public static void updateMessage(CloudQueue queue,
            CloudQueueMessage message, String newContent) {

        message.setMessageContent(newContent);
        EnumSet<MessageUpdateFields> updateFields = EnumSet.of(
                MessageUpdateFields.CONTENT, MessageUpdateFields.VISIBILITY);
        try {
            queue.updateMessage(message, 60, updateFields, null, null);
        } catch (StorageException e) {
            e.printStackTrace();
        }
    }

    public static void deleteMessages(CloudQueue queue) throws StorageException {

        //Сообщения из очереди забираются в количестве 20 штук, в это время таймаут на невидимость для других обработчиков
        //устанавливается в 300 секунд.
        for (CloudQueueMessage message : queue.retrieveMessages(20, 300, null,
                null)) {
        
            try {
                System.out.println("Сообщение с ID" + message.getId()
                        + " и содержимым " + message.getMessageContentAsString()
                        + " подготовлено к удалению");
                queue.deleteMessage(message);
            } catch (StorageException e) {
                e.printStackTrace();
            }
        }

    }

    public static void getQueueAttributes(CloudQueue queue) {

        try {
            queue.downloadAttributes();
        } catch (StorageException e) {
            e.printStackTrace();
        }

        long cachedMessageCount = queue.getApproximateMessageCount();
        System.out
                .println("Примерное количество сообщений в очереди под названием "
                        + queue.getName()
                        + " с URI "
                        + queue.getUri()
                        + ":"
                        + cachedMessageCount);

    }

    public static void deleteQueue(CloudQueue queue) {

        try {
            queue.delete();
        } catch (StorageException e) {

            e.printStackTrace();
        }

    }

    public static void main(String args[]) throws InvalidKeyException,
            URISyntaxException, StorageException {
        // Создание клиента очереди
        CloudQueueClient client = getQueueClient();

        // Создание очереди с именем myqueue
        CloudQueue queue = createQueue(client, "myqueue");
        // Создание нового сообщения для очереди.
        CloudQueueMessage message = new CloudQueueMessage(
                "This is the test message sent to the queue");
        addMessageToQueue(queue, message);

        // Получение первого сообщения из начала очереди, обновление его
        // содержимого. Можно получать несколько сообщений (до 32)

        CloudQueueMessage retrievedMessage = queue.retrieveMessage();
        updateMessage(queue, retrievedMessage, "Содержимое обновлено на русский язык.");

        getQueueAttributes(queue);

        // Удаление сообщений происходит в два этапа - при вызове
        // retrieveMessage получается первое сообщение в очереди.
        // После этого оно становится "невидимым" для обработчиков на 30 секунд
        // (по умолчанию, можно менять).
        // После этого необходимо вызвать для полного удаление сообщения метод
        // deleteMessage.
        deleteMessage(queue,retrievedMessage);
        //вторая версия метода удаления - удаление нескольких сообщений.
        deleteMessages(queue);
        // удаление очереди
        deleteQueue(queue);

    }
}

Обратите внимание на то, что в коде много раз выбрасывается набор исключений FileNotFoundException(может произойти с конструкторами FileInput(Output)Stream), StorageException (типичное исключение для клиентской библиотеки Windows Azure), URISyntaxException (выбрасывается методом getUri()).

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

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

image

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

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

 image

Рис.4. Ожидаемый результат выполнения программы.

Для того, чтобы просмотреть содержимое хранилища, вы можете воспользоваться либо Server Explorer в Visual Studio 2010 либо специальной утилитой Windows Azure Storage Explorer (рис. 4).

 image

Рис. 4. Использование Windows Azure Storage Explorer.

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

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: