توسعه InnerLoop با جاوا - SpringBoot

۱. مرور کلی

این آزمایشگاه ویژگی‌ها و قابلیت‌هایی را نشان می‌دهد که برای ساده‌سازی گردش کار توسعه برای مهندسان نرم‌افزاری که وظیفه توسعه برنامه‌های جاوا در یک محیط کانتینری را بر عهده دارند، طراحی شده‌اند. توسعه کانتینر معمولاً مستلزم آن است که کاربر جزئیات کانتینرها و فرآیند ساخت کانتینر را درک کند. علاوه بر این، توسعه‌دهندگان معمولاً باید جریان خود را بشکنند و از IDE خود خارج شوند تا برنامه‌های خود را در محیط‌های از راه دور آزمایش و اشکال‌زدایی کنند. با ابزارها و فناوری‌های ذکر شده در این آموزش، توسعه‌دهندگان می‌توانند بدون ترک IDE خود، به طور مؤثر با برنامه‌های کانتینری کار کنند.

آنچه یاد خواهید گرفت

در این آزمایشگاه روش‌های توسعه با کانتینرها در GCP را خواهید آموخت، از جمله:

  • تنظیمات و الزامات
  • ایجاد یک برنامه شروع کننده جدید جاوا
  • قدم زدن در فرآیند توسعه
  • توسعه یک سرویس استراحت CRUD ساده
  • پاکسازی

۲. تنظیمات و الزامات

تنظیم محیط خودتنظیم

  1. وارد کنسول گوگل کلود شوید و یک پروژه جدید ایجاد کنید یا از یک پروژه موجود دوباره استفاده کنید. اگر از قبل حساب جیمیل یا گوگل ورک اسپیس ندارید، باید یکی ایجاد کنید .

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • نام پروژه ، نام نمایشی برای شرکت‌کنندگان این پروژه است. این یک رشته کاراکتری است که توسط APIهای گوگل استفاده نمی‌شود و می‌توانید آن را در هر زمانی به‌روزرسانی کنید.
  • شناسه پروژه باید در تمام پروژه‌های گوگل کلود منحصر به فرد باشد و تغییرناپذیر است (پس از تنظیم، قابل تغییر نیست). کنسول کلود به طور خودکار یک رشته منحصر به فرد تولید می‌کند؛ معمولاً برای شما مهم نیست که چیست. در اکثر آزمایشگاه‌های کد، باید به شناسه پروژه ارجاع دهید (و معمولاً با نام PROJECT_ID شناخته می‌شود)، بنابراین اگر آن را دوست ندارید، یک شناسه تصادفی دیگر ایجاد کنید، یا می‌توانید شناسه خودتان را امتحان کنید و ببینید آیا در دسترس است یا خیر. سپس پس از ایجاد پروژه، آن "منجمد" می‌شود.
  • یک مقدار سوم هم وجود دارد، شماره پروژه که برخی از APIها از آن استفاده می‌کنند. برای اطلاعات بیشتر در مورد هر سه این مقادیر به مستندات مراجعه کنید.
  1. در مرحله بعد، برای استفاده از منابع/APIهای ابری، باید پرداخت صورتحساب را در کنسول ابری فعال کنید . اجرای این آزمایشگاه کد، اگر اصلاً هزینه‌ای نداشته باشد، هزینه زیادی نخواهد داشت. برای خاموش کردن منابع به طوری که پس از این آموزش متحمل پرداخت صورتحساب نشوید، دستورالعمل‌های «پاکسازی» موجود در انتهای آزمایشگاه کد را دنبال کنید. کاربران جدید Google Cloud واجد شرایط برنامه آزمایشی رایگان ۳۰۰ دلاری هستند.

ویرایشگر Cloudshell را شروع کنید

این آزمایشگاه برای استفاده با ویرایشگر پوسته ابری گوگل (Google Cloud Shell Editor) طراحی و آزمایش شده است. برای دسترسی به ویرایشگر،

  1. برای دسترسی به پروژه گوگل خود به آدرس https://console.cloud.google.com مراجعه کنید.
  2. در گوشه بالا سمت راست، روی آیکون ویرایشگر پوسته ابری کلیک کنید.

8560cc8d45e8c112.png

  1. یک پنل جدید در پایین پنجره شما باز خواهد شد
  2. روی دکمه باز کردن ویرایشگر کلیک کنید

9e504cb98a6a8005.png

  1. ویرایشگر با یک کاوشگر در سمت راست و ویرایشگر در ناحیه مرکزی باز خواهد شد.
  2. یک پنجره ترمینال نیز باید در پایین صفحه نمایش موجود باشد
  3. اگر ترمینال باز نیست، از ترکیب کلیدهای `ctrl+`` برای باز کردن یک پنجره ترمینال جدید استفاده کنید.

جی کلود را تنظیم کنید

در Cloud Shell، شناسه پروژه و منطقه‌ای که می‌خواهید برنامه‌تان در آن مستقر شود را تنظیم کنید. آن‌ها را به عنوان متغیرهای PROJECT_ID و REGION ذخیره کنید.

export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')

دریافت کد منبع

کد منبع این آزمایش در container-developer-workshop در GoogleCloudPlatform در GitHub قرار دارد. آن را با دستور زیر کلون کنید و سپس به دایرکتوری بروید.

git clone https://github.com/GoogleCloudPlatform/container-developer-workshop.git
cd container-developer-workshop/labs/spring-boot

فراهم کردن زیرساخت‌های مورد استفاده در این آزمایشگاه

در این آزمایش، شما کد را در GKE مستقر خواهید کرد و به داده‌های ذخیره شده در پایگاه داده CloudSql دسترسی خواهید داشت. اسکریپت راه‌اندازی زیر این زیرساخت را برای شما آماده می‌کند. فرآیند آماده‌سازی بیش از 10 دقیقه طول خواهد کشید. می‌توانید در حین پردازش تنظیمات، مراحل بعدی را ادامه دهید.

./setup.sh

۳. ایجاد یک برنامه‌ی آغازین جاوای جدید

در این بخش، شما یک برنامه Java Spring Boot جدید را از ابتدا با استفاده از یک برنامه نمونه ارائه شده توسط spring.io ایجاد خواهید کرد.

برنامه نمونه را کلون کنید

  1. یک برنامه‌ی آغازین ایجاد کنید
curl  https://start.spring.io/starter.zip -d dependencies=web -d type=maven-project -d javaVersion=11 -d packageName=com.example.springboot -o sample-app.zip
  1. برنامه را از حالت فشرده خارج کنید
unzip sample-app.zip -d sample-app
  1. به پوشه sample-app بروید و پوشه موجود در فضای کاری Cloud Shell IDE را باز کنید.
cd sample-app && cloudshell workspace .

اضافه کردن spring-boot-devtools و Jib

برای فعال کردن ابزارهای توسعه‌ی Spring Boot، فایل pom.xml را از اکسپلورر در ویرایشگر خود پیدا کرده و باز کنید. سپس کد زیر را بعد از خط توضیحات که <description>Demo project for Spring Boot</description> را نشان می‌دهد، قرار دهید.

  1. 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>
  1. فعال کردن jib-maven-plugin در pom.xml

Jib یک ابزار کانتینرسازی جاوای متن‌باز از گوگل است که به توسعه‌دهندگان جاوا اجازه می‌دهد کانتینرها را با استفاده از ابزارهای جاوایی که می‌شناسند، بسازند. Jib یک سازنده‌ی سریع و ساده‌ی تصویر کانتینر است که تمام مراحل بسته‌بندی برنامه‌ی شما در یک تصویر کانتینر را مدیریت می‌کند. نیازی به نوشتن Dockerfile یا نصب Docker ندارد و مستقیماً با Maven و Gradle ادغام می‌شود.

در فایل pom.xml به پایین اسکرول کنید و بخش Build را به‌روزرسانی کنید تا افزونه Jib را نیز شامل شود. بخش build پس از تکمیل باید مطابق با موارد زیر باشد.

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>

اگر در مورد تغییر فایل ساخت از شما سوال شد، Always را انتخاب کنید.

447a90338f51931f.png

تولید مانیفست

Skaffold ابزارهای یکپارچه‌ای را برای ساده‌سازی توسعه کانتینر ارائه می‌دهد. در این مرحله، شما skaffold را مقداردهی اولیه خواهید کرد که به طور خودکار فایل‌های YAML پایه Kubernetes را ایجاد می‌کند. این فرآیند سعی می‌کند دایرکتوری‌هایی را با تعاریف تصویر کانتینر، مانند یک Dockerfile، شناسایی کند و سپس برای هر کدام یک فایل استقرار و مانیفست سرویس ایجاد کند.

برای شروع فرآیند، دستور زیر را اجرا کنید.

  1. دستور زیر را در ترمینال اجرا کنید
skaffold init --generate-manifests
  1. وقتی از شما خواسته شد:
  • از فلش‌ها برای انتقال مکان‌نما به Jib Maven Plugin استفاده کنید.
  • برای انتخاب گزینه مورد نظر، کلید فاصله (spacebar) را فشار دهید.
  • برای ادامه، اینتر را فشار دهید
  1. برای پورت، عدد ۸۰۸۰ را وارد کنید.
  2. برای ذخیره تنظیمات، y را وارد کنید.

دو فایل به فضای کاری اضافه می‌شوند، یعنی skaffold.yaml و deployment.yaml

نام برنامه را به‌روزرسانی کنید

مقادیر پیش‌فرض موجود در پیکربندی در حال حاضر با نام برنامه شما مطابقت ندارند. فایل‌ها را به‌روزرسانی کنید تا به جای مقادیر پیش‌فرض، به نام برنامه شما ارجاع دهند.

  1. تغییر ورودی‌ها در پیکربندی Skaffold
  • skaffold.yaml را باز کنید.
  • نام تصویری که در حال حاضر به عنوان pom-xml-image تنظیم شده است را انتخاب کنید.
  • کلیک راست کرده و گزینه Change All Occurrences را انتخاب کنید.
  • نام جدید را به صورت demo-app تایپ کنید.
  1. تغییر ورودی‌ها در پیکربندی Kubernetes
  • فایل deployment.yaml را باز کنید
  • نام تصویری که در حال حاضر به عنوان pom-xml-image تنظیم شده است را انتخاب کنید.
  • کلیک راست کرده و گزینه Change All Occurrences را انتخاب کنید.
  • نام جدید را به صورت demo-app تایپ کنید.

همگام‌سازی سریع را فعال کنید

برای تسهیل یک تجربه بارگذاری مجدد داغ و بهینه، از ویژگی همگام‌سازی ارائه شده توسط Jib استفاده خواهید کرد. در این مرحله، Skaffold را برای استفاده از این ویژگی در فرآیند ساخت پیکربندی خواهید کرد.

توجه داشته باشید که پروفایل "sync" که در پیکربندی skaffold پیکربندی می‌کنید، از پروفایل "sync" Spring که در مرحله قبل پیکربندی کرده‌اید، استفاده می‌کند، جایی که پشتیبانی از spring-dev-tools را فعال کرده‌اید.

  1. پیکربندی 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/java: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);
    }
}

۴. گام برداشتن در مسیر توسعه

در این بخش، چند مرحله را با استفاده از افزونه Cloud Code طی خواهید کرد تا فرآیندهای اولیه را بیاموزید و پیکربندی و راه‌اندازی برنامه اولیه خود را تأیید کنید.

Cloud Code با skaffold ادغام می‌شود تا فرآیند توسعه شما را ساده‌تر کند. وقتی در مراحل بعدی در GKE مستقر می‌شوید، Cloud Code و Skaffold به طور خودکار تصویر کانتینر شما را می‌سازند، آن را به یک رجیستری کانتینر منتقل می‌کنند و سپس برنامه شما را در GKE مستقر می‌کنند. این اتفاق در پشت صحنه رخ می‌دهد و جزئیات را از جریان توسعه‌دهنده جدا می‌کند. Cloud Code همچنین با ارائه قابلیت‌های اشکال‌زدایی سنتی و همگام‌سازی سریع به توسعه مبتنی بر کانتینر، فرآیند توسعه شما را بهبود می‌بخشد.

استقرار در Kubernetes

  1. در پنل پایین ویرایشگر Cloud Shell، گزینه Cloud Code  را انتخاب کنید.

fdc797a769040839.png

  1. در پنلی که در بالا ظاهر می‌شود، گزینه Debug on Kubernetes را انتخاب کنید. در صورت درخواست، برای استفاده از زمینه فعلی Kubernetes، گزینه Yes را انتخاب کنید.

cfce0d11ef307087.png

  1. اولین باری که دستور را اجرا می‌کنید، پیامی در بالای صفحه ظاهر می‌شود که از شما می‌پرسد آیا می‌خواهید از چارچوب فعلی Kubernetes استفاده کنید یا خیر. برای پذیرش و استفاده از چارچوب فعلی، «بله» را انتخاب کنید.

۸۱۷ee33b5b412ff8.png

  1. در مرحله بعد، پیامی نمایش داده می‌شود که از شما می‌پرسد از کدام رجیستری کانتینر استفاده کنید. برای پذیرش مقدار پیش‌فرض ارائه شده، Enter را فشار دهید.

eb4469aed97a25f6.png

  1. برای مشاهده پیشرفت و اعلان‌ها، برگه خروجی را در پنل پایین انتخاب کنید.

f95b620569ba96c5.png

  1. برای مشاهده جزئیات بیشتر و گزارش‌های زنده از کانتینرها، در منوی کشویی سمت راست، گزینه "Kubernetes: Run/Debug - Detailed" را انتخاب کنید.

94acdcdda6d2108.png

  1. با انتخاب "Kubernetes: Run/Debug" از منوی کشویی، به نمای ساده‌شده برگردید.
  2. وقتی ساخت و آزمایش‌ها انجام شد، در برگه خروجی نوشته می‌شود: Resource deployment/demo-app status completed successfully و یک آدرس اینترنتی فهرست شده است: "URL ارسال شده از سرویس نسخه آزمایشی: http://localhost:8080"
  3. در ترمینال Cloud Code، نشانگر ماوس را روی URL موجود در خروجی (http://localhost:8080) نگه دارید و سپس در ابزار نمایش داده شده، گزینه Open Web Preview را انتخاب کنید.

پاسخ این خواهد بود:

Hello from your local environment!

استفاده از نقاط شکست

  1. برنامه HelloController.java واقع در /src/main/java/com/example/springboot/HelloController.java را باز کنید.
  2. عبارت return مربوط به مسیر ریشه را که return String.format("Hello from your %s environment!", target); را می‌خواند، پیدا کنید.
  3. با کلیک کردن روی فضای خالی سمت چپ شماره خط، یک نقطه توقف به آن خط اضافه کنید. یک نشانگر قرمز نشان داده می‌شود که نشان می‌دهد نقطه توقف تنظیم شده است.
  4. مرورگر خود را مجدداً بارگذاری کنید و توجه داشته باشید که اشکال‌زدا فرآیند را در نقطه توقف متوقف می‌کند و به شما امکان می‌دهد وضعیت متغیر sand برنامه‌ای را که از راه دور در GKE اجرا می‌شود، بررسی کنید.
  5. در بخش متغیرها کلیک کنید تا متغیر "Target" را پیدا کنید.
  6. مقدار فعلی را به عنوان "محلی" مشاهده کنید
  7. روی متغیر «target» دوبار کلیک کنید و در پنجره باز شده، مقدار آن را به چیزی متفاوت مانند «Cloud» تغییر دهید.
  8. روی دکمه ادامه در پنل کنترل اشکال‌زدایی کلیک کنید
  9. پاسخ را در مرورگر خود بررسی کنید که اکنون مقدار به‌روزرسانی‌شده‌ای را که وارد کرده‌اید نشان می‌دهد.

بارگیری مجدد داغ

  1. عبارت را طوری تغییر دهید که مقدار متفاوتی مانند "سلام از %s کد" را برگرداند.
  2. فایل به طور خودکار ذخیره و در کانتینرهای راه دور در GKE همگام‌سازی می‌شود.
  3. برای مشاهده نتایج به‌روزرسانی‌شده، مرورگر خود را به‌روزرسانی کنید.
  4. با کلیک روی مربع قرمز در نوار ابزار اشکال‌زدایی، جلسه اشکال‌زدایی را متوقف کنید. a13d42d726213e6c.png

۵. توسعه یک سرویس ساده CRUD Rest

در این مرحله، برنامه شما به طور کامل برای توسعه کانتینری پیکربندی شده است و شما گردش کار اولیه توسعه را با Cloud Code طی کرده‌اید. در بخش‌های بعدی، با اضافه کردن نقاط پایانی سرویس rest که به یک پایگاه داده مدیریت شده در Google Cloud متصل می‌شوند، آموخته‌های خود را تمرین می‌کنید.

پیکربندی وابستگی‌ها

کد برنامه از یک پایگاه داده برای ذخیره داده‌های سرویس باقی‌مانده استفاده می‌کند. با اضافه کردن موارد زیر در pom.xl، از در دسترس بودن وابستگی‌ها اطمینان حاصل کنید.

  1. فایل 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>

سرویس استراحت را کدگذاری کنید

نقل قول.java

فایلی به نام Quote.java در مسیر /src/main/java/com/example/springboot/ ایجاد کنید و کد زیر را در آن کپی کنید. این فایل، مدل Entity را برای شیء Quote مورد استفاده در برنامه تعریف می‌کند.

package com.example.springboot;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.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 برای ذخیره داده‌ها استفاده می‌کند. این کلاس از رابط JPARepository در Spring ارث‌بری می‌کند و امکان ایجاد کد سفارشی را فراهم می‌کند. در این کد، شما یک متد سفارشی 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) {
        try {
            quoteRepository.deleteById(id);
            return new ResponseEntity<>(HttpStatus.NO_CONTENT);
        } catch (RuntimeException e) {
            System.out.println(e.getMessage());
            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }    
}

اضافه کردن تنظیمات پایگاه داده

برنامه.yaml

پیکربندی مربوط به پایگاه داده backend که توسط سرویس قابل دسترسی است را اضافه کنید. فایلی به نام application.yaml را در مسیر src/main/resources ویرایش کنید (یا در صورت وجود نداشتن، آن را ایجاد کنید) و یک پیکربندی Spring پارامتری برای backend اضافه کنید.

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

اضافه کردن انتقال پایگاه داده

یک پوشه در مسیر src/main/resources/db/migration/ ایجاد کنید.

یک فایل 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 ذخیره می‌شود.

فایل deployment.yaml خود را با محتویات زیر به‌روزرسانی کنید.

استقرار.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

استقرار و اعتبارسنجی برنامه

  1. در پنل پایین ویرایشگر Cloud Shell، گزینه Cloud Code و سپس Debug on Kubernetes را در بالای صفحه انتخاب کنید.
  2. وقتی ساخت و آزمایش‌ها انجام شد، در برگه خروجی نوشته می‌شود: Resource deployment/demo-app status completed successfully و یک آدرس اینترنتی فهرست شده است: "URL ارسال شده از سرویس نسخه آزمایشی: http://localhost:8080"
  3. مشاهده نقل قول های تصادفی

از ترمینال cloudshell، دستور زیر را چندین بار در مقابل نقطه پایانی نقل قول تصادفی اجرا کنید. مشاهده کنید که فراخوانی مکرر، نقل قول‌های متفاوتی را برمی‌گرداند.

curl -v 127.0.0.1:8080/random-quote
  1. اضافه کردن نقل قول

با استفاده از دستور زیر، یک نقل قول جدید با شناسه ۶ ایجاد کنید و مشاهده کنید که درخواست چگونه بازتاب می‌شود.

curl -v -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 127.0.0.1:8080/quotes
  1. حذف نقل قول

حالا نقل قولی که با متد delete اضافه کردید را حذف کنید و کد پاسخ HTTP/1.1 204 را مشاهده کنید.

curl -v -X DELETE 127.0.0.1:8080/quotes/6
  1. خطای سرور

با اجرای مجدد آخرین درخواست پس از حذف ورودی، با حالت خطا مواجه شوید.

curl -v -X DELETE 127.0.0.1:8080/quotes/6

توجه داشته باشید که پاسخ، HTTP:500 Internal Server Error را برمی‌گرداند.

اشکال‌زدایی برنامه

در بخش قبلی، هنگام تلاش برای حذف ورودی‌ای که در پایگاه داده نبود، با یک خطا در برنامه مواجه شدید. در این بخش، یک نقطه توقف برای یافتن مشکل تعیین خواهید کرد. خطا در عملیات DELETE رخ داده است، بنابراین با کلاس QuoteController کار خواهید کرد.

  1. فایل src.main.java.com.example.springboot.QuoteController.java را باز کنید.
  2. متد deleteQuote() را پیدا کنید.
  3. خطی را که در آن یک آیتم از پایگاه داده حذف می‌شود، پیدا کنید: quoteRepository.deleteById(id);
  4. با کلیک کردن روی فضای خالی سمت چپ شماره خط، یک نقطه توقف روی آن خط تعیین کنید.
  5. یک نشانگر قرمز ظاهر می‌شود که نشان می‌دهد نقطه شکست تنظیم شده است.
  6. دستور delete را دوباره اجرا کنید
curl -v -X DELETE 127.0.0.1:8080/quotes/6
  1. با کلیک روی نماد در ستون سمت چپ، به نمای اشکال‌زدایی برگردید.
  2. به خط اشکال‌زدایی که در کلاس QuoteController متوقف شده است، توجه کنید.
  3. در دیباگر، step over کلیک کنید b814d39b2e5f3d9e.png و مشاهده کنید که یک استثنا ایجاد می‌شود
  4. مشاهده می‌کنید که یک RuntimeException was caught. این خطا، خطای داخلی سرور 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

کد را به‌روزرسانی کنید

کد نادرست است و بلوک استثنا باید طوری بازنویسی شود که استثنای EmptyResultDataAccessException را دریافت کند و کد وضعیت HTTP 404 not found را برگرداند.

خطا را اصلاح کنید.

  1. در حالی که جلسه اشکال‌زدایی هنوز در حال اجرا است، با فشردن دکمه «ادامه» در پنل کنترل اشکال‌زدایی، درخواست را تکمیل کنید.
  2. سپس بلوک زیر را به کد اضافه کنید:
       } catch (EmptyResultDataAccessException e){
            return new ResponseEntity<HttpStatus>(HttpStatus.NOT_FOUND);
        }

روش باید به شکل زیر باشد

    public ResponseEntity<HttpStatus> deleteQuote(@PathVariable("id") Integer id) {
        try {
            quoteRepository.deleteById(id);
            return new ResponseEntity<>(HttpStatus.NO_CONTENT);
        } catch(EmptyResultDataAccessException e){
            return new ResponseEntity<HttpStatus>(HttpStatus.NOT_FOUND);
        } catch (RuntimeException e) {
            System.out.println(e.getMessage());
            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
  1. دستور حذف را دوباره اجرا کنید
curl -v -X DELETE 127.0.0.1:8080/quotes/6
  1. دیباگر را بررسی کنید و مشاهده کنید که خطای EmptyResultDataAccessException رخ می‌دهد و خطای 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
  1. با کلیک روی مربع قرمز در نوار ابزار اشکال‌زدایی، جلسه اشکال‌زدایی را متوقف کنید. a13d42d726213e6c.png

۶. پاکسازی

تبریک! در این آزمایش شما یک برنامه جاوا جدید را از ابتدا ایجاد و آن را برای کار مؤثر با کانتینرها پیکربندی کرده‌اید. سپس برنامه خود را طبق همان جریان توسعه‌دهنده‌ای که در پشته‌های برنامه سنتی یافت می‌شود، در یک خوشه GKE از راه دور مستقر و اشکال‌زدایی کرده‌اید.

برای تمیز کردن پس از اتمام آزمایشگاه:

  1. فایل‌های استفاده شده در آزمایشگاه را حذف کنید
cd ~ && rm -rf container-developer-workshop
  1. پروژه را حذف کنید تا تمام زیرساخت‌ها و منابع مرتبط حذف شوند.