Tải bản đầy đủ (.pdf) (46 trang)

Lập trình hệ phân tán trong java

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (867.13 KB, 46 trang )

BAN CƠ YẾU CHÍNH PHỦ
HỌC VIỆN KỸ THUẬT MẬT MÃ
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯

BÁO CÁO

TÌM HIỂU VỀ LẬP TRÌNH HỆ PHÂN TÁN
TRONG JAVA
Học phần : Nguyên lý hệ điều hành
Sinh viên thực hiện:
Nguyễn Thị Huế

MSSV: AT13CLC0109

Nguyễn Thị Kim Huế

MSSV: AT13CLC0110

Hoàng Đăng Luân

MSSV: AT13CLC0115

Giảng viên:
Lê Đức Thuận
Khoa An tồn thơng tin – Học viện kỹ thuật mật mã

Hà Nội, 2017

Trang 1



LỜI MỞ ĐẦU
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 tố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ộ (Local-area networks-LAN) 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 (Wide-area network-WAN) 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.

Trang 2


MỤC LỤC

Chương 1: TỔNG QUAN VỀ LẬP TRÌNH PHÂN TÁN .................................. 3
1.1.Lập trình phân tán là gì? ....................................................................................................... 3
1.2.Vấn đề gọi phương thức từ xa ............................................................................................. 3
1.3.Lập trình phân tán với Java .................................................................................................. 4
Chương 2: LẬP TRÌNH PHÂN TÁN VỚI RMI .............................................. 5

2.1.Giới thiệu: ............................................................................................................................... 5
2.2.Kiến trúc RMI ........................................................................................................................ 5
2.2.1.Giao diện (interface) - trái tim của RMI: ............................................................... 5
2.2.2.Kiến trúc phân tầng của RMI ................................................................................... 6
2.3.Cài đặt ứng dụng RMI đơn giản ......................................................................................... 9
2.4.MỘT SỐ KHÁI NIỆM TRONG RMI .............................................................................. 12
2.4.1.Registry........................................................................................................................ 12
2.4.2.Biến Classpath............................................................................................................ 14
2.4.3.Codebase ..................................................................................................................... 15
2.5.CHUYỂN THAM SỐ TRONG LỜI GỌI PHƯƠNG THỨC TỪ XA....................... 17
2.5.1.Chuyển tham số theo tham trị và tham biến ......................................................... 17
2.5.2.Chuyển đối tượng đến trình chủ theo tham trị: .................................................... 18
2.5.3.Chuyển đối tượng đến trình chủ theo tham biến: ................................................ 21
2.6.TUẦN TỰ HÓA (SERIALIZABLE) ĐỐI TƯỢNG ..................................................... 25
2.7.MỘT SỐ KỸ THUẬT LẬP TRÌNH PHÂN TÁN VỚI RMI ...................................... 27
2.7.1.RMI REGISTRY và cách đăng ký đối tượng ...................................................... 27
2.7.2.Dùng một đối tượng sản sinh nhiều đối tượng .................................................... 30
2.7.3.Kỹ thuật gọi đối tượng ở xa bằng phương pháp động ( DYNAMIC METHOD
INVOKE )

................................................................................................................................ 34

2.7.4.Tự động kích hoạt đối tượng từ xa (ACTIVITION)........................................... 37
2.7.5.Trình mồi nạp ứng dụng từ xa................................................................................. 42

Trang 3


1.


Chương 1: TỔNG QUAN VỀ LẬP TRÌNH PHÂN TÁN

1.1. 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ộ: Các đối tượng của chương trình là thủ tục , hàm
được nạp trực tiếp vào bộ nhớ và thực thi ngay trên máy cục bộ. Đối với những hàm có sẵn
trong thư viện, người ta chỉ quan tâm đến tham số truyền cho nó và kết quả trả về.
Lập trình phân tán là việc lập trình trên các máy khác nhau trong đó các đối tượng của
chương trình gồm phương thức và thuộc tính có thể triệu gọi lẫn nhau.
Computer A

A2
Computer B

A1

B1

C1
C2
Computer C

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ộ. Tuy nhiên, đối tượng A1 ở máy A cũng có thể gọi đối tượng
C1 ở máy C hay B1 ở máy B cũng có thể gọi C1 ở máy C, đó là những lời gọi từ xa.
1.2. Vấn đề gọi phương thức từ xa
Trên máy cục bộ, các phương thức đều được chương trình quản lý và định vị trong
vùng nhớ, người lập trình có thể cho phép thực hiện lời gọi một cách bình thường mà không
cần quan tâm đến việc chúng giao tiếp với nhau thế nào. Còn các phương thức ở các máy khác
nhau hoạt động trên hai tiến trình, khơng gian địa chỉ khác nhau nên việc truyền tham số, tham

chiếu đến địa chỉ của nhau không thể thực hiện thông thường như trong trong cùng một máy
được.

Trang 4


Lời gọi phương thức cục bộ luôn đưa về kết quả, có nghĩa là ln thực hiện được
trong khi việc gọi các phương thức từ xa có thể khơng thực hiện được do sự cố mạng.
Đối với hàm trên máy cục bộ, tham số truyền cho hàm thường được đưa vào ngăn xếp
rồi chương trình sẽ lấy ra để thực hiện trong khi tham số truyền cho hàm ở xa phải được đóng
gói và chuyển qua mạng theo giao thức mạng mới đến được nơi cần đến.
1.3. 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 là một cơ chế gọi phương thức từ xa đã được tích hợp trong ngơn ngữ Java.
Phương pháp lập trình trong RMI là phương pháp hướng đối tượng, do đó các thao tác hay các
lời gọi phương thức đều liên quan đến đối tượng.
CORBA (Common Object Request Broker Architecture) là đặc tả của OMG (Object
Management Group) dành cho việc đạt được tính tương tác giữa các nút tính tốn phân tán.
Mục tiêu của CORBA là định nghĩa một cấu trúc mà có thể cho phép những mơi trường khơng
đồng nhất có thể liên lạc tại mức đối tượng, khơng quan tâm đến người nào đã thiết kế ra 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 hơn
trên các nền platform khác nhau.
*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 tốn phân tán. Ở đây 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ả hai 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ả hai 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ợ cả RMI và 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.
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

Trang 5


2.

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

2.1. Giới thiệu:
RMI (Remote Method Invoke)– triệu gọi phương thức từ xa - là công nghệ được SUN
giới thiệu đầu tiên trong JDK 1.1 (2/1997) đã mở ra một thời kì mới cho lĩnh vực lập trình
mạng. Được thiết kế tương đối dễ sử dụng nhưng RMI cũng là một công nghệ rất mạnh, cung
cấp đầy đủ các công cụ để người lập trình có thể phát triển ứng dụng phân tán một cách tốt
nhất.
Mục đích chính của việc thiết kế ra RMI là cho phép người lập trình phát triển các
chương trình phân tán bằng Java với cú pháp và ngữ nghĩa giống như lập trình thơng thường.
Để làm được việc này, họ đã ánh xạ các lớp và đối tượng làm việc trong một máy ảo Java đơn
thành các lớp và đối tượng làm việc trong một môi trường phân tán.
2.2. 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 một nguyên lý quan trọng: định nghĩa hành vi và việc mô tả hành
vi là hai khái niệm riêng biệt. RMI chấp nhận mã của định nghĩa hành vi và mã của mô tả hành

vi tách rời nhau và chạy trên các máy ảo Java độc lập.
Điều cần làm của hệ thống phân tán là ở máy khách sẽ định nghĩa dịch vụ mong muốn và
máy chủ là nơi cung cấp các dịch vụ đó.
Trong RMI, việc định nghĩa một dịch vụ từ xa được viết bằng interface trong Java. Mô tả
chi tiết dịch vụ từ xa được viết bằng một lớp - class. Yếu tố chính cần hiểu trong RMI là: các
giao diện định nghĩa hành vi của đối tượng từ xa và các lớp mơ tả định nghĩa đó.

Hình 2.2-1: Giao diện và lớp mô tả giao diện.

Trang 6


Một giao diện không chứa mã thực thi. RMI hỗ trợ hai lớp mô tả giao diện giống nhau.
Lớp thứ nhất là mơ tả hành vi và nó chạy trên server. Lớp thứ hai hoạt động như một đối
tượng trung gian cho các dịch vụ từ xa và nó chạy trên Client.
Biểu đồ minh họa
<<Interface>>
Service

Client
Service
Proxy

Server
RMI

Service
Implementation

Một chương trình Client thực hiện lời gọi phương thức thông qua đối tượng trung gian

(proxy), RMI sẽ gửi đến máy ảo từ xa và từ đó đi đến đối tượng mơ tả. Bất kì giá trị trả về nào
được tạo ra bởi đối tượng mô tả sẽ được gửi trở về proxy và sau đó proxy sẽ chyển đế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

Client Program

RMI
System

Server Program

Stub & Skeletons

Stub & Skeletons

Remote Reference Layer

Remote Reference Layer

Transport Layer

Hình 2.2-1: Kiến trúc phân tầng của RMI

Trang 7


Client
Object


Remote
Object

Stub

Skeleton

Remote Reference Layer

Remote Reference Layer
TCP

Transport Layer

Transport 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 dùng kiến trúc phân lớp nên mỗi lớp có thể được cải tiến hoặc thay thế mà khơng
ảnh hưởng đến tính chất của hệ thống. Ví dụ như lớp transport có thể được thay thế bởi một
lớp theo phương thức kết nối UDP/IP mà khơng ảnh hưởng các lớp trên nó.
*Lớp trung gian (STUB và SKELETON)
Việc gọi các phương thức từ xa là không giống như gọi các phương thức thông thường
trong cùng một máy. Vì thế, trong lập trình phân tán, cơ chế gọi từ xa là các đối tượng trên hai
máy khác nhau không gọi trực tiếp lẫn nhau mà thơng qua lớp trung gian. Người lập trình
khơng cần phải quan tâm lớp trung gian này chứa gì và làm việc ra sao. Lớp trung gian này sẽ
đón đầu các lời gọi phát sinh từ Client đến biến tham chiếu giao diện và gửi lại các lời gọi đó
cho dịch vụ RMI từ xa.
Lớp trung gian tồn tại ở hai phía máy khách (nơi thực hiện lời gọi) và máy chủ (nơi cài
đặt đối tượng được gọi). Phía máy khách, lớp trung gian được gọi là Stub và phía máy chủ, lớp

trung gian được gọi là Skeleton.

Trang 8


A2

Computer B

B1_skel

A1

B1_stub

Computer A

C1_stub

B1

Computer C
C1_skel

C1

C2

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.
*Remote Reference Layer
Lớp này làm phiên dịch và quản lý các tham chiếu tạo từ Client đến đối tượng từ xa.
Lớp Remote Reference Layers định nghĩa và hỗ trợ về ngữ nghĩa lời gọi của kết nối
RMI. Lớp này cung cấp một đối tượng RemoteRef, đối tượng này đại diện cho liên kết đến đối
tượng thực hiện dịch vụ từ xa.
Đối tượng stub dùng phương thức invoke() trong RemoteRef để chuyển tiếp lời gọi
phương thức. Đối tượng RemoteRef hiểu được ngữ nghĩa từ các lời gọi từ xa.
Trong JDK 1.1, RMI chỉ cung cấp một cách cho Client để kết nối đến dịch vụ từ xa:
unicast – một nối một. Trước khi một Client có thể dùng dịch vụ từ xa, dịch vụ từ xa đó phải
được cài đặt trên Server và được export trong RMI. Nếu nó là dịch vụ chính, nó phải được đặt
tên và đăng kí đến RMI Registry.
Trong Java 2 SDK, RMI thêm một ý nghĩa mới cho kết nối client-server. Trong phiên
bản này, RMI hỗ trợ đối tượng từ xa tích cực. Khi một lời gọi phương thức được tạo ra từ
proxy đến một đối tượng activatable remote objects, RMI xác định nếu đối tượng dịch vụ từ xa

Trang 9


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ó thể có các kiểu kết nối khác. VD: với multicast, một proxy đơn có thể gửi cùng
một yêu cầu đến nhiều đối tượng mô tả đồng thời… và nhận về sự đáp trả đầu tiên.
*Transport Layer - Tầng vận chuyển
Tầng này có nhiệm vụ tạo kết nối giữa hai máy ảo Java (JVM). Tất cả các kết nối là
kết nối mạng cơ bản dùng giao thức TCP/IP.
Nếu hai máy ảo đang chạy trên một máy vật lý, chúng kết nối thơng qua giao thức
ngăn xếp TCP/IP của chính máy tính đó. Điều này lí giải tại sao ta phải có một cấu hình
TCP/IP trên máy tính khi ta chạy các ví dụ.
TCP/IP cung cấp một kết nối an tồn giữa hai máy dựa trên đị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 được dùng là cơ bản cho tất cả các kết nối
từ máy này đến máy khác.
Lúc đầu dùng TCP/IP, RMI dùng giao thức JRMP, giao thức này là duy nhất. Nhưng
về sau, đã có hai phiên bản. Phiên bản đầu tiên được phát hành với JDK 1.1, RMI yêu cầu
dùng lớp Skeleton trên server. Phiên bản thứ 2 được phát hành với Java 2 SDK. Nó đã được
cải tiến để đạt hiệu năng và không yêu cầu lớp skeleton. Một vài thay đổi với Java 2 SDK là
interface khơng địi hỏi kế thừa từ java.rmi.Remote và các phương thức không cần thiết ném ra
ngoại lệ RemoteException
SUN và IBM đã liên kết làm việc cho phiên bản mới của RMI được gọi là RMI-IIOP,
RMI-IIOP dùng giao thức IIOP để thay thế cho JRMP.
2.3. Cài đặt ứng dụng RMI đơn giản
Ở ứng dụng phân tán RMI đơn giản này, ta cần có hai chương trình, một chương trình
chạy ở Server sẽ chờ phục vụ u cầu tính tốn và một chương trình u cầu chạy ở máy
Client.

Trang 10


RMI

Calculator._Skel

CalculatorClien
t

Calculator._Stub

Client

Server

Calculator

Hình 2.3-1: Triệu gọi đối tượng RMI Calculator từ xa
Ở chương trình trên Server, ta cần mơ tả lớp giao tiếp của đối tượng Calculator, sau đó cài đặt
chi tiết cho đối tượng Calculator và đưa cài đặt này vào trong một chương trình điều khiển gọi
là CalculatorServer.
Đầu tiên, ta đặ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;
}
Giải thích:
Mục đích của ứng dụng là gọi từ xa đối tượng Calculator cũng như các phương thức
của nó, để làm được như vậy, ta cần cài đặt đối tượng Calculator là một interface và interface
này được kế thừa từ interface Remote. Remote là interface rỗng, nó có chức năng là thông báo
cho máy ảo Java biết 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ụ:
// CalculatorServer.java


Trang 11


import java.rmi.server.*;
import java.rmi.*;
class CalculatorServer {
public static void main(String args[]) {
try {
CalculatorImpl c=new CalculatorImpl();
UnicastRemoteObject.exportObject(c);
System.out.println ("Da Export doi tuong Calculator");
Naming.bind("rmi://localhost/CalcProg",c);
System.out.println ("Da dang ki Calculator voi RMI");
}
catch(Exception e){}
}
}
Giải thích:
Ta đã mơ tả xong đối tượng Calculator, nhưng tự đối tượng này chưa thể giao tiếp với bên
ngồi được. Ta cần đưa nó vào trong chương trình CalculatorServer để có thể phục vụ lời gọi
từ xa.
UnicastRemoteObject.exportObject(c);
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: đặt cho đối tượng c một tên gợi nhớ và đăng kí tên này với bộ quản lý RMI.
Các chương trình ở máy khách sẽ dựa vào 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. Khi thực thi, bộ rmiregistry sẽ chờ để đón nhận các kết nối của phương

Trang 12


thức Naming.bind() gửi đến. Mặc định, rmiregistry sẽ các kết nối gửi đến cổng 1099. Ta có
thể thay đổi số hiệu cổng này.
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 {
Calculator c =(Calculator)Naming.lookup
System.out.print(“Ket qua: ”+c.Add(1,2));
}
catch(Exception e){System.out.print(e);}
}
}
Ở ứng dụng RMI đơn giản trên, ta thực hiện trên một máy, tất cả mã lệnh đều tập trung
trên một máy cục bộ, điều này trái với tư tưởng của lập trình phân tán. Thực tế, Client chỉ cần
các lớp CalculatorClient.class, CalculatorImpl_Stub.class, Calculator.class.
2.4. MỘT SỐ KHÁI NIỆM TRONG RMI
2.4.1. Registry
Bộ đăng ký Registry: tiếp nhận đăng ký tên của đối tượng giao tiếp từ xa. Máy khách

muốn sử dụng được các phương thức và gọi được các đối tượng từ xa, nó phải liên lạc với
RMI để lấy về tham chiếu đến đối tượng trên máy chủ. Dịch vụ đăng ký và truy tìm đối tượng
được Java quản lý bằng các hàm giao tiếp API JNDI (Java Naming Directory Interface). Các
hàm JNDI ở máy khách (Naming.lookup()) sẽ liên lạc với RMI để nhận về tham chiếu đến đối
tượng trong khi đó các hàm JNDI ở máy chủ (Naming.bind()) có nhiệm vụ đăng ký tên đối
tượng với RMI
Lưu ý:
Hàm Naming.bind() chỉ đăng ký tên đối tượng với RMI đúng một lần.

Trang 13


Vì vậy nếu tên đăng ký đã tồn tại khi thực hiện hàm này sẽ nhận được thông báo lỗi
AlreadyBoundException. Để khắc phục, ta cần tắt chương trình rmiregistry và chạy lại.Ta có
thể tránh điều đó bằng cách sử dụng hàm Naming.rebind() thay cho Naming.bind(). Hàm
Naming.rebind() có nhiệm vụ yêu cầu đăng ký mới tên đối tượng với rmiregistry nếu tên này
chưa có và đăng ký lại nếu tên đối tượng đã được đăng ký.
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:

Hình 2.4-1: Cơ chế làm việc của trình khách và trình chủ trên máy ảo Java
Đối tượng trên máy ảo Java B dùng hàm Naming.bind() hoặc hàm Naming.rebind() để
đăng ký đối với tượng với chương trình rmiregistry đang chạy 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 chạy trên máy ảo C sử dụng tham chiếu do hàm Naming.lookup() trả
về để truy và gọi phương thức của đối tượng đang chạy trên máy ảo B từ xa.
Thực tế mơ hình trên muốn hoạt động trên mạng thật thì chỉ có thể tách ra làm hai chứ
không thể tách ra ba, vì Java khơng cho phép rmiregistry chạy trên máy khác nơi đối tượng

rmi đang hoạt động.
Ví dụ:

Trang 14


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:
Naming.bind(“rmi://172.16.11.12/ CalcProg”,c);
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”);
2.4.2. Biến Classpath
CLASSPATH là một biến môi trường(environment variable) được sử dụng bởi Java
compiler và JVM để tìm kiếm các lớp cần thiết cho q trình biên dịch (compile) và thơng
dịch(interpret). Khi chạy chương trình, Java dựa vào biến mơi trường CLASSPATH để truy
tìm các tập tin .class. Nếu CLASSPATH chỉ sai đường dẫn, chương trình sẽ thơng báo 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:
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

CalculatorServer.class

Tạo ra đối tượng và đăng ký đối tượng với rmiregistry

Server

CalculatorImpl_Stub.class

Lớp giao tiếp trung gian

Server & Client

CalculatorClient.class

Gọi các phương thức của đối tượng trên Server

Client

Tên lớp

Trang 15


Khi CalculatorServer yêu cầu RMI đăng ký tên đối tượng, rmiregistry sẽ đi truy tìm
lớp trung gian CalculatorImpl_Stub.class. Trên máy ảo rmiregistry đang chạy nếu
CLASSPATH


không

trỏ

đến

đường

dẫn

E:\TIEU

LUAN\EXAM\First\

(CalculatorImpl_Stub.class nằm ở đường dẫn này) thì nhận được thơng báo lỗi trả về trên máy
ảo nơi CalculatorServer yêu cầu đăng ký đối tượng:
java.lang.ClassNotFoundException: CalculatorImpl_Stub
Để thay đổi giá trị cho CLASSPATH ta dùng lệnh: Set Classpath
VD: set classpath=E:\Doc
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.
2.4.3. Codebase
Khi viết chương trình từ phía máy khách, chỉ cần đến lớp giao tiếp Calculator.class.
Lớp trung gian CalculatorImpl_Stub.class khơng có ý nghĩa đối với các nhà phát triển ứng
dụng. Nó chỉ cần thiết cho cơ chế RMI của Java.
Chính vì điều này Java cung cấp cho ta cách thức 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ủ.

Ở máy chủ phải hỗ trợ thêm dịch vụ Web Server chạy trên máy nơi rmiregistry đang
chạy. Sau đó ta chép tập tin CalculatorImpl_Stub.class vào thư mục myclass của trình chủ web
server. Khi sử dụng tùy chọn Codebase thì biến Classpath trên máy ảo nơi rmiregistry đang
chạy không được trỏ đến cùng thư mục chứa CalculatorImpl_Stub.class. Nếu không
rmiregistry sẽ luôn ưu tiên lấy lớp CalculatorImpl_Stub.class từ đường dẫn Classpath mà 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/.

Trang 16


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 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 trả vế tham chiếu của đối tượng. Nếu phía
máy khách chưa tồn tại lớp CalculatorImpl_Stub.class bộ quản lý rmiregistry sẽ cho trình
khách biết địa chỉ URL để nạp 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

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ủ

Trang 17


2.5. 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) {
}
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ừ ngồi vào. Ví dụ:

}

class Program{
public static void doIncease(int n) {
n++;
public static void main (String args[]) {
doIncrease(num);
System.out.print(num);}

}

Trang 18


Ở 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.5.2. 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 tồ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, q 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.

Trang 19




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ủ:
//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;
}

Trang 20


Đố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
//Setup.java
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 ...");
}
}

Trang 21


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{
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());
}
}
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
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.
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.

Trang 22


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 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ủ.
đố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.*;
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.*;

Trang 23


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<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();
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);
AtServer server=(AtServer)Naming.lookup("rmi://localhost/serverobject");

Trang 24


server.registerClient(client);
server.callServerMethod("Client Contact Server");
}
}
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ị.

Trang 25


×