سرويس‌های مبتنی بر رويداد در SOA

نويسنده: Jeff Hanson
JavaWorld
مترجم: احسان اميريان

خلاصه:

پاسخ‌دهي به تغييرات بلادرنگ و رويدادها به يك شيوه زماني، يكي از مهمترين نيازمنديهاي چهارچوب فراگير مي‌باشد. اين مقاله تكنولوژي‌ها و مكانيسم‌هايي را كه يك چهارچوب سرويس‌گرا را قادر مي‌سازد تا به صورتي كارا به محرك‌هاي بلادرنگ پاسخ دهد و از اين رو رويدادهاي همزمان و غيرهمزمان را در ميان لايه‌هاي يك معماري ارسال و دريافت نمايد، بدون اينكه از جزئيات سيستم رويدادي تحت آن اطلاعي داشته باشد، شرح مي‌دهد.

تراكنش‌هاي اينترنتي، سيستم‌هاي (B2B) Business-to-Business، فرآيند‌هاي Peer-to-peer و جريان‌هاي كاري بلادرنگ بسيار پويا، براي مدل‌سازي با روش‌هاي سنتي پردازش ترتيبي و دنباله‌اي، بسيار پيچيده هستند. از اين رو نياز به تكنيك‌هاي پردازش غيرهمزمان بهتر و كاراتر، به سرعت پديدار مي‌شود. براي آدرس‌دهي و كنار آمدن با چنين محيط‌هاي غيرقابل پيش‌بيني، راهكار فعلي در معماري سيستم‌ها، طراحي سرويس‌گرا و برنامه‌نويسي مبتني‌بر رويداد مي‌باشد.

یک معماری سرویس‌گرا اساسا یک مچموعه از سرویس‌ها است که این سرویس‌ها با هم ارتباط و تعامل دارند. این ارتباط می‌تواند به صورت ارتباط از طریق پیغام باشد یا اینکه دو یا چند سرویس با همکاری هم، کاری را انجام دهند. معماری سرویس گرا چیز جدیدی نمی‌باشد. اولین معماری سرویس گرا برای کاربران در گذشته استفاده از DCOM یا ORB (Object Request Broker) بر پایه مشخصه‌های CORBA بود.

يك معماري سرويس‌گرا (SOA) يك محيط اجراي ديناميك ارائه مي‌دهد كه همكاري و ارتباط سبك و كم (Loose Coupling)، بين مهياكنندگان سرويس‌ و يا مصرف‌كنندگان و مشتريان سرويس، تعامل بين اجزاء قوي و انعطاف‌پذيري را منجر مي‌شود. ساخت يك مدل ارتباطي براي در خدمت گرفتن اين قدرت و انعطاف‌پذيري يك اولويت بالا براي توسعه دهندگان نرم‌افزاري كه رقيب يكديگر هستند، مي‌باشد.

يك مدل ارتباطي مبتني‌بر رويداد، پاسخ‌دهي بهتر به تغييرات و محرك‌هاي بلادرنگ را نسبت به مكانيسم‌هاي درخواست-پاسخ قراردادي، ممكن مي‌سازد.

معماري‌هاي سرويس‌گرا و مبتني‌بر رويداد به صورت طبيعي و ذاتي براي سيستم‌هاي توزيع شده، مناسب هستند، چرا؟ آنها ويژگي‌هاي يكسان زيادي نظير ماژولاريتي، سازگاري و Loose-Coupling را به اشتراك مي‌گذارند.

در اينجا طراحي موثر و كاراي يك پلت‌فرم سرويس‌گرا و مبتني‌بر رويداد با استفاده از Mule كه يك چهارچوب سبك پيغام - رويداد (event-messaging) بوده و در محدوده الگوي(ESB) Enterprise Service Bus  طراحي شده، ارائه كرده است.

اجزاء و برنامه‌هاي كاربردي مي‌توانند از Mule براي برقراري ارتباط از طريق يك فريم‌ورك پيغام رساني مشترك كه به صورت واضح و شفاف و با استفاده از سرويس پيغام رساني جاوا (JMS) پياده‌سازي شده يا ديگر تكنولوژي‌هاي پيغام رساني، استفاده كنند.

 

مروري بر معماري سرويس‌گرا

اصطلاح »سرويس‌گرا« براي تعريف معماري‌ استفاده شده است كه در آن يك سرويس، يك جزء نرم‌افزاري است كه مشتمل بر يك بخش اصلي از يك منطق كاري جامع است و ويژگي‌هاي ريز را دارد:

* اتصال سبك (Loosely Coupled): سرويس‌ها به طور اساسي با ديگر اجزاء تركيب نشده‌اند.

* مستقل از پروتكل: چندين پروتكل مي‌توانند بصورت آشكار و شفاف به يك سرويس ارائه شده دسترسي داشته باشند.

* يك سرويس‌ ارائه شده اصولا يك شكل تركيبي از منطق كاري را اجرا مي‌كند و نتايج را در يك فراخواني ارسال مي‌نمايد.

* سرويس‌ها به يك شيوه يكسان قابل دستيابي‌ هستند و مكان آنها مهم نمي‌باشد.

* حالت هيچ كاربري را نگه نمي‌دارد.

سرويس‌ها اساسا بصورت انحصاري بر حل مشكلات دامنه‌هاي تجاري تمركز دارند. عموما مشتريان سرويس بر پيكربندي داده، رجيستري‌ها و فاكتورهاي نرم‌افزاري براي تعيين مكان، پروتكل و واسط كاربري عمومي براي هر سرويس تمركز دارند.

اصولا برنامه‌هاي كاربردي اينگونه تشريح مي‌شوند كه آنها چه كار مي‌كنند و الزاما اينگونه نيست كه آنها چه هستند و يا شامل چه مي‌باشند. به اين منظور، سر راست‌تر است كه يك برنامه كاربردي را با استفاده از افعال (سرويس‌ها) تشريح كنيم تا استفاده از نام‌ها (اشياء). از آنجا كه اشياء چيزها را تعريف مي‌كنند نه اعمال را، در تلاش براي كپسوله‌سازي اينكه يك جزء چه كاري انجام مي‌دهد، در مقابل اينكه يك جزء چه هست، دچار ناسازگاري و عدم انطباق مي‌شويم.

در SOA يك برنامه‌ كاربردي بصورت ماهيتي و طبيعي تشريح مي‌شود، از اين رو هر عمليات منطقي از برنامه كاربردي كه مي‌تواند با فعل بيان شود، كانديدي مناسب براي يك سرويس است، بنابراين SOA ناسازگاري موجود را با اجازه دسترسي دادن به برنامه‌هاي كاربردي و اجزاء به عملكردهاي سرويس بر پايه اينكه سرويس‌ها چه كاري انجام مي‌دهند (يعني چه عملي را اجرا مي‌كنند)، حل مي‌كند.

در عوض، توسعه دهندگان برنامه‌هاي كاربردي مي‌توانند ساده‌تر نيازهاي خود را با سرويس‌هاي مناسب وفق دهند، تا جايي كه واسط‌هاي كاربري سرويس‌ها بصورت كاملتر مشكلاتي را كه حل مي‌كنند، تشريح مي‌نمايند.

 

مروري بر معماري مبتني بر رويداد

يك معماري مبتني‌بر رويداد (EDA)، يك متدولوژي براي طراحي و پياده‌سازي برنامه‌هاي كاربردي و سيستم‌ها تعريف مي‌كند، در حالتي كه رويدادها بين اجزاء نرم‌افزاري با اتصال سبك و سرويس‌ها منتقل مي‌شوند. يك سيستم مبتني ‌بر رويداد، اصولا شامل مصرف كنندگان رويداد و توليدكنندگان رويداد مي‌باشد. مصرف‌كنندگان رويداد به يك مدير رويداد مياني متصل شده و توليدكنندگان رويداد ارسال كننده به اين مدير هستند. هنگامي كه مدير رويداد، رويدادي را از توليد كننده دريافت نمود، آن را به مصرف كننده ارسال مي‌كند. اگر مصرف كننده در دسترس نباشد، مدير مي‌تواند رويداد را ذخيره و بعدا دوباره آن را ارسال نمايد. به اين شيوه، انتقال رويداد در سيستم‌هاي مبتني‌بر پيغام، ذخيره و ارسال اطلاق مي‌شود.

ساخت برنامه‌هاي كاربردي و سيستم‌ها در حيطه معماري مبتني‌بر رويداد اين امكان را فراهم مي‌سازد كه برنامه‌هاي كاربردي و سيستم‌ها به شيوه‌اي ساخته شوند كه به پاسخگويي بهتر كمك نمايند، چرا كه سيستم‌هاي مبتني‌بر رويداد، به دليل طراحي‌شان، براي محيط‌هاي غيرقابل پيش‌بيني و غيرهمزمان نرمال‌تر مي‌باشند.

 

مزاياي طراحي و توسعه مبتني‌بر رويداد

* طراحي و توسعه مبتني‌بر رويداد مزاياي زير را ارائه مي‌نمايد:

* اجازه توسعه و نگهداري آسان برنامه‌هاي كاربردي توزيع شده و در اندازه‌هاي بزرگ و همچنين سرويس‌هايي كه شامل وقايع غيرقابل پيش‌بيني و يا غيرهمزمان مي‌شوند، را مي‌دهد.

* اجازه پيكربندي، اسمبل كردن ساده و كم هزينه برنامه‌هاي كاربردي و سرويس‌هاي جديد و موجود را مي‌دهد.

* امكان استفاده مجدد از اجزاء و سرويس را فراهم مي‌آورد، بنابراين توسعه عاري از خطا و باگ و سريعتر سيستم را سبب مي‌شود.

* مزاياي كوتاه مدت: اجازه سفارشي نمودن ساده‌تر را مي‌دهد، چرا كه طراحي به فرآيندهاي پويا پاسخگوتر است.

* مزاياي بلند مدت: اجازه مي‌دهد كه صحت و سلامت سيستم و سازمان دقيق‌تر با همزماني بهتر، نزديكتر به تغييرات بلادرنگ باشد.

 

EDA  وSOA  با هم

برخلاف يك سيستم درخواست- پاسخ كه در آن فراخوان‌ها بايد بطور آشكار و واضح اطلاعات را درخواست كنند، يك معماري مبتني‌بر رويداد (EDA) مكانيسمي ارائه مي‌دهد كه سيستم هنگامي كه رويدادي واقع مي‌شود بصورت خودكار پاسخ دهد. در يك EDA، رويدادها توسط توليد كننده رويداد منتشر مي‌شوند و مصرف كنندگان رويداد هنگامي كه رويدادها روي مي‌دهند، آنها را دريافت مي‌كنند.

سيستم‌هاي تجاري و كاري از جنبه‌هاي هر دوي SOA وEDA  بهره مي‌برند، چرا كه يك EDA مي‌تواند مصرف كنندگان رويداد را به محض اينكه رويدادها روي دادند، تريگر نمايد و سرويس‌هاي با اتصال نيز مي‌توانند به سادگي مورد دستيابي قرار گرفته و از سوي مصرف‌كنندگان مورد جستجو واقع شوند.

سيستم‌هايي كه بايد قابليت پاسخگويي بالايي داشته باشند، بايد قادر باشند كه به سرعت اعمال لازم را در هنگام تريگر شدن رويدادها تعيين نمايند. بدين منظور، رويدادها بايد در تمام مرزهاي SOA كه شامل لايه‌هاي معماري و لايه‌هاي فيزيكي مي‌شود، منتشر و مصرف شوند.

شكل 1، رويدادهاي ممكن كه مي‌توانند در ميان لايه‌هاي معماري تريگر شوند، را نشان مي‌دهد.

 

 

 

جريان رويدادها در پشته معماري

 

در شكل 1، يك رويداد مي‌تواند به عنوان هر تغيير انتشار يافته در يك سيستم، پلات‌فرم، جزء كاري يا فرآيند برنامه كاربردي باشد. رويدادها در ويژگي‌ها مي‌توانند سطح بالا و كسب و كارگرا باشند يا سطح پايين و تكنيكي. از آنجا كه رويدادها مي‌توانند منتقل و دريافت شوند، برنامه‌هاي كاربردي و سرويس‌هايي كه از رويدادها پشتيباني مي‌كنند نيز مي‌توانند به تغييرات روي داده، در هنگام نياز پاسخ دهند.

 

طبقه‌بندي و علت رويداد

يك نكته حياتي كه بايد در مورد يك رويداد دانست اين است كه سبب و علت رويداد را در زماني كه رويداد واقع مي‌شود، بفهميم. اين دانش اغلب به عنوان عليت رويداد (Event Causality) خوانده مي‌شود. عليت رويداد، اصولا به دو دسته اصلي تقسيم مي‌شود.

* عليت افقي: هر دوي منبع رويداد و علت آن در يك لايه مفهومي در پشته معماري واقع شوند.

* عليت عمودي: منبع رويداد و علت آن در لايه‌هاي مختلف مفهومي در پشته معماري قرار دارند.

* عليت عمودي بر اين دلالت دارد كه طبقه‌بندي رويداد، گاهي در ميان لايه‌هاي مختلف يك سيستم ثابت بماند، چنانكه در ليست زير مشخص شده است:

* رويدادهاي چرخه حيات: تغييرات در چرخه حيات يك رويداد را تعيين مي‌كنند از قبيل آغاز يا توقف يك فرآيند.

* رويدادهاي اجرايي: وقايع زمان اجرا مثل فراخواني سرويس يا جزء را تعيين مي‌كنند.

* رويدادهاي مديريتي: تعيين مي‌كنند چه موقع مقادير آستانه (thresholds) از محدوديت‌ها و بازه‌هاي تعريف شده، تجاوز كنند.

* عليت افقي نيز سبب مي‌شود كه گاهي طبقه بندي رويداد در ميان يك لايه  سيستم، ثابت بماند. چنانكه در ليست زير نشان داده شده است:

* رويدادهاي لايه سيستم: فعاليت‌هاي سطح سيستم را از قبيل ساخت يك فايل يا بستن يك پورت، تعيين مي‌كنند.

* رويدادهاي سطح پلت‌فرم: فعاليت‌هاي سطح پلت‌فرم، مثل تغييرات يك منبع داده يا افزودن يك سرويس جديد را مشخص مي‌كنند.

* رويدادهاي لايه اجزاء: فعاليت‌هاي سطح جزء مثل، دگرگوني يك شيء View يا يك گذر ماشين حالت را تعيين مي‌كنند.

* رويدادهاي سطح كسب و كار: رويدادهاي سطح كسب و كار از قبيل ساخت و ايجاد يك كاربر يا حذف يك اكانت را مشخص مي‌كنند.

* رويدادهاي سطح كاربردي: فعاليت‌هاي سطح برنامه كاربردي مثل، افزايش پاداش را مشخص مي‌كنند.

مزيت‌هاي ارتباطات مبتني‌بر رويداد در داخل يك SOA، اكنون در حال واقعي شدن، توسط تعدادي از فريم‌ورك‌هاي پلات‌فرم‌هاي ESB است. يكي از اميدوارانه‌ترين آنها در داخل قلمرو توسعه جاوا، Mule است.

 

معرفي Mule

مول يك فريم‌ ورك منبع باز پيغام‌رساني ESB است كه بصورت سبك بر پايه معماري مبتني ‌بر رويداد مرحله‌اي (SEDA) مي‌باشد. SEDA يك پلات‌فرم فراگير و با همزماني بالا را با مراحلي (Stages: اجزاء خود شمول برنامه كاربردي) كه با صف‌ها به هم متصل شده‌اند، تعريف مي‌كند. مول از مفاهيم SEDA استفاده مي‌كند تا كارآيي رويدادهاي پردازشي را افزايش دهد.

مول پشتيباني از پردازش رويدادها بصورت غيرهمزمان، همزمان و درخواست- پاسخ را با استفاده از تكنولوژي‌هاي مختلف ارائه مي‌دهد و انتقال را با JMS، HTTP، نامه الكترونيكي و فراخواني از راه دور روال مبتني‌بر XML انجام مي‌دهد. مول مي‌تواند به آساني در هر فريم‌ورك برنامه كاربردي جاسازي شود و به‌طور واضح و آشكار چهارچوب Spring را حمايت مي‌كند. مول همچنين مسيريابي پويا، توصيفي، مبتني ‌بر محتوي و مبتني ‌بر قاعده پيغام‌ها را پشتيباني مي‌كند. مول پشتيباني از تراكنش توصيفي و قابل برنامه‌نويسي را ساده كرده كه شامل پشتيباني از تراكنش XA است.

مول به منظور دستيابي مبتني‌بر وب به رويدادها،Representational State Transfer (REST) API را فراهم نموده است. مدل EBS مول تمام سرويس‌هاي سيستم را بر پايه يك بك‌بن تجزيه شده ارتباط از طريق پيغام دارد. هر سرويسي تنها با پردازش رويدادهايي كه دريافت مي‌كند، در ارتباط است.

براي تشريح مفاهيم و ايده‌هاي بحث شده در اين مقاله از فريم‌ورك مول استفاده شده است.

 

معماري مول

معماري مول از عناصر زير تشكيل شده است:

UMOAPI: Universal Message Object (UMO) API سرويس‌ها و تعاملات اشيايي كه بايد توسط مول مديريت شوند را تعريف مي‌كند.

اجزاء UMO:

اجزاء UMO مي‌توانند هر جزئي در سيستم مول باشند كه داده را در قالب پيغام‌هاي رويداد دريافت، پردازش و ارسال مي‌كنند.

سرور مول:

سرور مول يك برنامه كاربردي سرور است كه در آغاز محيط مول شروع و بارگذاري مي‌شود.

 

توصيف‌گرها

اجزاء توصيف‌گر ويژگي‌هاي UMO مول را تشريح مي‌كنند. UMOهاي جديد مول مي‌توانند هنگامي كه نياز شد، از توصيفگرهاي مرتبط با آنها، مقداردهي اوليه شوند. يك توصيفگر شامل اجزاء زير است:

* نام جزءUMO

* نسخه جزء UMO

* كلاس پياده‌سازي جزء UMO

* يك استراتژي استثناء (exception)

* ارائه دهندگان Inbound وoutbound

* رياب‌هاي Inbound وoutbound

* كننده‌ها (Interceptors)

* نقاط پاياني دريافت و ارسال

* مبدل‌هاي Inbound وoutbound

* ويژگيهاي متفرقه

 

اتصال دهندگان

اتصال دهندگان اجزايي هستند كه پياده‌سازي‌ براي اتصال به يك سيستم يا پروتكل خارجي ارائه مي‌كنند و نشست‌هاي (Session) سيستم يا پروتكل را مديريت مي‌نمايند. يك اتصال دهنده مسئول ارسال داده به يك دريافت كننده‌هاي پيغام، مي‌باشد.

 

فراهم كنندگان (Providers)

فراهم كنندگان اجزائي هستند كه ارسال، دريافت و انتقال داده رويداد را از سيستم‌هاي خارجي مديريت مي‌كنند. آنها اتصال به سيستم‌هاي خارجي يا ديگر اجزاء را در مول ممكن مي‌سازند. يك ارائه دهنده به عنوان يك پل از سيستم خارجي به مول و برعكس عمل مي‌كند. اين جزء تركيبي است از مجموعه‌اي از اشياء كه براي اتصال و ارتباط با سيستم مورد نظر استفاده مي‌شود.

عناصر فراهم كننده عبارتند از:

* اتصال دهنده: مسئول ارتباط و اتصال به سيستم مورد نظر است.

* دريافت كننده پيغام: براي دريافت رويدادها از سيستم استفاده مي‌شود.

* توزيع كننده اتصال دهنده: داده را به سيستم مي‌فرستد.

* مبدل‌ها: داده‌اي كه از سيستم مي‌آيد و داده‌اي كه بايد به سيستم ارسال شود را تبديل مي‌كنند.

* نقاط انتهايي (Endpoints): به عنوان آدرس كانال هنگامي كه اتصالي ايجاد مي‌شود، استفاده مي‌شود.

* پيكربندي تراكنش: براي تعريف ويژگيهاي تراكنش اتصال استفاده مي‌شود.

* Endpoint Resolves:: تعيين مي‌كنند چه متدي بايد بر روي يك جزء UMO فراخواني شود، هنگامي كه جزء رويدادي را دريافت مي‌كند.

* Transformers  (مبدل‌ها):

اين اجزاء پيغام‌ها يا رويدادها را از يا به فرمت‌هاي مختلف داده انتقال مي‌دهند. انتقال دهنده‌ها مي‌توانند براي انجام انتقالهاي دنبال هم بر روي يك رويداد قبل از دريافتش توسط شي‌اي، به هم زنجيره شوند.

*  Message adapters:

اين اجزاء يك روش مشتركي را براي خواندن داده‌هاي مختلف از سيستم‌هاي خارجي ارائه مي‌دهند.

* دريافت كنندگان پيغام:

دريافت كنندگان پيغام‌ نخ‌هايي (Threads) هستند كه داده را از يك سيستم خارجي دريافت مي‌كنند.

 

* توزيع كنندگان پيغام (Message dispatchers):

اين اجزاء رويدادها را به تكنولوژي مورد استفاده ارسال (همزمان) يا توزيع (غيرهمزمان) مي‌كنند.

 

* مسيرياب‌هاي پيغام:

مسيرياب‌هاي پيغام، اجزايي هستند كه مي‌توانند براي جزء UMO پيكربندي گردند كه اين جزء بتواند پيغام را براي توليدكنندگان مختلف برمبناي پيغام يا ديگر پيكربندي‌ها مسيريابي نمايد.

 

* عامل‌ها (Agents):

عامل‌ها اجزايي هستند كه به سرويس‌هاي خارجي نظيرJMES (Extension Servers Java Management)، مقيد مي‌شوند.

 

* مدل مول:

مدل مول رفتار زمان اجراي نمونه سرور مول را كپسوله و مديريت مي‌كند. يك مدل شامل اجزاء زير است.

- توصيف‌گرها

- اجزاء UMO

- يك Endpoint Resolver

- يك استراتژي exception

- يك Lifecycle- adapter factory

- يك Component resolver

- يك Pool factory

 

* مدير مول:

مدير مول سرويس‌هاي زير را ارائه و نگهداري مي‌كند:

- عامل‌ها

- ارائه دهندگان

- اتصال دهندگان

- نقاط انتهايي

- مبدل‌ها

- پشته جدا كننده (Interceptor Stack)

 

معماري سطح بالاي مول

-مدل مول

- سرور مول

- مدير تراكنش

- ويژگيهايي برنامه كاربردي

- پيكربندي مول

 

 

دياگرام شكل 2، نماي سطح بالايي از جريان پيغام را براي معماري مول نشان مي‌دهد.

 

 

رويدادهاي مول

رويدادهاي مول شامل ويژگيها و داده‌هاي رويداد مي‌باشند كه توسط اجزاء آگاه از رويداد دستكاري و آزموده مي‌شوند. اين ويژگيها اختياري‌ هستند و مي‌توانند هر زماني پس از ايجاد رويداد تنظيم شوند.

كلاس org.mule.umo.UMOEvent يك رويداد كه در محيط مول واقع مي‌شود را ارائه مي‌دهد.

تمام داده‌هايي كه در محيط مول ارسال يا دريافت مي‌شوند به عنوان يك نمونه از UMOEvent بين اجزاء ردوبدل مي‌گردند. داده يك رويداد مول مي‌تواند به فرمت اوليه‌اش يا به فرمتي تبديل يافته قابل دسترس باشد. يك رويداد مول از مبدل مرتبط با فراهم كننده‌اي كه رويداد را دريافت مي‌كند براي تبديل Payload رويداد به فرمتي كه براي جزء دريافت كننده قابل درك است، استفاده مي‌نمايد.

Payload يك رويداد شامل يك نمونه از واسط كاربريorg.mule.Umo. UMOMessage است. يك نمونهUMOMessage  شامل خود Payload و ويژگيهايي مرتبط با آن مي‌باشد. اين رابط همچنين به عنوان يك تجريد مشترك از پياده‌سازي‌هاي پيغام‌هاي مختلف كه توسط تكنولوژي‌هاي مورد استفاده فراهم شده است، عمل مي‌كند.

كلاس API,org.mule.Extras.Client. MuleClient ساده‌اي را تعريف مي‌كند كه به مشتريان مول اجازه ارسال و دريافت رويدادها را به سرور مول مي‌دهد. در اغلب برنامه‌هاي كاربردي مول، رويدادها توسط بعضي وقايع و اتفاقات خارجي از قبيل پيغامي كه دريافت مي‌شود يا فايلي كه از يك دايركتوري‌ حذف مي‌شود، تريگر مي‌شوند.

قطعه كد زير نحوه ارسال همزمان رويدادي را به جزء ديگري در مول نشان مي‌دهد:

String componentName = "MyReceiver";

// The name of the receiving component.
String transformers = null;

// A comma-separated list of transformers
// to apply to the result message.
String payload = "A test event";

// The payload of the event.
java.util.Map messageProperties = null;

// Any properties to be associated
// with the payload.
MuleClient client = new MuleClient();
UMOMessage message = client.sendDirect(componentName, transformers, payload,

messageProperties);
System.out.println("Event result: " +

message.getPayloadAsString());

 

يك نمونه MuleClient، URL سروري را نياز دارد كه با آن نقطه انتهايي سرور راه دور مول كه نمونه MuleClient به آن متصل خواهد شد را تعريف كند. URL پروتكل، مقصد نقطه انتهايي پيغام و بصورت اختياري فراهم كننده‌اي كه هنگام توزيع رويداد استفاده مي‌شود را تعريف مي‌كند. مثالهايي از نقاط انتهايي عبارتند از:

* vm://com.jeffhanson-receivers.Default: با استفاده از فراهم كننده ماشين مجازي به مقصد com.jeffhanson.receiver.Default توزيع مي‌كند. فراهم كننده VM ارتباطات رويداد intra-VM بين اجزاء را با استفاده از صف‌هاي ماندگار يا گذرا ممكن مي‌سازد.

* jms://jmsProvider/accounts.topic: يك پيغام JMS را با استفاده از يك jsm Provider‌ اي كه بصورت سراسري رجيستر شده به مقصد accounts. topic توزيع مي‌كند.

* Jms://accounts.topic: يك پيغام JMS را از طريق اولين فراهم كننده JMS (پيش‌فرض) توزيع مي‌كند.

 

پردازش رويداد در مول

مول به سه طريق مختلف مي‌تواند رويدادها را ارسال و دريافت كند.

1. غيرهمزمان: يك جزء مي‌تواند بطور همزمان چند رويداد را كه توسط نخ‌هاي مختلف ارسال و دريافت شده‌اند، پردازش كند.

2. همزمان: پردازش يك رويداد بايد قبل از اينكه جزء بتواند اجرا را دوباره آغاز كند، تمام و كامل شود به عبارت ديگر هنگامي كه جزئي رويدادي را توليد و ارسال مي‌كند. بلاك مي‌شود تا زماني كه نتيجه آن برگردد، از اين رو در يك زمان تنها يك رويداد مي‌تواند پردازش شود.

3. درخواست - پاسخ: يك جزء بصورت مشخص، رويدادي را درخواست مي‌كند و زمان معين را براي دريافت يك پاسخ منتظر مي‌ماند.

كلاس پياده‌سازي org.mule.impl.MuleComponent كلاسي است كه شامل تمام عملكردهاي لازم براي ارسال و دريافت داده و ساخت رويداد است.

اشيايي كه به صورت همزمان اجرا مي‌شوند، واسط org.mule.umo.lifecycle.Cllable را كه متد Object on‍‍Call (UMOEventContext eventContext) را تعريف مي‌كند، پياده‌سازي مي‌كنند.

اينترفيس Callable واسطي را براي UMO فراهم مي‌كند كه فراخواني رويدادها را پشتيباني مي‌نمايد. اين واسط يك متد lifecycle، هنگامي كه جزء پياده‌سازي يك رويداد را دريافت مي‌كند، اجراء مي‌شود را فراهم مي‌نمايد. در ادامه يك پياده‌سازي ساده از اين اينترفيس را مي‌بينيم.

 

import org.mule.umo.lifecycle.Callable;

public class EchoComponent
   implements Callable
{
    public Object onCall(UMOEventContext context) throws Exception
    {
        String msg = context.getMessageAsString();
        // Print message to System.out
        System.out.println("Received synchronous message: " + msg);
        // Echo transformed message back to sender
        return context.getTransformedMessage();
    }
}

 

شيء‌اي كه از متد onCall() بازگردانده مي‌شود، هر چيزي مي‌تواند باشد. هنگامي كه UMOLifecycleAdapter مربوط به كامپوننتي اين شيء را دريافت مي‌كند، ابتدا چك مي‌نمايد كه شيء يك UMOMessage است يا نه؛ اگر شيء يك UMOMessage يا null باشد، يك پيغام جديد با استفاده از شيء بازگشتي به عنوان يك Payload ساخته خواهد شد، سپس اين رويداد جديد از طريق مسيرياب outbound پيكربندي شده در صورتي كه براي UMO پيكربندي شده باشد و متد SetStopFurtherProcessing (true) بر روي نمونه UMOEventContext فراخواني شده باشد، منتشر خواهد شد.

 

يك فريم‌ورك ساده با استفاده از مول

در اين بخش با استفاده از اجزائي از مول در كنار هم يك فريم ورك رويداد ساده مي‌سازيم. فريم‌ورك شامل يك مدير رويداد مسئول رجيستر و در رجيستر كردن سرويس‌هاست و مي‌تواند رويدادها را دريافت كند و براي مسيريابي همزمان و غيرهمزمان پيغام‌ها به اين سرويس‌ها استفاده شود.

پروتكل VM” مول نيازمند اينست كه يك فايل پيكربندي در دايركتوري‌ با نام META-INF/Services/org/mule/providers/vm. متناسب با دايركتوري كاري مدير رويداد، قرار داده شود. اين فايل چند كامپوننت مثل اتصال دهنده و توزيع كننده factory را براي پروتكل تعريف مي‌كند. محتويات فايل مثل زير است:

connector= org.mule.providers.vm.VMConnector
dispatcher.factory=org.mule.providers.vm.VMMessageDispatcherFactory
message.receiver= org.mule.providers.vm.VMMessageReceiver
message.adapter= org.mule.providers.vm.VMMessageAdapter
endpoint.builder= org.mule.impl.endpoint.ResourceNameEndpointBuilder

 

يك اينترفيس ساده نماي عمومي مدير رويداد را تعريف مي‌كند:

package com.jeffhanson.mule;

import org.mule.umo.FutureMessageResult;

public interface EventManager
{
   /**
    * Sends an event message synchronously to a given service.
    *
    * @param serviceName    The name of the service to which the event
    *                       message is to be sent.
    * @param payload        The content of the event message.
    * @return Object        The result, if any.
    * @throws EventException on error
    */
   public Object sendSynchronousEvent(String serviceName,
                                      Object payload)
      throws EventException;

   /**
    * Sends an event message asynchronously to a given service.
    *
    * @param serviceName    The name of the service to which the event
    *                       message is to be sent.
    * @param payload        The content of the event message.
    * @return FutureMessageResult The result, if any.
    * @throws EventException on error
    */
   public FutureMessageResult sendAsynchronousEvent(String serviceName,
                                                    Object payload)
      throws EventException;

   /**
    * Starts this event manager.
    */
   public void start();

   /**
    * Stops this event manager.
    */
   public void stop();

   /**
    * Retrieves the protocol this event manager uses.
    * @return
    */
   public String getProtocol();

   /**
    * Registers a service to receive event messages.
    *
    * @param serviceName      The name to associate with the service.
    * @param implementation   Either a container reference to the service
    *                         or a fully-qualified class name.
    */
   public void registerService(String serviceName,
                               String implementation)
      throws EventException;

   /**
    * Unregisters a service from receiving event messages.
    *
    * @param serviceName  The name associated with the service to unregister.
    */
   public void unregisterService(String serviceName)
      throws EventException;
}

كلاس پياده سازي مدير رويداد در داخل يك كلاس factory كپسوله مي‌شود كه سبب مي‌گردد بدون تحت تاثير قرار دادن كلاينت‌هاي مدير رويداد بتوان در صورت نياز پياده‌سازي را تغيير داد. پياده‌سازي مدير رويداد در اينجا نشان داده شده است.

package com.jeffhanson.mule;

import org.mule.umo.*;
import org.mule.extras.client.MuleClient;
import org.mule.impl.endpoint.MuleEndpoint;
import org.mule.config.QuickConfigurationBuilder;

import java.util.HashMap;
import java.util.Map;

public class EventManagerFactory
{
   private static HashMap instances = new HashMap();


   /**
    * Retrieves the event manager instance for a given protocol.
    *
    * @param protocol      The protocol to use.
    * @return EventManager The event manager instance.
    */
   public static EventManager getInstance(String protocol)
   {
      EventManager instance = (EventManager)instances.get(protocol);

      if (instance == null)
      {
         instance = new EventManagerImpl(protocol);
         instances.put(protocol, instance);
      }

      return instance;
   }

   /**
    * A concrete implementation for a simple event manager.
    */
   private static class EventManagerImpl
      implements EventManager
   {
      private UMOManager manager = null;
      private QuickConfigurationBuilder builder = null;
      private MuleClient eventClient = null;
      private String protocol = null;
      private MuleEndpoint receiveEndpoint = null;
      private MuleEndpoint sendEndpoint = null;

      private EventManagerImpl(String protocol)
      {
         this.protocol = protocol;
      }

      /**
       * Starts this event manager.
       */
      public void start()
      {
         try
         {
            builder = new QuickConfigurationBuilder();
            manager = builder.createStartedManager(true,
                                                   protocol + "tmp/events");
            eventClient = new MuleClient();
            receiveEndpoint = new MuleEndpoint(protocol
                                               + "tmp/events/receive");
            sendEndpoint = new MuleEndpoint(protocol + "tmp/events/send");
         }
         catch (UMOException e)
         {
            System.err.println(e);
         }
      }

      /**
       * Stops this event manager.
       */
      public void stop()
      {
         try
         {
            manager.stop();
         }
         catch (UMOException e)
         {
            System.err.println(e);
         }
      }

      /**
       * Retrieves the protocol this event manager uses.
       * @return
       */
      public String getProtocol()
      {
         return protocol;
      }

      /**
       * Registers a service to receive event messages.
       *
       * @param serviceName      The name to associate with the service.
       * @param implementation   Either a container reference to the service
       *                         or a fully-qualified class name
       *                         to use as the component implementation.
       */
      public void registerService(String serviceName,
                                  String implementation)
         throws EventException
      {
         if (!manager.getModel().isComponentRegistered(serviceName))
         {
            try
            {
               builder.registerComponent(implementation,
                                         serviceName,
                                         receiveEndpoint,
                                         sendEndpoint);
            }
            catch (UMOException e)
            {
               throw new EventException(e.toString());
            }
         }
      }

      /**
       * Unregisters a service from receiving event messages.
       *
       * @param serviceName  The name associated with the service to unregister.
       */
      public void unregisterService(String serviceName)
         throws EventException
      {
         try
         {
            builder.unregisterComponent(serviceName);
         }
         catch (UMOException e)
         {
            throw new EventException(e.toString());
         }
      }

      /**
       * Sends an event message synchronously to a given service.
       *
       * @param serviceName    The name of the service to which the event
       *                       message is to be sent.
       * @param payload        The content of the event message
       * @return Object        The result, if any.
       * @throws EventException on error
       */
      public Object sendSynchronousEvent(String serviceName,
                                         Object payload)
         throws EventException
      {
         try
         {
            if (!manager.getModel().isComponentRegistered(serviceName))
            {
               throw new EventException("Service: " + serviceName
                                        + " is not registered.");
            }

            String transformers = null;
            Map messageProperties = null;
            UMOMessage result = eventClient.sendDirect(serviceName,
                                                       transformers,
                                                       payload,
                                                       messageProperties);
            if (result == null)
            {
               return null;
            }
            return result.getPayload();
         }
         catch (UMOException e)
         {
            throw new EventException(e.toString());
         }
         catch (Exception e)
         {
            throw new EventException(e.toString());
         }
      }

      /**
       * Sends an event message asynchronously.
       *
       * @param serviceName    The name of the service to which the event
       *                       message is to be sent.
       * @param payload        The content of the event message.
       * @return FutureMessageResult The result, if any
       * @throws EventException on error
       */
      public FutureMessageResult sendAsynchronousEvent(String serviceName,
                                                       Object payload)
         throws EventException
      {
         FutureMessageResult result = null;

         try
         {
            if (!manager.getModel().isComponentRegistered(serviceName))
            {
               throw new EventException("Service: " + serviceName
                                        + " is not registered.");
            }

            String transformers = null;
            Map messageProperties = null;
            result = eventClient.sendDirectAsync(serviceName,
                                                 transformers,
                                                 payload,
                                                 messageProperties);
         }
         catch (UMOException e)
         {
            throw new EventException(e.toString());
         }

         return result;
      }
   }
}

 

فريم‌ورك مول پيغام‌ها را با نوع داده Payload توزيع مي‌كند. فريم‌ورك رويداد مي‌تواند اين مكانيسم توزيع مبتني‌بر Payload را با تعريف متدهاي كلي رويداد به كار گيرد كه اين متدها به عنوان دريافت‌كنندگان رويداد در سرويس‌هايي كه در مدير رويداد ثبت شده‌اند، عمل مي‌كنند. كد زير يكي از اين سرويس‌ها را با سه متد رويداد بارگذاري شده با نام receiveEvent()، نشان مي‌دهد:

package com.jeffhanson.mule;

import java.util.Date;

public class TestService
{
   public void receiveEvent(String eventMessage)
   {
      System.out.println("\n\nTestService.receiveEvent(String) received "
                         + "event message:  " + eventMessage + "\n\n");
   }

   public void receiveEvent(Integer eventMessage)
   {
      System.out.println("\n\nTestService.receiveEvent(Integer) received "
                         +"event message:  " + eventMessage + "\n\n");
   }

   public void receiveEvent(Date eventMessage)
   {
      System.out.println("\n\nTestService.receiveEvent(Date) received "
                         + "event message:  " + eventMessage + "\n\n");
   }
}

 

برنامه كاربردي كلاينت مدير رويداد، 3 رويداد را به سرويس تست براي تست كردن هر متد receiveEvent() ارسال مي‌كند. برنامه كاربردي كلاينت به قرار زير است:

package com.jeffhanson.mule;

import org.apache.log4j.Logger;
import org.apache.log4j.Level;
import org.apache.log4j.BasicConfigurator;

import java.util.Date;

public class EventClient
{
   static Logger logger = Logger.getLogger(EventClient.class);

   public static void main(String[] args)
   {
      // Set up a simple configuration that logs on the console.
      BasicConfigurator.configure();
      logger.setLevel(Level.ALL);

      try
      {
         EventManager eventManager =
            EventManagerFactory.getInstance("vm://");
         eventManager.start();

         String serviceName = TestService.class.getName();
         String implementation = serviceName;

         eventManager.registerService(serviceName, implementation);

         Object result =
            eventManager.sendSynchronousEvent(serviceName, "A test message");

         if (result != null)
         {
            System.out.println("Event result: " + result.toString());
         }

         result =
            eventManager.sendSynchronousEvent(serviceName, new Integer(23456));

         if (result != null)
         {
            System.out.println("Event result: " + result.toString());
         }

         result =
            eventManager.sendSynchronousEvent(serviceName, new Date());

         if (result != null)
         {
            System.out.println("Event result: " + result.toString());
         }

         eventManager.stop();
      }
      catch (EventException e)
      {
         System.err.println(e.toString());
      }
   }
}

داده‌ها و تجريدهاي اعمال شده در پلت‌فرم مول كه در اين فريم‌ورك فراهم شد، ما را قادر مي‌سازد كه رويدادهاي همزمان و غيرهمزمان را بدون نياز به دانستن جزئيات سيستم رويداد زيرين در ميان لايه‌هاي يك پشته معماري ارسال و دريافت كنيم.  به منظور ساده‌سازي و طراحي بسط پذير وLoosely- Coupled از عناصر اصلي SOA و الگوهاي Factory استفاده شد.

 

خلاصه

هنگامي كه سرويس‌ها و فرآيندها نيازمند تعامل در ميان چندين لايه و پروتكل هستند، طراحي يك سيستم نرم‌افزاري مبتني‌ بر رويداد كارا مي‌تواند پيچيده شود. هر چند ساخت يك معماري سرويس‌گرا در كنار يك لايه مديريت رويداد كه مناسب با الگوهاي استاندارد صنعتي طراحي شده است، مي‌تواند اين مشكلات را كاهش داده و حتي حذف نمايد. پلت‌فرم مول، APIها، كامپوننت‌ها و تجريد‌هايي را كه مي‌توانند براي ساخت يك سيستم مبتني‌بر رويداد قوي و مطمئن كه توسعه‌پذير و با قابليت نگهداري بالاست، بكار روند، فراهم آورده است.

 

 

 

منابع:

· Mule homepage: http://wiki.muleumo.org/display/MULEPROJ/Home

· Mule FAQ: http://wiki.muleumo.org/display/MULE/Mule+FAQ

· SourceForge link: http://sourceforge.net/projects/mule/

 

Copyright 2005 IDG News Service.All right reserved.
Copyright 2005, PC World Iran, All rights reserved.