John Squirrels
רָמָה
San Francisco

Java RMI

פורסם בקבוצה
היי! היום נשקול נושא מעניין למדי: Java RMI. זה קיצור של Remote Method Invocation. אתה יכול להשתמש ב-RMI כדי לאפשר לשתי תוכניות לתקשר זו עם זו, גם אם הן נמצאות במחשבים שונים. זה נשמע מגניב? :) וזה לא כל כך קשה לעשות! בשיעור של היום, ננתח את המרכיבים של האינטראקציה RMI ונבין כיצד להגדיר אותה. הדבר הראשון שאנחנו צריכים זה לקוח ושרת. אנחנו לא באמת צריכים לצלול עמוק לתוך מינוח המחשב. כשזה מגיע ל-RMI, אלו רק שתי תוכניות. אחד מהם יכלול אובייקט, והשני יקרא שיטות על אותו אובייקט. קריאה לשיטות של אובייקט שקיים בתוכנית אחרת - עכשיו זה משהו שעדיין לא עשינו! זה הזמן לנסות! :) כדי להימנע מלהסתבך, בואו נשמור על התוכנית שלנו פשוטה. באופן כללי, שרת מבצע כמה חישובים המבוקש על ידי לקוח. וכך זה יהיה גם אצלנו. השרת שלנו יהיה תוכנת מחשבון פשוטה. תהיה לו רק שיטה אחת: multiply() . הוא יכפיל שני מספרים שנשלחו אליו על ידי תוכנית הלקוח, ולאחר מכן יחזיר את התוצאה. קודם כל, אנחנו צריכים ממשק:
import java.rmi.Remote;
import java.rmi.RemoteException;

public interface Calculator extends Remote {

   int multiply(int x, int y) throws RemoteException;
}
למה אנחנו צריכים ממשק? כי RMI מסתמך על יצירת פרוקסי, שאותם למדת בשיעורים קודמים . כפי שאתם בוודאי זוכרים, אנו עובדים עם פרוקסי דרך ממשקים, לא מחלקות. ישנן 2 דרישות חשובות לממשק שלנו!
  1. זה חייב להרחיב את הממשק המרוחק.
  2. כל השיטות שלה חייבות לזרוק RemoteException (ה-IDE לא יעשה זאת אוטומטית - אתה צריך להוסיף את זה ידנית!).
כעת עלינו ליצור כיתת שרת המיישמת את ממשק המחשבון שלנו . RMI בפועל - 2גם כאן הכל די פשוט:
import java.rmi.RemoteException;

public class RemoteCalculationServer implements Calculator {

   @Override
   public int multiply(int x, int y) throws RemoteException {
       return x*y;
   }

}
אין ממש על מה להגיב כאן :) עכשיו אנחנו צריכים לכתוב תוכנת שרת שתגדיר ותפעיל את אובייקט המחשבון שלנו. זה ייראה כך:
import java.rmi.AlreadyBoundException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;

public class ServerMain {

   public static final String UNIQUE_BINDING_NAME = "server.calculator";

   public static void main(String[] args) throws RemoteException, AlreadyBoundException, InterruptedException {

       final RemoteCalculationServer server = new RemoteCalculationServer();

       final Registry registry = LocateRegistry.createRegistry(2732);

       Remote stub = UnicastRemoteObject.exportObject(server, 0);
       registry.bind(UNIQUE_BINDING_NAME, stub);

       Thread.sleep(Integer.MAX_VALUE);

   }
}
בוא נבין את זה :) בשורה הראשונה, אנו מכריזים על איזה משתנה מחרוזת:
public static final String UNIQUE_BINDING_NAME = "server.calculator";
מחרוזת זו היא השם הייחודי של האובייקט המרוחק. תוכנית הלקוח שלנו משתמשת בשם זה כדי למצוא את השרת שלנו: אתה תראה את זה מאוחר יותר. לאחר מכן, אנו יוצרים את אובייקט המחשבון שלנו:
final RemoteCalculationServer server = new RemoteCalculationServer();
הכל ברור כאן. מה שיבוא אחר כך מעניין יותר:
final Registry registry = LocateRegistry.createRegistry(2732);
אובייקט רישום זה הוא רישום של אובייקטים מרוחקים. אלו אובייקטים שתוכניות אחרות יכולות לגשת אליהם מרחוק :) העברנו את המספר 2732 לשיטת LocateRegistry.createRegistry() . זהו מספר היציאה - מספר ייחודי שתוכניות אחרות ישתמשו בו כדי למצוא את רישום האובייקטים שלנו (שוב, תראה זאת למטה). ממשיכים קדימה... בוא נראה מה קורה בשורה הבאה:
Remote stub = UnicastRemoteObject.exportObject(server, 0);
אנחנו יוצרים בדל בשורה הזו. בדל מקפל את כל השיחה המרוחקת. אתה יכול לראות בזה את המרכיב החשוב ביותר של RMI. מה זה עושה?
  1. הוא מקבל את כל המידע על שיחה מרחוק של שיטה כלשהי.
  2. אם לשיטה יש פרמטרים, הסטאב יבטל אותם בסידריאל. שימו לב לנקודה זו! הארגומנטים שאתה מעביר לשיטות הנקראות מרחוק חייבות להיות ניתנות לסידרה (אחרי הכל, הם ישודרו ברשת). זו לא בעיה עבורנו - אנחנו רק משדרים מספרים. אבל אם אתה משדר חפצים, אל תשכח את הדרישה הזו!
  3. לאחר מכן, הסטאב קורא לשיטה הרצויה.
אנו מעבירים את אובייקט שרת המחשבון שלנו לשיטת UnicastRemoteObject.exportObject() . כך אנו מאפשרים לקרוא מרחוק לשיטות שלה. נשאר רק דבר אחד לעשות:
registry.bind(UNIQUE_BINDING_NAME, stub);
אנו "נרשמים" את ה-stub שלנו ברישום האובייקטים המרוחק תחת השם שהמצאנו ממש בהתחלה. כעת הלקוח יוכל למצוא אותו! אולי שמתם לב שהרדמנו את השרשור הראשי של התוכנית בסוף:
Thread.sleep(Integer.MAX_VALUE);
אנחנו רק צריכים שהשרת יפעל לאורך זמן. ב-IDE נשיק במקביל שתי שיטות main() : ראשית, שיטת main() של השרת (במחלקה ServerMain , שכבר כתבנו), ושנית, שיטת main() של הלקוח (במחלקה ClientMain , שנכתוב להלן). חשוב שתוכנת השרת לא תסתיים בזמן שאנו מפעילים את הלקוח, אז פשוט נרדם אותו למשך זמן רב. בכל מקרה, זה ימשיך לפעול :) כעת נוכל להפעיל את השיטה main() של השרת שלנו . תן לזה לרוץ ולחכות שתוכנית הלקוח תקרא לשיטה כלשהי :) עכשיו בוא נכתוב את תוכנת הלקוח! זה ישלח מספרים לשרת שלנו להכפלה.
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class ClientMain {

   public static final String UNIQUE_BINDING_NAME = "server.calculator";

   public static void main(String[] args) throws RemoteException, NotBoundException {

       final Registry registry = LocateRegistry.getRegistry(2732);

       Calculator calculator = (Calculator) registry.lookup(UNIQUE_BINDING_NAME);

       int multiplyResult = calculator.multiply(20, 30);

       System.out.println(multiplyResult);
   }
}
זה נראה פשוט. אבל מה קורה כאן? ראשית, הלקוח חייב לדעת את השם הייחודי של האובייקט שלשיטות שלו הוא יקרא מרחוק. בהתאם לכך, בתוכנית הלקוח, יצרנו את המחרוזת הסופית הסטטית הציבורית UNIQUE_BINDING_NAME = "server.calculator"; מִשְׁתַנֶה. לאחר מכן, בשיטת main() אנו מקבלים גישה לרישום של אובייקטים מרוחקים. כדי לעשות זאת, עלינו לקרוא לשיטת LocateRegistry.getRegistry() ולהעביר את מספר היציאה המשמש ליצירת הרישום שלנו בתוכנית ServerMain (יציאה 2732; מספר זה הוא רק דוגמה - אתה יכול לנסות להשתמש במספר אחר):
final Registry registry = LocateRegistry.getRegistry(2732);
עכשיו אנחנו רק צריכים לקבל את האובייקט הרצוי מהרישום! זה קל, כי אנחנו יודעים את השם הייחודי שלו!
Calculator calculator = (Calculator) registry.lookup(UNIQUE_BINDING_NAME);
שימו לב לסוג הליהוק. אנו משליכים את האובייקט שהתקבל לממשק המחשבון , לא למחלקה RemoteCalculationServer . כפי שאמרנו בתחילת השיעור, RMI מסתמך על פרוקסי, כך ששיחות מרחוק זמינות רק לשיטות של ממשק, לא לשיטות של מחלקות. לבסוף, אנו קוראים מרחוק לשיטת multiply() באובייקט שלנו ומוציאים את התוצאה לקונסולה.
int multiplyResult = calculator.multiply(20, 30);
System.out.println(multiplyResult);
השיטה main() של המחלקה ServerMain כבר פועלת במשך זמן רב. עכשיו הגיע הזמן להפעיל את שיטת main() בתוכנת הלקוח ( ClientMain )! פלט מסוף:

600
זהו זה! התוכנית שלנו (שתי תוכניות, בעצם!) עשתה את מה שהיא הייתה אמורה לעשות :) אם יש לך זמן וחשק, אתה יכול לתבל את זה קצת. לדוגמה, הפוך את המחשבון לתמוך בארבע הפעולות האריתמטיות הסטנדרטיות, ולהעביר מספרים לא כסוגים פרימיטיביים, אלא כאובייקטים CalculationInstance(int x, int y) . נתראה בשיעור הבא! :)
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION