JavaRush /בלוג Java /Random-HE /מדריך ל-Java Microservices. חלק 1: יסודות וארכיטקטורה של ...

מדריך ל-Java Microservices. חלק 1: יסודות וארכיטקטורה של Microservices

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

Java Microservices: יסודות

כדי להבין מיקרו-שירותים, תחילה עליך להגדיר מה הם לא. האם זה לא "מונוליט" - מונוליט ג'אווה: מה זה ומה היתרונות או החסרונות שלו? מדריך ל-Java Microservices.  חלק 1: יסודות וארכיטקטורה של Microservices - 1

מהו מונוליט ג'אווה?

תאר לעצמך שאתה עובד עבור בנק או סטארטאפ פינטק. אתה מספק למשתמשים אפליקציה לנייד שבה הם יכולים להשתמש כדי לפתוח חשבון בנק חדש. בקוד Java זה יגרום לנוכחות של מחלקת בקר. בפשטות, זה נראה כך:
@Controller
class BankController {

    @PostMapping("/users/register")
    public void register(RegistrationForm form) {
        validate(form);
        riskCheck(form);
        openBankAccount(form);
        // etc..
    }
}
אתה צריך את הבקר כדי:
  1. אישר את טופס ההרשמה.
  2. בדק את הסיכונים בכתובת המשתמש כדי להחליט אם לספק לו חשבון בנק.
  3. פתח חשבון בנק.
המחלקה BankControllerתיארז יחד עם שאר המקורות שלך לקובץ bank.jar או bank.war לצורך פריסה - זהו מונוליט ישן וטוב המכיל את כל הקוד הדרוש להפעלת הבנק שלך. כהערכה גסה, הגודל הראשוני של קובץ jar (או .war) ינוע בין 1 ל-100 MB. עכשיו אתה יכול פשוט להריץ את קובץ ה-.jar בשרת שלך... וזה כל מה שאתה צריך לעשות כדי לפרוס את יישום ה-Java שלך. מדריך ל-Java Microservices.  חלק 1: יסודות וארכיטקטורה של Microservices - 2תמונה, מלבן שמאל למעלה: פריסה של בנק מונו(ליטי) java -jar bank.jar (cp .war/.ear לתוך appserver). מלבן ימני: פתח דפדפן.

מה הבעיה עם מונוליטים של Java?

אין שום דבר רע מטבעו במונוליטים של Java. עם זאת, הניסיון הראה שאם בפרויקט שלך:
  • יש הרבה מתכנתים/צוותים/יועצים שעובדים...
  • ...על אותו מונוליט תחת לחץ של לקוחות עם דרישות מאוד מעורפלות...
  • תוך כמה שנים...
... אז במקרה הזה קובץ bank.jar הקטן שלך הופך לג'יגה-בייט עצום של קוד לבדו, שמפחיד אפילו להתקרב אליו, שלא לדבר על הפריסה.

כיצד להקטין את הגודל של מונוליט ג'אווה?

מתעוררת שאלה טבעית: איך להקטין את המונוליט? כרגע bank.jar שלך פועל על JVM אחד, תהליך אחד על שרת אחד. לא יותר, לא פחות. וכרגע עלולה לעלות בראש מחשבה הגיונית: "אבל שירות אימות הסיכונים יכול לשמש מחלקות אחרות בחברה שלי! זה לא קשור ישירות לאפליקציה הבנקאית המונוליטית שלי! אולי צריך לחתוך אותו מהמונוליט ולפרוס אותו כמוצר נפרד? כלומר, מבחינה טכנית, להריץ אותו כתהליך Java נפרד."

מהו מיקרו-שירות Java?

בפועל, הביטוי הזה אומר שכעת קריאת השיטה riskCheck()לא תתבצע מ-BankController: שיטה זו או רכיב שעועית עם כל מחלקות העזר שלו יועברו לפרויקט Maven או Gradle משלו. זה גם ייפרס ויועבר תחת בקרת גרסאות ללא תלות במונוליט הבנקאי. עם זאת, כל תהליך החילוץ הזה לא הופך את מודול RiskCheck החדש שלך למיקרו-שירות כשלעצמו, מכיוון שההגדרה של מיקרו-שירות פתוחה לפרשנות. זה מוביל לדיונים תכופים בתוך צוותים וחברות.
  • האם 5-7 כיתות בפרויקט מיקרו או מה?
  • 100 או 1000 שיעורים... עדיין מיקרו?
  • האם שירות מיקרו בדרך כלל קשור למספר השיעורים או לא?
בואו נשאיר את ההיגיון התיאורטי מאחור, ובמקום זאת נצמד לשיקולים פרגמטיים ונעשה זאת:
  1. בואו נקרא לכל השירותים הפרוסים בנפרד למיקרו-שירותים, ללא קשר לגודלם או לגבולות הדומיין שלהם.
  2. בואו נחשוב איך לארגן תקשורת בין שירותים. שירותי המיקרו שלנו זקוקים לדרכים לתקשר זה עם זה.
אז, לסיכום: בעבר היה לך תהליך JVM אחד, מונוליט מוצק לניהול הבנק. כעת יש לך תהליך JVM מונוליט בנקאי ומיקרו-שירות RiskCheck נפרד שפועל במסגרת תהליך JVM משלו. ועכשיו, כדי לבדוק סיכונים, המונוליט שלך חייב להתקשר למיקרו-שירות הזה. איך לעשות את זה?

כיצד ליצור תקשורת בין שירותי מיקרו Java?

באופן כללי ובכלל, קיימות שתי אפשרויות - תקשורת סינכרונית וא-סינכרונית.

תקשורת סינכרונית: (HTTP)/REST

בדרך כלל, תקשורת מסונכרנת בין שירותי מיקרו מתרחשת באמצעות שירותים כמו HTTP ו-REST המחזירים XML או JSON. כמובן, עשויות להיות אפשרויות אחרות - קח לפחות מאגר פרוטוקול של Google . אם אתה צריך מענה מיידי, עדיף להשתמש בתקשורת REST. בדוגמה שלנו, זה בדיוק מה שצריך לעשות, מכיוון שנדרש אימות סיכונים לפני פתיחת חשבון. אם אין בדיקת סיכונים, אין חשבון. נדון בכלים להלן, בסעיף " אילו ספריות הן הטובות ביותר עבור קריאות Java REST סינכרוניות ".

הודעות - תקשורת אסינכרונית

תקשורת מיקרו-שירות אסינכרונית מתבצעת בדרך כלל על ידי החלפת הודעות עם מימוש JMS ו/או שימוש בפרוטוקול כגון AMQP . כתבנו כאן "בדרך כלל" מסיבה: נניח שאי אפשר לזלזל במספר שילובי הדוא"ל/SMTP. השתמש בו כאשר אתה לא צריך תגובה מיידית. לדוגמה, המשתמש לוחץ על כפתור "קנה עכשיו", ואתה, בתורו, רוצה להפיק חשבונית. תהליך זה בהחלט לא אמור להתרחש בתוך מחזור בקשת הרכישה-תגובה של המשתמש. להלן נתאר אילו כלים הם הטובים ביותר עבור הודעות Java אסינכרוניות .

דוגמה: קריאת REST API ב-Java

נניח שאנו בוחרים בתקשורת מיקרו שירות סינכרונית. במקרה זה, קוד ה-Java שלנו (זה שהצגנו למעלה) ברמה נמוכה ייראה בערך כך. (ברמה נמוכה כאן אנו מתכוונים לעובדה שעבור תקשורת מיקרו-שירותים, בדרך כלל נוצרות ספריות לקוח שמפשטות אותך מקריאות ה-HTTP בפועל).
@Controller
class BankController {

    @Autowired
    private HttpClient httpClient;

    @PostMapping("/users/register")
    public void register(RegistrationForm form) {
        validate(form);
        httpClient.send(riskRequest, responseHandler());
        setupAccount(form);
        // etc..
    }
}
בהתבסס על הקוד, מתברר שעלינו לפרוס שני שירותי Java (מיקרו), Bank ו-RiskCheck. כתוצאה מכך, יהיו לנו שני תהליכי JVM פועלים. מדריך ל-Java Microservices.  חלק 1: יסודות וארכיטקטורה של Microservices - 3זה כל מה שאתה צריך כדי לפתח פרויקט Java microservices: פשוט לבנות ולפרוס נתחים קטנים יותר (קובצי .jar או .war) במקום אחד מונוליטי. התשובה לשאלה נותרה לא ברורה: כיצד עלינו לחתוך את המונוליט לשירותי מיקרו? כמה קטנים צריכים להיות החלקים האלה, איך לקבוע את הגודל הנכון? בוא נבדוק.

ארכיטקטורת Java Microservices

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

ממונוליט לשירותי מיקרו

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

רעיון: לפרק את המונוליט למיקרו-שירותים

ניתן ליישם גישת מיקרו-שירות על פרויקטים מדור קודם. וזה למה:
  1. לרוב קשה לתחזק/לשנות/להרחיב פרויקטים כאלה.
  2. כולם, ממפתחים ועד ניהול, רוצים פישוט.
  3. יש לך גבולות תחום ברורים (יחסית), כלומר אתה יודע בדיוק מה התוכנה שלך צריכה לעשות.
אם נחזור לדוגמה שלנו, זה אומר שאתה יכול להסתכל על המונוליט הבנקאי שלך ב-Java ולנסות לפרק אותו על פני גבולות התחום.
  • לכן, יהיה זה הגיוני להפריד את העיבוד של נתוני המשתמש (כגון שמות, כתובות, מספרי טלפון) למיקרו-שירות נפרד "ניהול חשבון".
  • או "מודול בודק סיכונים" הנ"ל שבודק את רמות הסיכון של המשתמש ויכול לשמש פרויקטים רבים אחרים או אפילו מחלקות בחברה.
  • או מודול חשבונית ששולח חשבוניות בפורמט PDF או בדואר.

יישום רעיון: תן למישהו אחר לעשות את זה

הגישה שתוארה לעיל נראית נהדר על נייר ודיאגרמות דמויות UML. עם זאת, הכל לא כל כך פשוט. היישום המעשי שלו דורש הכנה טכנית רצינית: הפער בין ההבנה שלנו מה יהיה נחמד לחלץ ממונוליט לבין תהליך החילוץ עצמו הוא עצום. רוב הפרויקטים הארגוניים מגיעים לשלב שבו מפתחים חוששים, למשל, לשדרג גרסה בת 7 שנים של Hibernate לגרסה חדשה יותר. הספריות יתעדכנו יחד עם זה, אבל יש סכנה ממשית לשבור משהו. אז אותם מפתחים צריכים כעת לחפור בקוד מדור קודם עם גבולות עסקאות לא ברורים של מסד נתונים ולחלץ שירותי מיקרו מוגדרים היטב? לא פעם, בעיה זו מורכבת מאוד ולא ניתן "לפתור" אותה על לוח או בפגישות אדריכלות. מדריך ל-Java Microservices.  חלק 1: יסודות וארכיטקטורה של Microservices - 4כדי לצטט את מפתח הטוויטר @simonbrown: אני אגיד את זה שוב ושוב... אם אנשים לא יכולים לבנות מונוליטים בצורה נכונה, מיקרו-שירותים לא יעזרו. סיימון בראון

פרויקט מאפס מבוסס על ארכיטקטורת microservice

במקרה של פרויקטים חדשים של Java, שלוש הנקודות הממוספרות מהחלק הקודם נראות מעט שונות:
  1. אתה מתחיל עם לוח נקי, כך שאין "כבודה" לשמור.
  2. המפתחים היו רוצים לשמור על דברים פשוטים בעתיד.
  3. בעיה: יש לך תמונה הרבה יותר מטושטשת של גבולות תחום: אתה לא יודע מה התוכנה שלך אמורה לעשות (רמז: זריז ;))
זה מוביל לחברות המנסות פרויקטים חדשים עם שירותי מיקרו של Java.

ארכיטקטורת מיקרו שירות טכנית

נראה שהנקודה הראשונה היא הברורה ביותר עבור מפתחים, אבל יש גם מי שמונעים ממנה בתוקף. האדי חרירי ממליצה על ה-Refactoring "Extract Microservice" ב-IntelliJ. ולמרות שהדוגמה הבאה מפושטת מאוד, ההטמעות שנצפו בפרויקטים אמיתיים, למרבה הצער, לא מתרחקות ממנה. לפני שירותי מיקרו
@Service
class UserService {

    public void register(User user) {
        String email = user.getEmail();
        String username =  email.substring(0, email.indexOf("@"));
        // ...
    }
}
עם תת מחרוזת Java microservice
@Service
class UserService {

    @Autowired
    private HttpClient client;

    public void register(User user) {
        String email = user.getEmail();
        //теперь вызываем substring microservice via http
        String username =  httpClient.send(substringRequest(email), responseHandler());
        // ...
    }
}
אז אתה בעצם עוטף קריאת שיטת Java בקריאת HTTP, ללא סיבה ברורה לעשות זאת. עם זאת, סיבה אחת היא זו: חוסר ניסיון וניסיון לכפות גישת מיקרו-שירותי Java. המלצה: אל תעשה זאת.

ארכיטקטורת מיקרו-שירותים מוכוונת זרימת עבודה

הגישה הנפוצה הבאה היא לחלק את שירותי המיקרו של Java למודולים מבוססי זרימת עבודה. דוגמה לחיים האמיתיים: בגרמניה, כאשר אתה הולך לרופא (ציבורי), הוא חייב לתעד את הביקור שלך במערכת ה-CRM הרפואית שלו. כדי לקבל תשלום מהביטוח, הוא ישלח נתונים על הטיפול שלך (והטיפול בחולים אחרים) למתווך באמצעות XML. המתווך יסתכל על קובץ ה-XML הזה ו(בפשטות):
  1. יבדוק אם התקבל קובץ ה-XML הנכון.
  2. זה יבדוק את הסבירות של ההליכים: נניח, ילד בן שנה שקיבל שלושה הליכי ניקוי שיניים ביום אחד מרופא נשים נראה חשוד במקצת.
  3. ישלב XML עם כמה נתונים בירוקרטיים אחרים.
  4. יעביר את קובץ ה-XML לחברת הביטוח ליזום תשלומים.
  5. והוא יעביר את התוצאה לרופא, ויספק לו את ההודעה "הצלחה" או "אנא שלח מחדש את ההקלטה הזו ברגע שזה יהיה הגיוני."
הערה. בדוגמה זו, תקשורת בין שירותי מיקרו אינה משחקת תפקיד, אך בהחלט יכולה להתבצע באופן אסינכרוני על ידי מתווך הודעות (למשל RabbitMQ), מכיוון שהרופא ממילא אינו מקבל משוב מיידי. מדריך ל-Java Microservices.  חלק 1: יסודות וארכיטקטורה של Microservices - 5שוב, זה נראה נהדר על הנייר, אבל עולות שאלות טבעיות:
  • האם יש צורך לפרוס שש יישומים כדי לעבד קובץ XML אחד?
  • האם שירותי המיקרו האלה באמת בלתי תלויים זה בזה? האם ניתן לפרוס אותם ללא תלות זה בזה? עם גרסאות וסכימות API שונות?
  • מה עושה מיקרו-שירות האמינות אם מיקרו-שירות האימות לא עובד? האם המערכת עדיין עובדת?
  • האם המיקרו-שירותים האלה חולקים את אותו מסד נתונים (הם בהחלט צריכים כמה נתונים נפוצים בטבלאות DB), או שלכל אחד מהם יש משלהם?
  • … ועוד הרבה.
מעניין לציין שהתרשים לעיל נראה פשוט יותר מכיוון שלכל שירות יש כעת מטרה מדויקת ומוגדרת היטב. פעם זה היה נראה משהו כמו המונוליט המפחיד הזה: מדריך ל-Java Microservices.  חלק 1: יסודות וארכיטקטורה של Microservices - 6למרות שאתה יכול להתווכח על הפשטות של דיאגרמות אלה, אתה בהחלט צריך עכשיו לפתור את האתגרים התפעוליים הנוספים האלה.
  • אתה לא צריך רק לפרוס יישום אחד, אלא לפחות שישה.
  • ייתכן אפילו שתצטרך לפרוס מסדי נתונים מרובים, תלוי כמה רחוק אתה רוצה ללכת לארכיטקטורת שירותי המיקרו.
  • עליך לוודא שכל מערכת מקוונת ופועלת כהלכה.
  • עליך לוודא שהשיחות שלך בין מיקרו-שירותים באמת גמישות (ראה כיצד להפוך מיקרו-שירותי Java לחוסן?).
  • וכל מה שההגדרה הזו מרמזת - מהגדרות פיתוח מקומיות ועד לבדיקות אינטגרציה.
אז ההמלצה תהיה:
  • אם אתה לא נטפליקס (רוב הסיכויים שאתה לא נטפליקס)...
  • אלא אם כן יש לך כישורי עבודה סופר חזקים שבהם אתה פותח את סביבת הפיתוח וזה גורם לקוף כאוס שזורק את מסד הנתונים הייצור שלך אשר משוחזר בקלות תוך 5 שניות.
  • או שאתה מרגיש כמו @monzo ומוכן לנסות 1500 מיקרו-שירותים רק בגלל שאתה יכול.
→ אל תעשה זאת. ועכשיו זה פחות היפרבולי. הניסיון ליצור מודל של שירותי מיקרו על פני גבולות התחום נראה הגיוני למדי. אבל זה לא אומר שאתה צריך לקחת זרימת עבודה אחת ולפצל אותה לחלקים בודדים זעירים (קבלת XML, לאמת XML, להעביר XML). לפיכך, בכל פעם שאתה מתחיל פרויקט חדש עם Java microservices וגבולות התחום עדיין מעורפלים מאוד, נסה לשמור על גודל המיקרו-שירותים שלך ברמה נמוכה. אתה תמיד יכול להוסיף מודולים נוספים מאוחר יותר. וודא שיש לך DevOps מתקדם בצוות/חברה/חטיבה כדי לתמוך בתשתית החדשה שלך.

ארכיטקטורת מיקרו-שירותים מוכוונת צוות או פוליגלוט

ישנה גישה שלישית, כמעט ליברטריאנית, לפיתוח מיקרו-שירותים: לאפשר לצוותים או אפילו ליחידים ליישם סיפורי משתמשים באמצעות כל מספר של שפות או מיקרו-שירותים (משווקים מכנים גישה זו "תכנות פוליגלוט"). לפיכך, שירות אימות ה-XML שתואר לעיל יכול להיכתב ב-Java, בעוד שמיקרו-שירות האימות יכול להיכתב בו-זמנית ב-Haskell (כדי לגרום לו להישמע מתמטית). עבור המיקרו-שירות שילוח ביטוח, אתה יכול להשתמש ב-Erlang (כי זה באמת צריך לשנות את קנה המידה ;)). מה שעשוי להיראות מהנה מנקודת מבטו של מפתח (פיתוח המערכת המושלמת עם השפה המושלמת שלך בסביבה מבודדת) הוא, למעשה, לעולם לא מה שהארגון רוצה: הומוגניות וסטנדרטיזציה. המשמעות היא קבוצה סטנדרטית יחסית של שפות, ספריות וכלים כך שמפתחים אחרים יוכלו להמשיך לתמוך במיקרו-שירות Haskell שלך בעתיד כאשר תעבור לשטחי מרעה ירוקים יותר. מדריך ל-Java Microservices.  חלק 1: יסודות וארכיטקטורה של Microservices - 8ההיסטוריה מלמדת שסטנדרטיזציה בדרך כלל נעשית עמוק מדי. לדוגמה, מפתחים בחברות Fortune 500 גדולות לא הורשו לפעמים אפילו להשתמש ב-Spring כי זה "לא היה חלק ממפת הדרכים הטכנולוגית של החברה". עם זאת, מעבר מוחלט לגישת הפוליגלוט הוא כמעט אותו דבר, הצד השני של אותו מטבע. המלצה: אם אתה מתכוון להשתמש בתכנות פוליגלוט, נסה פחות מגוון בתוך אותה מערכת אקולוגית של שפת תכנות. אז עדיף להשתמש ב-Kotlin וב-Java ביחד (שתי השפות מבוססות על ה-JVM ותואמות אחת לשנייה ב-100%), ולא ב-Java, נגיד, Haskell. בחלק הבא תלמדו על פריסה ובדיקה של שירותי מיקרו של Java.
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION