Hệ thống máy tính đang trải qua một cuộc cách mạng. Từ năm 1945 cho đến năm 1985, các máy tính rất lớn và rất đắt. Hầu hết các tổ chức chỉ có rất ít máy tính và không biết cách nối chúng. Đến giữa những năm 1980, hai mở rộng trong kỹ thuật đã thay đổi tình hình đó. Thứ nhất là sự phát triển vi xử lý, từ CPU chỉ có 8 bit đã phát triển thành 16, 32, 64 bit. Nhiều CPU đã có một năng lực tính toán của một siêu máy tính nhưng với giá thấp. Sự phát triển thứ hai là sự phát minh mạng máy tính tốc độ cao, mạng cục bộ (Localarea networksLAN) cho phép hàng trăm máy tính có thể kết nối được với nhau để trao đổi thông tin. Mạng diện rộng (Widearea networkWAN) cho phép hàng triệu máy tính trên trái đất có thể được kết nối với tốc độ truyền dữ liệu từ64Kbps đến gigabits mỗi giây.Kết quả của những cải tiến về mặt kỹ thuật đem lại sự kết nối dễ dàng nhiều hệthống máy tính với nhau để tạo một mạng cao tốc. Chúng thường được gọi là : mạng máy tính hoặc hệ phân tán.Để xây dựng được một hệ phân tán hoạt động hiệu quả và tối ưu, nhà thiết kế phải giải quyết rất nhiều vấn đề có tính chiến lược. Tiểu luận nhằm trình bày một số vấn đềcơ bản khi xây dựng một hệ phân tán thông qua ngôn ngữ Java và đưa ra một số giải pháp nhằm giải quyết các vấn đề đó. Đồng thời tiểu luận còn đưa ra một số ví dụ minh họa cho những vấn đề và giải pháp trên.
TỔNG QUAN VỀ LẬP TRÌNH PHÂN TÁN
Lập trình phân tán là gì?
Lập trình thông thường trên máy cục bộ cho phép các đối tượng như thủ tục và hàm được nạp trực tiếp vào bộ nhớ và thực thi ngay trên máy Đối với các hàm có sẵn trong thư viện, người lập trình chỉ cần chú ý đến tham số đầu vào và kết quả đầu ra.
Lập trình phân tán là quá trình phát triển ứng dụng trên nhiều máy tính khác nhau, trong đó các đối tượng của chương trình, bao gồm phương thức và thuộc tính, có khả năng tương tác và gọi lẫn nhau.
Hình 1.1-1: Mô hình triệu gọi các đối tượng từ xa
Trong minh họa trên, các đối tượng A1 và A2 ở máy A có thể gọi nhau, B1 và B2 ở máy
B cũng vậy, đó là lời gọi cục bộ Đối tượng A1 tại máy A có khả năng gọi đối tượng C1 ở máy C, trong khi B1 tại máy B cũng có thể thực hiện cuộc gọi đến C1 ở máy C, thể hiện các cuộc gọi từ xa.
Vấn đề gọi phương thức từ xa
Trên máy cục bộ, các phương thức được quản lý trong vùng nhớ, cho phép lập trình viên thực hiện lời gọi mà không cần lo lắng về cách chúng giao tiếp Ngược lại, trên các máy khác nhau, các phương thức hoạt động trên hai tiến trình với không gian địa chỉ khác nhau, làm cho việc truyền tham số và tham chiếu địa chỉ trở nên phức tạp hơn so với trên cùng một máy.
Lời gọi phương thức cục bộ luôn đảm bảo thực hiện thành công, trong khi việc gọi các phương thức từ xa có thể gặp sự cố do mạng Đối với hàm chạy trên máy cục bộ, tham số thường được đưa vào ngăn xếp để chương trình lấy ra thực hiện, trong khi tham số cho hàm từ xa cần được đóng gói và chuyển qua mạng theo giao thức để đến nơi cần thiết.
Lập trình phân tán với Java
Java là một ngôn ngữ hỗ trợ lập trình phân tán với hai thư viện chuẩn là RMI và CORBA
RMI (Remote Method Invocation) là một cơ chế trong Java cho phép gọi phương thức từ xa Với phương pháp lập trình hướng đối tượng, RMI liên kết chặt chẽ các thao tác và lời gọi phương thức với các đối tượng, tạo điều kiện cho việc phát triển ứng dụng phân tán hiệu quả.
CORBA (Common Object Request Broker Architecture) là một đặc tả của OMG (Object Management Group) nhằm đạt được sự tương tác giữa các nút tính toán phân tán Mục tiêu của CORBA là tạo ra một cấu trúc cho phép các môi trường không đồng nhất giao tiếp ở mức đối tượng, bất kể ai đã thiết kế hai điểm cuối của ứng dụng phân tán CORBA là một ngôn ngữ trung lập, được thực thi rộng rãi trên nhiều nền tảng khác nhau.
*Sự khác nhau giữa RMI và CORBA:
RMI là một thành phần của bộ J2SDK, cung cấp các hàm thư viện hỗ trợ gọi phương thức từ xa và trả về giá trị cho các ứng dụng tính toán phân tán Ngôn ngữ Java được sử dụng cho cả hai phía: phía gọi và phía phương thức được gọi.
CORBA là một chuẩn công nghiệp cho phép gọi phương thức từ xa và nhận kết quả trả về, khác với RMI, CORBA hỗ trợ nhiều ngôn ngữ lập trình khác nhau Điều này có nghĩa là cả bên gọi và bên được gọi có thể sử dụng ngôn ngữ không phải Java, tạo ra tính linh hoạt cao trong việc phát triển ứng dụng.
RMI (Remote Method Invocation) là một tập hợp các hàm thư viện đơn giản, cho phép thực hiện các cuộc gọi phương thức từ xa một cách dễ dàng nhờ việc cả hai bên sử dụng chung ngôn ngữ lập trình và kiến trúc máy.
Bộ phát triển J2SDK hỗ trợ cả RMI và CORBA, cho phép các đối tượng Java giao tiếp với đối tượng CORBA thông qua hai phương pháp khác nhau.
Trong giới hạn bài tiểu luận chúng ta chỉ đi tìm hiểu về phương pháp lập trình phân tán trong RMI
LẬP TRÌNH PHÂN TÁN VỚI RMI
Giới thiệu
RMI (Remote Method Invocation) là công nghệ được SUN giới thiệu lần đầu trong JDK 1.1 vào tháng 2 năm 1997, đánh dấu bước ngoặt quan trọng trong lập trình mạng Với thiết kế dễ sử dụng, RMI cung cấp cho lập trình viên những công cụ mạnh mẽ cần thiết để phát triển ứng dụng phân tán hiệu quả.
Mục đích chính của RMI là hỗ trợ lập trình viên phát triển chương trình phân tán bằng Java với cú pháp và ngữ nghĩa tương tự như lập trình truyền thống Để đạt được điều này, RMI đã ánh xạ các lớp và đối tượng trong máy ảo Java thành các lớp và đối tượng trong môi trường phân tán.
Kiến trúc RMI
2.2.1 Giao diện (interface) - trái tim của RMI:
Kiến trúc RMI dựa trên nguyên lý phân tách giữa định nghĩa hành vi và mô tả hành vi Trong RMI, mã định nghĩa hành vi và mã mô tả hành vi hoạt động độc lập trên các máy ảo Java khác nhau Hệ thống phân tán yêu cầu máy khách định nghĩa dịch vụ mong muốn, trong khi máy chủ cung cấp các dịch vụ đó.
Trong RMI, dịch vụ từ xa được định nghĩa thông qua interface trong Java, trong khi mô tả chi tiết về dịch vụ này được thực hiện qua một lớp (class) Điều quan trọng cần nắm bắt trong RMI là các giao diện xác định hành vi của đối tượng từ xa, còn các lớp sẽ đảm nhận việc mô tả định nghĩa đó.
Hình 2.2-1: Giao diện và lớp mô tả giao diện
Giao diện không chứa mã thực thi trong RMI được chia thành hai lớp mô tả giống nhau Lớp đầu tiên mô tả hành vi và hoạt động trên server, trong khi lớp thứ hai đóng vai trò là đối tượng trung gian cho các dịch vụ từ xa và chạy trên Client.
Chương trình Client thực hiện các lời gọi phương thức thông qua một đối tượng trung gian (proxy) RMI sẽ chuyển tiếp các yêu cầu đến máy ảo từ xa, từ đó đến đối tượng mô tả Mọi giá trị trả về từ đối tượng mô tả sẽ được gửi về proxy, và sau đó proxy sẽ chuyển tiếp kết quả đến chương trình trên máy khách.
2.2.2 Kiến trúc phân tầng của RMI
RMI về cơ bản được xây dựng từ 3 lớp trừu tượng
Hình 2.2-1: Kiến trúc phân tầng của RMI
Transport Layer Remote Reference Layer
Hình 2.2-2: Hoạt động của các thành phần trong kiến trúc RMI
Việc áp dụng kiến trúc phân lớp cho phép cải tiến hoặc thay thế từng lớp mà không làm ảnh hưởng đến tính chất của toàn hệ thống Chẳng hạn, lớp transport có thể được thay thế bằng một lớp sử dụng phương thức kết nối UDP/IP mà vẫn đảm bảo các lớp phía trên hoạt động bình thường.
*Lớp trung gian (STUB và SKELETON)
Gọi các phương thức từ xa trong lập trình phân tán khác biệt hoàn toàn so với việc gọi các phương thức thông thường trên cùng một máy Trong bối cảnh này, các đối tượng trên hai máy khác nhau không tương tác trực tiếp mà thông qua một lớp trung gian Lập trình viên không cần quan tâm đến nội dung và hoạt động của lớp trung gian này, vì nó sẽ tiếp nhận các lời gọi từ Client và chuyển tiếp chúng đến dịch vụ RMI từ xa.
Lớp trung gian đóng vai trò quan trọng giữa máy khách và máy chủ trong quá trình thực hiện lời gọi Ở phía máy khách, lớp này được gọi là Stub, trong khi ở phía máy chủ, nó được gọi là Skeleton.
Hình 2.2-3: Vai trò của các lớp trung gian stub và skel
Trong Java 2 SDK, lớp skeleton không còn nữa RMI dùng reflection để tạo kết nối đến đối tượng dịch vụ từ xa
Lớp Remote Reference Layers đóng vai trò quan trọng trong việc phiên dịch và quản lý các tham chiếu từ Client đến đối tượng từ xa Nó định nghĩa và hỗ trợ ngữ nghĩa của các lời gọi kết nối RMI, cung cấp đối tượng RemoteRef đại diện cho liên kết đến dịch vụ từ xa Đối tượng stub sử dụng phương thức invoke() trong RemoteRef để chuyển tiếp lời gọi phương thức, trong khi RemoteRef hiểu rõ ngữ nghĩa của các lời gọi từ xa.
Trong JDK 1.1, RMI chỉ hỗ trợ kết nối unicast giữa Client và dịch vụ từ xa, tức là một nối một Để Client có thể sử dụng dịch vụ từ xa, dịch vụ đó cần được cài đặt trên Server và được export trong RMI Nếu dịch vụ là chính, nó phải được đặt tên và đăng ký với RMI Registry.
Trong Java 2 SDK, RMI đã cải tiến kết nối client-server bằng cách hỗ trợ các đối tượng từ xa tích cực Khi một phương thức được gọi từ proxy đến một đối tượng activatable remote objects, RMI sẽ xác định xem đối tượng dịch vụ từ xa có sẵn hay không.
B 1_st u b B 1_sk el không hoạt động, RMI sẽ khởi tạo đối tượng và khôi phục trạng thái của nó từ một file trên đĩa
Có nhiều kiểu kết nối khác nhau, chẳng hạn như multicast, trong đó một proxy đơn có thể gửi cùng một yêu cầu đến nhiều đối tượng mô tả cùng lúc và chỉ nhận về sự đáp trả đầu tiên.
*Transport Layer - Tầng vận chuyển
Tầng này chịu trách nhiệm kết nối hai máy ảo Java (JVM) thông qua các kết nối mạng cơ bản sử dụng giao thức TCP/IP.
Khi hai máy ảo hoạt động trên cùng một máy vật lý, chúng kết nối qua giao thức TCP/IP của máy tính đó Điều này giải thích tại sao cần thiết phải có cấu hình TCP/IP trên máy tính khi thực hiện các ví dụ.
TCP/IP tạo ra một kết nối an toàn giữa hai máy tính thông qua địa chỉ IP và số hiệu cổng Trong phiên bản RMI hiện tại, kết nối TCP/IP đóng vai trò cơ bản cho mọi kết nối giữa các máy.
Ban đầu, RMI sử dụng giao thức JRMP duy nhất khi áp dụng TCP/IP Tuy nhiên, đã có hai phiên bản được phát hành Phiên bản đầu tiên ra mắt cùng JDK 1.1 yêu cầu sử dụng lớp Skeleton trên server, trong khi phiên bản thứ hai, phát hành với Java 2 SDK, đã được cải tiến về hiệu năng và không cần lớp skeleton Một số thay đổi đáng chú ý trong Java 2 SDK bao gồm việc interface không còn yêu cầu kế thừa từ java.rmi.Remote và các phương thức không cần phải ném ra ngoại lệ RemoteException.
Cài đặt ứng dụng RMI đơn giản
Trong ứng dụng phân tán RMI đơn giản, cần có hai chương trình: một chương trình chạy trên Server để chờ phục vụ các yêu cầu tính toán và một chương trình yêu cầu chạy trên máy Client.
Trong chương trình trên Server, cần mô tả lớp giao tiếp của đối tượng Calculator và cài đặt chi tiết cho đối tượng này Sau đó, đưa cài đặt vào trong chương trình điều khiển gọi là CalculatorServer Đầu tiên, đặc tả lớp giao tiếp của đối tượng Calculator.
// Calculator.java import java.rmi.*; interface Calculator extends Remote
{ public int Add(int a,int b) throws RemoteException;
Mục đích của ứng dụng là gọi từ xa đối tượng Calculator và các phương thức của nó Để thực hiện điều này, cần cài đặt đối tượng Calculator như một interface, kế thừa từ interface Remote Remote là một interface rỗng, có chức năng thông báo cho máy ảo Java rằng interface kế thừa từ nó (Calculator) có khả năng giao tiếp từ xa.
*Cài đặt chi tiết cho đối tượng Calculator:
// CalculatorImpl.java import java.rmi.*; class CalculatorImpl implements Calculator
{ public int Add(int a,int b) throws RemoteException { return a+b;
*Đưa đối tượng Calculator vào trong chương trình phục vụ:
Ca lc u la to r _Stu b Ca lc u la to r _Sk el
RMI import java.rmi.server.*; import java.rmi.*; class CalculatorServer { public static void main(String args[]) { try { CalculatorImpl c=new CalculatorImpl();
System.out.println ("Da Export doi tuong Calculator");
Naming.bind("rmi://localhost/CalcProg",c);
System.out.println ("Da dang ki Calculator voi RMI");
Đối tượng Calculator đã được mô tả, nhưng để có thể giao tiếp với bên ngoài, nó cần được tích hợp vào chương trình CalculatorServer, giúp phục vụ các lời gọi từ xa.
Lệnh này để cho máy ảo Java nhận diện được đối tượng c là đối tượng có khả năng giao tiếp từ xa
Naming.bind("rmi://localhost/CalcProg",c);
Mục đích của việc đặt tên cho đối tượng c là để tạo ra một tên dễ nhớ và thực hiện việc đăng ký tên này với bộ quản lý RMI Các chương trình trên máy khách sẽ sử dụng tên này để tham chiếu đến đối tượng tương ứng.
Cú pháp của phương thức bind(rmi://hostname:port/ObjectName,Object)
- rmi: là giao thức dùng để đăng kí
- hostname:port: địa chỉ IP hoặc tên máy và số hiệu cổng của máy nơi mà bộ đăng kí RMI đang chạy
- ObjectName: tên gợi nhớ đặt cho đối tượng (Object) cần đăng kí
Bộ đăng kí rmiregisty: ta cần chạy bộ đăng kí này trước khi chạy chương trình
CalculatorServer sử dụng bộ rmiregistry để chờ nhận kết nối từ phương thức Naming.bind() Mặc định, rmiregistry lắng nghe trên cổng 1099, nhưng chúng ta có thể thay đổi số hiệu cổng này theo nhu cầu.
Ví dụ: ta chỉ định cổng 1090 bằng lệnh: rmiregistry.exe 1090
Chương trình yêu cầu trên Client: import java.rmi.*; class CalculatorClient { public static void main(String args[]) { try {
("rmi://localhost/CalcProg"); System.out.print(“Ket qua: ”+c.Add(1,2));
} catch(Exception e){System.out.print(e);}
Trong ứng dụng RMI đơn giản, toàn bộ mã lệnh được thực hiện trên một máy cục bộ, điều này không phù hợp với nguyên tắc lập trình phân tán Thực tế, Client chỉ cần các lớp CalculatorClient.class, CalculatorImpl_Stub.class và Calculator.class để hoạt động.
MỘT SỐ KHÁI NIỆM TRONG RMI
Bộ đăng ký Registry đóng vai trò quan trọng trong việc tiếp nhận đăng ký tên của các đối tượng giao tiếp từ xa Để máy khách có thể sử dụng các phương thức và gọi các đối tượng từ xa, nó cần phải thiết lập kết nối với bộ đăng ký này.
RMI cho phép lấy tham chiếu đến đối tượng trên máy chủ, trong khi dịch vụ đăng ký và truy tìm đối tượng được Java quản lý thông qua API JNDI (Java Naming Directory Interface) Tại máy khách, hàm JNDI (Naming.lookup()) sẽ kết nối với RMI để nhận tham chiếu đến đối tượng, trong khi đó, hàm JNDI ở máy chủ (Naming.bind()) có nhiệm vụ đăng ký tên đối tượng với RMI.
Hàm Naming.bind() chỉ đăng ký tên đối tượng với RMI đúng một lần
Khi thực hiện hàm đăng ký, nếu tên đã tồn tại, sẽ nhận được thông báo lỗi AlreadyBoundException Để khắc phục, cần tắt chương trình rmiregistry và khởi động lại Để tránh lỗi này, có thể sử dụng hàm Naming.rebind() thay vì Naming.bind() Hàm Naming.rebind() sẽ yêu cầu đăng ký tên đối tượng mới với rmiregistry nếu tên chưa tồn tại, hoặc đăng ký lại nếu tên đã được sử dụng.
Cơ chế làm việc của trình khách và trình chủ trên máy ảo Java khi đăng ký và truy xuất rmiregistry:
Trên máy ảo Java, đối tượng trong Java B sử dụng hàm Naming.bind() hoặc Naming.rebind() để đăng ký với chương trình rmiregistry đang hoạt động trên máy ảo Java A.
Chương trình khách chạy trên máy ảo Java C dùng hàm Naming.lookup() để yêu cầu bộ quản lý rmiregistry trên máy ảo A trả về tham chiếu đến đối tượng
Bộ quản lý trên máy ảo A trả về tham chiếu đến đối tượng đang tồn tại trên máy ảo B
Chương trình khách hoạt động trên máy ảo C sử dụng tham chiếu được cung cấp bởi hàm Naming.lookup() để truy cập và gọi phương thức của đối tượng đang chạy trên máy ảo B từ xa.
Mô hình này chỉ có thể hoạt động trên mạng thực khi được tách ra thành hai phần, không thể tách thành ba, vì Java không cho phép rmiregistry chạy trên máy khác với nơi mà đối tượng RMI đang hoạt động.
Hình 2.4-2: Rmiregistry trong mô hình phân tán thực tế Lúc này hàm đăng ký với rmiregistry được gọi như sau:
Và hàm truy tìm đối tượng từ máy khách sẽ được gọi như sau:
Calculator c = (Calculator) Naming.lookup(“rmi://172.16.11.12/CalcProg”);
CLASSPATH là biến môi trường quan trọng trong Java, giúp Java compiler và JVM xác định vị trí các lớp cần thiết cho biên dịch và thông dịch Khi thực thi chương trình, Java sử dụng CLASSPATH để tìm kiếm các tập tin class Nếu đường dẫn trong CLASSPATH không chính xác, sẽ xuất hiện lỗi java.lang.ClassNotFoundException: ClassName.
Trong ví dụ ứng dụng RMI đơn giản trên, ta có các lớp sau:
Tên lớp Mục đích Dùng trên
Calculator.class Đặc tả các phương thức giao tiếp của đối tượng Server & Client
CalculatorImpl.class Cài đặt chi tiết đối tượng Server
The CalculatorServer.class creates an object and registers it with the RMI registry server, while the CalculatorImpl_Stub.class serves as an intermediary communication layer between the server and client Meanwhile, the CalculatorClient.class is responsible for invoking the methods of the server's object from the client side.
Khi CalculatorServer yêu cầu RMI đăng ký tên đối tượng, rmiregistry sẽ tìm kiếm lớp trung gian CalculatorImpl_Stub.class Nếu CLASSPATH trên máy ảo rmiregistry không trỏ đến đường dẫn E:\TIEU LUAN\EXAM\First\, lỗi java.lang.ClassNotFoundException: CalculatorImpl_Stub sẽ xuất hiện Để thay đổi giá trị của CLASSPATH, người dùng cần sử dụng lệnh: Set Classpath.
Giá trị mặc định của Classpath là trỏ đến thư mục chứa hiện hành Lúc đó Classpath=E:\Doc
Khi thay đổi giá trị cho Classpath ở một máy ảo sẽ không làm ảnh hưởng tới Classpath của máy ảo khác
Khi phát triển ứng dụng từ phía máy khách, chỉ cần sử dụng lớp giao tiếp Calculator.class, trong khi lớp trung gian CalculatorImpl_Stub.class không cần thiết cho các nhà phát triển, mà chỉ phục vụ cho cơ chế RMI của Java.
Java cho phép nạp tự động lớp CalculatorImpl_Stub.class từ xa thông qua tùy chọn Codebase khi đăng ký đối tượng với rmiregistry trên máy chủ Để thực hiện điều này, máy chủ cần hỗ trợ dịch vụ Web Server chạy tại nơi rmiregistry hoạt động Tập tin CalculatorImpl_Stub.class cần được sao chép vào thư mục myclass của trình chủ web server Khi sử dụng tùy chọn Codebase, biến Classpath trên máy ảo nơi rmiregistry chạy không được trỏ đến cùng thư mục chứa CalculatorImpl_Stub.class; nếu không, rmiregistry sẽ ưu tiên lấy lớp này từ đường dẫn Classpath, bỏ qua tùy chọn Codebase.
Câu lệnh để chạy chương trình trên máy chủ được sửa lại như sau: java CalculatorServer –Djava.rmi.server.codebase=”http://172.16.11.12/myclass/”
Khi máy khách có yêu cầu rmiregistry trả về tham chiếu của đối tượng, nếu máy khách chưa có lớp CalculatorImpl_Stub.class, rmiregistry sẽ hướng dẫn máy khách tự động nạp lớp này từ địa chỉ codebase: http://172.16.11.12/myclass/
Hình 2.4-3: Cơ chế nạp tự động lớp trung gian CalculatorImpl_Stub.class xuống máy khách
Trình chủ CalculatorServer đăng ký đối tượng với remiregistry CalculatorServer yêu cầu bộ quản lý rmiregistry truy tìm lớp CalculatorImpl_Stub.class ở địa chỉ URL http://172.16.11.12/myclass/ java CalculatorServer –Djava.rmi.server.codebase=”http://172.16.11.12/myclass/”
Rmiregistry nhận được yêu cầu đăng ký đối tượng nó sẽ truy tìm lớp
CalculatorImpl_Stub.class trong lớp Classpath Nếu không thấy, rmiregistry sẽ dựa vào chuỗi
URL do trình CalculatorServer cung cấp yêu cầu Web server trả về lớp trung gian này
Rmiregistry ghi nhớ lại địa chỉ URL nơi lớp trung gian CalculatorImpl_Stub đang được lưu trữ
Chương trình máy khách yêu cầu rmiregistry cung cấp tham chiếu đối tượng Nếu lớp CalculatorImpl_Stub.class chưa có sẵn trên máy khách, rmiregistry sẽ cung cấp địa chỉ URL để tải lớp trung gian này về.
Chương trình ở máy khách yêu cầu Web Server cung cấp lớp trung gian http://172.16.11.12/myclass/CalculatorImpl_Stub.class Trình khách sử dụng lớp trung gian CalculatorImpl_Stub.class và lớp giao tiếp
Calculator.class để gọi các phương thức của đối tượng trên máy chủ
CHUYỂN THAM SỐ TRONG LỜI GỌI PHƯƠNG THỨC TỪ XA
2.5.1 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, giá trị của biến value sẽ là 13, vì phương thức doIncrease(num) đã làm tăng giá trị của đối tượng num thêm 1 đơn vị.
Các kiểu dữ liệu đơn giản như int, float, double, char, byte, long được truyền theo tham trị, tức là giá trị tham số mà hàm hoặc phương thức xử lý chỉ là bản sao của biến được truyền vào Ví dụ, trong class Program, phương thức doIncease nhận tham số int n và thực hiện tăng giá trị của n lên 1.
} public static void main (String args[]) { int num = 12; doIncrease(num);
Trong ví dụ này, kết quả hiển thị trên màn hình là 12 Tóm lại, khi truyền tham số cho phương thức trong Java, đối tượng được truyền theo tham biến, trong khi 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 bằng cơ chế RMI khác với cách truyền tham số thông thường, yêu cầu tất cả dữ liệu kiểu đối tượng phải cài đặt 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, trong khi các đối tượng cài đặt giao tiếp Serializable sẽ được truyền theo tham trị Đối với các kiểu dữ liệu đơn giản như int, char, chúng sẽ được truyền theo tham trị.
2.5.2 Chuyển đối tượng đến trình chủ theo tham trị:
Hầu hết các kiểu dữ liệu đối tượng cơ bản trong Java như String, Date, và Time đều triển khai giao diện Serializable, cho phép chúng được truyền qua các lời gọi phương thức từ xa theo cách tham trị.
Lớp Serializable trong Java là một lớp rỗng dùng để đánh dấu các đối tượng có khả năng tuần tự hóa Khi một lớp cài đặt giao diện Serializable, Java cho phép lưu trữ toàn bộ đối tượng lên đĩa cứng dưới dạng tập tin Tập tin này có thể được chuyển đến máy khác, và từ đó, đối tượng có thể được khôi phục về trạng thái ban đầu mà không cần khởi tạo lại bằng lệnh new.
Trong lập trình phân tán, việc truyền tham số đối tượng theo tham trị diễn ra thông qua cơ chế đóng gói và chuyển giao giữa máy khách và máy chủ Khi một phương thức của đối tượng ở xa được gọi, tham số kiểu đối tượng sẽ được đóng gói và gửi đến máy chủ, nơi chúng được mở ra và khôi phục về trạng thái ban đầu Quá trình này được thực hiện bởi lớp trung gian Stub trên máy khách và lớp trung gian Skel trên máy chủ Quy trình chuyển giao dữ liệu giữa Stub và Skel được gọi là mashaling data, đóng vai trò quan trọng trong việc quản lý tham số và dữ liệu trong môi trường phân tán.
Trong các lời gọi phương thức RMI, kiểu dữ liệu đối tượng cần phải được cài đặt giao thức Serializable hoặc Remote; nếu không, nó sẽ không thể được sử dụng làm tham số để chuyển qua mạng.
Chương trình minh họa chuyển tham số đối tượng qua mạng:
Chương trình mẫu dưới đây minh họa cách truyền một đối tượng từ máy khách đến máy chủ, tương tự như trò chơi ném bóng Trong đó, máy khách sẽ gửi một quả bóng (đối tượng Ball) tới máy chủ, và máy chủ sẽ tiếp nhận quả bóng đó trước khi trả lại cho máy 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ủ:
//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 được sử dụng để biểu diễn một quả banh có trọng lượng nhất định Mục tiêu của chúng ta là sử dụng đối tượng Ball làm tham số di chuyển giữa trình khách và trình chủ, vì vậy lớp Ball được thiết kế với 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 triển khai trên trình chủ, nơi mà phương thức ping() sẽ được gọi bởi trình khách Trình khách sử dụng phương thức này để gửi đối tượng Ball đến trình chủ Sau khi nhận được đố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
//Setup.java import java.rmi.*; import java.rmi.server.*; class Setup { public static void main(String args[]) throws Exception{
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
//Client.java import java.rmi.*; class Client { public static void main(String args[]) throws Exception{
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 ”
2.5.3 Chuyển đối tượng đến trình chủ theo tham biến: Đối tượng đượ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 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 Đối tượng trên máy khách Đối tượng trên máy chủ Gọi phương thức
Hình 2.5-1: Trình chủ và trình khách có thể triệu gọi lẫn nhau.
Trong Java, khi các đối tượng cài đặt giao diện Remote, chúng có thể được truyền qua mạng dưới dạng tham biến Cơ chế gọi ngược từ trình chủ đến trình khách, hay còn gọi là cơ chế callback, cho phép thực hiện các thao tác từ xa một cách hiệu quả.
Chương trình minh họa chuyển đối tượng qua mạng theo 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ủ
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ủ
Trình khách sẽ tạo đối tượng AtClient trên máy khách và gọi phương thức từ xa của AtServer để đăng ký AtClient với trình chủ Hai đối tượng AtClient và AtServer có khả năng 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 được thực hiện 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.*; 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 {
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 {
AtClient client; // lưu giữ tham chiếu đến đối tượng trên máy khách
// 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