Разпределени системи - необходимост от комуникация между изчислителни процеси работещи в различни адресни пространства, потенционално на различни машини.
Основната идея е създаването на фамилия от обекти които могат да се намират върху различни машини и които могат да комуникират помежду си чрез стандартни мрежови протоколи.
От гледна точка на езика за реализацията съществуват две възможности за фамилията обекти:
Втората възможност изисква обектите да могат да комуникират независимо от езика на създаването им. Стандартът CORBA (Common Object Request Broker Architecture) приет от OMG (Object Management Group, www.omg.org) дефинира общ механизъм позволяващ обмена на информация. Трябва да се спомене, че Microsoft поддържа протокола COM като конкурентен на CORBA, но акцента му е насочен към протокола .NET за комуникация между програмни компоненти.
В CORBA комуникационните услуги са делегирани на ORB (Object Request Broker), които са универсални преводачи. ORB комуникират с мрежови протоколи и следват установените от OMG спецификации за протокола IIOP (Internet Inter - ORB protocol).
CORBA е напълно независима от използваните програмни езици. Обектите написани на C++, Java, ... трябва да преминат през език на дефиниране на интерфейси (IDL - Interface Definition Language) за да специфицира подписите на съобщенията и типовете на данните, които обектите могат да си разменят. Едно от предимствата на този модел е, че спецификации IDL могат да се създават и за стари обекти създадени много преди CORBA след което да се използват ORB.
Като недостатък на подхода трябва да се отбележи, че понастоящем подхода има проблеми със скоростта и сложността на създаваните приложения.
Ако всички комуникиращи си обекти са създадени на Java сложността на и универсалността на CORBA не са необходими. Sun е разработил един по-прост и ефикасен механизъм наречен RMI (Remote Method Invocation) специално за поддържане на разпределени Java обекти.
Реализираната на Java RMI система разчита на хомогенната среда на Java виртуалната машина (JVM) и ползва предимствата на платформата на Java обектите. Основните цели на поддръжката на разпределени обекти в Java са:
Създаденият модел е естествен (следва идеологията на Java ) и лесен за използване
Двата модела си приличат по:
Двата модела си приличат по:
instanceof
оператор
може да се използва за проверка на отдалечен интерфейс поддържан от
отдалечен обект. Двата модела се различават по:
java.lang.Object
е специализирана за отдалечени обекти. RMI приложенията представляват две отделни програми - сървър и клиент.
Сървър - създава отдалечени обекти, прави псевдонимите(reference) им достъпни и чака извикване от страна на клиента.
Клиент - взима псевдонимите на един или повече отдалечени обекти и след това извиква методи върху тях
RMI осигурява механизъм за комуникация между сървъра и клиента.
Разпределеното приложение трябва да може да:
RMI използва стандартен механизъм за комуникация между
отдалечените обекти: stubs
и
skeletons. Обектът stub
представлява локалния представител при клиента - заместник (proxy) на
отдалечения обект. В RMI, stub на отдалечен обект
притежава същия интерфейс като този на отдалечения обект.
Когато се извиква отдалечен метод, извикването активира метод в обекта stub, който
Предназначението на stub е да скрие от потребителя сериализацията и десериализацията на обектите, и прехвърлянето на информацията на мрежово ниво, с оглед запазване простотата на извикването.
В отдалечената JVM, всеки отдалечен обект има съответстващ skeleton (ако всички виртуални машини работят в Java 2 среда, skeletons не са необходими). Обектът skeleton прехвърля заявката към актуалният отдалечен метод. При получаването на отдалечена заявка той:
В JVM на Java 2 са интегрирани допълнителни stub протоколи за да се избегне нуждата от skeleton.
stub - при клиента
автоматично зареждане - използва се стандартен WEB сървър за зареждането на stub.
Етапи при създаването на RMI система:
1. Дефиниране на отдалечените
интерфейси (интерфейсите на отдалечените обекти).
2. Разработване на отдалечените класове (класове - сървъри), които
наследяват отдалечените интерфейси.
3. Разработка на програмата сървър генерираща отдалечените обекти.
4. Разработка на програмата - клиент.
5. Компилиране на класовете.
6. Генериране на stubs и евентуално skeletons.
7. Стартиране на RMI registry.
8. Стартиране на отдалечените обекти.
9. Стартиране на клиента.
Нека отдалечения обект да предлага функции за:
1. Сумиране на две цели числа
2. Намиране на датата на сървъра
I.Локална разработка на клиента и сървъра
1. Отдалечен интерфейс
Клиентът трябва да има информация за услугите предлагани от от отдалечения обект. Това се реализира чрез интерфейс, който трябва да се намира при сървъра и при клиента:
/* SampleServer.java */ |
Клиентът достига до обекта на сървъра чрез локален stub, който наследява отдалечения интерфейс:
SampleServer remoteObject = ...
//достигането до локалния stub
е обяснено по-късно
System.out.println(" 1 + 2 = " + remoteObject.sum(1,2) );
Date d = remoteObject.getDate();
2.Отдалечен обект
Това е обектът на ниво сървър, който наследява отдалечения интерфейс и реализира отдалечените методи:
import java.rmi.*; |
Всички класове на сървъра трябва да наследяват класа RemoteServer от пакета java.rmi.server, но той е абстрактен клас, който дефинира само най-важните механизми за комуникация между обекта и съответният му отдалечен stub. Класът UnicastRemoteObject наследява абстрактният клас RemoteServer и може да бъде използван без писането на допълнителен код. На фигурата е показана диаграмата на връзките между тези класове.
Всеки обект UnicastRemoteObject е локализиран върху сървъра и трябва да бъде активен и на разположение през протокола TCP/IP, когато услугата е поискана. Това е единствения клас в текущата версия на rmi.
За в бъдеще се говори за други класове като например MulticastRemoteObject за обекти дублирани в повече сървъри.
Конструкторът на класа UnicastRemoteObject (извикан в случая с метода super() ) не само създава обект, но и го "изнася" (export) - прави го достъпен за отдалечено извикване. За достъп до обекта се използва порт, който се определя при изпълнението (anonymous port). Това позволява обектът да бъде поддържан и да не бъде събран от боклукосъбирача.
Ако класът вече наследява друг клас всеки обект и не може да бъде производен на UnicastRemoteObject, след създаването си трябва да бъде "изнесен" чрез статичния метод exportObject():
Remote server = new MyServer();
UnicastRemoteObject.exportObject(server,port);
// при създаването на обекта server
или: UnicastRemoteObject.exportObject(this,port); // в конструктора на server
3. Генериране на stub - тази стъпка не е необходима, ако версията при клиента е 1.5 или по-висока
За отдалечения клас SampleServerImpl трябва да бъде генериран stub. За целта има средство (сред компилиране на класа):
rmic -v1.2 SampleServerImpl
или
rmic -v1.1 SampleServerImpl
Във версия 1.5 опцията v1.2 е подразбираща се - тя генерира само stub без skeleton.
======================================
Класът Naming
Класът Naming наследява java.lang.Object и осигурява методи за съхраняване и получаване на псевдоними на обекти в регистъра на отдалечените обекти. Всеки метод от класа има като аргумент "name" от тип java.lang.String в URL формат във вида
//host:port/name
Където host е машината, където се намира регистърът, port е номерът на порта, на който регистърът слуша мрежата, и name е обикновен обект от java.lang.String, който не се интерпретира от регистъра. Той задава името на обекта с който той се закачва. Частта host:port може да се пропусне. Ако се пропусне host, се подразбира локалната машина, а ако се пропусне port, се използва подразбиращият се 1099.
Регистрирането (Binding) на името на отдалечен обект позволява под това име да бъде намерен обекта. След регистрацията клиентските програми могат да намират отдалечения обект, да получат ппсевдонима му и да извикват отдалечените му методи (наследяващите интерфейса Remote). Един регистър може да бъде споделен от всички сървъри на машината. Възможно е също така всеки сървър да създава и използва собствен регистър ( за повече подробности виж java.rmi.registry.LocateRegistry.createRegistry() ).
Класът притежава 5 метода (където name може да бъде зададено директно за локаланат машина или във формата //host:port/name ):
bind(String name, Remote obj) // Регистрира обекта с зададеното име; само на локалната машина;================================
4. Получаване на stub от клиента
За локализиране на отдалечения обект върху сървъра библиотеката предлага средство, което се нарича bootstrap registry service. Всеки клас - сървър може да регистрира обекти с тази услуга:
Naming.rebind(name, this);
Името трябва да бъде уникално за да могат да се различават.
Клиентът може да провери дали в регистрите на даден сървър има закачени обект с
String[] bindings = Naming.list(url); // ако url е "" се работи с локалната машина на подразбиращия се порт
и да получи stub за чрез
SampleServer remoteObject = (SampleServer)Naming.lookup(url);
URL rmi започват с rmi:// следвани от сървъра и евентуално номер на порта и името на обекта:
rmi://host_server:1099/object_name;
Подразбиращият се порт за rmi е 1099.
От съображения за сигурност едно приложение може да свързва, отвързва и променя обекти в регистрите само на локалната машина. Отдалечените клиенти обаче могат да търсят в регистрите.
5. Генериране на отдалечените обекти
import java.rmi.*; |
Програмата генерира един обект и го "записва" в регистрите под името"SAMPLE-SERVER".
В документацията на rmi се препоръчва инсталирането на RMISecurityManager, който да задава правата на сървъра. В случая, когато сървъра няма да се използва и като клиент (или ако не се налагат специални ограничения) това не е необходимо и излишно усложнява пускането на системата.
6. Стартиране на сървъра
Преди стартирането на сървъра трябва да се стартира услугата bootstrap registry service:
rmiregistry & под Unix
start rmiregistry под Windows
След което може да се стартира сървъра:
java SampleServerStart & Unix
start java SampleServerStart Windows
Забележка: не е добра идея да се използва javaw под Windows вместо start, защото Windows не управлява добре един процес javaw - не го показва в списъка на процесите, не извежда съобщенията за грешки.
7. Проверка дали обекта е регистриран
import java.rmi.*; |
резултат:
//:1099/SAMPLE-SERVER
8. Програмата клиент
Програмата клиент пренася в системата stub на отдалечения обект и трябва да инсталира управление на сигурността за контролиране на зарежданите stub .
System.setSecurityManager(new RMISecurityManager());
Ако всички класове включително класовете stub са локални, няма нужда от управление на сигурността, но когато stub трябва да се получи по мрежата е необходимо да се създаде файл за управление на сигурността - в нашия случай - client.policy и да се свърже с java.security.policy:
System.setProperty("java.security.policy","client.policy");
Политиката на управление може да се установи и при стартирането на клиента:
java -Djava.security.policy=client.policy SampleClient
файлът client.policy трябва да разрешава ползването на сокети на портовете над 1023:
grant |
клиентът:
import java.rmi.*; |
При стартирането на клиента трябва да се получи:
java SampleClient 127.0.0.1
Security Manager loaded
url:rmi://127.0.0.1/SAMPLE-SERVER
Got remote object
1 + 2 = 3
Date on server is Fri Dec 16 09:54:45 EET 2005