1. نظرة عامة
يوضح هذا التمرين المعملي ميزات وقدرات مصممة لتبسيط سير عمل التطوير لمهندسي البرمجيات المكلفين بتطوير تطبيقات Java في بيئة مدمجة. ويتطلّب تطوير الحاوية النموذجي من المستخدم فهم تفاصيل الحاويات وعملية إنشاء الحاوية. بالإضافة إلى ذلك، على المطوّرين إيقاف مسار العمل والخروج من بيئة التطوير المتكاملة (IDE) لاختبار تطبيقاتهم وتصحيح الأخطاء فيها في البيئات البعيدة. باستخدام الأدوات والتقنيات المذكورة في هذا البرنامج التعليمي، يستطيع المطورون العمل بفعالية مع التطبيقات المحوّلة بدون مغادرة بيئة التطوير المتكاملة (IDE).
ما سوف تتعلمه
ستتعلم في هذا التمرين المعملي طُرق التطوير باستخدام حاويات في Google Cloud Platform، بما في ذلك:
- تطوير InnerLoop باستخدام Cloud Workstations
- إنشاء تطبيق بدء تشغيل Java جديد
- التعرّف على عملية التطوير
- تطوير خدمة CRUD Rest بسيطة
- تصحيح أخطاء التطبيق في مجموعة GKE
- ربط التطبيق بقاعدة بيانات CloudSQL
2. الإعداد والمتطلبات
إعداد بيئة ذاتية
- سجِّل الدخول إلى Google Cloud Console وأنشئ مشروعًا جديدًا أو أعِد استخدام مشروع حالي. إذا لم يكن لديك حساب على Gmail أو Google Workspace، عليك إنشاء حساب.
- اسم المشروع هو الاسم المعروض للمشاركين في هذا المشروع. وهي سلسلة أحرف لا تستخدمها Google APIs. ويمكنك تعديله في أي وقت.
- يكون رقم تعريف المشروع فريدًا في جميع مشاريع Google Cloud وغير قابل للتغيير (لا يمكن تغييره بعد تحديده). تنشئ Cloud Console سلسلة فريدة تلقائيًا. فعادةً لا تهتم بما هو. في معظم الدروس التطبيقية حول الترميز، يجب الإشارة إلى رقم تعريف المشروع (يتم تحديده عادةً على أنّه
PROJECT_ID
). وإذا لم يعجبك المعرّف الذي تم إنشاؤه، يمكنك إنشاء رقم تعريف عشوائي آخر. ويمكنك بدلاً من ذلك تجربة طلبك الخاص ومعرفة ما إذا كان متوفّرًا. ولا يمكن تغييره بعد هذه الخطوة وسيبقى طوال مدة المشروع. - لمعلوماتك، هناك قيمة ثالثة، وهي رقم المشروع الذي تستخدمه بعض واجهات برمجة التطبيقات. اطّلِع على مزيد من المعلومات حول هذه القيم الثلاث في المستندات.
- بعد ذلك، عليك تفعيل الفوترة في Cloud Console لاستخدام الموارد/واجهات برمجة التطبيقات في Cloud. إنّ تنفيذ هذا الدرس التطبيقي حول الترميز لن يكون مكلفًا أو مكلفًا على الإطلاق. لإيقاف تشغيل الموارد حتى لا تتحمل الفوترة بعد هذا البرنامج التعليمي، يمكنك حذف الموارد التي أنشأتها أو حذف المشروع بالكامل. يكون مستخدمو Google Cloud الجدد مؤهَّلون للانضمام إلى برنامج فترة تجريبية مجانية بقيمة 300 دولار أمريكي.
بدء محرِّر Cloudshell
تم تصميم هذا الدرس التطبيقي واختباره للاستخدام مع Google Cloud Shell Editor. للوصول إلى المحرر،
- انتقِل إلى مشروع Google على https://console.cloud.google.com.
- في أعلى يسار الشاشة، انقر على رمز محرِّر Cloud Shell
- سيتم فتح جزء جديد في أسفل النافذة.
- انقر على الزر Open Editor (فتح المحرر).
- سيتم فتح المحرِّر مع ظهور مستكشف على اليمين ومحرِّر في المنطقة الوسطى
- ستظهر أيضًا لوحة طرفية في أسفل الشاشة.
- إذا لم تكن الوحدة الطرفية مفتوحة، يمكنك استخدام مجموعة المفاتيح "ctrl+ " لفتح نافذة طرفية جديدة.
إعداد gcloud
في Cloud Shell، اضبط رقم تعريف مشروعك والمنطقة التي تريد نشر التطبيق فيها. حفظها كمتغيّرات PROJECT_ID
وREGION
export REGION=us-central1
export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
استنساخ رمز المصدر
يمكنك العثور على رمز المصدر لهذا الدرس في ورشة عمل خاصة بمطوّري الحاويات على GoogleCloudPlatform على GitHub. استنساخه باستخدام الأمر أدناه ثم قم بتغييره إلى الدليل.
git clone https://github.com/GoogleCloudPlatform/container-developer-workshop.git
cd container-developer-workshop/labs/spring-boot
توفير البنية الأساسية المستخدمة في هذا التمرين المعملي
ستنشر في هذا التمرين المعملي الرمز البرمجي في GKE والوصول إلى البيانات المخزنة في قاعدة بيانات CloudSQL. يعمل النص البرمجي للإعداد أدناه على إعداد هذه البنية الأساسية من أجلك. ستستغرق عملية توفير المتطلبات اللازمة أكثر من 25 دقيقة. انتظر حتى اكتمال النص البرمجي قبل الانتقال إلى القسم التالي.
./setup_with_cw.sh &
مجموعة محطات العمل السحابية
افتح Cloud Workstations في Cloud Console. انتظِر إلى أن تصبح المجموعة في حالة READY
.
إنشاء إعداد محطات العمل
إذا تم إلغاء ربط جلسة Cloud Shell، انقر على "إعادة الاتصال" ثم قم بتشغيل الأمر gcloud cli لتعيين معرّف المشروع. استبدِل رقم تعريف المشروع النموذجي أدناه برقم تعريف مشروع qwiklabs قبل تشغيل الأمر.
gcloud config set project qwiklabs-gcp-project-id
شغِّل النص البرمجي أدناه في الوحدة الطرفية لإنشاء إعدادات Cloud Workstations.
cd ~/container-developer-workshop/labs/spring-boot
./workstation_config_setup.sh
تحقَّق من النتائج ضمن قسم "الإعدادات". سيستغرق الانتقال إلى حالة "جاهز" دقيقتين.
افتح Cloud Workstations في Console وأنشئ مثيلاً جديدًا.
غيِّر الاسم إلى my-workstation
واختَر الإعدادات الحالية: codeoss-java
.
تحقَّق من النتائج ضمن قسم "محطات العمل".
تشغيل محطة العمل
ابدأ تشغيل محطة العمل وشغِّلها.
يمكنك السماح بملفات تعريف الارتباط التابعة لجهات خارجية من خلال النقر على الرمز في شريط العناوين.
انقر على "الموقع الإلكتروني لا يعمل؟".
انقر على "السماح بملفات تعريف الارتباط".
بعد تشغيل محطة العمل، ستظهر حزمة Code OSS IDE. انقر على "وضع علامة تم". في صفحة "البدء"، الأول هو برنامج IDE لمحطة العمل
3- إنشاء تطبيق بدء تشغيل Java جديد
ستنشئ في هذا القسم تطبيقًا جديدًا لـ Java Spring Boot من الصفر باستخدام نموذج تطبيق يوفره spring.io. افتح نافذة Terminal جديدة.
استنساخ نموذج التطبيق
- إنشاء تطبيق للمبتدئين
curl https://start.spring.io/starter.zip -d dependencies=web -d type=maven-project -d javaVersion=17 -d packageName=com.example.springboot -o sample-app.zip
انقر على الزر "السماح" إذا رأيت هذه الرسالة، وذلك كي تتمكن من نسخها ولصقها في محطة العمل.
- فك ضغط التطبيق
unzip sample-app.zip -d sample-app
- افتح "نموذج التطبيق" مجلد
cd sample-app && code-oss-cloud-workstations -r --folder-uri="$PWD"
إضافة أدوات مطوّري برامج Spring-boot-devtools جيب
لتفعيل أدوات مطوري البرامج لـ Spring Boot، ابحث عن pom.xml وافتحه من المستكشف في المحرِّر. بعد ذلك، ألصِق الرمز التالي بعد سطر الوصف قراءة <description>Demo project for Spring Boot</description>
- إضافة spring-boot-devtools في pom.xml
افتح pom.xml
في جذر المشروع. أضِف الإعدادات التالية بعد إدخال Description
.
pom.xml
<!-- Spring profiles-->
<profiles>
<profile>
<id>sync</id>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
</profile>
</profiles>
- تفعيل jib-maven-plugin في pom.xml
Jib هي أداة مفتوحة المصدر لحاويات Java من Google تتيح لمطوّري البرامج في Java إنشاء حاويات باستخدام أدوات Java التي يعرفونها. Jib هي أداة سريعة وبسيطة لإنشاء صور حاويات تتعامل مع جميع خطوات تعبئة التطبيق في صورة حاوية. فهو لا يتطلب منك كتابة ملف Dockerfile أو تثبيت Docker، وهو مُدمَج مباشرةً في Maven وGradle.
انتقِل للأسفل في ملف pom.xml
وعدِّل القسم Build
لتضمين المكوّن الإضافي Jib. يجب أن يتطابق قسم الإصدار مع ما يلي عند اكتماله.
pom.xml
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- Jib Plugin-->
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>3.2.0</version>
</plugin>
<!-- Maven Resources Plugin-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.1.0</version>
</plugin>
</plugins>
</build>
إنشاء ملفات بيانات
توفّر Skaffold أدوات متكاملة لتبسيط عملية تطوير الحاويات. في هذه الخطوة، ستقوم بتهيئة Skaffold الذي سينشئ تلقائيًا ملفات YAML الأساسية لـ Kubernetes. تحاول العملية تحديد الأدلة التي تحتوي على تعريفات صور الحاوية، مثل ملف Dockerfile، ثم إنشاء بيان نشر وخدمة لكل منها.
نفِّذ الأمر أدناه في الوحدة الطرفية لبدء العملية.
- نفِّذ الأمر التالي في الوحدة الطرفية
skaffold init --generate-manifests
- عندما يُطلب منك ذلك:
- استخدِم الأسهم لتحريك المؤشر إلى
Jib Maven Plugin
. - يُرجى الضغط على مفتاح المسافة لتحديد الخيار.
- اضغط على مفتاح Enter للمتابعة.
- أدخِل 8080 للمنفذ.
- أدخِل y لحفظ الإعدادات.
تمت إضافة ملفين إلى مساحة العمل skaffold.yaml
وdeployment.yaml
.
ناتج سقالة:
تعديل اسم التطبيق
لا تتطابق القيم التلقائية المضمّنة في الإعداد حاليًا مع اسم التطبيق. عدِّل الملفات للإشارة إلى اسم التطبيق بدلاً من القيم التلقائية.
- تغيير الإدخالات في إعداد Skaffold
- فتح "
skaffold.yaml
" - اختَر اسم الصورة المحدَّد حاليًا على أنّه
pom-xml-image
. - انقر بزر الماوس الأيمن واختر "تغيير جميع مواضع الورود"
- اكتب الاسم الجديد كـ
demo-app
- تغيير الإدخالات في إعدادات Kubernetes
- فتح ملف
deployment.yaml
- اختَر اسم الصورة المحدَّد حاليًا على أنّه
pom-xml-image
. - انقر بزر الماوس الأيمن واختر "تغيير جميع مواضع الورود"
- اكتب الاسم الجديد كـ
demo-app
تفعيل وضع المزامنة التلقائية
لتسهيل تجربة محسنة لإعادة التحميل السريع، ستستخدم ميزة المزامنة التي يوفرها Jib. في هذه الخطوة، ستقوم بتهيئة Skaffold لاستخدام هذه الميزة في عملية التصميم.
لاحظ أن "مزامنة" الذي تقوم بتهيئته في تهيئة Skaffold الذي يستفيد من "مزامنة" Spring الملف الشخصي الذي أعددته في الخطوة السابقة، حيث فعّلت دعم أدوات spring-dev.
- تعديل إعدادات Skaffold
في الملف skaffold.yaml
، استبدل قسم الإصدار الكامل للملف بالمواصفات التالية. لا تغيّر أقسامًا أخرى من الملف.
skaffold.yaml
build:
artifacts:
- image: demo-app
jib:
project: com.example:demo
type: maven
args:
- --no-transfer-progress
- -Psync
fromImage: gcr.io/distroless/java17-debian11:debug
sync:
auto: true
إضافة مسار تلقائي
أنشئ ملفًا باسم "HelloController.java
" في مجلد "/src/main/java/com/example/springboot/
".
الصق المحتوى التالي في الملف لإنشاء مسار http افتراضي.
HelloController.java
package com.example.springboot;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Value;
@RestController
public class HelloController {
@Value("${target:local}")
String target;
@GetMapping("/")
public String hello()
{
return String.format("Hello from your %s environment!", target);
}
}
4. التعرّف على عملية التطوير
في هذا القسم، ستستعرض بضع خطوات باستخدام المكوّن الإضافي Cloud Code للتعرّف على العمليات الأساسية والتحقّق من صحة ضبط تطبيق المبتدئين وإعداده.
تتكامل خدمة Cloud Code مع Skaffold لتبسيط عملية التطوير. عند النشر على GKE باتّباع الخطوات التالية، ستنشئ Cloud Code وSkaffold صورة الحاوية تلقائيًا وترسلها إلى Container Registry ثم تنشر تطبيقك على GKE. يحدث هذا وراء الكواليس في استخلاص التفاصيل بعيدًا عن تدفق المطور. تُحسّن Cloud Code أيضًا عملية التطوير من خلال توفير إمكانات تقليدية لتصحيح الأخطاء والمزامنة من أجل التطوير المستند إلى الحاوية.
تسجيل الدخول إلى Google Cloud
انقر على رمز رمز السحابة الإلكترونية واختَر "تسجيل الدخول إلى Google Cloud":
انقر على "المتابعة لتسجيل الدخول".
تحقَّق من النتائج في الوحدة الطرفية وافتح الرابط:
سجِّل الدخول باستخدام بيانات اعتماد الطلاب في Qwiklabs.
حدد "السماح":
انسخ رمز إثبات الهوية وارجع إلى علامة التبويب "محطة العمل".
الصق رمز التحقق واضغط على Enter.
إضافة مجموعة Kubernetes
- إضافة مجموعة
- اختَر Google Kubernetes Engine:
- اختيار مشروع
- اختَر "مجموعة عروض الأسعار التقديرية". تم إنشاؤه في الإعداد الأولي.
ضبط رقم تعريف المشروع الحالي باستخدام gcloud cli
انسخ رقم تعريف المشروع الخاص بهذا التمرين المعملي من صفحة Qwiklabs.
شغِّل الأمر gcloud cli لضبط رقم تعريف المشروع. استبدِل نموذج رقم تعريف المشروع قبل تشغيل الأمر.
gcloud config set project qwiklabs-gcp-project-id
عينة الناتج:
تصحيح الأخطاء على Kubernetes
- في الجزء الأيمن في الجزء السفلي، حدد Cloud Code.
- في اللوحة التي تظهر ضمن "جلسات التطوير"، اختَر Debug on Kubernetes.
انتقِل للأسفل إذا لم يكن الخيار مرئيًا.
- حدد "نعم" استخدام السياق الحالي.
- اختَر "مجموعة عروض الأسعار التقديرية". تم إنشاؤه أثناء الإعداد الأولي.
- حدد مستودع الحاوية.
- اختَر علامة التبويب "الإخراج" في الجزء السفلي لعرض مستوى التقدّم والإشعارات.
- اختَر "Kubernetes: تشغيل/تصحيح الأخطاء - مفصّل" في القائمة المنسدلة للقناة على اليسار لعرض تفاصيل إضافية وسجلات البث المباشر من الحاويات.
انتظر حتى يتم نشر التطبيق.
- مراجعة التطبيق المنشور على GKE في Cloud Console
- يمكنك الرجوع إلى طريقة العرض المبسّطة من خلال اختيار "Kubernetes: Run/Debug". من القائمة المنسدلة في علامة التبويب OUTPUT.
- عند الانتهاء من عملية الإنشاء والاختبار، تعرض علامة التبويب "الإخراج" ما يلي:
Resource deployment/demo-app status completed successfully
، ويظهر عنوان URL: "عنوان URL تمت إعادة توجيهه من تطبيق تجريبي للخدمة: http://localhost:8080" - في الوحدة الطرفية لرمز السحابة الإلكترونية، مرِّر مؤشر الماوس فوق عنوان URL في الإخراج (http://localhost:8080)، ثم في نصيحة الأداة التي تظهر، اختَر "متابعة الرابط".
سيتم فتح علامة تبويب جديدة وسترى الناتج أدناه:
استخدام نقاط الإيقاف
- فتح تطبيق "
HelloController.java
" المتوفّر على الرابط/src/main/java/com/example/springboot/HelloController.java
- حدِّد عبارة الإرجاع للمسار الجذر الذي يظهر فيه
return String.format("Hello from your %s environment!", target);
. - أضف نقطة توقف إلى هذا السطر بالنقر على المساحة الفارغة على يمين رقم السطر. سيظهر مؤشر أحمر للإشارة إلى ضبط نقطة الإيقاف.
- أعد تحميل المتصفح ولاحظ أن برنامج تصحيح الأخطاء يوقف العملية عند نقطة التوقف ويسمح لك بالتحقيق في متغيرات التطبيق وحالته التي تعمل عن بُعد في GKE.
- انقر للأسفل في قسم "المتغيّرات" حتى تعثر على الخيار "Target" المتغير.
- يجب ملاحظة القيمة الحالية على أنّها "محلية".
- انقر مرّتين على اسم المتغيّر "target". وفي النافذة المنبثقة،
تغيير القيمة إلى "Cloud Workstations"
- انقر على الزر "متابعة" في لوحة تحكم تصحيح الأخطاء.
- يمكنك مراجعة الردّ في متصفّحك الذي يعرض الآن القيمة المعدّلة التي أدخلتها للتو.
- أزِل نقطة الإيقاف من خلال النقر على المؤشر الأحمر على يمين رقم السطر. سيؤدي هذا الإجراء إلى منع إيقاف تنفيذ الرمز في هذا السطر أثناء تقدمك في هذا التمرين.
إعادة التحميل السريع
- غيّر العبارة لإرجاع قيمة مختلفة مثل "مرحبًا من رمز %s"
- يتم حفظ الملف ومزامنته تلقائيًا في الحاويات البعيدة في GKE
- يُرجى إعادة تحميل صفحة المتصفّح للاطّلاع على النتائج المعدّلة.
- أوقف جلسة تصحيح الأخطاء عن طريق النقر على المربع الأحمر في شريط أدوات تصحيح الأخطاء
اختَر "نعم، حذف البيانات بعد كل عملية تشغيل".
5- تطوير خدمة CRUD Rest بسيطة
في هذه المرحلة، تم ضبط تطبيقك بالكامل للتطوير المجمّع، وقد اطّلعت على خطوات سير عمل التطوير الأساسي باستخدام Cloud Code. في الأقسام التالية، يمكنك ممارسة ما تعلمته من خلال إضافة نقاط نهاية خدمة REST المتصلة بقاعدة بيانات مُدارة في Google Cloud.
إعداد التبعيات
يستخدم رمز التطبيق قاعدة بيانات للاحتفاظ بباقي بيانات الخدمة. تأكَّد من توفُّر التبعيات من خلال إضافة ما يلي في pom.xl
- افتح ملف
pom.xml
وأضِف ما يلي إلى قسم الموارد التابعة في ملف الإعداد.
pom.xml
<!-- Database dependencies-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
<version>2.2</version>
</dependency>
خدمة Code REST
Quote.java
أنشِئ ملفًا باسم "Quote.java
" في "/src/main/java/com/example/springboot/
" وانسخه في الرمز أدناه. يحدد هذا نموذج الكيان لكائن "اقتباس" المستخدم في التطبيق.
package com.example.springboot;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.util.Objects;
@Entity
@Table(name = "quotes")
public class Quote
{
@Id
@Column(name = "id")
private Integer id;
@Column(name="quote")
private String quote;
@Column(name="author")
private String author;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getQuote() {
return quote;
}
public void setQuote(String quote) {
this.quote = quote;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Quote quote1 = (Quote) o;
return Objects.equals(id, quote1.id) &&
Objects.equals(quote, quote1.quote) &&
Objects.equals(author, quote1.author);
}
@Override
public int hashCode() {
return Objects.hash(id, quote, author);
}
}
QuoteRepository.java
أنشئ ملفًا باسم QuoteRepository.java
في src/main/java/com/example/springboot
وانسخ الرمز التالي
package com.example.springboot;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
public interface QuoteRepository extends JpaRepository<Quote,Integer> {
@Query( nativeQuery = true, value =
"SELECT id,quote,author FROM quotes ORDER BY RANDOM() LIMIT 1")
Quote findRandomQuote();
}
يستخدم هذا الرمز JPA للاحتفاظ بالبيانات. توسّع الفئة واجهة Spring JPARepository
وتسمح بإنشاء رموز مخصّصة. أضفت طريقة مخصّصة findRandomQuote
في الرمز البرمجي.
QuoteController.java
لعرض نقطة النهاية للخدمة، ستوفّر فئة QuoteController
هذه الوظيفة.
أنشئ ملفًا باسم "QuoteController.java
" في "src/main/java/com/example/springboot
" وانسخ المحتوى التالي.
package com.example.springboot;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class QuoteController {
private final QuoteRepository quoteRepository;
public QuoteController(QuoteRepository quoteRepository) {
this.quoteRepository = quoteRepository;
}
@GetMapping("/random-quote")
public Quote randomQuote()
{
return quoteRepository.findRandomQuote();
}
@GetMapping("/quotes")
public ResponseEntity<List<Quote>> allQuotes()
{
try {
List<Quote> quotes = new ArrayList<Quote>();
quoteRepository.findAll().forEach(quotes::add);
if (quotes.size()==0 || quotes.isEmpty())
return new ResponseEntity<List<Quote>>(HttpStatus.NO_CONTENT);
return new ResponseEntity<List<Quote>>(quotes, HttpStatus.OK);
} catch (Exception e) {
System.out.println(e.getMessage());
return new ResponseEntity<List<Quote>>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@PostMapping("/quotes")
public ResponseEntity<Quote> createQuote(@RequestBody Quote quote) {
try {
Quote saved = quoteRepository.save(quote);
return new ResponseEntity<Quote>(saved, HttpStatus.CREATED);
} catch (Exception e) {
System.out.println(e.getMessage());
return new ResponseEntity<Quote>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@PutMapping("/quotes/{id}")
public ResponseEntity<Quote> updateQuote(@PathVariable("id") Integer id, @RequestBody Quote quote) {
try {
Optional<Quote> existingQuote = quoteRepository.findById(id);
if(existingQuote.isPresent()){
Quote updatedQuote = existingQuote.get();
updatedQuote.setAuthor(quote.getAuthor());
updatedQuote.setQuote(quote.getQuote());
return new ResponseEntity<Quote>(updatedQuote, HttpStatus.OK);
} else {
return new ResponseEntity<Quote>(HttpStatus.NOT_FOUND);
}
} catch (Exception e) {
System.out.println(e.getMessage());
return new ResponseEntity<Quote>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@DeleteMapping("/quotes/{id}")
public ResponseEntity<HttpStatus> deleteQuote(@PathVariable("id") Integer id) {
Optional<Quote> quote = quoteRepository.findById(id);
if (quote.isPresent()) {
quoteRepository.deleteById(id);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} else {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
إضافة تهيئات قاعدة البيانات
application.yaml
إضافة إعداد لقاعدة بيانات الخلفية التي تصل إليها الخدمة عدِّل (أو أنشئ إذا لم يكن متوفّرًا) الملف المُسمّى application.yaml
ضمن src/main/resources
وأضِف إعداد Spring بمَعلمة للخلفية.
target: local
spring:
config:
activate:
on-profile: cloud-dev
datasource:
url: 'jdbc:postgresql://${DB_HOST:127.0.0.1}/${DB_NAME:quote_db}'
username: '${DB_USER:user}'
password: '${DB_PASS:password}'
jpa:
properties:
hibernate:
jdbc:
lob:
non_contextual_creation: true
dialect: org.hibernate.dialect.PostgreSQLDialect
hibernate:
ddl-auto: update
إضافة عملية نقل قاعدة البيانات
إنشاء المجلدات db/migration
ضمن src/main/resources
إنشاء ملف SQL: V1__create_quotes_table.sql
الصق المحتوى التالي في الملف.
V1__create_quotes_table.sql
CREATE TABLE quotes(
id INTEGER PRIMARY KEY,
quote VARCHAR(1024),
author VARCHAR(256)
);
INSERT INTO quotes (id,quote,author) VALUES (1,'Never, never, never give up','Winston Churchill');
INSERT INTO quotes (id,quote,author) VALUES (2,'While there''s life, there''s hope','Marcus Tullius Cicero');
INSERT INTO quotes (id,quote,author) VALUES (3,'Failure is success in progress','Anonymous');
INSERT INTO quotes (id,quote,author) VALUES (4,'Success demands singleness of purpose','Vincent Lombardi');
INSERT INTO quotes (id,quote,author) VALUES (5,'The shortest answer is doing','Lord Herbert');
إعدادات Kubernetes
تسمح الإضافات التالية إلى ملف deployment.yaml
للتطبيق بالاتصال بمثيلات CloudSQL.
- TARGET - لضبط المتغير للإشارة إلى البيئة التي يتم تنفيذ التطبيق فيها
- SPRING_PROFILES_ACTIVE - يعرض الملف الشخصي النشط في Spring، والذي سيتم ضبطه على
cloud-dev
- DB_HOST - عنوان IP الخاص لقاعدة البيانات، والذي تمت ملاحظته عند إنشاء مثيل قاعدة البيانات أو بالنقر على
SQL
في قائمة التنقل بوحدة التحكم في Google Cloud - يُرجى تغيير القيمة ! - DB_USER وDB_PASS - كما تم ضبطه في إعداد مثيل CloudSQL، ويتم تخزينه كسر في GCP
يُرجى تعديل ملف Publishing.yaml الذي يتضمّن المحتوى أدناه.
deployment.yaml
apiVersion: v1
kind: Service
metadata:
name: demo-app
labels:
app: demo-app
spec:
ports:
- port: 8080
protocol: TCP
clusterIP: None
selector:
app: demo-app
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-app
labels:
app: demo-app
spec:
replicas: 1
selector:
matchLabels:
app: demo-app
template:
metadata:
labels:
app: demo-app
spec:
containers:
- name: demo-app
image: demo-app
env:
- name: PORT
value: "8080"
- name: TARGET
value: "Local Dev - CloudSQL Database - K8s Cluster"
- name: SPRING_PROFILES_ACTIVE
value: cloud-dev
- name: DB_HOST
value: ${DB_INSTANCE_IP}
- name: DB_PORT
value: "5432"
- name: DB_USER
valueFrom:
secretKeyRef:
name: gke-cloud-sql-secrets
key: username
- name: DB_PASS
valueFrom:
secretKeyRef:
name: gke-cloud-sql-secrets
key: password
- name: DB_NAME
valueFrom:
secretKeyRef:
name: gke-cloud-sql-secrets
key: database
استبدل قيمة DB_HOST بعنوان قاعدة البيانات عن طريق تشغيل الأوامر أدناه في الوحدة الطرفية:
export DB_INSTANCE_IP=$(gcloud sql instances describe quote-db-instance \
--format=json | jq \
--raw-output ".ipAddresses[].ipAddress")
envsubst < deployment.yaml > deployment.new && mv deployment.new deployment.yaml
افتح Publishing.yaml وتحقَّق من تعديل قيمة DB_HOST باستخدام عنوان IP للمثيل.
نشر التطبيق والتحقّق من صحته
- في الجزء السفلي من "محرِّر Cloud Shell"، اختَر Cloud Code ثم اختَر Debug on Kubernetes في أعلى الشاشة.
- وعند الانتهاء من عملية الإنشاء والاختبار، تعرض علامة التبويب "الإخراج" ما يلي:
Resource deployment/demo-app status completed successfully
، ويظهر عنوان URL: "عنوان URL تمت إعادة توجيهه من تطبيق تجريبي للخدمة: http://localhost:8080". لاحظ أن المنفذ قد يختلف أحيانًا مثل 8081. في هذه الحالة، اضبط القيمة المناسبة. اضبط قيمة عنوان URL في الوحدة الطرفية.
export URL=localhost:8080
- عرض الاقتباسات العشوائية
من Terminal، شغِّل الأمر أدناه عدة مرات مقابل نقطة نهاية علامة الاقتباس العشوائية. ملاحظة المكالمات المتكرّرة التي تعرض علامات اقتباس مختلفة
curl $URL/random-quote | jq
- إضافة عرض أسعار تقديري
أنشِئ اقتباسًا جديدًا، مع id=6 باستخدام الأمر المُدرَج أدناه ولاحظ الطلب الذي يردّ على الرسالة.
curl -H 'Content-Type: application/json' -d '{"id":"6","author":"Henry David Thoreau","quote":"Go confidently in the direction of your dreams! Live the life you have imagined"}' -X POST $URL/quotes
- حذف عرض أسعار تقديري
يمكنك الآن حذف عرض الأسعار التقديري الذي أضفته للتو باستخدام طريقة الحذف ومراقبة رمز الاستجابة HTTP/1.1 204
.
curl -v -X DELETE $URL/quotes/6
- حدث خطأ في الخادم.
ظهور حالة خطأ من خلال إعادة تنفيذ الطلب الأخير بعد حذف الإدخال
curl -v -X DELETE $URL/quotes/6
لاحِظ أنّ الردّ يعرض HTTP:500 Internal Server Error
.
تصحيح أخطاء التطبيق
لقد وجدت في القسم السابق حالة خطأ في التطبيق عند محاولة حذف إدخال غير موجود في قاعدة البيانات. في هذا القسم، يمكنك تعيين نقطة توقف لتحديد المشكلة. حدث الخطأ في عملية DELETE، ولذلك ستعمل مع فئة SurveyController.
- فتح "
src/main/java/com/example/springboot/QuoteController.java
" - البحث عن طريقة
deleteQuote()
- البحث عن الخط:
Optional<Quote> quote = quoteRepository.findById(id);
- حدد نقطة توقف في هذا السطر بالنقر على المساحة الفارغة على يمين رقم السطر.
- سيظهر مؤشر أحمر يشير إلى ضبط نقطة الإيقاف.
- أعِد تشغيل الأمر
delete
.
curl -v -X DELETE $URL/quotes/6
- التبديل مرة أخرى إلى عرض تصحيح الأخطاء من خلال النقر على الرمز في العمود الأيمن
- لاحِظ أنّ سطر تصحيح الأخطاء توقّف في فئة QuestionController.
- في برنامج تصحيح الأخطاء، انقر على رمز
step over
.
- لاحظ أن الكود يعرض خطأ HTTP 500 في الخادم الداخلي للعميل وهذا ليس مثاليًا.
Trying 127.0.0.1:8080... * Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0) > DELETE /quotes/6 HTTP/1.1 > Host: 127.0.0.1:8080 > User-Agent: curl/7.74.0 > Accept: */* > * Mark bundle as not supporting multiuse < HTTP/1.1 500 < Content-Length: 0 < Date: < * Connection #0 to host 127.0.0.1 left intact
تعديل الرمز البرمجي
الرمز غير صحيح ويجب إعادة ضبط مجموعة الكتلة else
لإرسال رمز الحالة "لم يتم العثور على HTTP 404" مرة أخرى.
صحِّح الخطأ.
- بينما لا تزال جلسة تصحيح الأخطاء قيد التشغيل، أكمل الطلب بالضغط على زر "continue" في لوحة تحكم تصحيح الأخطاء.
- بعد ذلك، غيِّر مجموعة
else
إلى الرمز:
else {
return new ResponseEntity<HttpStatus>(HttpStatus.NOT_FOUND);
}
ينبغي أن تبدو الطريقة كما يلي
@DeleteMapping("/quotes/{id}") public ResponseEntity<HttpStatus> deleteQuote(@PathVariable("id") Integer id) { Optional<Quote> quote = quoteRepository.findById(id); if (quote.isPresent()) { quoteRepository.deleteById(id); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } else { return new ResponseEntity<HttpStatus>(HttpStatus.NOT_FOUND); } }
- إعادة تشغيل أمر الحذف
curl -v -X DELETE $URL/quotes/6
- تنقَّل في برنامج تصحيح الأخطاء وراجِع رسالة الخطأ HTTP 404 Not Found والتي تم إرجاعها إلى المتصل.
Trying 127.0.0.1:8080... * Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0) > DELETE /quotes/6 HTTP/1.1 > Host: 127.0.0.1:8080 > User-Agent: curl/7.74.0 > Accept: */* > * Mark bundle as not supporting multiuse < HTTP/1.1 404 < Content-Length: 0 < Date: < * Connection #0 to host 127.0.0.1 left intact
- أوقف جلسة تصحيح الأخطاء عن طريق النقر على المربع الأحمر في شريط أدوات تصحيح الأخطاء
6- تهانينا
تهانينا أنشأت في هذا التمرين المعملي تطبيقًا جديدًا من Java من البداية وتهيئته للعمل بفعالية مع الحاويات. ثم نشرت تطبيقك وصحّحته على مجموعة GKE بعيدة وفقًا لمسار المطوّرين نفسه المتوفّر في حِزم التطبيقات التقليدية.
ما تعلمته
- تطوير InnerLoop باستخدام Cloud Workstations
- إنشاء تطبيق بدء تشغيل Java جديد
- التعرّف على عملية التطوير
- تطوير خدمة CRUD REST بسيطة
- تصحيح أخطاء التطبيق في مجموعة GKE
- ربط التطبيق بقاعدة بيانات CloudSQL