MỘT SỐ KỸ THUẬT LẬP TRÌNH PHÂN TÁN VỚI RMI

Một phần của tài liệu TÌM HIỂU KỸ THUẬT LẬP TRÌNH PHÂN TÁN TRONG JAVA (Trang 41 - 65)

2. Chương 2: LẬP TRÌNH PHÂN TÁN VỚI RMI

1.7. MỘT SỐ KỸ THUẬT LẬP TRÌNH PHÂN TÁN VỚI RMI

2.1.9. RMI REGISTRY và cách đăng ký đối tượng

Bộ đăng ký rmiregistry được xem như là một dịch vụ phục vụ việc đăng kí và tìm kiếm các đối tượng từ xa. Các đối tượng chủ muốn được trình khách truy tìm được từ xa thì trước hết phải đăng ký với rmiregistry. Bộ đăng ký này là một chương trình dịch vụ chạy ở hậu trường (chương trình rmiregistry.exe) mở ổ cắm socket và lắng nghe các yêu cầu gởi đến cổng mặc định 1099, bạn có thể chỉ định một cổng khác với cổng 1099.

Ví dụ: C:\j2sdk1.4.0\bin\rmiregistry.exe 1000

Lúc này rmiregistry sẽ chạy trên cổng 1000, và đối tượng trên máy chủ sẽ đăng ký với rmiregistry theo cách sau:

Myobject o = new Myobject();

Naming.bind(“rmi://localhost:1000/myObjectname”,o);

Tiếp đến chương trình trên máy khách sẽ yêu cầu dịch vụ rmiregistry trả về tham chiếu đến đối tượng trên máy chủ:

Myobject o = (Myobject)Naming.lookup(“rmi://localhost:1000/myObjectname”,o);

Một dịch vụ đăng ký có thể tiếp nhận và quản lý cùng lúc nhiều đối tượng khác nhau.

Hình 1.1.1 : Hoạt động của rmiregistry

Trong Java cũng cho phép chúng ta lấy về danh sách các đối tượng mà rmiregistry đang nắm giữ . Dưới đây là chương trình tìm hiểu danh sách tên các đối tượng do rmiregistry nắm giữ. Ta xây dựng chương trình theo các bước sau:

Bước 1: Xây dựng chương trình duyệt rmiregistry

//Traverse.java

import java.rmi.registry.*; import java.rmi.*;

class Traverse {

public static void main (String args[]) { System.out.println ("Connecting registry…"); try {

//Kết nối với registry

Registry reg = LocateRegistry.getRegistry("localhost");

//Lấy về danh sách các đối tượng do registry nắm giữ

String object [] = reg.list();

//Liệt kê danh sách các đối tượng

for (int i=0; i<object.length; i++) System.out.println(object[i]); }

catch (RemoteException e){System.out.println (e);}

Máy chủ rmiregistry Object bal l Calculator Chương trình khách Chương trình khách

Đăng ký và yêu cầu rmiregistry

Lời gọi đến đối tượng

}

Thực thi chương trình:

- Chạy bộ đăng ký rmiregistry

- Đăng ký đối tượng Calculator với rmiregistry với tên là CTtinhtoan

- Đăng ký đối tượng PingServer với rmiregistry với tên là pingobject

- Kết quả:

 Tự tạo bộ đăng ký cục bộ:

Trong Java cho phép tự tạo bộ đăng kí mà không cần dùng đến chương trình rmiregistry.exe. Để tạo bộ đăng kí và tự đăng kí đối tượng chúng ta gọi phương thức tĩnh createRegistry() của lớp LocateRegistry.

Ví dụ: //Setup.java import java.rmi.*; import java.rmi.server.*; import java.rmi.registry.*; class Setup {

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

LocateRegistry.createRegistry (1099);

//Khởi tạo đối tượng

CalculatorImpl calc= new CalculatorImpl ();

//Cho máy ảo biết sự hiện diện của đối tượng

UnicastRemoteObject.exportObject (calc);

// Đăng ký đối tượng với dịch vụ tìm kiếm Naming

System.out.println (“Registering object…”); Naming.bind (“rmi://localhost/calcObj”, calc); System.out.println (“Waiting for client request…”); }

}

Ta chỉ cần thực thi chạy chương trình Setup trên từ máy chủ, chương trình sẽ tự động tạo bộ đăng kí rmiregistry ở cổng mặc định 1099, sau đó tự đăng ký đối tượng với rmiregistry đó. Tuy nhiên cách này chỉ có thể áp dụng cho một đối tượng. Nếu một đối tượng nào đó đã tạo ra bộ đăng kí rồi thì ta không gọi được

LocateRegistry.createRegistry() lần thứ hai trên cùng một cổng của máy chủ. Khi

đó, ta phải tạo một cổng khác cho bộ đăng ký, ví dụ:

LocateRegistry.createRegistry(1234); ………..

Naming.bind(“rmi://localhost:1234/calcObj”, calc);

2.1.10. Dùng một đối tượng sản sinh nhiều đối tượng

2.1.10.1 Mô hình xưởng chế tác đối tượng:

Như những phần trước, mỗi khi xây dựng một đối tượng chủ chúng ta đều phải đặt tên cho đối tượng, đăng kí tên đối tượng cho rmiregistry. Điểm bất tiện là rmiregistry phải quản lý quá nhiều tên đối tượng và khi viết chương trình trên máy khách chúng ta cũng phải nhớ tên của các đối tượng này. Để tiện lợi hơn, Java cho phép chúng ta xây dựng một đối tượng duy nhất và đăng kí đối tượng này với rmiregistry, nhiệm vụ của nó chỉ dùng để tạo ra các đối tượng con khác. Một đối tượng như vậy trong mô hình hướng đối tượng gọi là factory object.

Hình 2.1.1: Mô hình hoạt động của đối tượng Factory 1. Đối tượng Factory Object đăng ký tên mình với rmiregistry.

2. Trình khách muốn gọi Object A. Trước hết trình khách liên hệ với rmiregistry để lấy về tham chiếu đến đối tượng Factory Object.

3. Sau khi có tham chiếu của đối tượng Factory Object, trình khác triệu gọi đối tượng Factory Object yêu cầu tạo ra và cho phép tham chiếu đến Object A hoặc Object B.

4. Đối tượng chủ tạo ra và trả về tham chiếu của Object A, Object B theo yêu cầu của trình khách.

5. Dựa vào tham chiếu do Factory Object trả về trình khách thực hiện lời gọi đến Object A hoặc Object B.

2.1.10.2 Cài đặt ứng dụng Factory:

Trước hết, ứng dụng của ta trên máy chủ gồm hai đối tượng: Đối tượng News dùng để cung cấp thông tin cho máy khách và đối tượng Timer dùng để cung cấp thời gian hiện hành. Chúng ta không đăng ký đối tượng này với rmiregistry mà muốn một đối tượng FactoryService chịu trách nhiệm đứng trung gian giao tiếp với máy khách để cung cấp đối tượng News và Timer. Với yêu cầu trên, chương trình được xây dựng lần lượt qua các bước sau:

Bước 1: Đặc tả giao tiếp và cài đặt đối tượng Timer

Chương trình khách RMI REGISTRY Factory Object Object A Object B 2 5 1 3 4 Trình chủ

//Timer.java

import java.rmi.*; import java.util.*;

interface Timer extends Remote {

public Date getCurrentTime () throws RemoteException; }

// TimerImpl.java

import java.rmi.*; import java.util.*;

class TimerImpl implements Timer {

public Date getCurrentTime () throws RemoteException { System.out.println (“Client request time”);

return new Date(); }

}

Timer là một đối tượng RMI, cung cấp phương thức getCurrentTime() có thể gọi từ xa bởi máy khách, phương thức này trả về ngày giờ hiện hành trên máy chủ

Bước 2: Đặc tả giao tiếp và cài đặt đối tượng News

//News.java

import java.rmi.*;

interface News extends Remote {

public String getTodayNews () throws RemoteException; }

// NewsImpl.java

import java.rmi.*;

class NewsImpl implements News { String news[] = {

“Intel che tao chip Atom 16 loi”, “Facebook bi chan o Viet Nam”, “Kho dia chi IPv4 da can kiet”, }; static int index = 0;

public String getTodayNews () throws RemoteException { System.out.println (“Client request news”);

index ++;

if (index >2) index = 0; return news[index];

}

News là một đối tượng RMI, cung cấp phương thức getTodayNews() trả về thông tin cập nhật mới nhất, getTodayNews() lấy thông tin từ mảng news[]. Trong ứng dụng thực tế, ta có thể cung cấp thông tin cho trình khách từ cơ sở dữ liệu hay một nguồn tin nào đó. Đối tượng News của ta ở đây chỉ có khả năng cung cấp 3 tin mà thôi.

Bước 2: Đặc tả giao tiếp và cài đặt đối tượng Factory

//FactoryService.java

import java.rmi.*;

interface FactoryService extends Remote {

public News createNews () throws RemoteException; public Timer createTimer () throws RemoteException; }

//FactoryServiceImpl.java

import java.rmi.*;

import java.rmi.server.*;

class FactoryServiceImpl implements FactoryService { News newsObject = new NewsImpl ();

Timer timerObject = new TimerImpl (); public FactoryServiceImpl () {

try {

// Thông báo khả năng triệu gọi từ xa của đối tượng

UnicastRemoteObject.exportObject (newsObject); UnicastRemoteObject.exportObject (timerObject); }

catch (Exception e) { System.out.println(e);} }

public News createNews () throws RemoteException { return newsObject;

}

public Timer createTimer () throws RemoteException { return timerObject;

} }

Trình khách khi giao tiếp với FactoryService sẽ gọi createNews() và

FactoryService có nhiệm vụ kiến tạo thể hiện của hai đối tượng này đồng thời gọi

UnicastRemoteObject.exportObject() để thông báo khả năng triệu gọi từ xa của

News và Timer cho máy ảo Java.

Bước 4: Xây dựng trình cài đặt đối tượng và đăng ký FactoryService với rmiregistry.

//Setup.java

import java.rmi.*;

import java.rmi.server.*; class Setup {

public static void main (String srgs[]) throws Exception{ System.out.println (“Factory object created”);

// Tạo đối tượng

FactoryService obj = new FactoryServiceImpl(); UnicastRemoteObject.exportObject(obj);

// đăng ký FactoryService với rmiregistry

Naming.bind(“rmi://localhost/factObj”, obj); }

}

Trình Setup.java vẫn như thường lệ chịu trách nhiệm tạo đối tượng FactoryService và đăng ký đối tượng với rmiregistry.

Trình khách muốn gọi News và Timer chỉ cần nhớ tên đăng ký của FactoryService với rmiregistry là factObj. Liên hệ với FactoryService, trình khách sẽ có được tham chiếu để gọi đến News và Timer.

Bước 5: Xây dựng trình khách

//Client.java

import java.rmi.*;

import java.rmi.server.*; class Client {

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

//Liên hệ với đối tượng FactoryService

FactoryService service = (FactoryService)

News news = service.createNews(); Timer timer = service.createTimer();

// Triệu gọi phương thức của các đối tượng news và Timer

System.out.println (“Current: ” + timer.getCurrentTime()); System.out.println (“We have news: ” + news.getTodayNews());

// Dừng 1 giây

Thread.sleep(1000);

// Triệu gọi phương thức của các đối tượng news và Timer

System.out.println (“Current: ” + timer.getCurrentTime()); System.out.println (“We have news: ” + news.getTodayNews()); }

}

Như ta thấy, trình khách chỉ tham chiếu đến FactoryService bằng cách:

FactoryService service = (FactoryService) Naming.lookup(“rmi://localhost/factObj”);

Có đối tượng service, trình khách yêu cầu service cung cấp cho mình tham chiếu đến hai đối tượng con News và Timer bằng phương thức createNews() và

createTimer(). Sau đó, Client gọi phương thức getCurrentTime() và

getTodayNews() để lấy về giờ hiện hành và thông tin trên máy chủ.

Thực hiện chương trình:

- Biên dịch, tạo stub và skel, khởi động rmiregistry:

- Chạy máy khách và kết quả thu được:

2.1.11. Kỹ thuật gọi đối tượng ở xa bằng phương pháp động ( DYNAMIC METHOD INVOKE )

Qua các chương trình trên, ta thấy các trình khách muốn gọi được các phương thức của trình chủ cần thiết phải được cung cấp lớp giao tiếp interface của bản thân đối tượng. Trình khách sẽ dựa vào interface này để chuyển kiểu đối tượng về dạng tường minh sau khi nhận tham chiếu đến đối tượng bằng hàm

Naming.lookup(), ví dụ:

PingServer server=(PingServer)Naming.lookup("rmi://localhost/pingobject");

Trong Java còn cho phép ta gọi phương thức ở xa mà không cần lớp giao tiếp interface bằng cách sử dụng kỹ thuật phản chiếu (reflect) để gọi phương thức động. Với phương pháp này, trình khách lấy về tham chiếu của đối tượng bằng hàm

Naming.lookup() ở dạng tổng quát là Object, sau đó gọi phương thức getMethod(),

trình khách sẽ biết được phương thức mà đối tượng hiện đang nắm giữ. Hàm

invoke() sẽ giúp trình khách thực thi phương thức bên trong đối tượng.

Ví dụ:

Bước 1: Đặc tả giao tiếp cho lớp đối tượng Friend

//Friend.java

public String greeting(String name) throws RemoteException; }

Bước 2: Cài đặt chi tiết cho đối tượng

//FriendImpl.java

import java.rmi.*;

class FriendImpl implements Friend{

public String greeting(String name) throws RemoteException{ return ("Hi "+name+ "! Nice to meet you");

}

}

Bước 3: Thiết lập và đăng ký đối tượng trên máy chủ

//Setup.java

import java.rmi.*;

import java.rmi.server.*; class Setup {

public static void main(String args[]) throws Exception{ Friend obj=new FriendImpl();

UnicastRemoteObject.exportObject(obj);

System.out.println("Friend waiting for client request...");

Naming.bind("rmi://localhost/myfriend", obj);

}

}

Bước 4: Viết chương trình trên máy Client.

//Client.java

import java.rmi.*;

import java.lang.reflect.*; class Client {

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

//Tìm tham chiếu của đối tượng

Object o=Naming.lookup("rmi://localhost/myfriend");

//Truy tìm xuất xứ của lớp

Class c = o.getClass();

//Định kiểu của phương thức cần gọi

//Truy tìm phương thức cần gọi

Method theMethod;

theMethod = c.getMethod("greeting", parameterTypes);

//Định đối số truyền vào phương thức cần gọi

Object[] arguments = new Object[] {"Johny"};

//Gọi phương thức và nhận kết quả trả về.

String result = (String) theMethod.invoke(o, arguments); System.out.println(result);

}

}

Thực hiện chương trình:

- Biên dịch, tạo stub và skel, chạy rmiregistry.

- Chạy trình chủ

- Chạy trình khách và kết quả thu được

Nhận xét:

Chương trình khách Client lấy về tham chiếu đến đối tượng myfriend trên máy chủ chỉ ở dạng tổng quát là Object. Chương trình không sử dụng lớp Friend.class để chuyển kiểu, thay vào đó ta tìm cách khảo sát nội dung của đối tượng này. Trước hết, ta dùng lớp Class tổng quát để lấy về xuất xứ của lớp mà đối

Class c = o.getClasss();

Tiếp đến, đối tượng của ta có chứa phương thức greeting() dùng để yêu cầu một tham số có kiểu String.Ta cung cấp thông tin này để Java lấy về phương thức cần gọi như sau:

// Định kiểu tham số của phương thức cần gọi

Class[] parameterTypes = new Class[] {String.class};

// Yêu cầu Java truy tìm phương thức cần gọi

Method theMethod;

theMethod = c.getMethod("greeting", parameterTypes);

Công việc tiếp theo là gọi phương thức. Đối tượng theMethod cung cấp phương thức invoke() để gọi phương thức mà nó trỏ đến. Trước khi gọi phương thức, ta cần tạo danh sách đối số để truyền vào phương thức như sau:

Object[] arguments = new Object[] {"Johny"};

Bước cuối cùng là triệu gọi gián tiếp phương thức:

String result = (String) theMethod.invoke(o, arguments);

Phương thức invoke() yêu cầu 2 đối số, đối số thứ nhất là đối tượng chứa phương thức, đối số thứ hai là danh sách các đối số cần truyền vào phương thức.

2.1.12. Tự động kích hoạt đối tượng từ xa (ACTIVITION)

Theo những gì đã trình bày ở các phần trước thì quy trình thực hiện khi cài đặt trên máy chủ là:

 Chạy chương trình rmiregistry để khởi động bộ đăng kí hay gọi hàm

LocateRegistry.createRegistry().

 Tạo ra đối tượng từ xa trên máy chủ và đăng kí nó với rmiregistry  Lắng nghe các yêu cầu hay lời gọi phương thức từ máy khách.

Cách cài đặt cổ điển đó chỉ thích hợp với hệ thống có ít đối tượng phục vụ. Nếu trên máy chủ có số lượng lớn (khoảng vài nghìn) đối tượng và chúng đều có khả năng phục vụ cho máy khách thì để sử dụng được các đối tượng này ta phải đăng kí tất cả chúng với rmiregistry, hơn nữa còn phải dành không gian bộ nhớ lớn

để lưu trữ tất cả. Sau đó, tất cả các đối tượng đều được đặt trong trạng thái chờ máy khách. Điều này làm mất thời gian của người lập trình, hao phí nhiều tài nguyên bởi vì không phải tất cả các đối tượng đều được yêu cầu vào cùng một thời điểm.

Để giải quyết vấn đề trên, Java cung cấp cơ chế tự kích hoạt đối tượng (activation) trên máy chủ chỉ khi có yêu cầu sử dụng đối tượng đó. Cách hoạt động của cơ chế này là ta chỉ cần đăng kí sự hiện diện của đối tượng trên máy chủ (không cần cấp phát bộ nhớ và đặt đối tượng trong trạng thái sẵn sàng). Java cung cấp chương trình rmid.exe làm nhiệm vụ tiếp nhận sự hiện diện của đối tượng và tự động khởi tạo đối tượng khi máy khách yêu cầu.

• Chương trình minh họa:

Bước 1: Xây dựng lớp giao tiếp của đối tượng mang tên MyAutoObject

// MyAutoObject.java

import java.rmi.*;

interface MyAutoObject extends Remote {

public String callMeRemotely() throws RemoteException; }

Đối tượng MyAutoObject chỉ đơn giản cung cấp một phương thức callMeRemotely() cho trình khách gọi từ xa.

Bước 2: Cài đặt chi tiết cho đối tượng MyAutoObject thông qua lớp MyAutoObjectImpl.

//MyAutoObjectImpl.java

import java.rmi.*;

import java.rmi.activation.*;

class MyAutoObjectImpl extends Activatable implements MyAutoObject{

public MyAutoObjectImpl (ActivationID id, MarshalledObject data) throws RemoteExeption {

super(id, 0); }

public String callMeRemotely() throws RemoteException { return “Success Server Call”;

} }

Giải thích:

id là định danh của đối tượng cần kích hoạt

data là dữ liệu bên ngoài cần truyền vào khi đối tượng được kích hoạt.

Khác với cách cài đặt thông thường, đối tượng muốn tự kích hoạt được bởi dịch vụ rmid phải dẫn xuất thêm từ lớp cha Activatable. Các lớp hỗ trợ cho kĩ thuật kích hoạt tự động nằm trong gói thư viện java.rmi.activation.*.

Phương thức khởi tạo super(id, 0) nhận hai đối số: đối số thứ nhất cho biết định danh kích hoạt do dịch vụ kích hoạt rmi truyền vào. Đối số thứ hai (data) có kiểu đối tượng MarshalledObject là đối tượng tổng quát có thể truyền qua lại trên mạng, dùng cho mục đích chung khi ta muốn truyền thông tin từ bên ngoài vào trong quá trình đối tượng khởi động. MarshalledObject cũng giống như các đối tượng khác muốn truyền đi được cũng được cài đặt giao diện Serializable. Khi ta cung cấp thông tin về id và data cho phương thức khởi tạo, lớp cha là lớp Activatable sẽ thông báo và đăng kí sự hiện diện của đối tượng với dịch vụ rmid và máy ảo Java.

Bước 3: Viết chương trình cài đặt và đăng kí khả năng kích hoạt của đối tượng.

// Setup.java

import java.rmi.*;

import java.rmi.activation.*; import java.util.Properties; class Setup {

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

System.setSecurityManager (new RMISecurityManager()); Properties prop = new Properties();

prop.put (“java.security.policy”, “”);

// tạo môi trường chứa các đối tượng cần kích hoạt

ActivationGroupDesc.CommandEnvironment ace = null;

ActivationGroupDesc exampleGroup =

new ActivationGroupDesc (prop, ace);

/* dựa vào thông tin của nhóm, ta yêu cầu hệ thống cung cấp một định danh kích hoạtcho nhóm*/

ActivationGroupID agi =

ActivationGroup.getSystem().registerGroup(exampleGroup);

/* sau khi có định danh nhóm, chương trình yêu cầu hệ thống tạo ranhóm để chứa đối tượng kích hoạt*/

ActivationGroup.createGroup(agi, exampleGroup,0);

/* Khi nhận được yêu cầu từ máy khách, dịch vụ rmid sẽ dựa vào chuỗi URL của biến location dưới đây để đi tìm lớp đối tượng trên máy chủ và kíchhoạt nó.Ta có thể sử dụng giao thức file hoặc http*/

Một phần của tài liệu TÌM HIỂU KỸ THUẬT LẬP TRÌNH PHÂN TÁN TRONG JAVA (Trang 41 - 65)

Tải bản đầy đủ (DOC)

(135 trang)
w