מדריך למיקרו-שירותים תגובתי באמצעות Lagom Framework

1. סקירה כללית

במאמר זה נחקור את מסגרת Lagom ו- ליישם יישום לדוגמא באמצעות ארכיטקטורה מונעת מיקרו-שירותים.

במילים פשוטות, יישומי תוכנה תגובתי נשענים על תקשורת אסינכרונית מונעת הודעות והם מאוד מגיב, מִתאוֹשֵׁשׁ מַהֵר ו אֵלַסטִי בטבע.

על ידי ארכיטקטורה מונעת מיקרו-שירותים, התכוונו לפצל את המערכת לגבולות בין שירותים שיתופיים להשגת יעדים של בידוד, אוטונומיה, אחריות יחידה, ניידותוכו '. לקריאה נוספת על שני מושגים אלה, עיין במאפיין תגובתי ותגובת מיקרו-שירותים.

2. מדוע לגום?

לגום היא מסגרת קוד פתוח שנבנתה תוך התחשבות במונוליטים לאדריכלות יישומים מונעת מיקרו-שירותים. זה מפשט את המורכבות של בנייה, הפעלה וניטור של יישומים המונעים על ידי מיקרו-שירותים.

מאחורי הקלעים, מסגרת לגום משתמשת ב- Play Framework, זמן ריצה מונע הודעות Akka, Kafka לשירותי ניתוק, אירוע Sourcing ודפוסי CQRS, ותמיכה ב- ConductR לניטור ושינוי גודל המיקרו-שירותים בסביבת המכולות.

3. שלום עולם בלגום

אנו ניצור אפליקציית Lagom שתטפל בבקשת ברכה מהמשתמש ותשיב בחזרה עם הודעת ברכה יחד עם סטטיסטיקות מזג האוויר להיום.

ונפתח שני מיקרו-שירותים נפרדים: בְּרָכָה ו מזג אוויר.

בְּרָכָה יתמקד בטיפול בבקשת ברכה, באינטראקציה עם שירות מזג האוויר כדי להשיב למשתמש. ה מזג אוויר microservice תגיש את הבקשה לסטטיסטיקה של מזג האוויר להיום.

במקרה של משתמש קיים באינטראקציה עם בְּרָכָה שירות מיקרו, הודעת הברכה השונה תוצג למשתמש.

3.1. תנאים מוקדמים

  1. להתקין סקאלה (אנו משתמשים כרגע בגרסת 2.11.8) מכאן
  2. להתקין sbt כלי לבנות (אנו משתמשים כעת ב- 0.13.11) מכאן

4. הגדרת פרויקט

בואו נסתכל במהירות על השלבים להגדרת מערכת לגום עובדת.

4.1. SBT לבנות

צור תיקיית פרויקט lagom-hello-world ואחריו קובץ ה- buildbuild.sbt. מערכת Lagom מורכבת בדרך כלל ממערכת sbt בונה עם כל מבנה המתאים לקבוצת שירותים קשורים:

ארגון ב- ThisBuild: = "com.baeldung" scalaVersion ב- ThisBuild: = "2.11.8" lagomKafkaEnabled ב- ThisBuild: = false false val greetingApi = project ("greeting-api") .settings (גרסה: = "1.0-SNAPSHOT", libraryDependencies ++ = Seq (lagomJavadslApi)) lazy val greetingImpl = project ("greeting-impl"). enablePlugins (LagomJava) .settings (version: = "1.0-SNAPSHOT", libraryDependencies ++ = Seq (lagomJavadslPersistenceCassandra)). greetingApi, weatherApi) lazy val weatherApi = project ("weather-api") .settings (version: = "1.0-SNAPSHOT", libraryDependencies ++ = Seq (lagomJavadslApi)) lazy val weatherImpl = project ("weather-impl"). enablePlugins (LagomJava) .settings (גרסה: = "1.0-SNAPSHOT") .dependsOn (weatherApi) def project (id: String) = Project (id, base = file (id))

ראשית, ציינו את פרטי הארגון, סקאלה גרסה, ומושבת קפקא לפרויקט הנוכחי. לגום עוקבת אחר מוסכמה של שני פרויקטים נפרדים לכל מיקרו-שירות: פרויקט API ופרויקט יישום.

פרויקט ה- API מכיל את ממשק השירות עליו תלוי היישום.

הוספנו תלות למודולי Lagom הרלוונטיים כמו lagomJavadslApi, lagomJavadsl עקביות קסנדרה לשימוש ב- API של Lagom Java במיקרו-שירותים שלנו ואחסון אירועים הקשורים לישות המתמשכת ב- קסנדרה, בהתאמה.

וגם ה ברכה- impl הפרויקט תלוי ב מזג אוויר API פרויקט להביא ולהגיש נתונים סטטיסטיים על מזג האוויר תוך ברכה למשתמש.

תמיכה בתוסף לגום מתווספת על ידי יצירת תיקיית תוסף באמצעות plugins.sbt קוֹבֶץ, בעל ערך לתוסף לגום. הוא מספק את כל התמיכה הדרושה לבנייה, הפעלה ופריסה של היישום שלנו.

וגם ה תלוש תוסף יהיה שימושי אם נשתמש ב- Eclipse IDE לפרויקט זה. הקוד שלהלן מציג את התוכן של שני התוספים:

addSbtPlugin ("com.lightbend.lagom"% "lagom-sbt-plugin"% "1.3.1") addSbtPlugin ("com.typesafe.sbteclipse"% "sbteclipse-plugin"% "3.0.0")

לִיצוֹר project / build.properties הקובץ וציין sbt גרסה לשימוש:

sbt.version = 0.13.11

4.2. יצירת פרויקטים

רץ sbt פקודה משורש הפרויקט תיצור את תבניות הפרויקט הבאות:

  1. ברכה- api
  2. ברכה- impl
  3. מזג אוויר API
  4. impl. weather-impl

לפני שנתחיל ליישם את שירותי המיקרו, בואו נוסיף את ה- src / main / java ו src / main / java / resources תיקיות בתוך כל אחד מהפרויקטים, כדי לעקוב אחר פריסת ספריית הפרויקטים כמו Maven.

כמו כן, שני פרויקטים דינמיים נוצרים בפנים פרויקט שורש / יעד / לגום-דינמי-פרויקטים:

  1. lagom-internal-meta-project-cassandra
  2. lagom-internal-meta-project-service-locator

פרויקטים אלה משמשים באופן פנימי על ידי לגום.

5. ממשק שירות

בתוך ה ברכה- api בפרויקט, אנו מציינים את הממשק הבא:

ממשק ציבורי GreetingService מרחיב את השירות {public ServiceCall handleGreetFrom (משתמש מחרוזת); @Override ברירת מחדל מתאר המתאר () {return בשם ("greetingservice") .withCalls (restCall (Method.GET, "/ api / greeting /: fromUser", זה :: handleGreetFrom)) .withAutoAcl (true); }}

GreetingService חושף handleGreetFrom () לטיפול בבקשת הברכה מהמשתמש. א ServiceCall API משמש כסוג ההחזרה של שיטות אלה. ServiceCall לוקח שני פרמטרים מסוגים בַּקָשָׁה ו תְגוּבָה.

ה בַּקָשָׁה הפרמטר הוא סוג הודעת הבקשה הנכנסת, ו- תְגוּבָה הפרמטר הוא סוג הודעת התגובה היוצאת.

בדוגמה שלמעלה איננו משתמשים בעומס מטען של בקשה, סוג הבקשה הוא לא בשימוש, ו תְגוּבָה סוג הוא חוּט הודעת שלום.

GreetingService מציין גם מיפוי לתחבורה בפועל המשמשת במהלך ההפעלה, על ידי מתן יישום ברירת המחדל של ה- Service.descriptor () שיטה. שירות בשם שירות ברכה מוחזר.

handleGreetFrom () שיחת שירות ממופה באמצעות מזהה מנוחה: לקבל סוג השיטה ומזהה הנתיב / api / greeting /: fromUser ממופה ל handleGreetFrom () שיטה. עיין בקישור זה לקבלת פרטים נוספים על מזהי השירות.

באותם קווים אנו מגדירים WeatherService ממשק ב מזג אוויר API פּרוֹיֶקט. weatherStatsForToday () שיטה ו מתאר () שיטה די מסבירה את עצמה:

ממשק ציבורי WeatherService מרחיב את השירות {public ServiceCall weatherStatsForToday (); @Override ברירת מחדל של מתאר המתאר () {החזרה בשם ("שירות מזג האוויר"). WithCalls (restCall (Method.GET, "/ api / weather", זה :: weatherStatsForToday)). עם AutoAcl (נכון); }};

WeatherStats מוגדר כאנומר עם ערכי מדגם למזג אוויר שונה ולחיפוש אקראי להחזרת תחזית מזג האוויר ליום:

Enum WeatherStats {STATS_RAINY ("הולך לגשם, קח מטריה"), STATS_HUMID ("הולך להיות לח מאוד, קח מים"); WeatherStats פומבי ציבורי forToday () {return VALUES.get (RANDOM.nextInt (SIZE)); }}

6. לאגום התמדה - מקורות אירועים

במילים פשוטות, במערכת המשתמשת מקורות אירועיםנוכל לתפוס את כל השינויים כשאירועי תחום בלתי ניתנים לשינוי צורפו בזה אחר זה. המצב הנוכחי נגזר על ידי הפעלה מחדש ועיבוד אירועים. פעולה זו היא למעשה א foldLeft פעולה ידועה מפרדיגמת התכנות הפונקציונלית.

המקור לאירועים עוזר להשיג ביצועי כתיבה גבוהים על ידי צירוף האירועים והימנעות מעודכונים וממחיקות של אירועים קיימים.

בואו נסתכל על היישות המתמדת שלנו בפרויקט implicit-greeting, ברכה עתיקה:

Class class GreetingEntity מרחיב את PersistentEntity {@Override public Behavior initialBehavior (אופציונלי snapshotState) {BehaviorBuilder b = newBehaviorBuilder (GreetingState חדש ("שלום")); b.setCommandHandler (ReceiveGreetingCommand.class, (cmd, ctx) -> {String fromUser = cmd.getFromUser (); String currentGreeting = state (). getMessage (); return ctx.thenPersist (חדש ReceiveGreetingEvent (fromUser), evt -> ctx.reply (CurrentGreeting + fromUser + "!"))}}; b.setEventHandler (ReceiveGreetingEvent.class, evt -> state (). withMessage ("שלום שוב")); החזר b.build (); }}

לגום מספקת ישות מתמשכת ממשק API לעיבוד אירועים נכנסים מהסוג פקודה באמצעות setCommandHandler () שיטות ושינויים במצב מתמשך כאירועים מסוג מִקרֶה. מצב אובייקט התחום מתעדכן על ידי החלת האירוע למצב הנוכחי באמצעות ה- setEventHandler () שיטה. התנהגות הראשונית () שיטה מופשטת מגדירה את התנהגות של הישות.

ב initialBehavior (), אנו בונים מקוריים שלום לברכה טקסט "שלום". ואז נוכל להגדיר א קיבלו פקודה ברכה מטפל בפיקוד - שמייצר א קיבלGreetingEvent אירוע ונמשך בהתמדה ביומן האירועים.

שלום לברכה מחושב מחדש ל "שלום שוב" על ידי קיבלGreetingEvent שיטת מטפל באירועים. כפי שהוזכר קודם לכן, איננו קוראים לקובעים - במקום זאת אנו יוצרים מופע חדש של מדינה מהאירוע הנוכחי שעובד.

לגום עוקב אחר המוסכמה של ברכהפקודה ו GreetingEvent ממשקים להחזקת כל הפקודות והאירועים הנתמכים:

ממשק ציבורי GreetingCommand מרחיב את Jsonable {@JsonDeserialize class public ReceiveGreetingCommand מיישם את GreetingCommand, CompressedJsonable, PersistentEntity.ReplyType {@JsonCreator public ReceiveGreetingCommand (מחרוזת מ- User) {this.fromUser = תנאי. }}}
ממשק ציבורי GreetingEvent מרחיב את Jsonable {class ReceiveGreetingEvent מיישם את GreetingEvent {@ JsonCreator public ReceiveGreetingEvent (String fromUser) {this.fromUser = fromUser; }}}

7. יישום שירות

7.1. שירות ברכה

מחלקה ציבורית GreetingServiceImpl מיישמת GreetingService {@Inject public GreetingServiceImpl (PersistentEntityRegistry persistentEntityRegistry, WeatherService weatherService) {this.persistentEntityRegistry = persistentEntityRegistry; this.weatherService = weatherService; persistentEntityRegistry.register (GreetingEntity.class); } @Override ServiceCall ציבורי handleGreetFrom (משתמש מחרוזת) {בקשת החזרה -> {PersistentEntityRef ref = persistentEntityRegistry.refFor (GreetingEntity.class, user); CompletableFuture greetingResponse = ref.ask (חדש ReceiverGreetingCommand (משתמש)) .toCompletableFuture (); CompletableFuture todaysWeatherInfo = (CompletableFuture) weatherService .weatherStatsForToday (). הפעל (); נסה {return CompletableFuture.completedFuture (greetingResponse.get () + "נתוני מזג האוויר של היום:" + todaysWeatherInfo.get (). getMessage ()); } לתפוס (InterruptedException | ExecutionException e) {return CompletableFuture.completedFuture ("מצטער שגיאה בסוף, עובד על זה"); }}; }}

במילים פשוטות, אנחנו מזריקים את PersistentEntityRegistry ו WeatherService תלות באמצעות @לְהַזרִיק (המסופק על ידי רעיון מסגרת), ואנחנו רושמים את המתמיד ברכה עתיקה.

ה handleGreetFrom () היישום הוא שליחה קיבלו פקודה ברכה אל ה ברכה עתיקה לעבד ולהחזיר מחרוזת ברכה בצורה אסינכרונית באמצעות העתיד הטמעה של שלב השלמה ממשק API.

באופן דומה, אנו מבצעים שיחת אסינכרון ל- מזג אוויר מיקרו-שירות להביא נתונים סטטיסטיים על מזג האוויר להיום.

לבסוף, אנו מצרפים את שתי הפלטים ומחזירים את התוצאה הסופית למשתמש.

לרישום יישום של ממשק מתאר השירות GreetingService עם Lagom, בואו ניצור GreetingServiceModule כיתה שמתארכת תקציר מודול ומכשירים ServiceGuiceSupport:

מחלקה ציבורית GreetingServiceModule מרחיב את AbstractModule מיישם את ServiceGuiceSupport {@Override מוגן הריק להגדיר () {bindServices (serviceBinding (GreetingService.class, GreetingServiceImpl.class)); bindClient (WeatherService.class); }} 

כמו כן, לגום משתמשת באופן פנימי ב- Play Framework. וכך, אנו יכולים להוסיף את המודול שלנו לרשימת המודולים המופעלים ב- Play src / main / resources / application.conf קוֹבֶץ:

play.modules.enabled + = com.baeldung.lagom.helloworld.greeting.impl.GreetingServiceModule

7.2. שירות מזג אוויר

לאחר שהסתכל על GreetingServiceImpl, WeatherServiceImpl הוא די פשוט ומסביר את עצמו:

מחלקה ציבורית WeatherServiceImpl מיישמת את WeatherService {@Override Public ServiceCall weatherStatsForToday () {return req -> CompletableFuture.completedFuture (WeatherStats.forToday ()); }}

אנו מבצעים את אותם השלבים כפי שעשינו לעיל למודול ברכה לרישום מודול מזג האוויר אצל לגום:

מחלקה ציבורית WeatherServiceModule מרחיב את AbstractModule מיישם את ServiceGuiceSupport {@Override מוגן ריק להגדיר () {bindServices (serviceBinding (WeatherService.class, WeatherServiceImpl.class)); }}

כמו כן, רשום את מודול מזג האוויר לרשימת המסגרות של Play עם המודולים המאופשרים:

play.modules.enabled + = com.baeldung.lagom.helloworld.weather.impl.WeatherServiceModule

8. הפעלת הפרויקט

לגום מאפשר הפעלת מספר שירותים כלשהו יחד עם פקודה אחת.

אנו יכולים להתחיל את הפרויקט שלנו על ידי לחיצה על הפקודה הבאה:

sbt lagom: runAll

זה יתחיל את המוטבע איתור שירות, מוטבע קסנדרה ואז להתחיל מיקרו-שירותים במקביל. אותה פקודה טוענת גם את המיקרו-שירות האישי שלנו כאשר הקוד משתנה כך שאנחנו לא צריך להפעיל אותם מחדש באופן ידני.

אנו יכולים להיות ממוקדים בלוגיקה שלנו ולאגום מטפל בהידור ובטעינה מחדש. לאחר שהתחלנו בהצלחה, נראה את הפלט הבא:

................ [מידע] שרת קסנדרה פועל ב- 127.0.0.1:4000 [מידע] איתור השירות פועל ב // localhost: 8000 [info] שער השירות פועל ב / / localhost: 9000 [מידע] האזנה למזג האוויר ב- HTTP ב- 0: 0: 0: 0: 0: 0: 0: 0: 56231 וכיצד האינטראקציה בין השירותים דרך [info] שירות ברכה- impl האזנה ל- HTTP ב- 0 : 0: 0: 0: 0: 0: 0: 0: 49356 [מידע] (השירותים התחילו, לחץ על Enter כדי לעצור וחזור למסוף ...)

לאחר שהתחלנו בהצלחה אנו יכולים להגיש בקשת תלתל לברכה:

תלתל // localhost: 9000 / api / ברכה / עמית

נראה את הפלט הבא בקונסולה:

שלום עמית! נתונים סטטיסטיים של מזג האוויר של היום: הולך לגשם, קח מטריה

הפעלת אותה בקשת תלתל עבור משתמש קיים תשנה את הודעת הברכה:

שלום שוב עמית! נתונים סטטיסטיים של מזג האוויר של היום: הולך לגשם, קח מטריה

9. מסקנה

במאמר זה סקרנו כיצד להשתמש במסגרת Lagom כדי ליצור שני שירותי מיקרו האינטראקציה האינכרונית.

קוד המקור השלם וכל קטעי הקוד עבור מאמר זה זמינים בפרויקט GitHub.


$config[zx-auto] not found$config[zx-overlay] not found