Виклик віддалених процедур

Матеріал з Вікіпедії — вільної енциклопедії.
Перейти до: навігація, пошук

Виклик віддалених процедур (англ. Remote procedure call, RPC) — протокол, що дозволяє програмі, запущеній на одному комп'ютері бути викликаною на іншому комп'ютері без написання безпосередньо коду для цієї операції.

Концепція віддаленого виклику процедур[ред.ред. код]

Ідея виклику віддалених процедур (англ. Remote Procedure Call — RPC) полягає у розширенні механізму передачі управління і даних усередині програми, що виконується на одній машині, на передачу керування й даних через мережу. Засоби віддаленого виклику процедур призначені для полегшення організації розподілених обчислень. Найбільша ефективність використання RPC досягається в тих додатках, в яких існує інтерактивний зв'язок між віддаленими компонентами з невеликим часом відповідей і відносно малою кількістю переданих даних. Такі програми називаються RPC-орієнтованими.

Існує ряд технологій, що забезпечують RPC, зокрема:

  • Sun RPC (бінарний протокол на базі TCP та UDP)
  • Net Remoting (бінарний протокол на базі TCP, UDP, HTTP)
  • XML-RPC (текстовий протокол на базі HTTP)
  • SOAP — Simple Object Access Protocol (текстовий протокол на базі HTTP)
  • Java RMI — Java Remote Method Invocation
  • JSON-RPC JavaScript Object Remote Procedure Calls (текстовий, на базі HTTP)

Характерними рисами виклику локальних процедур є:

  • Асиметричність, тобто одна із сторін є ініціатором;
  • Синхронність, тобто виконання процедури, що викликає віддалену процедуру, призупиняється з моменту видачі запиту і відновлюється тільки після повернення з викликаної процедури.

Реалізація віддалених викликів істотно складніше реалізації викликів локальних процедур. Почнемо з того, що оскільки викликаюча і викликана процедури виконуються на різних машинах, то вони мають різні адресні простори, і це створює проблеми при передачі параметрів і результатів, особливо якщо машини не ідентичні. Так як RPC не може розраховувати на розподілену пам'ять, то це означає, що параметри RPC не повинні містити вказівників на дані в нестековій пам'яті і що значення параметрів повинні копіюватися з одного комп'ютера на інший.

Але в реалізації RPC беруть участь щонайменше два процеси — по одному в кожній машині. У випадку, якщо один з них аварійно завершиться, можуть виникнути такі ситуації: при аварії викликаючої процедури віддалено викликані процедури стануть «осиротілими», а при аварійному завершенні віддалених процедур стануть «знедоленими батьками» викликаючі процедури, які будуть безрезультатно чекати відповіді від віддалених процедур.

Крім того, існує ряд проблем, пов'язаних з неоднорідністю мов програмування та операційних середовищ: структури даних і структури виклику процедур, підтримувані в будь-якому однією мовою програмування, не підтримуються точно так само у всіх інших мовах.

Ці та деякі інші проблеми вирішує широко поширена технологія RPC, що лежить в основі багатьох розподілених операційних систем, наприклад Plan 9.

Базові операції RPC[ред.ред. код]

Щоб зрозуміти роботу RPC, розглянемо спочатку виконання виклику локальної процедури у звичайній машині, що працює автономно. Нехай це, наприклад, буде системний виклик: count = read (fd, buf, nbytes); де fd — ціле число, buf — масив символів, nbytes — ціле число.

Щоб здійснити виклик, викликаюча процедура заштовхує параметри в стек у зворотному порядку. Після того, як виклик read виконаний, він поміщає значення, що повертається в регістр, переміщує адресу повернення і повертає управління викликаючій процедурі, яка вибирає параметри з стека, повертаючи його в початковий стан. Зауважимо, що в мові С параметри можуть викликатися або по посиланню (by name), або за значенням (by value). По відношенню до викликаючої процедури параметри-значення є ініціалізованими локальними змінними. Викликана процедура може змінити їх, і це не вплине на значення оригіналів цих змінних в викликаючій процедурі. Якщо в визвану процедуру передається покажчик на змінну, то зміна значення цієї змінної визваної процедури тягне зміну значення цієї змінної і для викликаючої процедури. Цей факт дуже істотний для RPC.

Існує також інший механізм передачі параметрів, який не використовується в мові С. Він називається call-by-copy/restore і полягає в необхідності копіювання викликаючою програмою змінних в стек у вигляді значень, а потім копіювання назад після виконання виклику поверх оригінальних значень викликаючої процедури.

Рішення про те, який механізм передачі параметрів використовувати, приймається розробниками мови. Іноді це залежить від типу переданих даних. У мові С, наприклад, цілі та інші скалярні дані завжди передаються за значенням, а масиви — по посиланню.


Ідея, покладена в основу RPC, полягає в тому, щоб зробити виклик віддаленої процедури по можливості схожим до виклику локальної процедури. Іншими словами — зробити RPC прозорим: викликаючій процедурі не потрібно знати, що викликається процедура знаходиться на іншій машині, і навпаки.

RPC досягає прозорості наступним шляхом. Коли викликана процедура дійсно є віддалена, в бібліотеку поміщається замість локальної процедури інша версія процедури, яка називається клієнтським стабом (stub — заглушка). Подібно оригінальної процедурі, стаб викликається з використанням викликаючої послідовності, так само відбувається переривання при зверненні до ядра. Тільки на відміну від оригінальної процедури він не поміщає параметри в регістри і не запитує у ядра дані, замість цього він формує повідомлення для відправки ядру віддаленої машини.

Етапи виконання RPC[ред.ред. код]

Взаємодія програмних компонентів при виконанні віддаленого виклику процедури ілюструється малюнком 2. Після того, як клієнтський стаб був викликаний програмою-клієнтом, його першим завданням є заповнення буфера відправлені повідомленням. У деяких системах клієнтський стаб має єдиний буфер фіксованої довжини, що заповнюється щоразу з самого початку при вступі кожного нового запиту. В інших системах буфер повідомлення являє собою пул буферів для окремих полів повідомлення, причому деякі з цих буферів вже заповнені. Цей метод особливо підходить для тих випадків, коли пакет має формат, що складається з великої кількості полів, але значення багатьох з цих полів не змінюються від виклику до виклику.

Потім параметри повинні бути перетворені у відповідний формат і вставлені в буфер повідомлення. До цього моменту повідомлення готове до передачі, тому виконується переривання за викликом ядра.

Коли ядро отримує управління, воно перемикає контексти, зберігає регістри процесора і карту пам'яті (дескриптори сторінок), встановлює нову карту пам'яті, яка буде використовуватися для роботи в режимі ядра. Оскільки контексти ядра і користувача розрізняються, ядро має точно скопіювати повідомлення в свій власний адресний простір, так, щоб мати до нього доступ, запам'ятати адресу призначення (а, можливо, і інші поля заголовка), а також воно має передати його мережевому інтерфейсу. На цьому завершується робота на клієнтській стороні. Включається таймер передачі, і ядро може або виконувати циклічне опитування наявності відповіді, або передати управління планувальником, який обере будь-який інший процес на виконання. У першому випадку прискорюється виконання запиту, але відсутнє мультипрограмування.

На стороні сервера біти, що надходять, поміщаються приймаючої апаратурою або у вбудований буфер, або в оперативну пам'ять. Коли вся інформація буде отримана, генерується переривання. Обробник переривання перевіряє правильність даних пакета і визначає, якому стабу слід їх передати. Якщо жоден із стабів не очікує цей пакет, обробник повинен або помістити його в буфер, або взагалі відмовитися від нього. Якщо є очікуючий стаб, то повідомлення копіюється йому. Нарешті, виконується переключення контекстів, в результаті чого відновлюються регістри і карта пам'яті, приймаючи ті значення, які вони мали в момент, коли стаб зробив виклик receive.

Тепер починає роботу серверний стаб. Він розпаковує параметри і поміщає їх відповідним чином в стек. Коли все готово, виконується виклик сервера. Після виконання процедури сервер передає результати клієнту. Для цього виконуються всі описані вище етапи, тільки в зворотному порядку.

14 етапів виконання RPC:

Клієнт 1. Виклик стабу 2. Підготувати буфер 3. Упакувати параметри 4. Заповнити поле заголовка 5. Обчислити контрольну суму в повідомленні 6. Переривання до ядра 7. Черга пакету на виконання 8. Передача повідомлення контролеру по шині QBUS 9. Час передачі по мережі Ethernet

Сервер 10. Отримати пакет від контролера 11. Процедура обробки переривання 12. Обчислення контрольної суми 13. Переключення контексту в простір користувача 14. Виконання серверного стабу

Динамічне зв'язування[ред.ред. код]

Розглянемо питання про те, як клієнт задає місце розташування сервера. Одним з методів вирішення цієї проблеми є безпосереднє використання адресу сервера в клієнтській програмі. Недолік такого підходу — його надзвичайна негнучкість: при переміщенні сервера, або при збільшенні числа серверів, або при зміні інтерфейсу у всіх цих та багатьох інших випадках необхідно перекомпілювати всі програми, які використовували жорстке завдання адреси сервера. Для того, щоб уникнути всіх цих проблем, в деяких розподілених системах використовується так зване динамічне зв'язування.

Початковим моментом для динамічного зв'язування є формальне визначення (специфікація) сервера. Специфікація містить ім'я файлу-сервера, номер версії і список процедур-послуг, що надаються даним сервером для клієнтів. Для кожної процедури дається опис її параметрів із зазначенням того, чи є даний параметр вхідним чи вихідним щодо сервера. Деякі параметри можуть бути одночасно вхідними і вихідними — наприклад, деякий масив, який надсилається клієнтом на сервер, модифікується там, а потім повертається назад клієнтові.

Формальна специфікація сервера використовується в якості вихідних даних для програми-генератора стабів, яка створює як клієнтські, так і серверні стаби. Потім вони поміщаються у відповідні бібліотеки. Коли користувальницька (клієнтська) програма викликає якусь процедуру, визначену в специфікації сервера, відповідна стаб-процедура зв'язується з двійковим кодом програми.

При запуску сервера найпершою його дією є передача свого серверного інтерфейсу спеціальною програмою, так званим binder'ом. Цей процес, відомий як процес реєстрації сервера, включає передачу сервером свого імені, номера версії, унікального ідентифікатора і описувача місцезнаходження сервера. Описувач системно незалежний і може представляти собою IP, Ethernet, X.500 або ще яку-небудь адресу. Крім того, він може містити й іншу інформацію, наприклад дані, що стосуються аутентифікації.

Коли клієнт викликає одну з віддалених процедур перший раз, наприклад, read, клієнтський стаб бачить, що він ще не підключений до сервера, і надсилає повідомлення binder-програмі з проханням про імпорт інтерфейсу потрібної версії потрібного сервера. Якщо такий сервер існує, то binder передає описувач і унікальний ідентифікатор клієнтському стабу.

Клієнтський стаб при посилці повідомлення із запитом використовує в якості адреси описувач. У повідомленні містяться параметри і унікальний ідентифікатор, який ядро сервера використовує для того, щоб направити надійшло повідомлення в потрібний сервер у випадку, якщо їх декілька на цій машині.

Цей метод, що полягає в імпорті / експорті інтерфейсів, володіє високою гнучкістю. Наприклад, може бути кілька серверів, що підтримують один і той же інтерфейс, і клієнти розподіляються по серверах випадковим чином. У рамках цього методу стає можливим періодичне опитування серверів, аналіз їх працездатності та, у разі відмови, автоматичне відключення, що підвищує загальну відмовостійкість системи. Цей метод може також підтримувати аутентифікацію клієнта. Наприклад, сервер може визначити, що він може бути використаний тільки клієнтами з певного списку.

Однак у динамічного зв'язування є недоліки, наприклад, додаткові накладні витрати (тимчасові витрати) на експорт та імпорт інтерфейсів. Величина цих витрат може бути значною, тому що багато клієнтські процеси існують короткий час, а при кожному старті процесу процедура імпорту інтерфейсу повинна бути знову виконана. Крім того, у великих розподілених системах може бути вузьким місцем програма binder, а створення декількох програм аналогічного призначення також збільшує накладні витрати на створення і синхронізацію процесів.

Семантика RPC в разі відмов[ред.ред. код]

В ідеалі RPC повинен функціонувати правильно і у випадку відмов. Розглянемо наступні класи відмов:

  1. Клієнт не може визначити місцезнаходження сервера, наприклад, у разі відмови потрібного сервера, або через те, що програма клієнта була скомпільована давно і використовувала стару версію інтерфейсу сервера. У цьому випадку у відповідь на запит клієнта надходить повідомлення, що містить код помилки.
  2. Втрачено запит від клієнта до сервера. Найпростіше рішення — через певний час повторити запит.
  3. Втрачено повідомлення-відповідь від сервера до клієнта. Цей варіант складніший від попереднього, так як деякі процедури не є ідемпотентними. Ідемпотентною називається процедура, запит на виконання якої можна повторити кілька разів, і результат при цьому не зміниться. Прикладом такої процедури може бути читання файлу. Але ось процедура зняття деякої суми з банківського рахунку не є ідемпотентною, і в разі втрати відповіді повторний запит може істотно змінити стан рахунку клієнта. Одним з можливих рішень є приведення всіх процедур до ідемпотентного виду. Однак на практиці це не завжди вдається, тому може бути використаний інший метод — послідовна нумерація всіх запитів клієнтським ядром. Ядро сервера запам'ятовує номер самого останнього запиту від кожного з клієнтів, і при отриманні кожного запиту виконує аналіз — чи є цей запит первинним або повторним.
  4. Сервер зазнав аварію після отримання запиту. Тут також важливо властивість Ідемпотентний, але на жаль не може бути застосований підхід з нумерацією запитів. У цьому випадку має значення, коли відбулася відмова — до чи після виконання операції. Але клієнтське ядро не може розпізнати ці ситуації, для нього відомо тільки те, що час відповіді вичерпано.

Існує три підходи до цієї проблеми:

  1. Чекати до тих пір, поки сервер не перезавантажиться і намагатися виконати операцію знову. Цей підхід гарантує, що RPC був виконаний до кінця принаймні один раз, а можливо і більше.
  2. Відразу повідомити додатка про помилку. Цей підхід гарантує, що RPC був виконаний не більше одного разу.
  3. Третій підхід не гарантує нічого. Коли сервер відмовляє, клієнтові не надається ніякої підтримки. RPC може бути або не виконано взагалі, чи виконано багато разів. В усякому разі цей спосіб дуже легко реалізувати.

Див. також[ред.ред. код]

Комп'ютер Це незавершена стаття про комп'ютери.
Ви можете допомогти проекту, виправивши або дописавши її.