lab2

advertisement
Лабораторная работа №2
Изучение способов передачи объектов в RMI
Цель работы: изучить способы передачи различных объектов. Определить
зависимость повышения производительности от количества вычислительных
узлов при решении слабосвязанной задачи.
Краткие теоретические сведения
Передача объектов в RMI
Передаваемые аргументы или возвращаемые из удаленных методов
значения могут быть практически любого типа, включая локальные объекты,
удаленные объекты и примитивные типы. Удаленному методу может быть
передан экземпляр любого типа, который может быть примитивным типом
данных, удаленным объектом или сериализуемым объектом, т.е.
реализующим интерфейс java.io.Serializable.
Некоторые типы объектов не удовлетворяют таким критериям и
поэтому не могут быть переданы или возвращены из удаленного метода.
Большинство таких объектов – таких, например, как файловый дескриптор,
инкапсулируют информацию, которая имеет смысл только внутри одного
адресного пространства. Многие центральные (core) классы, включая классы
библиотеки java.lang and java.util, реализуют интерфейс Serializable.
Правила, регулирующие передачу аргументов и возврат значений,
следующие.
 Удаленные объекты передаются исключительно по ссылке (by
reference). Объектной ссылкой удаленного объекта -- remote object
reference является stub, т.е. "прокси" со стороны клиента,
реализующий полный набор удаленных интерфейсов, которые
реализует удаленный объект (иногда stub называют также
заглушкой удаленного объекта).
 Локальные объекты передаются копированием, используя
механизм сериализации объектов. По умолчанию копируются все
поля, за исключением тех, которые отмечены как static или
transient. Принятый по умолчанию характер сериализации может
быть переопределен.
Передача объекта по ссылке (как это делается с удаленными
объектами) означает, что любые изменения, произведенные над состоянием
объекта при вызовах удаленных методов, отражаются в оригинале
удаленного объекта. При передаче удаленного объекта для принимающего
доступны только те интерфейсы, которые являются удаленными
интерфейсами. Любые методы, определенные в реализации класса или
определенные как не-удаленные интерфейсы, реализуемые данным классом,
для принимающего недоступны.
В удаленных методах объекты (параметры, возвращаемые значения и
исключения), которые не являются удаленными объектами, передаются не по
ссылке, а по значению. Это означает, что на принимающей виртуальной
машине создается копия объекта. Любые изменения в их состоянии,
выполненные на стороне принимающего, отражаются только на этой
клиентской копии, но не на исходном экземпляре.
Ход работы
Создание демонстрационного приложения.
Вычислительный сервер позволяет выполнять пользовательские
задачи. Клиент передает серверу объект, позволяющий вычислять число Pi с
заданной точностью. Сервер возвращает результат вычисления.
1. Интерфейс сервера
package compute;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Compute extends Remote {
//всего один метод, для выполнения задачи
Object executeTask(Task t) throws RemoteException;
}
2. Интерфейс задачи, передаваемой серверу; Интерфейс Task определяет
один метод execute, который возвращает Object, не имеет параметров и
не выдает исключений. Поскольку интерфейс не расширяет Remote,
метод
в
этом
интерфейсе
не
должен
перечислять
java.rmi.RemoteException в своем условии throws . интерфейс Task
расширяет интерфейс java.io.Serializable. RMI использует механизм
сериализации объектов для передачи объектов по значениям между
виртуальными машинами Java. Реализация Serializable отмечает класс
как способный к преобразованию в самоопределенный поток байтов
(self-describing byte stream), который при обратном чтении объекта из
потока можно реконструировать как точную копию сериализованного
объекта.
package compute;
import java.io.Serializable;
public interface Task extends Serializable {
Object execute();
}
3. реализация сервера
package engine;
import
import
import
import
import
java.rmi.*;
java.rmi.registry.LocateRegistry;
java.rmi.registry.Registry;
java.rmi.server.*;
compute.*;
public class ComputeEngine extends UnicastRemoteObject
implements Compute
{
public ComputeEngine() throws RemoteException {
super();
}
public Object executeTask(Task t) {
return t.execute();
}
public static void main(String[] args) {
System.setProperty("java.security.policy","D:\\rmi.policy");
if (System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
}
String name = "rmi://localhost/Compute";
try {
LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
Compute engine = new ComputeEngine();
Naming.rebind(name, engine);
System.out.println("ComputeEngine bound");
} catch (Exception e) {
System.err.println("ComputeEngine exception: " +
e.getMessage());
e.printStackTrace();
}
}
}
4. Создаем файл rmi.policy. Путь к файлу должен совпадать с
указанным в классе сервера. Содержимое файла:
grant {
permission java.net.SocketPermission "*:1024-65535", "connect, accept";
permission java.io.FilePermission "-", "read";
permission java.net.SocketPermission "*:80", "connect";
};
5. Реализуем задачу
package client;
import compute.*;
import java.math.*;
// Вычисляем число Пи. Алгоритм нас не сильно интересует,
// главное что реализуем интерфейс Task
public class Pi implements Task {
/** constants used in pi computation
private static final BigDecimal ZERO
BigDecimal.valueOf(0);
private static final BigDecimal ONE
BigDecimal.valueOf(1);
private static final BigDecimal FOUR
BigDecimal.valueOf(4);
*/
=
=
=
/** rounding mode to use during pi computation */
private static final int roundingMode =
BigDecimal.ROUND_HALF_EVEN;
/** digits of precision after the decimal point */
private int digits;
/**
* Construct a task to calculate pi to the specified
* precision.
*/
public Pi(int digits) {
this.digits = digits;
}
/**
* Calculate pi.
*/
public Object execute() {
return computePi(digits);
}
/**
* Compute the value of pi to the specified number of
* digits after the decimal point. The value is
* computed using Machin's formula:
*
*
pi/4 = 4*arctan(1/5) - arctan(1/239)
*
* and a power series expansion of arctan(x) to
* sufficient precision.
*/
public static BigDecimal computePi(int digits) {
int scale = digits + 5;
BigDecimal arctan1_5 = arctan(5, scale);
BigDecimal arctan1_239 = arctan(239, scale);
BigDecimal pi = arctan1_5.multiply(FOUR).subtract(
arctan1_239).multiply(FOUR);
return pi.setScale(digits,
BigDecimal.ROUND_HALF_UP);
}
/**
* Compute the value, in radians, of the arctangent of
* the inverse of the supplied integer to the speficied
* number of digits after the decimal point. The value
* is computed using the power series expansion for the
* arc tangent:
*
* arctan(x) = x - (x^3)/3 + (x^5)/5 - (x^7)/7 +
*
(x^9)/9 ...
*/
public static BigDecimal arctan(int inverseX,
int scale)
{
BigDecimal result, numer, term;
BigDecimal invX = BigDecimal.valueOf(inverseX);
BigDecimal invX2 =
BigDecimal.valueOf(inverseX * inverseX);
numer = ONE.divide(invX, scale, roundingMode);
result = numer;
int i = 1;
do {
numer =
numer.divide(invX2, scale, roundingMode);
int denom = 2 * i + 1;
term =
numer.divide(BigDecimal.valueOf(denom),
scale, roundingMode);
if ((i % 2) != 0) {
result = result.subtract(term);
} else {
result = result.add(term);
}
i++;
} while (term.compareTo(ZERO) != 0);
return result;
}
}
6. Реализуем клиент
package client;
import java.rmi.*;
import java.math.*;
import compute.*;
public class ComputePi {
public static void main(String args[]) {
System.setProperty("java.security.policy","D:\\rmi.policy");
if (System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
}
try {
String name = "rmi://localhost/Compute";
Compute comp = (Compute) Naming.lookup(name);
Pi task = new Pi(Integer.parseInt(args[0]));
BigDecimal pi = (BigDecimal) (comp.executeTask(task));
System.out.println(pi);
} catch (Exception e) {
System.err.println("ComputePi exception: " + e.getMessage());
e.printStackTrace();
}
}
}
7. экспортируем полученные пакеты в jar файлы. Получаем client.jar,
compute.jar, engine.jar. Размещаем client.jar и engine.jar на разных
компьютерах или на одном компьютере в разных папках (следует
учесть, что в исходном тексте клиента прописан сервер rmiregistry).
compute.jar должен находится и на клиенте и на сервере.
8. Запускаем сервер.
java -cp "compute.jar;engine.jar" engine.ComputeEngine
9. Запускаем клиент
java -Djava.rmi.server.codebase="file:///d:/lab2_client.jar" -cp "
compute.jar;client.jar" client.ComputePi 20
java.rmi.server.codebase – определяет место, откуда сервер сможет
загрузить класс задачи, поскольку при сериализации осуществляется
сериализация его состояния, но сам класс не передается. Можно указать либо
путь к файлу либо URL. Соответствующие разрешения должны быть
прописаны в rmi.policy.
Задача на самостоятельную работу
Реализовать программную систему распределенного восстановления
паролей.
Описание системы:
Существует несколько вычислительных узлов. Клиент подключается к
нескольким узлам и передает им задачу, осуществляющую подбор пароля
по указанному значению md5. Задача получает диапазон строк для
проверки и md5 пароля, проверяет указанный диапазон строк символов на
предмет совпадения md5 строки с эталонным md5. Например: диапазон
начало aaaaa конец zzzzz, md5=aeaeaeaeaeaeaeae… В качестве
допустимого набора символов принять маленькие английские буквы.
Задача возвращает совпадающую строку, если она найдена.
Определить зависимость быстродействия от количества используемых
вычислительных узлов.
Download