Java RMI - Hướng dẫn nhanh
RMI là viết tắt của Remote Method Invocation. Nó là một cơ chế cho phép một đối tượng nằm trong một hệ thống (JVM) truy cập / gọi một đối tượng đang chạy trên một JVM khác.
RMI được sử dụng để xây dựng các ứng dụng phân tán; nó cung cấp giao tiếp từ xa giữa các chương trình Java. Nó được cung cấp trong góijava.rmi.
Kiến trúc của một ứng dụng RMI
Trong một ứng dụng RMI, chúng tôi viết hai chương trình, server program (nằm trên máy chủ) và một client program (nằm trên máy khách).
Bên trong chương trình máy chủ, một đối tượng từ xa được tạo và tham chiếu của đối tượng đó được cung cấp cho máy khách (sử dụng sổ đăng ký).
Chương trình khách yêu cầu các đối tượng từ xa trên máy chủ và cố gắng gọi các phương thức của nó.
Sơ đồ sau đây cho thấy kiến trúc của một ứng dụng RMI.
Bây giờ chúng ta hãy thảo luận về các thành phần của kiến trúc này.
Transport Layer- Lớp này kết nối giữa máy khách và máy chủ. Nó quản lý kết nối hiện có và cũng thiết lập các kết nối mới.
Stub- Sơ khai là một đại diện (proxy) của đối tượng từ xa tại máy khách. Nó nằm trong hệ thống khách hàng; nó hoạt động như một cổng cho chương trình khách hàng.
Skeleton - Đây là đối tượng nằm ở phía máy chủ. stub giao tiếp với khung này để chuyển yêu cầu đến đối tượng từ xa.
RRL(Remote Reference Layer) - Là lớp quản lý các tham chiếu được thực hiện bởi máy khách đến đối tượng từ xa.
Hoạt động của một ứng dụng RMI
Các điểm sau đây tóm tắt cách hoạt động của một ứng dụng RMI:
Khi máy khách thực hiện một cuộc gọi đến đối tượng từ xa, nó sẽ được nhận bởi phần khai thác cuối cùng chuyển yêu cầu này đến RRL.
Khi RRL phía máy khách nhận được yêu cầu, nó sẽ gọi một phương thức được gọi là invoke() của đối tượng remoteRef. Nó chuyển yêu cầu đến RRL ở phía máy chủ.
RRL ở phía máy chủ chuyển yêu cầu đến Skeleton (proxy trên máy chủ) mà cuối cùng sẽ gọi đối tượng được yêu cầu trên máy chủ.
Kết quả được chuyển trở lại máy khách.
Marshalling và Unmarshalling
Bất cứ khi nào khách hàng gọi một phương thức chấp nhận các tham số trên một đối tượng từ xa, các tham số sẽ được gói lại thành một thông báo trước khi được gửi qua mạng. Các tham số này có thể thuộc kiểu nguyên thủy hoặc các đối tượng. Trong trường hợp kiểu nguyên thủy, các tham số được đặt cùng nhau và một tiêu đề được đính kèm với nó. Trong trường hợp các tham số là các đối tượng thì chúng được tuần tự hóa. Quá trình này được gọi làmarshalling.
Ở phía máy chủ, các tham số được đóng gói được bỏ nhóm và sau đó phương thức bắt buộc được gọi. Quá trình này được gọi làunmarshalling.
Cơ quan đăng ký RMI
Đăng ký RMI là một không gian tên mà trên đó tất cả các đối tượng máy chủ được đặt. Mỗi khi máy chủ tạo một đối tượng, nó sẽ đăng ký đối tượng này với RMIregistry (sử dụngbind() hoặc là reBind()phương pháp). Chúng được đăng ký bằng một tên duy nhất được gọi làbind name.
Để gọi một đối tượng từ xa, máy khách cần một tham chiếu của đối tượng đó. Tại thời điểm đó, máy khách tìm nạp đối tượng từ sổ đăng ký bằng tên liên kết của nó (sử dụnglookup() phương pháp).
Hình minh họa sau đây giải thích toàn bộ quy trình -
Mục tiêu của RMI
Sau đây là các mục tiêu của RMI -
- Để giảm thiểu sự phức tạp của ứng dụng.
- Để giữ an toàn loại.
- Thu gom rác phân tán.
- Giảm thiểu sự khác biệt giữa làm việc với các đối tượng cục bộ và từ xa.
Để viết một ứng dụng Java RMI, bạn sẽ phải làm theo các bước dưới đây:
- Xác định giao diện từ xa
- Phát triển lớp thực thi (đối tượng từ xa)
- Phát triển chương trình máy chủ
- Phát triển chương trình khách hàng
- Biên dịch ứng dụng
- Thực thi ứng dụng
Xác định giao diện từ xa
Giao diện từ xa cung cấp mô tả về tất cả các phương thức của một đối tượng từ xa cụ thể. Máy khách giao tiếp với giao diện từ xa này.
Để tạo giao diện từ xa -
Tạo giao diện mở rộng giao diện xác định trước Remote thuộc về gói.
Khai báo tất cả các phương thức nghiệp vụ có thể được gọi bởi khách hàng trong giao diện này.
Vì có khả năng xảy ra sự cố mạng trong các cuộc gọi từ xa, một ngoại lệ có tên RemoteExceptioncó thể xảy ra; vứt nó.
Sau đây là một ví dụ về giao diện từ xa. Ở đây chúng tôi đã xác định một giao diện với tênHello và nó có một phương thức được gọi là printMsg().
import java.rmi.Remote;
import java.rmi.RemoteException;
// Creating Remote interface for our application
public interface Hello extends Remote {
void printMsg() throws RemoteException;
}
Phát triển lớp triển khai (Đối tượng từ xa)
Chúng ta cần triển khai giao diện từ xa đã tạo ở bước trước. (Chúng ta có thể viết một lớp triển khai riêng biệt hoặc chúng ta có thể trực tiếp làm cho chương trình máy chủ triển khai giao diện này.)
Để phát triển một lớp triển khai -
- Thực hiện giao diện đã tạo ở bước trước.
- Cung cấp triển khai cho tất cả các phương thức trừu tượng của giao diện từ xa.
Sau đây là một lớp thực hiện. Ở đây, chúng tôi đã tạo một lớp có tênImplExample và triển khai giao diện Hello đã tạo ở bước trước và được cung cấp body đối với phương pháp này sẽ in một thông báo.
// Implementing the remote interface
public class ImplExample implements Hello {
// Implementing the interface method
public void printMsg() {
System.out.println("This is an example RMI program");
}
}
Phát triển chương trình máy chủ
Một chương trình máy chủ RMI nên triển khai giao diện từ xa hoặc mở rộng lớp thực thi. Ở đây, chúng ta nên tạo một đối tượng từ xa và liên kết nó vớiRMIregistry.
Để phát triển một chương trình máy chủ -
Tạo một lớp khách hàng từ nơi bạn muốn gọi đối tượng từ xa.
Create a remote object bằng cách khởi tạo lớp triển khai như hình dưới đây.
Xuất đối tượng từ xa bằng phương pháp exportObject() của lớp có tên UnicastRemoteObject cái nào thuộc về gói java.rmi.server.
Nhận sổ đăng ký RMI bằng cách sử dụng getRegistry() phương pháp của LocateRegistry lớp thuộc về gói java.rmi.registry.
Ràng buộc đối tượng từ xa được tạo vào sổ đăng ký bằng cách sử dụng bind() phương thức của lớp có tên Registry. Đối với phương thức này, hãy chuyển một chuỗi đại diện cho tên liên kết và đối tượng được xuất dưới dạng tham số.
Sau đây là một ví dụ về chương trình máy chủ RMI.
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class Server extends ImplExample {
public Server() {}
public static void main(String args[]) {
try {
// Instantiating the implementation class
ImplExample obj = new ImplExample();
// Exporting the object of implementation class
// (here we are exporting the remote object to the stub)
Hello stub = (Hello) UnicastRemoteObject.exportObject(obj, 0);
// Binding the remote object (stub) in the registry
Registry registry = LocateRegistry.getRegistry();
registry.bind("Hello", stub);
System.err.println("Server ready");
} catch (Exception e) {
System.err.println("Server exception: " + e.toString());
e.printStackTrace();
}
}
}
Phát triển Chương trình Khách hàng
Viết một chương trình khách trong đó, tìm nạp đối tượng từ xa và gọi phương thức cần thiết bằng đối tượng này.
Để phát triển một chương trình khách hàng -
Tạo một lớp khách hàng từ nơi bạn định gọi đối tượng từ xa.
Nhận sổ đăng ký RMI bằng cách sử dụng getRegistry() phương pháp của LocateRegistry lớp thuộc về gói java.rmi.registry.
Tìm nạp đối tượng từ sổ đăng ký bằng phương pháp lookup() của lớp Registry cái nào thuộc về gói java.rmi.registry.
Đối với phương thức này, bạn cần chuyển một giá trị chuỗi đại diện cho tên liên kết dưới dạng tham số. Điều này sẽ trả lại cho bạn đối tượng từ xa.
Lookup () trả về một đối tượng kiểu từ xa, truyền xuống kiểu Hello.
Cuối cùng gọi phương thức được yêu cầu bằng cách sử dụng đối tượng từ xa thu được.
Sau đây là một ví dụ về chương trình khách hàng RMI.
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class Client {
private Client() {}
public static void main(String[] args) {
try {
// Getting the registry
Registry registry = LocateRegistry.getRegistry(null);
// Looking up the registry for the remote object
Hello stub = (Hello) registry.lookup("Hello");
// Calling the remote method using the obtained object
stub.printMsg();
// System.out.println("Remote method invoked");
} catch (Exception e) {
System.err.println("Client exception: " + e.toString());
e.printStackTrace();
}
}
}
Biên dịch ứng dụng
Để biên dịch ứng dụng -
- Biên dịch giao diện Từ xa.
- Biên dịch lớp thực hiện.
- Biên dịch chương trình máy chủ.
- Biên dịch chương trình khách hàng.
Hoặc là,
Mở thư mục mà bạn đã lưu trữ tất cả các chương trình và biên dịch tất cả các tệp Java như hình dưới đây.
Javac *.java
Thực thi ứng dụng
Step 1 - Bắt đầu rmi đăng ký bằng cách sử dụng lệnh sau.
start rmiregistry
Điều này sẽ bắt đầu một rmi đăng ký trên một cửa sổ riêng biệt như hình dưới đây.
Step 2 - Chạy tệp lớp máy chủ như hình dưới đây.
Java Server
Step 3 - Chạy tệp lớp khách hàng như hình dưới đây.
java Client
Verification - Ngay khi bạn khởi động máy khách, bạn sẽ thấy kết quả sau trong máy chủ.
Trong chương trước, chúng ta đã tạo một ứng dụng RMI mẫu. Trong chương này, chúng tôi sẽ giải thích cách tạo một ứng dụng RMI trong đó máy khách gọi một phương thức hiển thị cửa sổ GUI (JavaFX).
Xác định giao diện từ xa
Ở đây, chúng tôi đang xác định một giao diện từ xa có tên Hello với một phương pháp có tên animation() trong đó.
import java.rmi.Remote;
import java.rmi.RemoteException;
// Creating Remote interface for our application
public interface Hello extends Remote {
void animation() throws RemoteException;
}
Phát triển lớp triển khai
Trong lớp Triển khai (Đối tượng Từ xa) của ứng dụng này, chúng tôi đang cố gắng tạo một cửa sổ hiển thị nội dung GUI, sử dụng JavaFX.
import javafx.animation.RotateTransition;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
import javafx.util.Duration;
// Implementing the remote interface
public class FxSample extends Application implements Hello {
@Override
public void start(Stage stage) {
// Drawing a Box
Box box = new Box();
// Setting the properties of the Box
box.setWidth(150.0);
box.setHeight(150.0);
box.setDepth(100.0);
// Setting the position of the box
box.setTranslateX(350);
box.setTranslateY(150);
box.setTranslateZ(50);
// Setting the text
Text text = new Text(
"Type any letter to rotate the box, and click on the box to stop the rotation");
// Setting the font of the text
text.setFont(Font.font(null, FontWeight.BOLD, 15));
// Setting the color of the text
text.setFill(Color.CRIMSON);
// Setting the position of the text
text.setX(20);
text.setY(50);
// Setting the material of the box
PhongMaterial material = new PhongMaterial();
material.setDiffuseColor(Color.DARKSLATEBLUE);
// Setting the diffuse color material to box
box.setMaterial(material);
// Setting the rotation animation to the box
RotateTransition rotateTransition = new RotateTransition();
// Setting the duration for the transition
rotateTransition.setDuration(Duration.millis(1000));
// Setting the node for the transition
rotateTransition.setNode(box);
// Setting the axis of the rotation
rotateTransition.setAxis(Rotate.Y_AXIS);
// Setting the angle of the rotation
rotateTransition.setByAngle(360);
// Setting the cycle count for the transition
rotateTransition.setCycleCount(50);
// Setting auto reverse value to false
rotateTransition.setAutoReverse(false);
// Creating a text filed
TextField textField = new TextField();
// Setting the position of the text field
textField.setLayoutX(50);
textField.setLayoutY(100);
// Handling the key typed event
EventHandler<KeyEvent> eventHandlerTextField = new EventHandler<KeyEvent>() {
@Override
public void handle(KeyEvent event) {
// Playing the animation
rotateTransition.play();
}
};
// Adding an event handler to the text feld
textField.addEventHandler(KeyEvent.KEY_TYPED, eventHandlerTextField);
// Handling the mouse clicked event(on box)
EventHandler<javafx.scene.input.MouseEvent> eventHandlerBox =
new EventHandler<javafx.scene.input.MouseEvent>() {
@Override
public void handle(javafx.scene.input.MouseEvent e) {
rotateTransition.stop();
}
};
// Adding the event handler to the box
box.addEventHandler(javafx.scene.input.MouseEvent.MOUSE_CLICKED, eventHandlerBox);
// Creating a Group object
Group root = new Group(box, textField, text);
// Creating a scene object
Scene scene = new Scene(root, 600, 300);
// Setting camera
PerspectiveCamera camera = new PerspectiveCamera(false);
camera.setTranslateX(0);
camera.setTranslateY(0);
camera.setTranslateZ(0);
scene.setCamera(camera);
// Setting title to the Stage
stage.setTitle("Event Handlers Example");
// Adding scene to the stage
stage.setScene(scene);
// Displaying the contents of the stage
stage.show();
}
// Implementing the interface method
public void animation() {
launch();
}
}
Chương trình máy chủ
Một chương trình máy chủ RMI nên triển khai giao diện từ xa hoặc mở rộng lớp thực thi. Ở đây, chúng ta nên tạo một đối tượng từ xa và liên kết nó vớiRMIregistry.
Sau đây là chương trình máy chủ của ứng dụng này. Ở đây, chúng tôi sẽ mở rộng lớp đã tạo ở trên, tạo một đối tượng từ xa và đăng ký nó vào sổ đăng ký RMI với tên liên kếthello.
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class Server extends FxSample {
public Server() {}
public static void main(String args[]) {
try {
// Instantiating the implementation class
FxSample obj = new FxSample();
// Exporting the object of implementation class
// (here we are exporting the remote object to the stub)
Hello stub = (Hello) UnicastRemoteObject.exportObject(obj, 0);
// Binding the remote object (stub) in the registry
Registry registry = LocateRegistry.getRegistry();
registry.bind("Hello", stub);
System.err.println("Server ready");
} catch (Exception e) {
System.err.println("Server exception: " + e.toString());
e.printStackTrace();
}
}
}
Chương trình khách hàng
Sau đây là chương trình khách hàng của ứng dụng này. Ở đây, chúng tôi đang tìm nạp đối tượng từ xa và gọi phương thức của nó có tênanimation().
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class Client {
private Client() {}
public static void main(String[] args) {
try {
// Getting the registry
Registry registry = LocateRegistry.getRegistry(null);
// Looking up the registry for the remote object
Hello stub = (Hello) registry.lookup("Hello");
// Calling the remote method using the obtained object
stub.animation();
System.out.println("Remote method invoked");
} catch (Exception e) {
System.err.println("Client exception: " + e.toString());
e.printStackTrace();
}
}
}
Các bước để chạy ví dụ
Sau đây là các bước để chạy Ví dụ RMI của chúng tôi.
Step 1 - Mở thư mục mà bạn đã lưu trữ tất cả các chương trình và biên dịch tất cả các tệp Java như hình dưới đây.
Javac *.java
Step 2 - Bắt đầu rmi đăng ký bằng cách sử dụng lệnh sau.
start rmiregistry
Điều này sẽ bắt đầu một rmi đăng ký trên một cửa sổ riêng biệt như hình dưới đây.
Step 3 - Chạy tệp lớp máy chủ như hình dưới đây.
Java Server
Step 4 - Chạy tệp lớp khách hàng như hình dưới đây.
java Client
Verification - Ngay khi bạn khởi động máy khách, bạn sẽ thấy kết quả sau trong máy chủ.
Trong chương trước, chúng ta đã tạo một ứng dụng RMI mẫu trong đó máy khách gọi một phương thức hiển thị cửa sổ GUI (JavaFX).
Trong chương này, chúng ta sẽ lấy một ví dụ để xem cách một chương trình khách có thể truy xuất các bản ghi của một bảng trong cơ sở dữ liệu MySQL nằm trên máy chủ.
Giả sử chúng ta có một bảng tên student_data trong cơ sở dữ liệu details như hình bên dưới.
+----+--------+--------+------------+---------------------+
| ID | NAME | BRANCH | PERCENTAGE | EMAIL |
+----+--------+--------+------------+---------------------+
| 1 | Ram | IT | 85 | [email protected] |
| 2 | Rahim | EEE | 95 | [email protected] |
| 3 | Robert | ECE | 90 | [email protected] |
+----+--------+--------+------------+---------------------+
Giả sử tên của người dùng là myuser và mật khẩu của nó là password.
Tạo lớp học sinh viên
Tạo một Student lớp học với setter và getter như hình bên dưới.
public class Student implements java.io.Serializable {
private int id, percent;
private String name, branch, email;
public int getId() {
return id;
}
public String getName() {
return name;
}
public String getBranch() {
return branch;
}
public int getPercent() {
return percent;
}
public String getEmail() {
return email;
}
public void setID(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setBranch(String branch) {
this.branch = branch;
}
public void setPercent(int percent) {
this.percent = percent;
}
public void setEmail(String email) {
this.email = email;
}
}
Xác định giao diện từ xa
Xác định giao diện từ xa. Ở đây, chúng tôi đang xác định một giao diện từ xa có tênHello với một phương pháp có tên getStudents ()trong đó. Phương thức này trả về một danh sách chứa đối tượng của lớpStudent.
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.*;
// Creating Remote interface for our application
public interface Hello extends Remote {
public List<Student> getStudents() throws Exception;
}
Phát triển lớp triển khai
Tạo một lớp và triển khai ở trên đã tạo interface.
Ở đây chúng tôi đang triển khai getStudents() phương pháp của Remote interface. Khi bạn gọi phương thức này, nó truy xuất các bản ghi của một bảng có tênstudent_data. Đặt các giá trị này cho lớp Student bằng cách sử dụng các phương thức setter của nó, thêm nó vào đối tượng danh sách và trả về danh sách đó.
import java.sql.*;
import java.util.*;
// Implementing the remote interface
public class ImplExample implements Hello {
// Implementing the interface method
public List<Student> getStudents() throws Exception {
List<Student> list = new ArrayList<Student>();
// JDBC driver name and database URL
String JDBC_DRIVER = "com.mysql.jdbc.Driver";
String DB_URL = "jdbc:mysql://localhost:3306/details";
// Database credentials
String USER = "myuser";
String PASS = "password";
Connection conn = null;
Statement stmt = null;
//Register JDBC driver
Class.forName("com.mysql.jdbc.Driver");
//Open a connection
System.out.println("Connecting to a selected database...");
conn = DriverManager.getConnection(DB_URL, USER, PASS);
System.out.println("Connected database successfully...");
//Execute a query
System.out.println("Creating statement...");
stmt = conn.createStatement();
String sql = "SELECT * FROM student_data";
ResultSet rs = stmt.executeQuery(sql);
//Extract data from result set
while(rs.next()) {
// Retrieve by column name
int id = rs.getInt("id");
String name = rs.getString("name");
String branch = rs.getString("branch");
int percent = rs.getInt("percentage");
String email = rs.getString("email");
// Setting the values
Student student = new Student();
student.setID(id);
student.setName(name);
student.setBranch(branch);
student.setPercent(percent);
student.setEmail(email);
list.add(student);
}
rs.close();
return list;
}
}
Chương trình máy chủ
Một chương trình máy chủ RMI nên triển khai giao diện từ xa hoặc mở rộng lớp thực thi. Ở đây, chúng ta nên tạo một đối tượng từ xa và liên kết nó vớiRMI registry.
Sau đây là chương trình máy chủ của ứng dụng này. Ở đây, chúng tôi sẽ mở rộng lớp đã tạo ở trên, tạo một đối tượng từ xa và đăng ký nó vào sổ đăng ký RMI với tên liên kếthello.
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class Server extends ImplExample {
public Server() {}
public static void main(String args[]) {
try {
// Instantiating the implementation class
ImplExample obj = new ImplExample();
// Exporting the object of implementation class (
here we are exporting the remote object to the stub)
Hello stub = (Hello) UnicastRemoteObject.exportObject(obj, 0);
// Binding the remote object (stub) in the registry
Registry registry = LocateRegistry.getRegistry();
registry.bind("Hello", stub);
System.err.println("Server ready");
} catch (Exception e) {
System.err.println("Server exception: " + e.toString());
e.printStackTrace();
}
}
}
Chương trình khách hàng
Sau đây là chương trình khách hàng của ứng dụng này. Ở đây, chúng tôi đang tìm nạp đối tượng từ xa và gọi phương thức có têngetStudents(). Nó lấy các bản ghi của bảng từ đối tượng danh sách và hiển thị chúng.
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.*;
public class Client {
private Client() {}
public static void main(String[] args)throws Exception {
try {
// Getting the registry
Registry registry = LocateRegistry.getRegistry(null);
// Looking up the registry for the remote object
Hello stub = (Hello) registry.lookup("Hello");
// Calling the remote method using the obtained object
List<Student> list = (List)stub.getStudents();
for (Student s:list)v {
// System.out.println("bc "+s.getBranch());
System.out.println("ID: " + s.getId());
System.out.println("name: " + s.getName());
System.out.println("branch: " + s.getBranch());
System.out.println("percent: " + s.getPercent());
System.out.println("email: " + s.getEmail());
}
// System.out.println(list);
} catch (Exception e) {
System.err.println("Client exception: " + e.toString());
e.printStackTrace();
}
}
}
Các bước để chạy ví dụ
Sau đây là các bước để chạy Ví dụ RMI của chúng tôi.
Step 1 - Mở thư mục mà bạn đã lưu trữ tất cả các chương trình và biên dịch tất cả các tệp Java như hình dưới đây.
Javac *.java
Step 2 - Bắt đầu rmi đăng ký bằng cách sử dụng lệnh sau.
start rmiregistry
Điều này sẽ bắt đầu một rmi đăng ký trên một cửa sổ riêng biệt như hình dưới đây.
Step 3 - Chạy tệp lớp máy chủ như hình dưới đây.
Java Server
Step 4 - Chạy tệp lớp khách hàng như hình dưới đây.
java Client