מעבר מ-Java 8 לזמן הריצה העדכני של Java

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

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

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

הסבר על אפשרויות ההעברה

כדי לצמצם את המאמץ והמורכבות של ההעברה בזמן הריצה, הסביבה הרגילה של App Engine מאפשרת לכם לגשת להרבה שירותים ולממשקי API מדור קודם, כמו Memcache, בזמני הריצה של Java מהדור השני. אפליקציית Java יכולה לקרוא לממשקי ה-API של השירותים הכלולים דרך קובץ ה-JAR של App Engine API, ולקבל גישה לרוב היכולות כמו בסביבת זמן הריצה של Java 8.

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

מידע נוסף על מעבר לשירותים לא מקובצים זמין במאמר מעבר משירותים מקובצים.

יש כמה הבדלים באופן שבו מבצעים את ההעברה בזמן הריצה, בהתאם לבחירה אם להשתמש בשירותים הקודמים בחבילה:

מעבר לזמני ריצה של Java מדור שני עם שירותים בחבילה מעבר לזמני ריצה של Java מדור שני בלי שירותים בחבילה
גישה לשירותים בחבילה באמצעות קובץ ה-JAR של ממשקי ה-API של App Engine. אפשר גם להשתמש ב Google Cloud מוצרים מומלצים או בשירותים של צד שלישי.

משתמשים ב- appengine-web.xml וב- web.xml להגדרת האפליקציה. בקובצי appengine-web.xml ו-web.xml, מגדירים את השדה app-engine-bundled-services כדי להפעיל גישה לשירותים ספציפיים בחבילה.

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

משתמשים ב- app.yaml כדי להגדיר את האפליקציה.

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

הפריסה של האפליקציות מתבצעת באמצעות Jetty. משתמשים בפורמט WAR כדי לארוז את האפליקציה. הפריסה של האפליקציות מתבצעת באמצעות השרת שלכם. משתמשים בפורמט JAR כדי לארוז את האפליקציה. כדי לקבל מידע נוסף על המרת קובץ WAR קיים לקובץ JAR שניתן להפעלה, אפשר לעיין במאמר אריזה מחדש של קובץ WAR.

סקירה כללית של תהליך המיגרציה

בהמשך מפורטים כמה שינויים שאולי תצטרכו לבצע באפליקציית Java 8 הקיימת שלכם ב-App Engine ובתהליך הפריסה כדי להשתמש בסביבות זמן הריצה של Java מהדור השני:

ההבדלים העיקריים בין Java 8 לבין סביבות זמן הריצה של Java מהדור השני

בהמשך מופיע סיכום של ההבדלים בין Java 8 לבין סביבות זמן הריצה של Java מהדור השני בסביבת App Engine סטנדרטית:

זמן ריצה של Java 8 סביבות זמן ריצה של Java מהדור השני
פריסת השרת השרת נפרס בשבילכם באמצעות Jetty אם האפליקציה שלכם לא משתמשת בשירותים הקודמים שצורפו לחבילה, תצטרכו לפרוס שרת בעצמכם.1
שירותים בחבילה מדור קודם של App Engine מסופק מסופק
אפשרות להשתמש בספריות לקוח של Cloud ל-Java כן כן
תמיכה בתוסף שפה ובספריית המערכת כן כן
גישה לרשת חיצונית כן כן
גישה למערכת הקבצים גישת קריאה/כתיבה אל /tmp גישת קריאה/כתיבה אל /tmp
שפת זמן הריצה הותאם ל-App Engine זמן ריצה ללא שינויים, קוד פתוח
מנגנון בידוד ארגז חול לקונטיינרים מבוסס gVisor ארגז חול לקונטיינרים מבוסס gVisor
בדיקה באמצעות שרת פיתוח מקומי נתמך נתמך
הגדרת thread safety אפשר לציין את הגרסה בקובץ appengine-web.xml. אי אפשר לציין את הערך הזה בקובצי התצורה. ההנחה היא שכל האפליקציות בטוחות לשימוש עם שרשורים.3
רישום ביומן משתמשים ב-java.util.logging.
ConsoleHandler, שכותב ל-
stderr ומרוקן את הזרם
אחרי כל רשומה.
‫Standard Cloud Logging 2
תמיכה בתוסף DataNucleus בגרסה 2.x נתמך לא נתמך 4

הערות:

  1. אם האפליקציה שלכם לא משתמשת בשירותים המצורפים מדור קודם, סביבות זמן הריצה של Java מהדור השני יכולות להריץ כל מסגרת Java, כל עוד אתם אורזים שרת אינטרנט שמוגדר להגיב לבקשות HTTP ביציאה שצוינה במשתנה הסביבה PORT (מומלץ) או ביציאה 8080. לדוגמה, סביבות זמן הריצה של Java מהדור השני יכולות להריץ קובץ JAR של Spring Boot Uber כמו שהוא. דוגמאות נוספות מופיעות בקטע גמישות המסגרת.

    אם האפליקציה שלכם משתמשת בשירותים הקודמים שצורפו, App Engine פורס אותה באמצעות Jetty באותו אופן כמו בסביבת זמן הריצה של Java 8.

  2. הרישום ביומן בזמני הריצה של Java מדור שני מתבצע לפי תקן הרישום ביומן ב-Cloud Logging. בזמני הריצה של Java מהדור השני, יומני האפליקציות כבר לא מצורפים ליומני הבקשות, אלא מופרדים ברשומות שונות. מידע נוסף על קריאה וכתיבה של יומנים בסביבות זמן הריצה של Java מהדור השני זמין במדריך לרישום ביומן.

  3. כדי להגדיר אפליקציה לא בטוחה לשימוש עם שרשורים בסביבת זמן ריצה של Java מהדור השני, בדומה להגדרה של <threadsafe>false</threadsafe> ב-Java 8, צריך להגדיר את הערך של max concurrency ל-1 בקובץ app.yaml או בקובץ appengine-web.xml אם משתמשים בשירותים מדור קודם.

  4. ‫Google לא תומכת בספריית DataNucleus בסביבות זמן ריצה מהדור השני. גרסאות חדשות יותר של DataNucleus לא תואמות לאחור לגרסאות שנעשה בהן שימוש ב-Java 8. כדי לגשת ל-Datastore, מומלץ להשתמש בספריית לקוח במצב Datastore או בפתרון Objectify (גרסה 6 ואילך) של Java. ‏ Objectify הוא API בקוד פתוח ל-Datastore שמספק רמה גבוהה יותר של הפשטה.

הבדלים בשימוש בזיכרון

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

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

הבדלים בשימוש במעבד

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

הבדלים בכותרות הבקשה

סביבות זמן ריצה מהדור הראשון מאפשרות להעביר לאפליקציה כותרות של בקשות עם קווים תחתונים (למשל X-Test-Foo_bar). בסביבות זמן ריצה מהדור השני,‏ Nginx מוצגת בארכיטקטורת המארח. כתוצאה מהשינוי הזה, סביבות זמן ריצה מהדור השני מוגדרות להסרה אוטומטית של כותרות עם קווים תחתונים (_). כדי למנוע בעיות באפליקציה, מומלץ להימנע משימוש בקווים תחתונים בכותרות של בקשות לאפליקציה.

גמישות המסגרת

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

יש hello world דוגמאות לשימוש במסגרות פופולריות של Java לאינטרנט בGoogle Cloud מאגר GitHub:

העברה של קובצי XML לפורמטים של קובצי YAML

‫ה-CLI של gcloud לא תומך בפורמטים הבאים של קבצים:

  • cron.xml
  • datastore-index.xml
  • dispatch.xml
  • queue.xml

בדוגמאות הבאות מוסבר איך להעביר קובצי xml לקובצי yaml.

העברת הקבצים באופן אוטומטי

כדי להעביר את הקבצים ב-xml באופן אוטומטי:

  1. צריך לוודא שמותקנת גרסה 226.0.0 ואילך של ה-CLI של gcloud. כדי לעדכן לגרסה האחרונה:

    gcloud components update
    
  2. לכל קובץ שרוצים להעביר, מציינים אחת מהפקודות המשניות הבאות (cron-xml-to-yaml, ‏ datastore-indexes-xml-to-yaml,‏ dispatch-xml-to-yaml, ‏ queue-xml-to-yaml) ואת שם הקובץ:

    gcloud beta app migrate-config queue-xml-to-yaml MY-QUEUE-XML-FILE.xml
    
  3. לפני שפורסים את הקובץ המומר בסביבת הייצור, כדאי לבדוק אותו ידנית.

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

העברת הקבצים באופן ידני

כדי להעביר ידנית את קובצי xml לקובצי yaml:

cron.yaml

יוצרים קובץ cron.yaml עם אובייקט cron שמכיל רשימה של אובייקטים, שלכל אחד מהם יש שדות שתואמים לכל אחד ממאפייני התג <cron> בקובץ cron.xml, כמו שמוצג בהמשך.

קובץ cron.yaml שהומר:

cron:
- url: '/recache'
  schedule: 'every 2 minutes'
  description: 'Repopulate the cache every 2 minutes'
- url: '/weeklyreport'
  schedule: 'every monday 08:30'
  target: 'version-2'
  timezone: 'America/New_York'
  description: 'Mail out a weekly report'

קובץ cron.xml מקורי:

<?xml version="1.0" encoding="UTF-8"?>
<cronentries>
  <cron>
    <url>/recache</url>
    <description>Repopulate the cache every 2 minutes</description>
    <schedule>every 2 minutes</schedule>
  </cron>
  <cron>
    <url>/weeklyreport</url>
    <description>Mail out a weekly report</description>
    <schedule>every monday 08:30</schedule>
    <timezone>America/New_York</timezone>
    <target>version-2</target>
  </cron>
</cronentries>

מידע נוסף מופיע בcron.yamlמאמרי העזרה.

dispatch.yaml

יוצרים קובץ dispatch.yaml עם אובייקט dispatch שמכיל רשימה של אובייקטים, שלכל אחד מהם יש שדות שתואמים לכל אחד מהמאפיינים של תג <dispatch> בקובץ dispatch.xml, כמו שמוצג בהמשך.

קובץ dispatch.yaml שהומר:

dispatch:
- url: '*/favicon.ico'
  module: default
- url: 'simple-sample.uc.r.appspot.com/'
  module: default
- url: '*/mobile/*'
  module: mobile-frontend

קובץ dispatch.xml מקורי

<?xml version="1.0" encoding="UTF-8"?>
<dispatch-entries>
  <dispatch>
      <url>*/favicon.ico</url>
      <module>default</module>
  </dispatch>
  <dispatch>
      <url>simple-sample.uc.r.appspot.com/</url>
      <module>default</module>
  </dispatch>
  <dispatch>
      <url>*/mobile/*</url>
      <module>mobile-frontend</module>
  </dispatch>
</dispatch-entries>

מידע נוסף מופיע במאמרי העזרה בנושא dispatch.yaml.

index.yaml

יוצרים קובץ index.yaml עם אובייקט indexes שמכיל רשימה של אובייקטים, שלכל אחד מהם יש שדות שתואמים לכל אחד ממאפייני התג <datastore-index> בקובץ datastore-indexes.xml, כמו שמוצג בהמשך.

קובץ index.yaml שהומר:

indexes:
- ancestor: false
  kind: Employee
  properties:
  - direction: asc
    name: lastName
  - direction: desc
    name: hireDate
- ancestor: false
  kind: Project
  properties:
  - direction: asc
    name: dueDate
  - direction: desc
    name: cost

קובץ datastore-index.xml מקורי:

<?xml version="1.0" encoding="utf-8"?>
<datastore-indexes
 autoGenerate="true">
   <datastore-index kind="Employee" ancestor="false">
       <property name="lastName" direction="asc" />
       <property name="hireDate" direction="desc" />
   </datastore-index>
   <datastore-index kind="Project" ancestor="false">
       <property name="dueDate" direction="asc" />
       <property name="cost" direction="desc" />
   </datastore-index>
</datastore-indexes>

מידע נוסף מופיע בindex.yamlמאמרי העזרה.

queue.yaml

יוצרים קובץ queue.yaml עם אובייקט queue שמכיל רשימה של אובייקטים, שלכל אחד מהם יש שדות שתואמים למאפיינים של תג <queue> בקובץ queue.xml, כמו שמוצג בהמשך.

קובץ queue.yaml שהומר:

queue:
- name: fooqueue
  mode: push
  rate: 1/s
  retry_parameters:
    task_retry_limit: 7
    task_age_limit: 2d
- name: barqueue
  mode: push
  rate: 1/s
  retry_parameters:
    min_backoff_seconds: 10
    max_backoff_seconds: 200
    max_doublings: 0

קובץ queue.xml מקורי:

<queue-entries>
  <queue>
    <name>fooqueue</name>
    <rate>1/s</rate>
    <retry-parameters>
      <task-retry-limit>7</task-retry-limit>
      <task-age-limit>2d</task-age-limit>
    </retry-parameters>
  </queue>
  <queue>
    <name>barqueue</name>
    <rate>1/s</rate>
    <retry-parameters>
      <min-backoff-seconds>10</min-backoff-seconds>
      <max-backoff-seconds>200</max-backoff-seconds>
      <max-doublings>0</max-doublings>
    </retry-parameters>
  </queue>
<queue-entries>