Статьи

Как выполнить интенсивные вычисления в Java на виртуальной машине

Примечание куратора. Это руководство изначально появилось в Центре разработчиков Windows Azure Java .

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

В этом руководстве предполагается, что вы знаете, как создавать консольные приложения Java, импортировать библиотеки в приложение Java и создавать архив Java (JAR). Никаких знаний о Windows Azure не предполагается.

Ты выучишь:

  • Как создать виртуальную машину.
  • Как удаленно войти на вашу виртуальную машину.
  • Как установить JRE или JDK на вашу виртуальную машину.
  • Как создать пространство имен служебной шины.
  • Как создать приложение Java, которое выполняет задачу, требующую больших вычислительных ресурсов.
  • Как создать приложение Java, которое отслеживает ход выполнения задачи, требующей больших вычислительных ресурсов.
  • Как запустить приложения Java.
  • Как остановить приложения Java.

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

Коммивояжер

Ниже приведен пример приложения Java, контролирующего задачу, требующую большого объема вычислений:

Коммивояжер проблемный клиент

Запись

Чтобы выполнить это руководство, вам нужна учетная запись Windows Azure с включенной функцией виртуальных машин Windows Azure. Вы можете создать бесплатную пробную учетную запись и включить функции предварительного просмотра всего за пару минут. Подробности см. В разделе « Создание учетной записи Windows Azure и включение функций предварительного просмотра» .

Чтобы создать виртуальную машину

  1. Войдите на портал управления Windows Azure Preview .
  2. Нажмите Новый .
  3. Нажмите Виртуальная машина .
  4. Нажмите Быстрое создание .
  5. На экране Создать виртуальную машину введите значение для имени DNS .
  6. В раскрывающемся списке « Изображение» выберите изображение, например Windows Server 2008 R2 SP1 .
  7. Введите пароль в поле « Новый пароль» и повторно введите его в поле « Подтверждение» . Запомните этот пароль, вы будете использовать его при удаленном входе на виртуальную машину.
  8. В раскрывающемся списке « Местоположение» выберите местоположение центра обработки данных для вашей виртуальной машины; например, запад США .
  9. Нажмите Создать виртуальную машину . Ваша виртуальная машина будет создана. Вы можете отслеживать состояние в разделе Виртуальные машины портала управления.

Для удаленного входа на вашу виртуальную машину

  1. Войдите на портал управления предварительным просмотром .
  2. Нажмите Виртуальные машины .
  3. Нажмите на имя виртуальной машины, в которую вы хотите войти.
  4. Нажмите Подключиться .
  5. Отвечайте на запросы по мере необходимости для подключения к виртуальной машине. При запросе пароля используйте пароль, который вы указали при создании виртуальной машины.

Чтобы установить JRE или JDK на вашу виртуальную машину

Для запуска приложений Java на вашей виртуальной машине вам необходимо установить и установить среду выполнения Java (JRE). В целях данного руководства мы установим Java Developer Kit (JDK) на вашу виртуальную машину и будем использовать JRE JDK. Однако вы можете установить только JRE, если захотите.

Для целей данного руководства JDK будет установлен с сайта Oracle.

  1. Войдите в свою виртуальную машину.
  2. В вашем браузере откройте http://www.oracle.com/technetwork/java/javase/downloads/index.html .
  3. Нажмите кнопку « Загрузить» для JDK, который вы хотите загрузить. Для целей данного руководства использовалась кнопка « Загрузить» для Java SE 6 Update 32 JDK.
  4. Примите лицензионное соглашение.
  5. Нажмите на загружаемый исполняемый файл для Windows x64 (64-разрядная версия) .
  6. Следуйте инструкциям и отвечайте по мере необходимости, чтобы установить JDK на виртуальную машину.

Обратите внимание , что функциональность Service Bus требует сертификат GTE CyberTrust Global Root должен быть установлен как часть вашего JRE в cacerts магазине. Этот сертификат автоматически включается в JRE, используемую в этом руководстве. Если у вас нет этого сертификата в вашем хранилище JRE cacerts , его можно установить, скопировав содержимое сертификата с https://secure.omniroot.com/cacert/ct_root.der , сохранив содержимое в файл .cer и добавив это к cacerts хранить через Keytool . Более подробные инструкции по добавлению сертификата в cacerts магазине, см Добавление сертификата в хранилище сертификатов CA Java .

Как создать пространство имен служебной шины

Чтобы начать использовать очереди служебной шины в Windows Azure, сначала необходимо создать пространство имен службы. Пространство имен службы предоставляет контейнер области действия для адресации ресурсов служебной шины в вашем приложении.

Чтобы создать пространство имен службы:

  1. Войдите на портал управления Windows Azure (обратите внимание, что это не тот портал, что портал управления Windows Azure Preview).
  2. В нижней левой навигационной панели портала управления щелкните Сервисная шина, Контроль доступа и Кэширование .
  3. В верхней левой панели портала управления выберите узел служебной шины и нажмите кнопку « Создать» .
    Скриншот узла служебной шины
  4. В диалоговом окне « Создание нового пространства имен службы » введите пространство имен , а затем, чтобы убедиться в его уникальности, нажмите кнопку « Проверить наличие» .
    Создать новый скриншот пространства имен
  5. Убедившись, что имя пространства имен доступно, выберите страну или регион, в котором должно быть размещено ваше пространство имен, а затем нажмите кнопку « Создать пространство имен» .

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

Получите учетные данные управления по умолчанию для пространства имен

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

  1. На левой навигационной панели щелкните узел Service Bus , чтобы отобразить список доступных пространств имен:
    Скриншот доступных пространств имен
  2. Выберите пространство имен, которое вы только что создали, из показанного списка:
    Скриншот списка пространств имен
  3. В правой панели свойств будут перечислены свойства нового пространства имен:
    Скриншот панели свойств
  4. Ключ по умолчанию скрыт. Нажмите кнопку « Просмотр» , чтобы отобразить учетные данные безопасности:
    Скриншот ключа по умолчанию
  5. Запишите имя эмитента по умолчанию и ключа по умолчанию, так как вы будете использовать эту информацию ниже для выполнения операций с пространством имен.

Как создать приложение Java, которое выполняет задачу, требующую больших вычислительных ресурсов

  1. На компьютере разработчика (который не обязательно должен быть виртуальной машиной, которую вы создали) загрузите Windows Azure SDK для Java .
  2. Создайте консольное приложение Java, используя пример кода в конце этого раздела. В целях данного руководства мы будем использовать TSPSolver.java в качестве имени файла Java. Измените заполнители your_service_bus_namespace , your_service_bus_owner и your_service_bus_key, чтобы использовать пространство имен служебной шины , значения по умолчанию для эмитента и ключа по умолчанию , соответственно.
  3. После кодирования экспортируйте приложение в работающий архив Java (JAR) и упакуйте необходимые библиотеки в сгенерированный JAR. Для целей данного руководства мы будем использовать TSPSolver.jar в качестве сгенерированного имени JAR.
// TSPSolver.java

import com.microsoft.windowsazure.services.core.Configuration;
import com.microsoft.windowsazure.services.core.ServiceException;
import com.microsoft.windowsazure.services.serviceBus.*;
import com.microsoft.windowsazure.services.serviceBus.models.*;
import java.io.*;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class TSPSolver {

    //  Value specifying how often to provide an update to the console.
    private static long loopCheck = 100000000;  

    private static long nTimes = 0, nLoops=0;

    private static double[][] distances;
    private static String[] cityNames;
    private static int[] bestOrder;
    private static double minDistance;
    private static ServiceBusContract service;

    private static void buildDistances(String fileLocation, int numCities) throws Exception{
        try{
            BufferedReader file = new BufferedReader(new InputStreamReader(new DataInputStream(new FileInputStream(new File(fileLocation)))));
            double[][] cityLocs = new double[numCities][2];
            for (int i = 0; i<numCities; i++){
                String[] line = file.readLine().split(", ");
                cityNames[i] = line[0];
                cityLocs[i][0] = Double.parseDouble(line[1]);
                cityLocs[i][1] = Double.parseDouble(line[2]);               
            }
            for (int i = 0; i<numCities; i++){
                for (int j = i; j<numCities; j++){
                    distances[i][j] = Math.hypot(Math.abs(cityLocs[i][0] - cityLocs[j][0]), Math.abs(cityLocs[i][1] - cityLocs[j][1]));
                    distances[j][i] = distances[i][j];
                }
            }
        } catch (Exception e){
            throw e;
        }
    }

    private static void permutation(List<Integer> startCities, double distSoFar, List<Integer> restCities) throws Exception {

        try
        {
            nTimes++;
            if (nTimes == loopCheck)
            {
                nLoops++;
                nTimes = 0;
                DateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
                Date date = new Date();
                System.out.print("Current time is " + dateFormat.format(date) + ". ");
                System.out.println(  "Completed " + nLoops + " iterations of size of " + loopCheck + ".");
            }

            if ((restCities.size() == 1) && ((minDistance == -1) || (distSoFar + distances[restCities.get(0)][startCities.get(0)] + distances[restCities.get(0)][startCities.get(startCities.size()-1)] < minDistance))){
                startCities.add(restCities.get(0));
                newBestDistance(startCities, distSoFar + distances[restCities.get(0)][startCities.get(0)] + distances[restCities.get(0)][startCities.get(startCities.size()-2)]);
                startCities.remove(startCities.size()-1);
            }
            else{
                for (int i=0; i<restCities.size(); i++){
                    startCities.add(restCities.get(0));
                    restCities.remove(0);
                    permutation(startCities, distSoFar + distances[startCities.get(startCities.size()-1)][startCities.get(startCities.size()-2)],restCities);
                    restCities.add(startCities.get(startCities.size()-1));
                    startCities.remove(startCities.size()-1);
                }
            }
        }
        catch (Exception e)
        {
            throw e;
        }
    }

    private static void newBestDistance(List<Integer> cities, double distance) throws ServiceException, Exception {
        try 
        {
            minDistance = distance;
            String cityList = "Shortest distance is "+minDistance+", with route: ";
            for (int i = 0; i<bestOrder.length; i++){
                bestOrder[i] = cities.get(i);
                cityList += cityNames[bestOrder[i]];
                if (i != bestOrder.length -1)
                    cityList += ", ";
            }
            System.out.println(cityList);
            service.sendQueueMessage("TSPQueue", new BrokeredMessage(cityList));
        } 
        catch (ServiceException se) 
        {
            throw se;
        }
        catch (Exception e) 
        {
            throw e;
        }
    }

    public static void main(String args[]){

        try {

            Configuration config = ServiceBusConfiguration.configureWithWrapAuthentication(
                    "your_service_bus_namespace", "your_service_bus_owner", "your_service_bus_key");

            service = ServiceBusService.create(config);

            int numCities = 10;  // Use as the default, if no value is specified at command line. 
            if (args.length != 0) 
            {
                if (args[0].toLowerCase().compareTo("createqueue")==0)
                {
                    // No processing to occur other than creating the queue.
                    QueueInfo queueInfo = new QueueInfo("TSPQueue");

                    service.createQueue(queueInfo);

                    System.out.println("Queue named TSPQueue was created.");

                    System.exit(0);
                }

                if (args[0].toLowerCase().compareTo("deletequeue")==0)
                {
                    // No processing to occur other than deleting the queue.
                    service.deleteQueue("TSPQueue");

                    System.out.println("Queue named TSPQueue was deleted.");

                    System.exit(0);
                }

                // Neither creating or deleting a queue.
                // Assume the value passed in is the number of cities to solve.
                numCities = Integer.valueOf(args[0]);  
            }

            System.out.println("Running for " + numCities + " cities.");

            List<Integer> startCities = new ArrayList<Integer>();
            List<Integer> restCities = new ArrayList<Integer>();
            startCities.add(0);
            for(int i = 1; i<numCities; i++)
                restCities.add(i);
            distances = new double[numCities][numCities];
            cityNames = new String[numCities];
            buildDistances("c:\\TSP\\cities.txt", numCities);
            minDistance = -1;
            bestOrder = new int[numCities];
            permutation(startCities, 0, restCities);
            System.out.println("Final solution found!");
            service.sendQueueMessage("TSPQueue", new BrokeredMessage("Complete"));
        } 
        catch (ServiceException se) 
        {
            System.out.println(se.getMessage());
            se.printStackTrace();
            System.exit(-1);
        }        
        catch (Exception e) 
        {
            System.out.println(e.getMessage());
            e.printStackTrace();
            System.exit(-1);
        }
    }

}

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

  1. На компьютере разработчика создайте консольное приложение Java, используя пример кода в конце этого раздела. В целях данного руководства мы будем использовать TSPClient.java в качестве имени файла Java. Как и выше, измените пространство имен your_service_bus_name , your_service_bus_owner и your_service_bus_key, чтобы использовать пространство имен служебной шины , значения по умолчанию для эмитента и ключа по умолчанию , соответственно.
  2. Экспортируйте приложение в работающий JAR и упакуйте необходимые библиотеки в сгенерированный JAR. В целях данного руководства мы будем использовать TSPClient.jar в качестве сгенерированного имени JAR.
// TSPClient.java

import java.util.Date;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import com.microsoft.windowsazure.services.serviceBus.*;
import com.microsoft.windowsazure.services.serviceBus.models.*;
import com.microsoft.windowsazure.services.core.*;

public class TSPClient 
{

    public static void main(String[] args) 
    {
            try
            {

                DateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
                Date date = new Date();
                System.out.println("Starting at " + dateFormat.format(date) + ".");

                String namespace = "your_service_bus_namespace";
                String issuer = "your_service_bus_owner";
                String key = "your_service_bus_key";

                Configuration config;
                config = ServiceBusConfiguration.configureWithWrapAuthentication(
                        namespace, issuer, key);

                ServiceBusContract service = ServiceBusService.create(config);

                BrokeredMessage message;

                int waitMinutes = 3;  // Use as the default, if no value is specified at command line. 
                if (args.length != 0) 
                {
                    waitMinutes = Integer.valueOf(args[0]);  
                }

                String waitString;

                waitString = (waitMinutes == 1) ? "minute." : waitMinutes + " minutes."; 

                // This queue must have previously been created.
                service.getQueue("TSPQueue");

                int numRead;

                String s = null;

                while (true)
                {

                    ReceiveQueueMessageResult resultQM = service.receiveQueueMessage("TSPQueue");
                    message = resultQM.getValue();

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

                        // Display the queue message.
                        byte[] b = new byte[200];

                        System.out.print("From queue: ");

                        s = null;
                        numRead = message.getBody().read(b);
                        while (-1 != numRead)
                        {
                            s = new String(b);
                            s = s.trim();
                            System.out.print(s);
                            numRead = message.getBody().read(b);
                        }
                        System.out.println();
                        if (s.compareTo("Complete") == 0)
                        {
                            // No more processing to occur.
                            date = new Date();
                            System.out.println("Finished at " + dateFormat.format(date) + ".");
                            break;
                        }
                    }
                    else
                    {
                        // The queue is empty.
                        System.out.println("Queue is empty. Sleeping for another " + waitString);
                        Thread.sleep(60000 * waitMinutes);
                    }
                } 

        }
        catch (ServiceException se)
        {
            System.out.println(se.getMessage());
            se.printStackTrace();
            System.exit(-1);
        }
        catch (Exception e)
        {
            System.out.println(e.getMessage());
            e.printStackTrace();
            System.exit(-1);
        }

    }

}

Как запустить приложения Java

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

Как запустить ресурсоемкое приложение

  1. Войдите в свою виртуальную машину.
  2. Создайте папку, в которой вы будете запускать ваше приложение. Например, c: \ TSP .
  3. Скопируйте TSPSolver.jar в c: \ TSP ,
  4. Создайте файл с именем c: \ TSP \ towns.txt со следующим содержимым:

City_1, 1002.81, -1841.35
City_2, -953.55, -229.6
City_3, -1363.11, -1027.72
City_4, -1884.47, -1616.16
City_5, 1603.08, -1030.03
City_6, -1555.58, 218.58
City_7, 578.8, -12.87
City_8, 1350.76, 77.79
City_9, 293.36, -1820.01
City_10, 1883.14, 1637.28
City_11, -1271.41, -1670.5
City_12, 1475.99, 225.35
City_13, 1250.78, 379.98
City_14, 1305.77, 569.75
City_15, 230.77, 231.58
City_16, -822.63, -544.68
City_17, -817.54, -81.92
City_18, 303.99, -1823.43
City_19, 239.95, 1007.91
City_20, -1302.92, 150.39
City_21, -116.11, 1933.01
City_22, 382.64, 835.09
City_23, -580.28, 1040.04
City_24, 205.55, -264.23
City_25, -238.81, -576.48
City_26, -1722.9, -909.65
City_27, 445.22, 1427.28
City_28, 513.17, 1828.72
City_29, 1750.68, -1668.1
City_30, 1705.09, -309.35
City_31, -167.34, 1003.76
City_32, -1162.85, -1674.33
City_33, 1490.32, 821.04
City_34, 1208.32, 1523.3
City_35, 18.04, 1857.11
City_36, 1852.46, 1647.75
City_37, -167.44, -336.39
City_38, 115.4, 0.2
City_39, -66.96, 917.73
City_40, 915.96, 474.1
City_41, 140.03, 725.22
City_42, -1582.68, 1608.88
City_43, -567.51, 1253.83
City_44, 1956.36, 830.92
City_45, -233.38, 909.93
City_46, -1750.45, 1940.76
City_47, 405.81, 421.84
City_48, 363.68, 768.21
City_49, -120.3, -463.13
City_50, 588.51, 679.33
  1. В командной строке измените каталоги на c: \ TSP.

  2. Убедитесь, что папка bin JRE находится в переменной окружения PATH.
  3. Вам нужно будет создать очередь служебной шины перед запуском перестановок решения TSP. Выполните следующую команду, чтобы создать очередь служебной шины:

java -jar TSPSolver.jar createqueue
  1. Теперь, когда очередь создана, вы можете запустить перестановки решателя TSP. Например, выполните следующую команду, чтобы запустить решатель для 8 городов.

    java -jar TSPSolver.jar 8
  2. Если вы не укажете номер, он будет работать для 10 городов. По мере того, как решатель находит текущие кратчайшие маршруты, он добавляет их в очередь.

    Запись

    Чем больше указанное вами число, тем дольше будет работать решатель. Например, бег по 14 городам может занять несколько минут, а бег по 15 городам может занять несколько часов. Увеличение до 16 или более городов может привести к дням времени выполнения (в конце концов, неделям, месяцам и годам). Это связано с быстрым увеличением числа перестановок, оцениваемых решателем по мере увеличения числа городов.

Как запустить клиентское приложение мониторинга

  1. Войдите на свой компьютер, где вы будете запускать клиентское приложение. Это не обязательно должен быть тот же компьютер, на котором запущено приложение TSPSolver , хотя это может быть.
  2. Создайте папку, в которой вы будете запускать ваше приложение. Например, c: \ TSP .
  3. Скопируйте TSPClient.jar в c: \ TSP ,
  4. Убедитесь, что папка bin JRE находится в переменной окружения PATH.
  5. В командной строке измените каталоги на c: \ TSP.
  6. Запустите следующую команду:

    java -jar TSPClient.jar

    При желании укажите количество минут для сна между проверкой очереди, передав аргумент командной строки. Период ожидания по умолчанию для проверки очереди составляет 3 минуты, что используется, если аргумент командной строки не передается в TSPClient . Если вы хотите использовать другое значение для интервала ожидания, например, одну минуту, выполните:

    java -jar TSPClient.jar 1

    Клиент будет работать до тех пор, пока не увидит сообщение очереди «Завершено». Обратите внимание, что если вы запускаете несколько экземпляров решателя без запуска клиента, вам может потребоваться запустить клиент несколько раз, чтобы полностью очистить очередь. Кроме того, вы можете удалить очередь, а затем создать ее снова. Чтобы удалить очередь, выполните следующую команду TSPSolver (не TSPClient ):

    java -jar TSPSolver.jar deletequeue

    Солвер будет работать до тех пор, пока не закончит изучение всех маршрутов.

Как остановить приложения Java

Как для солвера, так и для клиентских приложений вы можете нажать Ctrl + C, чтобы выйти, если хотите завершить работу до обычного завершения.