Phát triển innerLoop bằng Java – SpringBoot

1. Tổng quan

Phòng thí nghiệm này trình bày các tính năng và khả năng được thiết kế để hợp lý hoá quy trình phát triển dành cho các kỹ sư phần mềm được giao nhiệm vụ phát triển các ứng dụng Java trong môi trường vùng chứa. Thông thường, quá trình phát triển vùng chứa yêu cầu người dùng hiểu thông tin chi tiết về vùng chứa và quy trình xây dựng vùng chứa. Ngoài ra, các nhà phát triển thường phải ngắt luồng của mình, rời khỏi IDE để kiểm thử và gỡ lỗi ứng dụng trong môi trường từ xa. Với các công cụ và công nghệ được đề cập trong hướng dẫn này, nhà phát triển có thể làm việc hiệu quả với các ứng dụng trong vùng chứa mà không cần rời khỏi IDE của họ.

Kiến thức bạn sẽ học được

Trong phòng thí nghiệm này, bạn sẽ tìm hiểu các phương pháp phát triển bằng vùng chứa trong GCP, bao gồm:

  • Thiết lập và yêu cầu
  • Tạo ứng dụng khởi động Java mới
  • Tìm hiểu về quá trình phát triển
  • Phát triển một dịch vụ nghỉ ngơi CRUD đơn giản
  • Dọn dẹp

2. Thiết lập và yêu cầu

Thiết lập môi trường theo tiến độ riêng

  1. Đăng nhập vào Google Cloud Console rồi tạo dự án mới hoặc sử dụng lại dự án hiện có. Nếu chưa có tài khoản Gmail hoặc Google Workspace, bạn phải tạo một tài khoản.

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • Tên dự án là tên hiển thị của những người tham gia dự án này. Đây là một chuỗi ký tự không được API của Google sử dụng và bạn có thể cập nhật chuỗi này bất cứ lúc nào.
  • Mã dự án phải là duy nhất trong tất cả các dự án Google Cloud và không thể thay đổi (bạn không thể thay đổi mã này sau khi đặt). Cloud Console sẽ tự động tạo một chuỗi duy nhất; thường bạn không quan tâm đến sản phẩm đó là gì. Trong hầu hết các lớp học lập trình, bạn sẽ cần tham chiếu đến Mã dự án (và mã này thường được xác định là PROJECT_ID). Vì vậy, nếu không thích, bạn có thể tạo một mã ngẫu nhiên khác hoặc bạn có thể thử mã của riêng mình để xem có mã này chưa. Sau đó, video sẽ được "đóng băng" sau khi tạo dự án.
  • Có giá trị thứ ba là Project Number (Số dự án) mà một số API sử dụng. Tìm hiểu thêm về cả ba giá trị này trong tài liệu này.
  1. Tiếp theo, bạn sẽ cần bật tính năng thanh toán trong Cloud Console để sử dụng tài nguyên/API trên Cloud. Việc chạy qua lớp học lập trình này sẽ không tốn nhiều chi phí. Để tắt các tài nguyên để bạn không phải chịu thanh toán ngoài hướng dẫn này, hãy làm theo mọi thao tác "dọn dẹp" hướng dẫn ở cuối lớp học lập trình. Người dùng mới của Google Cloud đủ điều kiện tham gia chương trình Dùng thử miễn phí 300 USD.

Khởi động Cloudshell Editor

Phòng thí nghiệm này được thiết kế và thử nghiệm để sử dụng với Google Cloud Shell Editor. Để truy cập vào trình chỉnh sửa,

  1. truy cập vào dự án google của bạn tại https://console.cloud.google.com.
  2. Ở góc trên cùng bên phải, hãy nhấp vào biểu tượng trình chỉnh sửa vỏ đám mây

8560cc8d45e8c112.pngS

  1. Một ngăn mới sẽ mở ra ở cuối cửa sổ
  2. Nhấp vào nút Open Editor (Mở trình chỉnh sửa)

9e504cb98a6a8005.pngS

  1. Trình chỉnh sửa sẽ mở ra cùng với một trình khám phá ở bên phải và trình chỉnh sửa ở khu vực trung tâm
  2. Ngăn thiết bị đầu cuối cũng sẽ xuất hiện ở cuối màn hình
  3. Nếu cửa sổ dòng lệnh KHÔNG mở, hãy sử dụng tổ hợp phím "ctrl+" để mở cửa sổ dòng lệnh mới

Thiết lập gcloud

Trong Cloud Shell, hãy đặt mã dự án và khu vực mà bạn muốn triển khai ứng dụng. Hãy lưu các biến này dưới dạng biến PROJECT_IDREGION.

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

Lấy mã nguồn

Mã nguồn cho phòng thí nghiệm này nằm tại Container-developer-workshop trong GoogleCloudPlatform trên GitHub. Hãy sao chép tệp đó bằng lệnh bên dưới rồi thay đổi vào thư mục.

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

Cung cấp cơ sở hạ tầng dùng trong phòng thí nghiệm này

Trong phòng thí nghiệm này, bạn sẽ triển khai mã cho GKE và truy cập vào dữ liệu được lưu trữ trong cơ sở dữ liệu CloudSql. Tập lệnh thiết lập dưới đây chuẩn bị cơ sở hạ tầng này cho bạn. Quá trình cấp phép sẽ mất hơn 10 phút. Bạn có thể tiếp tục thực hiện một vài bước tiếp theo trong khi quá trình thiết lập đang diễn ra.

./setup.sh

3. Tạo ứng dụng khởi đầu Java mới

Trong phần này, bạn sẽ tạo một ứng dụng Java Spring Boot mới từ đầu bằng cách dùng một ứng dụng mẫu do spring.io cung cấp

Sao chép ứng dụng mẫu

  1. Tạo ứng dụng khởi đầu
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. Giải nén ứng dụng
unzip sample-app.zip -d sample-app
  1. Chuyển sang thư mục ứng dụng mẫu và mở thư mục đó trong không gian làm việc IDE Cloud Shell
cd sample-app && cloudshell workspace .

Thêm spring-boot-devtools & Kim chỉ nam

Để bật Công cụ cho nhà phát triển khởi động Spring, hãy tìm và mở tệp pom.xml qua trình khám phá trong trình chỉnh sửa của bạn. Tiếp theo, hãy dán mã sau vào sau dòng mô tả có nội dung <description> Demo project for Spring Boot</description> (Dự án minh hoạ cho Spring Boot</description>)

  1. Thêm spring-boot-devtools trong pom.xml

Mở tệp pom.xml trong thư mục gốc của dự án. Thêm cấu hình sau đây sau mục nhập 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. Bật jib-maven-plugin trong pom.xml

Jib là một công cụ chứa Java nguồn mở của Google cho phép nhà phát triển Java xây dựng vùng chứa bằng các công cụ Java mà họ biết. Jib là trình tạo hình ảnh vùng chứa nhanh và đơn giản, xử lý tất cả các bước đóng gói ứng dụng của bạn vào hình ảnh vùng chứa. Cấu hình này không yêu cầu bạn phải viết Dockerfile hoặc cài đặt docker, đồng thời được tích hợp trực tiếp vào Maven và Gradle.

Di chuyển xuống tệp pom.xml rồi cập nhật phần Build để thêm trình bổ trợ Jib. Phần bản dựng phải khớp với phần sau khi hoàn tất.

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>

Chọn Always nếu được nhắc về việc thay đổi tệp bản dựng.

447a90338f51931f.png.

Tạo tệp kê khai

Skaffold cung cấp các công cụ tích hợp để đơn giản hoá việc phát triển vùng chứa. Ở bước này, bạn sẽ khởi chạy skafprint, thao tác này sẽ tự động tạo các tệp YAML của Kubernetes cơ sở. Quá trình này sẽ cố gắng xác định các thư mục có định nghĩa hình ảnh vùng chứa, chẳng hạn như Dockerfile, sau đó tạo một tệp kê khai dịch vụ và triển khai cho mỗi thư mục.

Thực thi lệnh dưới đây để bắt đầu quy trình.

  1. Thực thi lệnh sau trong dòng lệnh
skaffold init --generate-manifests
  1. Khi được nhắc:
  • Sử dụng các mũi tên để di chuyển con trỏ đến Jib Maven Plugin
  • Nhấn phím cách để chọn tuỳ chọn.
  • Nhấn phím Enter để tiếp tục
  1. Nhập 8080 cho cổng
  2. Nhập y để lưu cấu hình

Hai tệp được thêm vào không gian làm việc viz, skaffold.yamldeployment.yaml

Cập nhật tên ứng dụng

Các giá trị mặc định có trong cấu hình hiện không khớp với tên ứng dụng của bạn. Cập nhật các tệp để tham chiếu tên ứng dụng của bạn thay vì các giá trị mặc định.

  1. Thay đổi các mục trong cấu hình Skaffold
  • Mở skaffold.yaml
  • Chọn tên hình ảnh hiện được đặt là pom-xml-image
  • Nhấp chuột phải rồi chọn Thay đổi tất cả lần xuất hiện
  • Nhập tên mới là demo-app
  1. Thay đổi các mục trong cấu hình Kubernetes
  • Mở tệp deployment.yaml
  • Chọn tên hình ảnh hiện được đặt là pom-xml-image
  • Nhấp chuột phải rồi chọn Thay đổi tất cả lần xuất hiện
  • Nhập tên mới là demo-app

Bật tính năng đồng bộ hoá nóng

Để tối ưu hoá trải nghiệm tải lại nóng, bạn sẽ dùng tính năng Đồng bộ hoá do Jib cung cấp. Ở bước này, bạn sẽ định cấu hình Skaffold để sử dụng tính năng đó trong quy trình xây dựng.

Lưu ý rằng thao tác "đồng bộ hoá" cấu hình mà bạn đang định cấu hình trong cấu hình skaffold tận dụng tính năng "đồng bộ hoá" Spring Hồ sơ bạn đã định cấu hình ở bước trước, trong đó bạn đã bật tính năng hỗ trợ cho spring-dev-tools.

  1. Cập nhật cấu hình skaffold

Trong tệp skaffold.yaml, hãy thay thế toàn bộ phần bản dựng của tệp bằng thông số kỹ thuật sau. Đừng thay đổi các phần khác của tệp.

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

Thêm tuyến đường mặc định

Tạo một tệp có tên HelloController.java tại /src/main/java/com/example/springboot/

Dán những nội dung sau vào tệp để tạo tuyến http mặc định

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. Tìm hiểu về quá trình phát triển

Trong phần này, bạn sẽ tìm hiểu một số bước sử dụng trình bổ trợ Mã đám mây để tìm hiểu các quy trình cơ bản, cũng như để xác thực cấu hình và chế độ thiết lập ứng dụng khởi đầu.

Cloud Code tích hợp với skaffold để đơn giản hoá quy trình phát triển của bạn. Khi bạn triển khai GKE ở các bước sau, Cloud Code và Skaffold sẽ tự động tạo hình ảnh vùng chứa của bạn, đẩy hình ảnh vùng chứa đó vào Container Registry, sau đó triển khai ứng dụng của bạn lên GKE. Quá trình này xảy ra trong nền, trừu tượng chi tiết khỏi quy trình của nhà phát triển. Cloud Code cũng giúp cải thiện quá trình phát triển bằng cách cung cấp các chức năng gỡ lỗi và đồng bộ hoá nóng truyền thống cho hoạt động phát triển dựa trên vùng chứa.

Triển khai cho Kubernetes

  1. Trong ngăn ở cuối Cloud Shell Editor, hãy chọn Cloud Code ‡

fdc797a769040839.png

  1. Trong bảng điều khiển xuất hiện ở trên cùng, hãy chọn Debug on Kubernetes. Nếu được nhắc, hãy chọn Có để sử dụng ngữ cảnh Kubernetes hiện tại.

cfce0d11ef307087.png

  1. Trong lần đầu tiên bạn chạy lệnh, một lời nhắc sẽ xuất hiện ở đầu màn hình và hỏi xem bạn có muốn sử dụng ngữ cảnh hiện tại của Kubernetes không, hãy chọn "Có" để chấp nhận và sử dụng ngữ cảnh hiện tại.

817ee33b5b412ff8.png.

  1. Tiếp theo, một lời nhắc sẽ xuất hiện hỏi xem bạn nên dùng sổ đăng ký vùng chứa nào. Nhấn phím Enter để chấp nhận giá trị mặc định được cung cấp

eb4469aed97a25f6.png

  1. Chọn thẻ Output (Kết quả) trong ngăn phía dưới để xem tiến trình và thông báo

f95b620569ba96c5.png

  1. Chọn "Kubernetes: Chạy/Gỡ lỗi – Chi tiết" trong trình đơn thả xuống ở bên phải của kênh để xem thêm thông tin chi tiết và nhật ký phát trực tiếp từ các vùng chứa

94acdcdda6d2108.pngS

  1. Quay lại chế độ xem đơn giản bằng cách chọn "Kubernetes: Chạy/Gỡ lỗi" trong trình đơn thả xuống
  2. Khi quá trình tạo và kiểm thử hoàn tất, thẻ Đầu ra sẽ cho biết: Resource deployment/demo-app status completed successfully, và một URL có trong danh sách: "Được chuyển tiếp URL từ ứng dụng minh hoạ dịch vụ: http://localhost:8080"
  3. Trong cửa sổ dòng lệnh Mã đám mây, hãy di chuột qua URL trong dữ liệu đầu ra (http://localhost:8080), sau đó chọn "Open Web Preview" (Xem trước trên web) hiện ra trong chú giải công cụ vừa xuất hiện.

Câu trả lời sẽ là:

Hello from your local environment!

Khai thác các điểm ngắt

  1. Mở ứng dụng HelloController.java tại /src/main/java/com/example/springboot/HelloController.java
  2. Tìm câu lệnh trả về cho đường dẫn gốc có nội dung return String.format("Hello from your %s environment!", target);
  3. Thêm điểm ngắt vào dòng đó bằng cách nhấp vào khoảng trống ở bên trái số dòng. Một chỉ báo màu đỏ sẽ xuất hiện để ghi chú điểm ngắt đã được đặt
  4. Tải lại trình duyệt và lưu ý rằng trình gỡ lỗi sẽ dừng quá trình này tại điểm ngắt và cho phép bạn điều tra trạng thái cát thay đổi của ứng dụng đang chạy từ xa trong GKE
  5. Nhấp vào mục biến cho đến khi bạn thấy mục "Mục tiêu" biến.
  6. Quan sát giá trị hiện tại dưới dạng "cục bộ"
  7. Nhấp đúp vào tên biến "mục tiêu" và trong cửa sổ bật lên, hãy thay đổi giá trị thành một giá trị khác, chẳng hạn như "Cloud"
  8. Nhấp vào nút Tiếp tục trong bảng điều khiển gỡ lỗi
  9. Xem lại phản hồi trong trình duyệt để hiện giá trị được cập nhật mà bạn vừa nhập.

Tải lại nhanh

  1. Thay đổi câu lệnh để trả về một giá trị khác, chẳng hạn như "Hello from %s Code" (Xin chào từ %s Code)
  2. Tệp được tự động lưu và đồng bộ hoá vào các vùng chứa từ xa trong GKE
  3. Làm mới trình duyệt của bạn để xem kết quả được cập nhật.
  4. Dừng phiên gỡ lỗi bằng cách nhấp vào hình vuông màu đỏ trên thanh công cụ gỡ lỗi a13d42d726213e6c.png

5. Phát triển một dịch vụ nghỉ ngơi CRUD đơn giản

Tại thời điểm này, ứng dụng của bạn đã được định cấu hình đầy đủ để phát triển theo vùng chứa và bạn đã tìm hiểu quy trình phát triển cơ bản bằng Cloud Code. Trong các phần sau, bạn sẽ thực hành những gì đã học được bằng cách thêm thiết bị đầu cuối cho dịch vụ nghỉ ngơi kết nối với một cơ sở dữ liệu được quản lý trong Google Cloud.

Định cấu hình phần phụ thuộc

Mã xử lý ứng dụng dùng cơ sở dữ liệu để lưu trữ dữ liệu dịch vụ còn lại. Đảm bảo các phần phụ thuộc có sẵn bằng cách thêm đoạn mã sau vào tệp pom.xl

  1. Mở tệp pom.xml rồi thêm nội dung sau vào mục phần phụ thuộc của cấu hình

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>

Viết mã cho dịch vụ còn lại

Quote.java

Tạo một tệp có tên Trích dẫn.java trong /src/main/java/com/example/springboot/ rồi sao chép vào mã bên dưới. Thao tác này xác định mô hình Thực thể cho đối tượng Trích dẫn được dùng trong ứng dụng.

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

Tạo một tệp có tên QuoteRepository.java tại src/main/java/com/example/springboot rồi sao chép vào đoạn mã sau

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();
}

Mã này sử dụng JPA để duy trì dữ liệu. Lớp này mở rộng giao diện Spring JPARepository và cho phép tạo mã tuỳ chỉnh. Trong mã, bạn đã thêm một phương thức tuỳ chỉnh findRandomQuote.

QuoteController.java

Để hiển thị điểm cuối của dịch vụ, lớp QuoteController sẽ cung cấp chức năng này.

Tạo một tệp có tên QuoteController.java tại src/main/java/com/example/springboot và sao chép vào nội dung sau

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);
        }
    }    
}

Thêm cấu hình cơ sở dữ liệu

application.yaml

Thêm cấu hình cho cơ sở dữ liệu phụ trợ mà dịch vụ truy cập. Chỉnh sửa (hoặc tạo nếu không có) tệp có tên là tệp application.yaml trong src/main/resources và thêm một cấu hình Spring có tham số cho phần phụ trợ.

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

Thêm quy trình di chuyển cơ sở dữ liệu

Tạo một thư mục tại src/main/resources/db/migration/

Tạo tệp SQL: V1__create_cites_table.sql

Dán các nội dung sau vào tệp

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');

Cấu hình Kerberos

Những nội dung bổ sung sau đây cho tệp deployment.yaml cho phép ứng dụng kết nối với các thực thể CloudSQL.

  • TARGET – định cấu hình biến để cho biết môi trường nơi ứng dụng được thực thi
  • SPbạn_Hồ sơ_HOẠT ĐỘNG – hiển thị hồ sơ Spring đang hoạt động, hồ sơ này sẽ được định cấu hình thành cloud-dev
  • DB_HOST – IP riêng tư của cơ sở dữ liệu, đã được ghi chú khi thực thể cơ sở dữ liệu được tạo hoặc bằng cách nhấp vào SQL trong Trình đơn điều hướng của Google Cloud Console – vui lòng thay đổi giá trị!
  • DB_USER và DB_PASS – như được đặt trong cấu hình phiên bản CloudSQL, được lưu trữ dưới dạng Khoá bí mật trong GCP

Hãy cập nhật triển khai.yaml của bạn theo nội dung bên dưới.

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 

Thay thế giá trị DB_HOST bằng địa chỉ của Cơ sở dữ liệu của bạn

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

Triển khai và xác thực ứng dụng

  1. Trong ngăn ở cuối Cloud Shell Editor, chọn Cloud Code rồi chọn Debug on Kubernetes ở đầu màn hình.
  2. Khi quá trình tạo và kiểm thử hoàn tất, thẻ Đầu ra sẽ cho biết: Resource deployment/demo-app status completed successfully, và một URL có trong danh sách: "Được chuyển tiếp URL từ ứng dụng minh hoạ dịch vụ: http://localhost:8080"
  3. Xem trích dẫn ngẫu nhiên

Trên ứng dụng Cloudshell Terminal, hãy chạy lệnh bên dưới nhiều lần đối với điểm cuối trích dẫn ngẫu nhiên. Quan sát cuộc gọi lặp lại và trả về các dấu ngoặc kép khác nhau

curl -v 127.0.0.1:8080/random-quote
  1. Thêm báo giá

Tạo một bản báo giá mới, với id=6 bằng cách sử dụng lệnh được liệt kê bên dưới và quan sát yêu cầu được lặp lại

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. Xoá bản báo giá

Bây giờ, hãy xoá bản báo giá bạn vừa thêm bằng phương thức xoá và quan sát mã phản hồi HTTP/1.1 204.

curl -v -X DELETE 127.0.0.1:8080/quotes/6
  1. Lỗi máy chủ

Trải nghiệm trạng thái lỗi bằng cách chạy lại yêu cầu gần đây nhất sau khi mục nhập đã bị xoá

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

Lưu ý rằng phản hồi trả về HTTP:500 Internal Server Error.

Gỡ lỗi ứng dụng

Ở phần trước, bạn đã phát hiện trạng thái lỗi trong ứng dụng khi cố gắng xoá một mục không có trong cơ sở dữ liệu. Trong phần này, bạn sẽ đặt một điểm ngắt để xác định vấn đề. Đã xảy ra lỗi trong thao tác DELETE, vì vậy, bạn sẽ làm việc với lớp QuoteController.

  1. Mở src.main.java.com.example.springboot.QuoteController.java
  2. Tìm phương thức deleteQuote()
  3. Tìm dòng xoá một mặt hàng khỏi cơ sở dữ liệu: quoteRepository.deleteById(id);
  4. Đặt điểm ngắt trên dòng đó bằng cách nhấp vào khoảng trống ở bên trái số dòng.
  5. Một chỉ báo màu đỏ sẽ xuất hiện cho biết điểm ngắt đã được đặt
  6. Chạy lại lệnh delete
curl -v -X DELETE 127.0.0.1:8080/quotes/6
  1. Chuyển về chế độ xem gỡ lỗi bằng cách nhấp vào biểu tượng ở cột bên trái
  2. Quan sát dòng gỡ lỗi bị dừng trong lớp QuoteController.
  3. Trong trình gỡ lỗi, hãy nhấp vào biểu tượng step over b814d39b2e5f3d9e.png và quan sát thấy một ngoại lệ được gửi
  4. Hãy lưu ý rằng một RuntimeException was caught. rất chung. Thao tác này sẽ trả về Lỗi máy chủ nội bộ HTTP 500 và lỗi này không lý tưởng.
   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

Cập nhật đoạn mã

Mã không chính xác và bạn nên tái cấu trúc khối ngoại lệ để phát hiện ngoại lệ EmptyResultDataAccessException và gửi lại mã trạng thái không tìm thấy HTTP 404.

Sửa lỗi.

  1. Khi phiên Gỡ lỗi vẫn đang chạy, hãy hoàn tất yêu cầu bằng cách nhấn "tiếp tục" trong bảng điều khiển gỡ lỗi.
  2. Tiếp theo, hãy thêm khối sau vào mã:
       } catch (EmptyResultDataAccessException e){
            return new ResponseEntity<HttpStatus>(HttpStatus.NOT_FOUND);
        }

Phương thức sẽ có dạng như sau

    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. Chạy lại lệnh xoá
curl -v -X DELETE 127.0.0.1:8080/quotes/6
  1. Thao tác trên trình gỡ lỗi và quan sát thấy EmptyResultDataAccessException bị phát hiện và phản hồi HTTP 404 Not found (Không tìm thấy) trả về phương thức gọi.
   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. Dừng phiên gỡ lỗi bằng cách nhấp vào hình vuông màu đỏ trên thanh công cụ gỡ lỗi a13d42d726213e6c.png

6. Dọn dẹp

Xin chúc mừng! Trong phòng thí nghiệm này, bạn đã tạo một ứng dụng Java mới từ đầu và định cấu hình để ứng dụng này hoạt động hiệu quả với vùng chứa. Sau đó, bạn đã triển khai và gỡ lỗi ứng dụng cho một cụm GKE từ xa theo cùng một quy trình dành cho nhà phát triển trong các ngăn xếp ứng dụng truyền thống.

Cách làm sạch sau khi hoàn thành phòng thí nghiệm:

  1. Xoá các tệp được sử dụng trong phòng thí nghiệm
cd ~ && rm -rf container-developer-workshop
  1. Xoá dự án để xoá tất cả cơ sở hạ tầng và tài nguyên có liên quan