John Squirrels
مستوى
San Francisco

جافا RMI

نشرت في المجموعة
أهلاً! اليوم سننظر في موضوع مثير للاهتمام إلى حد ما: Java RMI. هذا يعني استدعاء الطريقة عن بعد. يمكنك استخدام 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 يعتمد على إنشاء البروكسيات التي درستها في الدروس السابقة . كما تتذكر على الأرجح، نحن نعمل مع الوكلاء من خلال الواجهات، وليس من خلال الفئات. هناك متطلبان مهمان لواجهتنا!
  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);
نقوم "بتسجيل" كعب الروتين الخاص بنا في سجل الكائنات البعيدة تحت الاسم الذي اخترناه في البداية. الآن سيتمكن العميل من العثور عليه! ربما لاحظت أننا وضعنا الموضوع الرئيسي للبرنامج في وضع السكون في النهاية:
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);
انتبه إلى نوع الصب. لقد قمنا بإرسال الكائن المستلم إلى واجهة الحاسبة ، وليس إلى فئة RemoteCaculationServer . كما قلنا في بداية الدرس، يعتمد RMI على الوكيل، لذا فإن الاستدعاءات عن بعد متاحة فقط لطرق الواجهة، وليس لطرق الفئات. أخيرًا، نستدعي طريقة الضرب () عن بعد على كائننا ونخرج النتيجة إلى وحدة التحكم.
int multiplyResult = calculator.multiply(20, 30);
System.out.println(multiplyResult);
لقد تم بالفعل تشغيل الطريقة الرئيسية () لفئة ServerMain لفترة طويلة. حان الوقت الآن لتشغيل الطريقة main() في برنامج العميل ( ClientMain )! إخراج وحدة التحكم:

600
هذا كل شيء! لقد قام برنامجنا (برنامجان، في الواقع!) بما كان من المفترض أن يفعله :) إذا كان لديك الوقت والرغبة، يمكنك إضافة المزيد من الإثارة إلى هذا الأمر. على سبيل المثال، اجعل الآلة الحاسبة تدعم العمليات الحسابية القياسية الأربعة، وقم بتمرير الأرقام ليس كأنواع بدائية، ولكن ككائنات CalculationInstance(int x, int y) . نراكم في الدرس القادم! :)
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION