B]MRI & CORBA[/B] là hai khái niệm cơ bản và nền tảng trong kiến thức về Java. Bài viết này sẽ giúp bạn hiểu để ứng dụng hai khái niệm này.
Các đối tượng phân tán
Trong lập trình Java thông thường thì tất cả các đối tượng tạo nên một chương trình sẽ nằm trên cùng một máy và trong cùng một tiến trình hay trên cùng một máy ảo Java (JVM).
Khi một đối tượng gọi một phương thức trên một đối tượng khác thì đối tượng gọi phải biết địa chỉ vùng nhớ của đối tượng bị gọi và có thể chỉ thay đổi bộ đếm chương trình máy để bắt đầu chạy trong một đối tượng mới. Địa chỉ của đối tượng được goik được lưu trữ trong ngăn xếp của máy, vì thế khi phương thức được gọi trả về giá trị thì địa chỉ trong ngăn xếp sẽ được nạp lại vào trong bộ đếm chương trình máy và tiếp tục thực thi trở lại trong đối tượng gọi. Dãy các bước trên là chung đối với tất cả các ngôn ngữ lập trình mà thực thi trong một tiến trình đơn, trên cùng một máy
Triệu gọi phương thức từ xa (RMI) và kiển trúc môi giới yêu cầu đối tượng chung (CORBA) là tầng giữa hỗ trợ việc gọi phương thức phân tán
Nền Java lý tưởng cho việc lập trình phân tán vì nhiều lý do:
· Nền Java định nghĩa một kích thước nhất quán và định dạng cho các kiểu cơ bản, như kiểu số nguyên và số thực, cũng như việc thể hiện các số giống nhau cho tất cả cài đặt trên nền. Đây là phương tiện để cơ chế chuyển tải có thể được đơn giản hóa một cách quan trọng vì nó không phải thực hiện bất kỳ dịch chuyển hay điều chỉnh nào khi nói chuyện với phía bên kia.
· Các hàm thư viện chính dành cho lập trình phân tán trên Java là RMI lại là một phần trong phiên bản chuẩn của ngôn ngữ Java và vì thể nó có trên tất cả các hệ thống hỗ trợ Java
· Java RMI dễ sử dụng
· Các bộ nạp lớp (classloader) của Java có thể tự động tải các đoạn mã bên phía máy khách đối với mô hình nặng về phía máy khách hay các ứng dụng ngang hàng
· Các tham số của Java có thể được truyền tham chiếu hay tham trị
· Nếu bạn không chắc rằng ngôn ngữ Java có trên tất cả các phia của một ứng dụng phân tán, bạn vẫn có thể dùng ngôn ngữ để gọi các đối tượng trên hệ thống khác được viết bằng một ngôn ngữ lập trình khác, ví dụ C++, bằng cách sử dụng CORBA
· Cuối cùng nền Java cũng hỗ trợ cho nhiều kỹ thuật lập trình phân tán khác nhu mô hình lập trình socket
Tại sao chúng ta lại muốn tính toán phân tán?
· Tính toán phân tán được dùng khi có một nguồn tài nguyên trung tâm, ví dụ như một cơ sở dữ liệu mà chúng ta muốn chia sẻ cho nhiều người sử dụng hay nhiều máy khách
· Tính toán phân tán được dùng để kết hợp sức mạnh tính toán của nhiều hệ thống để giải quyết một bài toán hiệu quả và nhanh hơn việc thực hiện với một hệ thống đơn.
Nhiều hệ thống máy tính có thể được cấu hình theo nhiều cách để chia sẻ xử lý, bao gồm chia sẻ bộ nhớ, chia sẽ đĩa, và còn chia sẻ kênh giao tiếp chung. Kỹ thuật gần đây cho phép các hệ thống mà các thành phần vật lý ở xa nhau có thể hợp tác với nhau trong việc giải quyểt các bài toán tính toán
Cũng trên ảnh hưởng của chủ đề sức mạnh tính toán thì sự trỗi dậy của mạng Internet và giao thức TCP/IP đi kèm cũng dẫn đến việc kết nối chưa hề thấy của hàng triệu các hệ thống máy tính. Đối với một vài ứng dụng thì cần sức mạnh tính toán để giải quyết các bài toán.
Chúng ta hãy cùng xem xét 3 mô hình đã từng ảnh hưởng đối với tính toán phân tán:
· Thin client-server: mô hình này đã phổ biến hôm nay, với sự phổ biến của các trình duyệt web. Trong mô hình này thì không có các đoạn mã của ứng dụng chạy trên máy khách – máy khách thực hiện yêu cầu của các phản hồi nhận được từ máy chủ thông qua trình duyệt web; máy chủ sẽ thực thi yêu cầu và gởi về các phản hồi cho máy khách. Đặc tính đẹp nhất của mô hình này là một người dùng có thể thực thi ứng dụng một cách tùy ý mà không cần phải cài đặt, và người dùng có thể chạy ứng dụng từ bất cứ nơi đâu thông qua Internet. Mô hình này ít sử dụng đến các tính toán phân tán, trừ các giao diện người dùng và một vài kiểm tra định dạng và các hàm xử lý đơn giản thông qua các ngôn ngữ như JavaScript. RMI có thể được sử dụng như là một giao thức trong các môi trường này nhưng nói chung thì không nên thay vào đó thì HTTP là giao thức phổ biến hơn trong mô hình này.
· Thick client-server: Mô hình này được dùng khi cần nhiều xử lý bên phía máy khách. Thick client có thể cung cấp một người dùng với một ứng dụng nhiều chức năng. Tuy nhiên phải có một bước cài đặt đẻ lấy mã người dùng cho ứng dụng trên máy người dùng. RMI thì được dùng phổ biến trong mô hình này
· Peer-to-peer: Nếu chúng ta đặt mối quan hệ giữa máy chủ và máy khách ngang nhau, cho phép cả 2 có thể gởi yêu cầu cho nhau khi đó chúng ta có mô hình ngang hàng. Mô hình này đang phát triển và ngày càng phổ biển, nhiều chương trình chia sẻ nhạc MP3 đều sử dụng mô hình này. RMI có thể được dùng như một giao thức trong môi trường này
Có rất nhiều kỹ thuật có sẵn hiện nay cho việc truyền đạt thông tin, như việc thực thi các đoạn mã chương trình từ xa, và phối hợp xử lý giữa các tiến trình trên cùng máy hay các máy đặt xa nhau.
Một trong những kỹ thuật sớm nhất và vẫn còn phổ biến là mở đường liên kết để truyền đạt thông tin giữa các tiến trình. Khi mà liên kết này được thiết lập thì một tiến trình có thể gởi một luồng dữ liệu được mã hóa cho những hành động xác định và thông tin cần truyền đạt đển một tiến trình khác. Tiến trình này sẽ giải mã luông thông tin và thực hiện một hành động, thường là trả kết quả ngược lại cho tiến trình khởi tạo. Lập trình Socket là một ví dụ của kỹ thuật này, một ví dụ khác là giao thức HTTP cho phép chuyển các thông tin HTML mã hóa giữa máy khách và máy chủ
Vấn đề của kỹ thuật này là người lập trình phải suy nghĩ ra một giao thức và sự phối hợp các lệnh cho mỗi ứng dụng. Kỹ thuật này dẫn đến 2 biến hóa khác nhau là gọi một thủ tục, mô hình truyền tham số địa phương tới ngôn ngữ lập trình và mô hình xử lý lệnh cho lập trình socket. Bên cạnh đó cũng có những điểm khác biệt trong định dạng dữ liệu và thứ tự chuẩn của các bit giữa 2 máy, từ đó làm phức tạp mã.
Một mô hình khác là triệu gọi thủ tục từ xa (RPCs). Ở đây, một lập trình viên có thể gọi một thủ tục từ xa và truyền tham số ngược trở lại và về phía trước giống như việc gọi một thủ tục cục bộ. Điều này cho phép phong cách lập trình giống nhau có thể được dùng cho cả lập trình cục bộ và lập trình phân tán. Vẫn còn tồn tại vấn đề định dạng dữ liệu và thứ tự chuẩn của các bit, ngoài ra RPC không tuân theo mô hình hướng đối tượng
RMI là một giải pháp của Java. RMI cho phép các mô hình lập trình giống nhau để gọi các phương thức hướng đối tượng trên cùng máy cục bộ hay gọi phân tán, giả sử rằng ngôn ngữ Java được dùng cho cả bên gọi và bên phương thức được gọi. RMI cho phép các tham số có thể được truyền một trong 2 cách là:
· Truyền theo tham trị: khi một đối tượng mới được tạo ra trong một tiến trình khác và một bản sao của tham số được tạo ra và đưa vào đối tượng mới.
· Truyền theo tham chiếu: một đối tượng không được tạo ra trong tiến trình khác mà được tham chiếu đến từ xa.
Sự khác nhau giữa RMI và CORBA
· RMI là một phần của bộ J2SDK và là các hàm thư viện hỗ trợ các lời 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. Chúng ta giả sử rằng ngôn ngữ Java được sử dụng ở cả phía gọi và phía bên phương thức được gọi
· CORBA là một chuẩn công nghiệp cho phép gọi các phương thức từ xa và nhận kết quả trả về, nhưng không giống như RMI, nó có thể được sử dụng khi bên phía gọi và bên phía phương thức được gọi có thể sử dụng các ngôn ngữ lập trình khác nhau, bao gồm cả trường hợp là cả 2 bên đều không sử dụng ngôn ngữ Java
· RMI là một tập các hàm thư viện đơn giản vì cả 2 bên đều sử dụng cùng môt ngôn ngữ lập trình và kiến trúc máy. Điều này sẽ làm cho vấn để triệu gọi phương thức từ xa dễ giải quyết hơn.
· Bộ phát triển J2SDK cũng hỗ trợ RMI-CORBA, cho phép một đối tượng Java gọi một đối tượng CORBA sử dụng hai cách tiếp cận khác nhau.
RMI
1) TỒNG QUAN VỂ RMI
Trong một ứng dụng không phân tán của java, đoạn mã trong một đối tượng có thể gọi phương thức của một đối tượng khác và máy ảo Java phân giải địa chỉ và truyền tham số từ đối tượng gọi đến phương thức được gọi, ngoài ra nó cũng trả về các giá trị cho đối tượng gọi thực thi phương thức
Trong ứng dụng phân tán, mặc dù đoạn mã lập trình phương thức trông có vẻ giống như trong trường hợp ứng dụng không phân tán, nhưng là một cơ chế hoàn toàn khác nhau được dùng để móc những đối tượng này. Khi một đối tượng muốn gọi một phương thức, nó sẽ gọi một đối tượng bè bạn bên phía máy khách, đối tượng này sẽ đại diện cho đối tượng gọi phương thức bên phía máy chủ. Đối tượng này được gọi là stub
Stub sẽ gọi kiến trúc RMI bên phía máy khách và di chuyển dữ liệu qua mạng đến kiến trúc RMI trên máy chủ, đến lược nó sẽ gọi thực thi một đối tượng bè bạn bên phía máy chủ gọi là skeleton.
Đối tượng skeleton sẽ gọi phương thức của đối tượng thật sự bên phía máy chủ. Đến khi trả lại kết quả thì một cơ chế giống hệt như trên sẽ được gọi thực thi nhưng theo thứ tự ngược lại, khi đó kết quả trả về sẽ được truyền cho đối tượng skeleton trên máy chủ, và được đối tượng này truyền theo đường mạng sử dụng kiến trúc RMI, tiếp đến nó sẽ gọi đối tượng stub bên phía máy khách, và trả về giá trị cho đối tượng gọi phương thức
Cái hay của kiến trúc đối tượng phân tán RMI là người lập trình chỉ cần lập trình các lời gọi phương thức vì đối tượng được gọi đã hiện diện trong máy ảo Java của nó
2) Stub và skeletons
Stub và skeleton được phát sinh ra từ đối tượng gọi và đối tượng được gọi bằng cách sử dụng một công cụ biên dịch của RMI là rmic. Các trình đóng gói ứng dụng phải đảm bảo là các stubs phải được đóng gói kèm theo với các đoạn mã bên phía máy khách hay các tập tin JAR, và các skeleton phải đi kèm với các đoạn mã bên phía máy chủ hoặc các thư viện JAR
Các Stub có thể được tự động tải về từ máy chủ web khi có yêu cầu. Hình dưới đây minh họa luồng dữ liệu giữa stub và skeleton trong kiến trúc của RMI
3) Xây dựng các đối tượng
Hầu như không có khác biệt giữa lập trình trên máy cục bộ và lập trình phân tán trong RMI. Lập trình phân tán chỉ yêu cầu một ít yêu cầu lập trình
Tất cả các lớp muốn có thể được triệu gọi từ xa thì phải có 2 phần: phần giao diện (interface) và phần cài đặt (implementation). Phần giao diện phải thừa kế từ một lớp của Java là lớp Remote. Phần cài đặt không những cài đặt phần giao diện mà còn phải thừa kế từ một lớp của Java là lớp UnicastRemoteObject
Thật sự có một mạng liên lạc tồn tại dưới cơ chế gọi phương thức từ xa, và mạng này có thể có lỗi hay bị ngắt kết nối hoàn toàn. Hoặc máy chủ có thể bị lỗi. Vì vậy bên máy khách có thể có vài lỗi mà nó phải xử lý trong lập trình phân tán. Những lỗi này xuất hiện dưới với dạng ngoại lệ RemoteException, vì thế tất cả phương thức trên đối tượng của máy chủ được thiết kế để được triệu gọi từ xa phải khai báo là nó sẽ tung ra ngoại lệ RemoteException. Mã bên phía máy người dùng nên xử lý những lỗi này khi nó xuất hiện
Dưới đây là một giao diện đơn giản cho một lớp truyền vào và lấy ra một String:
// Example.java
//
// Giao diện cho đối tượng triệu gọi từ xa
//
import java.rmi.*;
public interface Example extends Remote {
public void setString( String s ) throws RemoteException;
public String getString() throws RemoteException;
}
Và đây là phần cài đặt:
// ExampleServer.java
//
// Phần cài đặt đối tượng từ xa cho giao diện Example
//
import java.rmi.*;
import java.rmi.server.*;
public class ExampleServer extends UnicastRemoteObject implements Example {
private String stringState;
public ExampleServer() throws RemoteException{}
public void setString( String s ) throws RemoteException{
stringState = s;
}
public String getString() throws RemoteException{
return stringState;
}
}
Phần tiếp theo chúng ta sẽ tạo stub và skeleton cho lớp ExampleServer bằng cách sử dụng lệnh
rmic ExampleServer
4) RMI Registry
Chúng ta phải xác định được vị trí của đối tượng từ xa, RMI cung cấp một máy chủ chuyên quản lý tên là RMI Registry để thực hiện chức năng này
Một đối tượng có một tên (trong ví dụ này nó có tên là “Example”) và một vị trí của máy (trong ví dụ là “localhost”). Trong đoạn mã sau, chúng ta sẽ tạo một đối tượng ExampleServer và tạo một lối vào RMI Registry đặt trên máy cục bộ và đặt cho nó một cái tên là Example
Chúng ta sử dụng phương thức rebind() để tránh lỗi trong trường hợp tên này đã tồn tại trong RMI Registry, ngược lại chúng ta cũng có thể sử dụng phương thức bind(). Chúng ta chỉ cần chạy đoạn mã này một lần đến khi nào tiến trình của RMI Registry còn hoạt động. Thông tin trong registry không được lưu trữ xuống vĩnh viễn(persistent).
// Server.java
//
// Chương trình trên máy chủ tạo một đối tượng từ xa "Example" và đưa nó vào
// RMI registry
//
import java.io.*;
import java.rmi.*;
import java.rmi.server.*;
public class Server {
public static void main ( String[] args ) throws RemoteException, java.net.MalformedURLException {
//
// Create a new example object and enter it into the RMI registry
// located on "localhost" under the alias "Example"
ExampleServer es = new ExampleServer();
Naming.rebind( "rmi://localhost/Example", es );
}
}
5) Xây dựng chương trình bên phía máy khách
Cuối cùng chúng ta xây dựng một chương trình bên phía máy khách để thực hiện lời gọi từ xa. Đầu tiên, chúng ta phải xác định được vị trí của đối tượng bên phía máy chủ, vì vậy chúng ta phải tìm đối tượng Example trong RMI Registry và gọi một phương thức để truyền vào một chuỗi và đọc ngược nó ra và cuối cùng là xuất nó ra màn hình
// ExampleClient.java
//
// Chương trình bên phía máy khác gọi đối tượng từ xa Example để truyền vào một //.
// chuỗi và đọc nó ra
import java.rmi.*;
public class ExampleClient {
public static void main ( String[] args ) {
try {
// Tìm đối tượng từ xa "Example" trong RMI registry
Example example = (Example) Naming.lookup( "Example" );
//
// Truyền vào một chuỗi và đọc nó ra trở lại rồi xuất ra màn hình
example.setString( "Success!" );
System.out.println( example.getString() );
} catch (Exception e) {
e.printStackTrace();
}
}
}
Để chạy ví dụ chúng ta phải khởi động RMI Registry lên, chạy chương trình bên phía máy chủ một lần, sau đó chúng ta có thể chạy chương trình bên phía người dùng
start rmiregistry
start java Server
java ExampleClient
Nếu thực hiện thành công bạn sẽ thấy kết quả là “Success” trên màn hình
6) Triển khai RMI trên 2 máy khác nhau
Chúng ta sẽ xem xét ứng dụng RMI được viết và chạy một chương trình trong một tiến trình, cùng một JVM và nó liên lạc với một tiến trình khác chạy trên cùng máy. Khi chúng ta tách rời chức năng của hai máy ra, chúng ta sẽ xem xét việc triển khai như thế nào
Nếu chúng ta muốn chạy ví dụ trên 2 máy khác nhau thì
Bên phía máy khách cần có các lớp sau: Example.class, ExampleClient.class, ExampleServer_Stub.class
Bên phía máy chủ cần có các lớp sau: ExampleServer.class, Server.class, Example.class, ExampleServer_Stub.class, ExampleServer_Skel.class
Để có thể linh động hơn, chúng ta có thể đặt tập tin ExampleServer_Stub.class trên một máy chủ Web để tập tin này có thể được tải xuống khi có yêu cầu nếu bạn sử dụng tùy chọn -Djava.rmi.server.codebase khi thực hiện bằng dòng lệnh để chạy chương trình bên phía máy khách. Ví dụ:
java -Djava.rmi.server.codebase=
http://www.mywebserver.com/ ExampleClient
Và dưới đây là sơ đồ để triển khai một ứng dụng RMI tiêu biểu
Tham khảo
http://www.itgatevn.com.vn
http://www.sun.com
http://www.ibm.com