1. 總覽
本研究室介紹了專為在容器化環境中開發 Java 應用程式的軟體工程師所設計的功能和功能,協助他們簡化開發工作流程。一般的容器開發作業會要求使用者瞭解容器和容器建構程序的詳細資料。此外,開發人員通常必須中斷流程,從 IDE 中移出 IDE,才能在遠端環境中對應用程式進行測試及偵錯。有了本教學課程中提及的工具和技術,開發人員不必離開 IDE,就能有效地使用容器化應用程式。
學習目標
在本研究室中,您將瞭解在 GCP 中使用容器進行開發的方法,包括:
- 設定和需求
- 建立新的 Java 範例應用程式
- 逐步完成開發程序
- 開發簡易的 CRUD 靜態服務
- 清除所用資源
2. 設定和需求
自修環境設定
- 登入 Google Cloud 控制台,建立新專案或重複使用現有專案。如果您還沒有 Gmail 或 Google Workspace 帳戶,請先建立帳戶。
- 「專案名稱」是這項專案參與者的顯示名稱。這是 Google API 不使用的字元字串,您可以隨時更新。
- 所有 Google Cloud 專案的專案 ID 均不得重複,且設定後即無法變更。Cloud 控制台會自動產生一個不重複的字串。但通常是在乎它何在在大部分的程式碼研究室中,您必須參照專案 ID (通常稱為
PROJECT_ID
),因此如果您不喜歡的話,請隨機產生一個,或者,您也可以自行嘗試看看是否可用。是「凍結」建立專案後 - 還有第三個值,也就是部分 API 使用的專案編號。如要進一步瞭解這三個值,請參閱說明文件。
- 接下來,您需要在 Cloud 控制台中啟用計費功能,才能使用 Cloud 資源/API。執行這個程式碼研究室並不會產生任何費用,如果有的話。如要關閉資源,以免產生本教學課程結束後產生的費用,請按照任「清除所用資源」操作請參閱本程式碼研究室結尾處的操作說明。Google Cloud 的新使用者符合 $300 美元免費試用計畫的資格。
啟動 Cloud Shell 編輯器
本研究室專為與 Google Cloud Shell 編輯器搭配使用而設計和測試。如要存取編輯器
- 前往 https://console.cloud.google.com 存取您的 Google 專案。
- 按一下右上角的「Cloud Shell 編輯器」圖示
- 視窗底部隨即會開啟新的窗格
- 點選「開啟編輯器」按鈕
- 編輯器隨即開啟,右側會顯示多層檢視,中央區則顯示編輯者
- 畫面底部應會顯示終端機窗格
- 如果終端機「未」開啟,使用 `ctrl+` 的按鍵組合開啟新的終端機視窗
設定 gcloud
在 Cloud Shell 中設定專案 ID 和您要部署應用程式的區域。將其儲存為 PROJECT_ID
和 REGION
變數。
export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
取得原始碼
本研究室的原始碼位於 GitHub 的 GoogleCloudPlatform 中的 container-developer-workshop。請使用下列指令複製資料夾,然後變更至目錄中。
git clone https://github.com/GoogleCloudPlatform/container-developer-workshop.git
cd container-developer-workshop/labs/spring-boot
佈建本研究室中使用的基礎架構
在本研究室中,您會將程式碼部署至 GKE 並存取 CloudSql 資料庫中儲存的資料。下列設定指令碼可協助您完成這個基礎架構的設定。佈建程序需要 10 分鐘以上。設定處理期間,您可以繼續進行後續步驟。
./setup.sh
3. 建立新的 Java 範例應用程式
在本節中,您將使用 Spring.io 提供的範例應用程式,從頭開始建立新的 Java Spring Boot 應用程式
複製範例應用程式
- 建立範例應用程式
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
- 將應用程式解壓縮
unzip sample-app.zip -d sample-app
- 切換至 sample-app 目錄,並在 Cloud Shell IDE 工作區中開啟該資料夾
cd sample-app && cloudshell workspace .
新增 Spring-boot-devtools 和吉布
如要啟用 Spring Boot DevTools,請在編輯器中的 Explorer 找出並開啟 pom.xml。接著,將下列程式碼貼入說明行的後方,該行內容為 <description>Spring Boot 示範專案</description>
- 在 pom.xml 中新增 Spring-boot-devtools
開啟專案根目錄中的 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>
- 在 pom.xml 中啟用 jib-maven-plugin
Jib 是 Google 提供的開放原始碼 Java 容器化工具,可讓 Java 開發人員使用熟悉的 Java 工具建構容器。Jib 是一款快速簡單的容器映像檔建構工具,可處理將應用程式封裝至容器映像檔的所有步驟。這項服務已直接整合至 Maven 和 Gradle,因此您無須編寫 Dockerfile 或安裝 docker。
在 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>
如果系統提示您建構檔案變更,請選擇 Always
。
產生資訊清單
Skaffold 提供整合式工具,可簡化容器開發程序。在這個步驟中,您將初始化 skaffold,進而自動建立基礎 Kubernetes YAML 檔案。這項程序會嘗試找出含有容器映像檔定義的目錄 (例如 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「sync」您在上一個步驟設定的設定檔,已啟用 Spring-dev-tools 的支援功能。
- 更新 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
新增預設路徑
在 /src/main/java/com/example/springboot/ 建立名為 HelloController.java 的檔案
將下列內容貼入檔案,建立預設的 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 對容器型開發提供傳統的偵錯和熱同步功能,可改善您的開發程序。
部署到 Kubernetes
- 在 Cloud Shell 編輯器底部的窗格中,選取「Cloud Code」 圖示 。
- 在頂端的面板中,選取 [在 Kubernetes 上偵錯]。如果出現提示,請選取「是」以使用目前的 Kubernetes 結構定義。
- 首次執行命令時,畫面頂端會顯示提示,詢問您是否要使用目前的 Kubernetes 環境。請選取「是」。可接受並使用目前的內容。
- 接著系統會顯示提示,詢問要使用哪個 Container Registry。按下 Enter 鍵即可接受提供的預設值
- 選取下方窗格中的「Output」分頁標籤,即可查看進度和通知
- 選取「Kubernetes: Run/Debug - 詳細」按一下管道下拉式選單中的右側,即可查看其他詳細資料和從容器串流的即時記錄檔
- 選取「Kubernetes:執行/偵錯」即可返回簡化的檢視畫面
- 建構和測試完成後,「Output」(輸出) 分頁標籤會顯示
Resource deployment/demo-app status completed successfully
,以及一個網址:「Forwarded URL from service demo-app: http://localhost:8080」 - 在 Cloud Code 終端機中,將滑鼠遊標懸停在輸出 (http://localhost:8080) 的網址上,然後在顯示的工具提示中選取「Open Web Preview」。
回應將為:
Hello from your local environment!
善用中斷點
- 開啟位於 /src/main/java/com/example/springboot/HelloController.java 的 HelloController.java 應用程式
- 找出讀取
return String.format("Hello from your %s environment!", target);
的根路徑傳回陳述式 - 按一下行號左側的空白處,為該行新增中斷點。系統會顯示紅色指標,說明已設定中斷點
- 重新載入瀏覽器並留意偵錯工具,會在中斷點停止程序,並針對在 GKE 中遠端執行的應用程式調查變數和狀態
- 按一下「變數」部分,直到找到「目標」為止變數。
- 觀察目前的值為「local」
- 按兩下變數名稱「target」在彈出式視窗中,將值變更為其他值,例如「Cloud」
- 按一下偵錯控制台中的 [繼續] 按鈕
- 請在瀏覽器中查看回應,以便顯示剛才輸入的更新值。
熱重新載入
- 變更陳述式以傳回不同的值,例如「Hello from %s Code」
- 檔案會自動儲存並同步到 GKE 中的遠端容器
- 重新整理瀏覽器即可查看更新後的結果。
- 按一下偵錯工具列中的紅色方塊,即可停止偵錯工作階段
5. 開發簡易的 CRUD 靜態服務
此時您的應用程式已全面完成容器化開發作業,您也完成了 Cloud Code 的基本開發工作流程。在接下來的各節中,您將學到以下內容,新增連結至 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>
為其餘服務編寫程式碼
Quote.java
在 /src/main/java/com/example/springboot/ 中建立名為 quote.java 的檔案,然後複製下方的程式碼。這定義了應用程式中使用 quote 物件的 Entity 模型。
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
在 src/main/java/com/example/springboot 上建立名為 QuoteRepository.java 的檔案,並將下列程式碼複製
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
類別會提供這項功能。
在 src/main/java/com/example/springboot 上建立名為 quoteController.java 的檔案,然後在下列內容中複製內容
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);
}
}
}
新增資料庫設定
application.yaml
為服務存取的後端資料庫新增設定。編輯 (或建立) src/main/resources
底下的 application.yaml
檔案,然後為後端新增參數化 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
新增資料庫遷移作業
在 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
檔案後,應用程式就能連線至 Cloud SQL 執行個體。
- TARGET - 設定變數以指出執行應用程式的環境
- SPRING_PROFILES_ACTIVE:顯示使用中的 Spring 設定檔 (設為
cloud-dev
) - DB_HOST - 資料庫的私人 IP (在建立資料庫執行個體時都已記下,或在 Google Cloud 控制台的導覽選單中按一下
SQL
)。請變更值! - DB_USER 和 DB_PASS - 在 CloudSQL 執行個體設定中,會以密鑰的形式儲存在 GCP 中
使用以下內容更新 deployment.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
部署及驗證應用程式
- 在 Cloud Shell 編輯器底部的窗格中,依序選取「Cloud Code」和畫面頂端的「Debug on Kubernetes」。
- 建構和測試完成後,「Output」(輸出) 分頁標籤會顯示
Resource deployment/demo-app status completed successfully
,以及一個網址:「Forwarded URL from service demo-app: http://localhost:8080」 - 檢視隨機引述
在 Cloud Shell 終端機中,針對隨機報價端點多次執行下列指令。觀察重覆呼叫並傳回不同的報價
curl -v 127.0.0.1:8080/random-quote
- 新增報價
使用下方列出的指令以 id=6 建立新報價,然後觀察系統傳回的要求
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
- 刪除報價
現在,刪除您剛才使用刪除方法新增的引用,然後觀察 HTTP/1.1 204
回應代碼。
curl -v -X DELETE 127.0.0.1:8080/quotes/6
- 伺服器錯誤
項目刪除後,再次執行上一個要求,藉此顯示錯誤狀態
curl -v -X DELETE 127.0.0.1:8080/quotes/6
請注意,回應會傳回 HTTP:500 Internal Server Error
。
對應用程式進行偵錯
在上一節中,當您嘗試刪除資料庫中沒有的項目時,在應用程式中發現錯誤狀態。在本節中,您將設定中斷點以找出問題。DELETE 作業發生錯誤,因此您必須使用 quoteController 類別。
- 開啟 src.main.java.com.example.springboot.quoteController.java
- 找出
deleteQuote()
方法 - 找到從資料庫中刪除商品的一行:
quoteRepository.deleteById(id);
- 按一下行號左側的空白空間,設定該行的中斷點。
- 出現紅色指標,表示已設定中斷點
- 再次執行
delete
指令
curl -v -X DELETE 127.0.0.1:8080/quotes/6
- 按一下左欄中的圖示,切換回偵錯檢視畫面
- 觀察 QuoteController 類別中的偵錯行。
- 在偵錯工具中,按一下
step over
圖示 ,然後觀察是否擲回例外狀況 - 觀察結果是非常籠統的
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 找不到狀態碼。
修正錯誤。
- 偵錯工作階段仍在執行時,按下「continue」以完成要求按鈕,就能前往偵錯控制台
- 接下來,請在程式碼中加入以下區塊:
} 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); } }
- 重新執行刪除指令
curl -v -X DELETE 127.0.0.1:8080/quotes/6
- 逐步執行偵錯工具,觀察系統擷取到
EmptyResultDataAccessException
,同時將 HTTP 404 找不到傳回呼叫端。
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 叢集,並進行偵錯。
如要在完成研究室後清除所用資源,請按照下列步驟操作:
- 刪除研究室中使用的檔案
cd ~ && rm -rf container-developer-workshop
- 刪除專案,移除所有相關的基礎架構和資源