1. Introduction, or what’s wrong with sockets?
Sockets là một phần cơ bản của mạng máy khách-máy chủ. Chúng cung cấp một cơ chế tương đối dễ dàng để một chương trình thiết lập kết nối với một chương trình khác, trên một máy tính từ xa hoặc cục bộ và gửi tin nhắn qua lại (thậm chí chúng ta có thể sử dụng lệnh gọi hệ thống đọc và ghi). Tuy nhiên, giao diện này buộc chúng tôi phải thiết kế các ứng dụng phân tán của mình bằng giao diện đọc / ghi (đầu vào / đầu ra), đây không phải là cách chúng tôi thường thiết kế các ứng dụng không phân tán. Trong việc thiết kế các ứng dụng tập trung, lệnh gọi thủ tục thường là mô hình giao diện chuẩn. Nếu chúng ta muốn làm cho máy tính phân tán giống như máy tính tập trung, thì truyền thông dựa trên đầu vào / đầu ra không phải là cách để thực hiện điều này.
Năm 1984, Birrell và Nelson đã nghĩ ra một cơ chế cho phép các chương trình gọi các thủ tục trên các máy khác. Một tiến trình trên máy A có thể gọi một thủ tục trên máy B. Khi nó làm như vậy, tiến trình trên A bị tạm dừng và việc thực thi tiếp tục trên B. Khi B quay trở lại, giá trị trả về được chuyển cho A và A tiếp tục thực hiện. Cơ chế này được gọi là Cuộc gọi Thủ tục Từ xa (RPC). Đối với lập trình viên, nó xuất hiện như thể một cuộc gọi thủ tục bình thường đang diễn ra. Rõ ràng, lệnh gọi thủ tục từ xa khác với lệnh gọi cục bộ ở cách triển khai cơ bản.
2. Steps in a remote procedure call
Hãy để chúng tôi kiểm tra cách thực hiện các lệnh gọi thủ tục cục bộ. Điều này khác nhau giữa các trình biên dịch và kiến trúc, vì vậy chúng tôi sẽ tổng quát hóa. Mỗi bộ xử lý đều cung cấp cho chúng ta một số dạng lệnh gọi, lệnh này đẩy địa chỉ của lệnh tiếp theo lên ngăn xếp và chuyển quyền điều khiển đến địa chỉ được chỉ định bởi lệnh gọi. Khi thủ tục được gọi được thực hiện xong, nó sẽ đưa ra một lệnh trả về, làm bật địa chỉ từ trên cùng của ngăn xếp và chuyển quyền điều khiển đến đó. Đó chỉ là cơ chế xử lý cơ bản giúp bạn dễ dàng triển khai các lệnh gọi thủ tục. Các chi tiết thực sự của việc xác định các tham số, đặt chúng vào ngăn xếp, thực hiện lệnh gọi là tùy thuộc vào trình biên dịch. Trong hàm được gọi, trình biên dịch chịu trách nhiệm đảm bảo bất kỳ thanh ghi nào có thể bị che khuất đều được lưu, cấp phát không gian ngăn xếp cho các biến cục bộ, sau đó khôi phục các thanh ghi và ngăn xếp trước khi trả về.
Không có cách nào trong số này sẽ hoạt động để gọi một thủ tục được tải trên một máy từ xa. Điều này có nghĩa là trình biên dịch phải làm một cái gì đó khác để tạo ra ảo giác gọi một thủ tục từ xa. Điều này làm cho các cuộc gọi thủ tục từ xa trở thành một cấu trúc mức ngôn ngữ trái ngược với các ổ cắm, là một cấu trúc mức hệ điều hành. Chúng tôi sẽ phải mô phỏng các cuộc gọi thủ tục từ xa bằng các công cụ mà chúng tôi có, cụ thể là các cuộc gọi thủ tục cục bộ và các ổ cắm cho giao tiếp mạng.
Toàn bộ thủ thuật trong việc thực hiện các cuộc gọi thủ tục từ xa là ở việc tạo ra các hàm sơ khai làm cho người dùng thấy rằng cuộc gọi thực sự là cục bộ. Trên máy khách, một hàm sơ khai trông giống như hàm mà người dùng định gọi nhưng thực sự chứa mã để gửi và nhận tin nhắn qua mạng. Trình tự các hoạt động diễn ra, được thể hiện trong Hình 1 (từ trang 693 của W. Richard Steven’s UNIX Network Programming), là:
Hình 1. Các bước thực hiện lệnh gọi thủ tục từ xa
1. Máy khách gọi một thủ tục cục bộ, được gọi là máy khách gốc. Đối với quy trình khách hàng, đây có vẻ là quy trình thực tế, bởi vì nó là quy trình cục bộ thông thường. Nó chỉ làm một cái gì đó khác vì thủ tục thực sự nằm trên máy chủ. Máy khách đóng gói các tham số cho thủ tục từ xa (điều này có thể liên quan đến việc chuyển đổi chúng sang định dạng chuẩn) và xây dựng một hoặc nhiều thông báo mạng. Việc đóng gói các đối số thành một thông điệp mạng được gọi là sắp xếp và yêu cầu tuần tự hóa tất cả các phần tử dữ liệu thành một định dạng mảng-byte phẳng.
2. Thông điệp mạng được gửi bởi máy khách đến hệ thống từ xa (thông qua một lệnh gọi hệ thống tới hạt nhân cục bộ bằng cách sử dụng giao diện ổ cắm).
3. Các thông điệp mạng được hạt nhân chuyển đến hệ thống từ xa thông qua một số giao thức (không kết nối hoặc hướng kết nối).
4. Một máy chủ gốc, đôi khi được gọi là bộ xương, nhận các thông báo trên máy chủ. Nó giải nén các đối số từ các thông báo và nếu cần, chuyển đổi chúng từ một định dạng mạng tiêu chuẩn thành một dạng dành riêng cho máy.
5. Sơ khai máy chủ gọi hàm máy chủ (đối với máy khách, là thủ tục từ xa), chuyển cho nó các đối số mà nó nhận được từ máy khách.
6. Khi chức năng máy chủ kết thúc, nó sẽ trở về gốc máy chủ với các giá trị trả về của nó.
7. Sơ khai máy chủ chuyển đổi các giá trị trả về, nếu cần, và sắp xếp chúng thành một hoặc nhiều thông điệp mạng để gửi đến sơ khai máy khách.
8. Tin nhắn được gửi trở lại trên mạng tới cuống máy khách.
9. Sơ khai máy khách đọc các thông báo từ hạt nhân cục bộ.
10. Sau đó, máy khách trả về kết quả cho hàm máy khách, chuyển đổi chúng từ biểu diễn mạng thành biểu diễn cục bộ nếu cần.
Những lợi ích chính của RPC là gấp đôi. Đầu tiên, lập trình viên bây giờ có thể sử dụng ngữ nghĩa lời gọi thủ tục để gọi các hàm từ xa và nhận phản hồi. Thứ hai, việc viết các ứng dụng phân tán được đơn giản hóa vì RPC ẩn tất cả mã mạng thành các hàm sơ khai. Các chương trình ứng dụng không phải lo lắng về các chi tiết như ổ cắm, số cổng, chuyển đổi và phân tích cú pháp dữ liệu. Trên mô hình tham chiếu OSI, RPC trải dài cả lớp phiên và lớp trình bày (lớp năm và lớp sáu).
3. Implementing remote procedure calls
Một số vấn đề nảy sinh khi chúng ta nghĩ về việc thực hiện các cuộc gọi thủ tục từ xa.
3.1. Làm thế nào để bạn vượt qua các tham số?
Chuyển theo giá trị rất đơn giản: chỉ cần sao chép giá trị vào tin nhắn mạng. Vượt qua tài liệu tham khảo là khó. Không có ý nghĩa gì khi chuyển một địa chỉ đến một máy từ xa vì vị trí bộ nhớ đó có thể trỏ đến một cái gì đó hoàn toàn khác trên hệ thống từ xa. Nếu bạn muốn hỗ trợ truyền bằng tham chiếu, bạn sẽ phải gửi một bản sao của các đối số, đặt chúng vào bộ nhớ trên hệ thống từ xa, chuyển một con trỏ tới chúng tới hàm máy chủ, rồi gửi đối tượng trở lại máy khách, sao chép nó qua tài liệu tham khảo. Nếu các lệnh gọi thủ tục từ xa phải hỗ trợ các tham chiếu đến cấu trúc phức tạp, chẳng hạn như cây và danh sách được liên kết, chúng sẽ phải sao chép cấu trúc thành một biểu diễn không có con trỏ (ví dụ: một cây phẳng), truyền nó và tạo lại cấu trúc dữ liệu ở phía từ xa.
3.2. Làm thế nào để chúng tôi biểu diễn dữ liệu?
Trên một hệ thống cục bộ không có vấn đề về không tương thích dữ liệu; định dạng dữ liệu luôn giống nhau. Với RPC, một máy từ xa có thể có thứ tự byte khác nhau, kích thước số nguyên khác nhau và biểu diễn dấu phẩy động khác nhau.
Ví dụ, biểu diễn endian lớn lưu trữ các byte quan trọng nhất của một số nguyên nhiều byte trong bộ nhớ thấp. Biểu diễn endian nhỏ lưu trữ các byte quan trọng nhất của một số nguyên trong bộ nhớ cao. Nhiều bộ vi xử lý cũ hơn, chẳng hạn như Sun SPARC và Motorola 680x0s đã sử dụng bộ nhớ endian lớn. Hầu hết các hệ thống của Intel đều triển khai ít dung lượng lưu trữ cuối cùng, dẫn đến sự thống trị của định dạng này. Nhiều kiến trúc khác sử dụng định dạng bi-endian, trong đó bộ xử lý có thể được cấu hình tại thời điểm khởi động để hoạt động ở chế độ endian nhỏ hoặc endian lớn. Ví dụ về các bộ xử lý này là ARM, MIPS, PowerPC, SPARC v9 và Intel IA-64 (Itanium).
Vấn đề đã được giải quyết trong bộ giao thức IP bằng cách buộc mọi người sử dụng thứ tự byte endian lớn cho tất cả các trường 16 và 32 bit trong tiêu đề (do đó sử dụng các hàm htons và htonl). Đối với RPC, chúng ta cần đưa ra một mã hóa “tiêu chuẩn” cho tất cả các kiểu dữ liệu có thể được truyền dưới dạng tham số nếu chúng ta muốn giao tiếp với các hệ thống không đồng nhất. Ví dụ: ONC RPC sử dụng một định dạng được gọi là XDR (Biểu diễn dữ liệu vĩnh cửu) cho quá trình này. Các định dạng biểu diễn dữ liệu này có thể sử dụng kiểu nhập ẩn hoặc rõ ràng. Với kiểu nhập ngầm, chỉ giá trị được truyền, không phải tên hoặc kiểu của biến. ONC RPC’s XDR và DCE RPC’s NDR là những ví dụ về biểu diễn dữ liệu sử dụng kiểu nhập ngầm. Với cách nhập rõ ràng, kiểu của mỗi trường được truyền cùng với giá trị. Tiêu chuẩn ISO ASN.1 (Ký hiệu cú pháp trừu tượng), JSON (Ký hiệu đối tượng JavaScript), Bộ đệm giao thức của Google và các định dạng biểu diễn dữ liệu dựa trên XML khác nhau sử dụng cách nhập rõ ràng.
3.3. Chúng ta nên liên kết với máy và cổng nào?
Chúng tôi cần xác định vị trí một máy chủ từ xa và quy trình thích hợp (cổng hoặc địa chỉ truyền tải) trên máy chủ đó. Một giải pháp là duy trì một cơ sở dữ liệu tập trung có thể định vị một máy chủ lưu trữ cung cấp một loại dịch vụ. Đây là cách tiếp cận được Birell và Nelson đề xuất trong bài báo giới thiệu RPC năm 1984 của họ. Một máy chủ gửi một thông báo đến một cơ quan trung ương cho biết họ sẵn sàng chấp nhận một số cuộc gọi thủ tục từ xa. Sau đó, khách hàng liên hệ với cơ quan trung tâm này khi họ cần tìm một dịch vụ. Một giải pháp khác, ít trang nhã hơn nhưng dễ quản lý hơn, là yêu cầu khách hàng biết máy chủ mà nó cần liên hệ. Máy chủ định danh trên máy chủ lưu trữ đó duy trì cơ sở dữ liệu về các dịch vụ được cung cấp tại địa phương.
3.4. Tầng giao vận được sử dụng như thế nào?
Một số triển khai chỉ cho phép một phương thức được sử dụng (ví dụ: TCP). Hầu hết các triển khai RPC đều hỗ trợ một số và cho phép người dùng lựa chọn.
Điều gì xảy ra khi mọi thứ diễn ra không như ý muốn?
Có nhiều cơ hội cho lỗi bây giờ. Máy chủ có thể tạo ra lỗi, có thể có sự cố trong mạng, máy chủ có thể gặp sự cố hoặc máy khách có thể biến mất trong khi máy chủ đang chạy mã cho nó. Tính minh bạch của các cuộc gọi thủ tục từ xa bị phá vỡ ở đây vì các cuộc gọi thủ tục cục bộ không có khái niệm về sự thất bại của cuộc gọi thủ tục. Do đó, các chương trình sử dụng lệnh gọi thủ tục từ xa phải được chuẩn bị để kiểm tra sự thất bại của lệnh gọi thủ tục từ xa hoặc bắt một ngoại lệ.
3.3. Ngữ nghĩa của các cuộc gọi từ xa là gì?
Ngữ nghĩa của việc gọi một thủ tục thông thường rất đơn giản: một thủ tục được thực thi chính xác một lần khi chúng ta gọi nó. Với một thủ tục từ xa, khía cạnh chính xác một lần là khá khó để đạt được. Một thủ tục từ xa có thể được thực hiện:
Lần 0: nếu máy chủ gặp sự cố hoặc quá trình bị chết trước khi chạy mã máy chủ.
Lần 1: nếu mọi thứ hoạt động tốt.
Lần 2: nếu máy chủ bị lỗi sau khi quay lại sơ khai máy chủ nhưng trước khi gửi phản hồi. Khách hàng sẽ không nhận được phản hồi trả lại và có thể quyết định thử lại, do đó thực thi chức năng nhiều lần. Nếu không thử lại, hàm sẽ được thực thi một lần.
Lần tiếp theo hoặc hiều hơn một lần: nếu máy khách hết thời gian chờ và truyền lại. Có thể là yêu cầu ban đầu có thể đã bị trì hoãn. Cả hai đều có thể được thực thi (hoặc không).
Các hệ thống RPC thường sẽ cung cấp ít nhất một lần hoặc nhiều nhất một lần ngữ nghĩa hoặc sự lựa chọn giữa chúng. Người ta cần hiểu bản chất của ứng dụng và chức năng của các thủ tục từ xa để xác định xem liệu có thể gọi một hàm nhiều lần là an toàn hay không. Nếu một hàm có thể được chạy bất kỳ số lần nào mà không gây hại, thì nó là không quan trọng (ví dụ: thời gian trong ngày, các hàm toán học, đọc dữ liệu tĩnh). Nếu không, nó là một hàm không cần ưu tiên (ví dụ: nối thêm hoặc sửa đổi một tệp).
3.4. Programming with remote procedure calls
Hình 2. Các bước biên dịch cho các cuộc gọi thủ tục từ xa
Nhiều ngôn ngữ phổ biến ngày nay (C, C ++, Python, Scheme, v.v.) không được thiết kế với cú pháp tích hợp cho các thủ tục từ xa và do đó không có khả năng tạo ra các hàm sơ khai cần thiết. Để cho phép sử dụng các lệnh gọi thủ tục từ xa với các ngôn ngữ này, giải pháp thường được áp dụng là cung cấp một trình biên dịch riêng biệt tạo ra các hàm sơ khai của máy khách và máy chủ. Trình biên dịch này lấy đầu vào từ định nghĩa do người lập trình chỉ định về giao diện gọi thủ tục từ xa. Định nghĩa như vậy được viết bằng ngôn ngữ định nghĩa giao diện.
Định nghĩa giao diện nhìn chung tương tự như khai báo nguyên mẫu hàm: nó liệt kê tập hợp các hàm cùng với các tham số đầu vào và trả về. Sau khi trình biên dịch RPC được chạy, các chương trình máy khách và máy chủ có thể được biên dịch và liên kết với các hàm sơ khai thích hợp (Hình 2). Thủ tục máy khách phải được sửa đổi để khởi động cơ chế RPC (ví dụ: định vị máy chủ và có thể thiết lập kết nối) và để xử lý lỗi của các lệnh gọi thủ tục từ xa.
Advantages of RPC
Bạn không phải lo lắng về việc lấy một địa chỉ vận chuyển duy nhất (chọn một số cổng duy nhất cho một ổ cắm trên máy). Máy chủ có thể liên kết với bất kỳ cổng nào có sẵn và sau đó đăng ký cổng đó với máy chủ định danh RPC. Máy khách sẽ liên hệ với máy chủ định danh này để tìm số cổng tương ứng với chương trình mà nó cần. Tất cả điều này sẽ vô hình đối với lập trình viên.
Hệ thống có thể độc lập với nhà cung cấp vận tải. Phần cuống máy chủ được tạo tự động có thể tự cung cấp cho mọi nhà cung cấp dịch vụ truyền tải trên hệ thống, cả TCP và UDP. Khách hàng có thể chọn động và không cần lập trình thêm vì mã để gửi và nhận tin nhắn được tạo tự động.
Các ứng dụng trên máy khách chỉ cần biết một địa chỉ truyền tải: địa chỉ của máy chủ định danh chịu trách nhiệm cho ứng dụng biết nơi kết nối cho một tập hợp các chức năng máy chủ nhất định.
Mô hình gọi hàm có thể được sử dụng thay cho giao diện gửi / nhận (đọc / ghi) được cung cấp bởi các socket. Người dùng không phải xử lý các thông số sắp xếp và sau đó phân tích cú pháp chúng ra ở phía bên kia.
(còn tiếp)
» Tin mới nhất:
» Các tin khác: