CHUYỂN THAM SỐ TRONG LỜI GỌI PHƯƠNG THỨC TỪ XA

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 27 - 37)

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

1.5.CHUYỂN THAM SỐ TRONG LỜI GỌI PHƯƠNG THỨC TỪ XA

2.1.6. Chuyển tham số theo tham trị và tham biến

Đối với Java, trên máy cục bộ, hầu hết các biến kiểu đối tượng đều truyền theo tham biến trong các lời gọi hàm. Nghĩa là một khi biến đối tượng được truyền vào phương thức, nếu bên trong phương thức thay đổi giá trị của đối tượng thì khi lời gọi phương thức chấm dứt, giá trị của đối tượng cũng thay đổi theo. Ví dụ:

class Number{

public int value=0; public Number (int v) {

value=v; }

}

class Program{

public static void doIncrease(Number n) { n.value++;

}

public static void main (String args[]){ Number num=new Number(12); doIncrease(num);

System.out.println(num.value); }

}

Khi chạy chương trình, value sẽ có giá trị là 13 vì sau khi thực hiện phương thức doIncrease(num), giá trị value của đối tượng num được tăng lên 1 đơn vị.

Tuy nhiên các kiểu dữ kiệu đơn giản như: int, float, double, char, byte, long…lại được truyền theo tham trị. Có nghĩa là giá trị của tham số mà hàm hay phương thức xử lý chỉ là bản sao của biến truyền từ ngoài vào. Ví dụ:

class Program{

public static void doIncease(int n) { n++;

}

public static void main (String args[]) { int num = 12;

doIncrease(num); System.out.print(num); }

}

Ở ví dụ này kết quả xuất ra màn hình là 12. Tóm lại, việc truyền tham số cho phương thức trong máy cục bộ, Java cho phép đối tượng được truyền theo tham biến còn các kiểu dữ liệu đơn giản được truyền theo tham trị.

Việc truyền tham số qua mạng theo cơ chế RMI lại khác với cách truyền tham số thông thường. Tất cả các dữ liệu kiểu đối tượng muốn truyền qua mạng đều buộc phải cài đặt một trong hai giao tiếp Remote hoặc Serializable. Các đối tượng cài đặt giao tiếp Remote sẽ được truyền theo tham biến còn các đối tượng cài đặt giao tiếp Serializable sẽ được tuyền theo tham trị. Còn tất cả các kiểu dữ liệu đơn giản như int, char… đều được truyền theo tham trị.

2.1.7. Chuyển đối tượng đến trình chủ theo tham trị:

Hầu như các kiểu dữ liệu đối tượng cơ bản như String, Date, Time…trong Java đều cài đặt giao diện Serializable, cho nên chúng được chuyển cho các lời gọi phương thức từ xa theo tham trị.

Tương tự lớp Remote, lớp Serializable chỉ là một lớp rỗng dùng cho mục đích báo hiệu. Tất cả các lớp đối tượng cài đặt giao diện Serializable đều được Java xem là có khả năng tuần tự hóa (serialize). Nghĩa là ta có thể yêu cầu Java lưu toàn bộ đối tượng lên đĩa cứng thành một tập tin nào đó, sau đó đem tập tin này đến máy khác, ta có thể khôi phục lại đối tượng về trạng thái ban đầu mà không cần phải khởi tạo đối tượng mới bằng lệnh new.

Với cơ chế truyền tham số đối tượng theo tham trị, khi ta viết chương trình gọi một phương thức của đối tượng ở xa, nếu trong lời gọi có yêu cầu tham số kiểu đối tượng, đối tượng sẽ được đóng gói và chuyển toàn bộ đến máy chủ (nơi tiếp

nhận tham số thực thi phương thức). Tại máy chủ, đối tượng sẽ được mở ra, trở lại trạng thái ban đầu và đưa vào sử dụng. Quá trình đóng gói tham số trên máy khách để chuyển đi được thực hiện bởi lớp trung gian Stub. Ngược lại, quá trình mở gói dữ liệu để khôi phục tham số diễn ra trên máy chủ được thực hiện bởi lớp trung gian Skel. Quy trình chuyển tham số và dữ liệu qua lại giữa hai lớp trung gian Stub và Skel trong thuật ngữ lập trình phân tán gọi là mashaling data.

Trong các lời gọi phương thức RMI, kiểu dữ liệu đối tượng nếu không cài đặt một trong hai giao tiếp Serializable hoặc Remote thì sẽ không thể dùng làm tham số chuyển qua mạng được.

Chương trình minh họa chuyển tham số đối tượng qua mạng:

Dưới đây là một chương trình ví dụ minh họa cho thấy cách chuyển một đối tượng từ máy khách đến máy chủ. Chương trình này giống như trò chơi ném bóng, trình khách sẽ ném một quả bóng (đối tượng Ball) đến máy chủ, trình chủ sẽ tiếp nhận quả bóng và trả về cho trình khách.

Bước 1: Thiết kế lớp đối tượng Ball.class được dùng làm tham số chuyển qua mạng giữa trình khách và trình chủ: (adsbygoogle = window.adsbygoogle || []).push({});

//Ball.java

import java.io.*;

class Ball implements Serializable{ int weight=0;

public Ball(int w){ weight=w; }

public int getWeight(){ return weight;

}

public void setWeight(int w){ weight=w;

} }

Lớp Ball dùng để biểu diễn một quả banh có trọng lượng weight. Mục đích của ta là sử dụng đối tượng Ball để làm tham số di chuyển trên mạng giữa trình khách và trình chủ, cho nên lớp Ball được khai báo là có khả năng tuần tự hóa.

Bước 2: Đặc tả interface cho đối tượng trên máy chủ

//PingServer.java

import java.rmi.*;

interface PingServer extends Remote {

public Ball ping(Ball b) throws RemoteException; }

Đối tượng PingServer được đặt trên trình chủ. Phương thức ping() của đối tượng này sẽ được gọi bởi trình khách. Trình khách sử dụng phương thức ping() để ném quả bóng lên trình chủ thông qua tham số có kiểu đối tượng là Ball. Sau khi tiếp nhận đối tượng Ball, trình chủ sẽ trả lại đối tượng này cho trình khách.

Bước 3: Cài đặt chi tiết cho giao diện PingServer

//PingServerImpl.java

import java.rmi.*;

class PingServerImpl implements PingServer{ public Ball ping(Ball b) throws RemoteException {

System.out.println(“Client send a ball objectweight” + b.getWeight()); b.setWeight(b.getWeight()+15);

return b; }

}

Trình chủ tiếp nhận quả bóng từ trình khách và tăng trọng lượng của nó lên, sau đó trả về cho trình khách.

Bước 4: cài đặt chương trình điều khiển trên server

import java.rmi.*;

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

public static void main(String args[]) throws Exception{ PingServer server=new PingServerImpl();

UnicastRemoteObject.exportObject(server); Naming.bind("rmi://localhost/pingobject",server); System.out.println("Waiting for client request ..."); }

}

Bước 5: Cài đặt chương trình client gọi phương thức của đối tượng PingServer (adsbygoogle = window.adsbygoogle || []).push({});

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

public static void main(String args[]) throws Exception{ Ball ball=new Ball(12);

PingServer server=

(PingServer)Naming.lookup("rmi://localhost/pingobject"); System.out.println("Ball weight before send to server "+ball.getWeight()); Ball anotherBall=server.ping(ball);

System.out.println("Ball weight after send to server "+ball.getWeight()); System.out.println("Ball weight return by server ”

+anotherBall.getWeight()); }

}

Các bước thực hiện chương trình:

- Để đơn giản, ta gom tất cả các lớp vào một thư mục, ở đây là

- Tiến hành biên dịch các lớp:

- Tạo ra stub, chạy rmiregistry:

- Chạy trình chủ:

- Chạy trình khách:

Kết quả cho thấy: trong trình khách, đối tượng Ball tại Client trước và sau gọi phương thức ping() vẫn giữa nguyên giá trị là 12. Trong khi đó đối tượng Ball đưa lên trình chủ được tăng giá trị lên 15. Kết quả trả về từ trình chủ là một đối tượng anotherBall khác với đối tượng Ball được chuyển đi từ trình khách – anotherBall có giá trị là 12 + 15 = 27. Như vậy đối tượng Ball được chuyển đi trong phương thức ping() theo tham trị.

Nếu bỏ thuộc tính Serializable của lớp Ball, quá trình biên dịch vẫn thành công nhưng khi thực thi, chương trình sẽ phát hiện lỗi và ném ra ngoại lệ như sau:

2.1.8. Chuyển đối tượng đến trình chủ theo tham biến:

Ở ví dụ chuyền quả banh trên, đối tượng Ball được di chuyển qua lại giữa trình khách và trình chủ, mỗi lần truyền đi lại phải đóng gói và truyền theo giao thức mạng. Nếu kích thước đối tượng quá lớn thì sẽ ảnh hưởng đến tốc độ thực hiện của chương trình. Vấn đề đặt ra ở đây là có cách nào để trình khách có thể yêu cầu trình chủ tham chiếu và xử lý trực tiếp đối tượng trên trình khách hay không? Có nghĩa là trình chủ được trình khách truy xuất từ xa thì trình khách cũng được trình chủ truy xuất từ xa.

Hình 2.5-10: Trình chủ và trình khách có thể triệu gọi lẫn nhau.

Như trên đã nói, với Java, các đối tượng nếu cài đặt giao diện Remote sẽ có khả năng được truyền qua mạng theo tham biến. Cơ chế gọi ngược từ xa từ trình chủ đến trình khách còn được gọi là cơ chế callback.

Chương trình minh họa chuyển đối tượng qua mạng theo tham biến:

Chương trình sau cho thấy cách cài đặt kĩ thuật callback triệu gọi lẫn nhau giữa đối tượng trên trình khách và đối tượng trên trình chủ sử dụng tham biến. Ta sẽ tạo ra hai đối tượng, đối tượng AtClient chạy trên máy khách và đối tượng AtServer chạy trên máy chủ. Đầu tiên, như cách làm ở các ví dụ trước, trình khách sẽ liên lạc với rmiregistry để tìm tham chiếu đến đối tượng AtServer trên máy chủ.

Tiếp đến, trình khách sẽ tạo ra đối tượng AtClient ngay trên máy khách và gọi phương thức của đối tượng AtServer từ xa để đăng kí đối tượng AtClient với trình chủ. Bắt đầu từ thời điểm đó, đối tượng AtClient trên máy khách và AtServer trên máy chủ có thể tự do điều khiển và triệu gọi lẫn nhau. Các bước cài đặt chương trình như sau:

Bước 1: Đặc tả interface cho đối tượng AtClient

//AtClient.java

import java.rmi.*;

interface AtClient extends Remote {

public void callClientMethod(String message) throws RemoteException; }

Bước 2: Đặc tả interface cho đối tượng AtServer

//AtServer.java

import java.rmi.*; (adsbygoogle = window.adsbygoogle || []).push({});

interface AtServer extends Remote {

public void registerClient(AtClient c) throws RemoteException;

public void callServerMethod(String message) throws RemoteException; }

Bước 3: Cài đặt chi tiết cho đối tượng AtClient thông qua lớp AtClientImpl

//AtClientImpl.java

import java.rmi.*;

class AtClientImpl implements AtClient {

public void callClientMethod(String message) throws RemoteException { System.out.println(message);

} }

Bước 4: Cài đặt chi tiết cho đối tượng AtServer thông qua lớp AtServerImpl

//AtServerImpl.java

import java.rmi.*;

class AtServerImpl implements AtServer {

// phương thức này được trình khách triệu gọi để đăng ký đối tượng AtClient với trình chủ

public void registerClient(AtClient c) throws RemoteException {

client = c;

}

public void callServerMethod(String message) throws RemoteException {

System.out.println(message);

for (int i=1; i<10; i++) {

String msg= "Server response "+ Math.random()*1000;

// Triệu gọi phương thức của đối tượng chạy trên máy khách

client.callClientMethod(msg);

}

} }

Bước 5: Xây dựng chương trình điều khiển trên Server và chương trình yêu cầu trên Client.

// Setup.java

import java.rmi.*;

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

public static void main(String args[]) throws Exception{ AtServer server=new AtServerImpl(); (adsbygoogle = window.adsbygoogle || []).push({});

UnicastRemoteObject.exportObject(server);

Naming.bind("rmi://localhost/serverobject", server); System.out.println("Waiting for client request ..."); } } //Client.java import java.rmi.*; import java.rmi.server.*; class Client {

public static void main(String args[]) throws Exception{ AtClient client=new AtClientImpl();

UnicastRemoteObject.exportObject(client);

server.registerClient(client);

server.callServerMethod("Client Contact Server"); }

}

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

- Chạy trình chủ:

- Chạy trình khách và kết quả:

Vì Client là một đối tượng có khả năng tham chiếu từ xa cho nên giống như đối tượng trên máy chủ, ta phải dùng phương thức

UnicastRemoteObject.exportObject() để thông báo sự hiện diện và khả năng giao

tiếp từ xa của đối tượng AtClient với máy ảo Java. Sau khi lấy về tham chiếu đến đối tượng AtServer trên máy chủ bằng phương thức Naming.lookup(), trình khách sẽ gọi phương thức registryClient() để cho phép trình chủ tham chiếu được đến đối tượng AtClient trên máy khách. Sau đó, trình chủ sẽ không cần phải dùng phương thức Naming.lookup() để tìm tham chiếu đến đối tượng AtClient trên rmiregistry nữa.

Khi trình khách gọi phương thức callServerMethod() của đối tượng AtServer trên máy chủ, phương thức này lại sử dụng phương thức callClientMethod() của đối tượng AtClient trên máy khách. Trước khi gọi callClientMethod(), trình chủ thực hiện một số Calculator (sinh số ngẫu nhiên), kết quả Calculator sau đó được chuyển cho trình khách và trình khách tự động in ra kết quả. Nếu như không dùng kĩ thuật callback thì trình chủ sẽ không có cách nào liên lạc với trình khách để trả về kết quả trực tiếp. Khi đó, có hai cách để trình khách nhận được kết quả gián tiếp:

- Cách thứ nhất: Trình chủ chủ động đóng gói tất cả các kết quả Calculator (số ngẫu nhiên) sau khi thực hiện xong rồi gửi cho trình khách. Trình khách sẽ hiển thị kết quả nhận được. Cách này giống như ví dụ ném bóng.

- Cách thứ hai: Trình khách chủ động yêu cầu trình chủ trả kết quả sau một khoảng thời gian nhất định nào đó.

Ta thấy cả hai cách đều có nhược điểm. Vì thế, áp dụng kĩ thuật callback là tối ưu.

Lưu ý: Nếu ta cài đặt cả 2 giao tiếp Remote và Serializable thì đối tượng sẽ cùng lúc có 2 khả năng vừa có thể dùng làm tham biến và vừa có thể làm tham trị. Nếu ta dùng lệnh UnicastRemoteObject.exportObject() cho đối tượng thì nó sẽ được truyền đi theo tham biến, nếu không dùng lệnh này thì đối tượng sẽ được truyền đi theo tham trị.

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 27 - 37)