Chương 2 Hệ thống đối tượng phân tán
2.2 Sự phân tán trong môi trường Java: RMI
2.2.3 Kiến trúc của cơ chế RMI
2.2.3.1 Interfaces nòng cốt của RMI
Kiến trúc RMI dựa trên một yếu tố quan trọng đó là: việc định nghĩa ra các hành vi và sự thi hành của các hành vi đó, các hành vi đó là các nội dung khác nhau. RMI cho phép các đoạn mã định nghĩa ra các hành vi và các đoạn mã thi hành các hành vi đó duy trì ở hai vị trí tách biệt nhau và chạy trên các máy ảo Java khác nhau. Yếu tố này rất phù hợp với các nhu cầu của một hệ thống phân tán, hệ thống phân tán là một hệ thống mà có các client quan tâm đến định nghĩa (giao diện của dịch vụ) của một dịch vụ và các server thì tập trung vào việc cung cấp các dịch vụ[10].
Một cách cụ thể hơn, trong RMI, định nghĩa của một dịch vụ từ xa được viết mã nhờ sử dụng một interface của java, sự thi hành của dịch vụ từ xa được viết mã bên trong một lớp. Do đó chìa khoá để hiểu được cơ chế RMI là đó là: các interface thì định nghĩa ra các hành vi, còn các lớp thì định nghĩa ra các thi hành của hành vi đó
Hình 2.3: Kiến trúc của RMI
Interface của Java không chứa các mã thi hành. RMI hỗ trợ hai lớp thi hành trên cùng một interface. Lớp thứ nhất là sự thực thi của hành vi và nó chạy trên server. Còn lớp thứ hai hoạt động như là một sự uỷ quyền của một dịch vụ từ xa và nó chạy trên client.
Hình 2.4: Sự hỗ trợ thi hành của RMI
Một chương trình phía client tạo ra các lời gọi phương thức trên đối tượng uỷ quyền. RMI sẽ gửi yêu cầu này tới các máy ảo ở xa, tiến hành thực thi yêu cầu. Bất cứ giá trị trả về nào được trả về bởi sự thi hành lời gọi phương thức đều được gửi trở lại dịch vụ uỷ quyền (Proxy) và sau đó tới chương trình của phía khách.
Các giao diện và lớp mô tả các hành vi từ xa của hệ thống RMI được định nghĩa trong gói Java.rmi. Hình minh hoạ dưới đây sẽ mô tả mối quan hệ giữa giao diện và lớp.
Trong RMI, giao diện từ xa khai báo tập các phương thức có thể được gọi từ một máy ảo Java từ xa. RMI cung cấp một giao diện từ xa có tên Remote. Khi một lớp muốn đối tượng của nó có thể truy cập được từ xa thì lớp đó phải thi hành giao diện này. Một giao diện từ xa phải thoả mãn các yêu cầu sau:
- Một giao diện từ xa phải là mở rộng (trực tiếp hoặc gián tiến) của giao diện java.rmi.Remote
Mỗi phương thức khai báo trong một giao diện hoặc trong các siêu giao diện của chúng phải thoả mãn khai báo phương thức từ xa như sau:
Một khai báo phương thức từ xa phải bao gồm ngoại lệ java.rmi.RemoteException (hoặc một trong các siêu lớp của nó phải là java.io.IOException hoặc java.lang.
Exception) trong mệnh đề throws của nó, khi thêm vào các ngoại lệ ứng dụng đặc biệt bất kỳ (chú ý rằng các ngoại lệ ứng dụng đặc biệt không cần thiết phải là mở rộng của java.rmi.RemoteException).
Trong một khai báo phương thức từ xa, một đối tượng từ xa, được khai báo như là một biến hay giá trị trả về (có thể khai báo trực tiếp trong danh sách các biến hoặc có thể nhúng trong một biến của đối tượng không phải là từ xa), phải khai báo như là một giao diện từ xa chứ không phải là lớp thi hành của giao diện.
Giao diện java.rmi.Remote là giao diện rỗng không có phương thức nào: Public interface Remote()
Một giao diện từ xa phải ít nhất là mở rộng của giao diện java.rmi.Remote ( hoặc là một giao diện từ xa mở rộng của giao diện java.rmi.Remote). Tuy nhiên một giao diện từ xa cũng có thể là mở rộng của một giao diện không từ xa với điều kiện sau:
Một giao diện từ xa có thể là mở rộng của một giao diện không từ xa, miễn là tất cả các phương thức của giao diện mở rộng thoả mãn các yêu cầu của phương thức khai báo từ xa
Ví dụ sau đây, giao diện BankAccount định nghĩa một giao diện từ xa để truy cập vào tài khoản ngân hàng. Nó bao gồm các phương thức gửi tiền vào ngân hàng, lấy số dư tài khoản, rút tiền từ tài khoản.
public interface BankAccount extends java.rmi.Remote { public void deposit(float amount)
throws java.rmi.RemoteException; public void withdraw(float amount)
throws OverdrawnException, java.rmi.RemoteException; public float getBalance()
throws java.rmi.RemoteException; }
Ví dụ sau sẽ thể hiện một giao diện từ xa hợp lệ Beta là mở rộng của giao diện không từ xa Alpha, có các phương thức từ xa và giao diện java.rmi.Remote:
public interface Alpha {
public final String okay = "constants are okay too"; public Object foo(Object obj)
throws java.rmi.RemoteException; public void bar() throws java.io.IOException; public int baz() throws java.lang.Exception; }
public interface Beta extends Alpha, java.rmi.Remote { public void ping() throws java.rmi.RemoteException; }
2.2.3.2 Kiến trúc các tầng giao thức RMI
Các thi hành của RMI cần thiết phải được tạo nên từ ba tầng trừu tượng. Tầng thứ nhất đó là tầng của Stub và Skeleton, là tầng nằm ở ngay phía dưới tầm nhìn thấy của
nhà phát triển, tầng này nó chặn lại các lời gọi phương thức của phía client và nó gửi một lần nữa tới một dịch vụ RMI ở xa. Tầng tiếp theo đó là tầng tham chiếu từ xa , tầng này nó sẽ biết cách để thể hiện và quản lý các tham chiếu mà được tạo ra từ phía client tới các đối tượng dịch vụ từ xa. Trong JDK 1.1 (phiên bản Java) tầng này nó sẽ kết nối các client tới các đối tượng dịch vụ từ xa đang chạy và được xuất khẩu trên một server. Kết nối này là liên kết một-một. Và trong Java 2 SDK thì tầng này được nâng cấp để hỗ trợ việc kích hoạt các đối tượng dịch vụ từ xa đang không hoạt động qua Remote Object Activation. Tầng cuối cùng là tầng giao vận, tầng này dựa trên các kết nối TCP/IP giữa các máy tính trên một mạng[10].
Hình 2.6: Các tầng kiến trúc của RMI
Bằng cách sử dụng một kiến trúc được phân tầng, thì mỗi tầng của nó có thể được nâng cấp và được thay thể mà không ảnh hưởng gì tới những tầng còn lại của hệ thống. Ví dụ tầng giao vận có thể được thay thế bởi một tầng UDP/IP mà không hề ảnh hưởng tới các tầng ở trên. Bây giờ ta đi vào tìm hiểu chi tiết từng tầng.
Tầng Stub và Skeleton
Tầng Stub và Skeleton của RMI nằm ở ngay phía dưới tầm nhìn của các nhà phát triển Java. Trong tầng này thì RMI sử dụng kiểu thiết kế Proxy. Trong kiểu thiết kế Proxy, thì một đối tượng trong một ngữ cảnh này thì sẽ được trình diễn bởi một đối tượng khác trong một ngữ cảnh khác. Proxy sẽ biết cách để gửi các lời gọi phương thức giữa các đối tượng tham gia.
Skeleton là một lớp trợ giúp, nó được sinh ra để cho RMI sử dụng. Lớp Skeleton sẽ biết cách để giao tiếp với lớp Stub qua một liên kết RMI. Lớp skeleton điều khiển cuộc trao đổi với lớp stub. Nó đọc các tham số từ lời gọi phương thức từ liên kết, sau đó tạo ra lời gọi phương thức tới đối tượng thi hành dịch vụ từ xa, sau đó nhận giá trị trả về, rồi chuyển giá trị trả về đó cho lớp Stub.
Tầng tham chiếu từ xa
Tầng này hỗ trợ một đối tượng RemoteRef, đối tượng này thể hiện một liên kết tới một đối tượng thực thi dịch vụ từ xa. Các đối tượng stub sử dụng phương thức invoke() trong đối tượng RemoteRef để gửi lời gọi phương thức.
Sự thi hành của RMI trong phiên bản JDK 1.1 chỉ cung cấp một cách thức để các client kết nối tới các đối tượng thi hành dịch vụ từ xa, đó là kết nối điểm - điểm . Trước khi một client có thể sử dụng một dịch vụ từ xa, thì dịch vụ từ xa đó phải được trình diễn trên server và được đăng ký tới hệ thống RMI ( nếu nó là dịch vụ chính, thì nó cũng phải được đặt tên ,và được đăng ký trong register RMI)
Trong phiên bản Java2 JDK sự thi hành của RMI thêm vào một semantic cho kết nối client-server. Trong phiên bản này RMI hỗ trợ các đối tượng từ xa có khả năng tự kích hoạt, khi một lời gọi phương thức được tạo ra từ Proxy tới đối tượng có khả năng tự kích hoạt. Lúc đó RMI sẽ xác định xem: nếu sự thi đối tượng dịch vụ từ xa là chưa được kích hoạt, thì RMI sẽ kích hoạt nó và khôi phục trạng thái của nó tới một file trên đĩa
Tầng giao vận
Tầng giao vận tạo ra các kết nối giữa các máy ảo. Tất cả các kết nối đó là các kết nối mạng dựa trên luồng sử dụng giao thức TCP/IP.
Thậm chí hai máy ảo chạy trên cùng một máy tính vật lý, thì chúng sẽ kết nối thông qua giao thức mạng TCP/IP nằm ở trên máy chủ có địa chỉ cục bộ localhost (127.0.0.0). Biểu đồ sau đã đưa ra khả năng sử dụng không giới hạn của các kết nối TCP/IP giữa các máy ảo.
Như chúng ta đã biết, TCP/IP cung cấp kết nối có tính liên tục, theo luồng giữa hai máy dựa trên địa chỉ IP và số của cổng (port number) tại mỗi điểm cuối. Thông thường, tên một DNS được sử dụng thay cho địa chỉ IP; điều đó có nghĩa là chúng ta có thể nói về kết nối TCP/IP giữa flicka.magelang.com:3452 và
rosa.jguru.com:4432. Trong phiên bản này, kết nối TCP/IP được sử dụng cho tất cả các kết nối máy tới máy.
Trong top của TCP/IP, RMI sử dụng mức giao thức dùng dây gọi là Java Remote Method Protocol (JRMP). JRMP là một giao thức dựa trên luồng, và hiện tại có hai phiên bản. Phiên bản thứ nhất JDK 1.1 yêu cầu phải sử dụng các lớp Skeleton trên server. Phiên bản thứ hai Java 2 SDK được tối ưu cho sự thi hành và không yêu cầu các lớp skeleton.
Tầng giao vận RMI được thiết kế để tạo kết nối giữa client và server. Thường thì tầng giao vận được sử dụng cho các kết nối đa TCP/IP, nhưng có một số cấu hình mạng chỉ cho phép kết đơn TCP/IP giữa một client và server.
Trong biểu đồ trên, máy ảo đa thành phần kết nối trong một TCP/IP đơn.