1. Panoramica
Questo lab mostra funzionalità e capacità progettate per semplificare il flusso di lavoro di sviluppo per gli ingegneri software incaricati di sviluppare applicazioni Java in un ambiente containerizzato. Il tipico sviluppo di container richiede all'utente di comprendere i dettagli dei container e il processo di compilazione dei container. Inoltre, gli sviluppatori in genere devono interrompere il flusso di lavoro, uscire dall'IDE per testare ed eseguire il debug delle applicazioni in ambienti remoti. Con gli strumenti e le tecnologie menzionati in questo tutorial, gli sviluppatori possono lavorare in modo efficace con le applicazioni in contenitori senza uscire dall'IDE.
Cosa imparerai a fare
In questo lab imparerai i metodi per sviluppare con i container in Google Cloud, tra cui:
- Configurazione e requisiti
- Creazione di una nuova applicazione Java iniziale
- Esaminare il processo di sviluppo
- Sviluppo di un semplice servizio REST CRUD
- Esegui la pulizia
2. Configurazione e requisiti
Configurazione dell'ambiente autonomo
- Accedi alla console Google Cloud e crea un nuovo progetto o riutilizzane uno esistente. Se non hai ancora un account Gmail o Google Workspace, devi crearne uno.



- Il nome del progetto è il nome visualizzato per i partecipanti a questo progetto. È una stringa di caratteri non utilizzata dalle API di Google e puoi aggiornarla in qualsiasi momento.
- L'ID progetto deve essere univoco in tutti i progetti Google Cloud ed è immutabile (non può essere modificato dopo essere stato impostato). Cloud Console genera automaticamente una stringa univoca, di solito non ti interessa di cosa si tratta. Nella maggior parte dei codelab, devi fare riferimento all'ID progetto (che in genere è identificato come
PROJECT_ID), quindi, se non ti piace, generane un altro casuale oppure puoi provare il tuo e vedere se è disponibile. Viene "congelato" dopo la creazione del progetto. - Esiste un terzo valore, un numero di progetto, utilizzato da alcune API. Scopri di più su tutti e tre questi valori nella documentazione.
- Successivamente, devi abilitare la fatturazione in Cloud Console per utilizzare le risorse/API Cloud. L'esecuzione di questo codelab non dovrebbe costare molto, se non nulla. Per arrestare le risorse in modo da non incorrere in costi di fatturazione al termine di questo tutorial, segui le istruzioni di "pulizia" riportate alla fine del codelab. I nuovi utenti di Google Cloud possono beneficiare del programma prova senza costi di 300$.
Avvia l'editor di Cloud Shell
Questo lab è stato progettato e testato per l'utilizzo con l'editor di Google Cloud Shell. Per accedere all'editor:
- accedi al tuo progetto Google all'indirizzo https://console.cloud.google.com.
- Nell'angolo in alto a destra, fai clic sull'icona dell'editor Cloud Shell.

- Si aprirà un nuovo riquadro nella parte inferiore della finestra.
- Fai clic sul pulsante Apri editor.

- L'editor si aprirà con un explorer a destra e l'editor nell'area centrale
- Nella parte inferiore dello schermo dovrebbe essere disponibile anche un riquadro del terminale
- Se il terminale NON è aperto, utilizza la combinazione di tasti "Ctrl + `" per aprire una nuova finestra del terminale.
Configura gcloud
In Cloud Shell, imposta l'ID progetto e la regione in cui vuoi eseguire il deployment dell'applicazione. Salvali come variabili PROJECT_ID e REGION.
export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
Recupera il codice sorgente
Il codice sorgente di questo lab si trova in container-developer-workshop in GoogleCloudPlatform su GitHub. Clonalo con il comando riportato di seguito, quindi passa alla directory.
git clone https://github.com/GoogleCloudPlatform/container-developer-workshop.git
cd container-developer-workshop/labs/spring-boot
Esegui il provisioning dell'infrastruttura utilizzata in questo lab
In questo lab eseguirai il deployment del codice in GKE e accederai ai dati archiviati in un database Cloud SQL. Lo script di configurazione riportato di seguito prepara questa infrastruttura per te. La procedura di provisioning richiederà più di 10 minuti. Puoi continuare con i passaggi successivi mentre la configurazione è in corso.
./setup.sh
3. Creazione di una nuova applicazione Java iniziale
In questa sezione creerai una nuova applicazione Java Spring Boot da zero utilizzando un'applicazione di esempio fornita da spring.io
Clona l'applicazione di esempio
- Creare un'applicazione iniziale
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
- Decomprimi l'applicazione
unzip sample-app.zip -d sample-app
- Passa alla directory sample-app e apri la cartella nello spazio di lavoro dell'IDE Cloud Shell
cd sample-app && cloudshell workspace .
Aggiungi spring-boot-devtools e Jib
Per attivare Spring Boot DevTools, trova e apri il file pom.xml dall'explorer nell'editor. Poi incolla il seguente codice dopo la riga descrittiva che riporta <description>Demo project for Spring Boot</description>
- Aggiungi spring-boot-devtools in pom.xml
Apri il file pom.xml nella radice del progetto. Aggiungi la seguente configurazione dopo la voce 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>
- Abilita jib-maven-plugin in pom.xml
Jib è uno strumento open source di Google per la creazione di container Java che consente agli sviluppatori Java di creare container utilizzando gli strumenti Java che conoscono. Jib è un builder di immagini container veloce e semplice che gestisce tutti i passaggi di pacchettizzazione dell'applicazione in un'immagine container. Non richiede di scrivere un Dockerfile o di installare Docker ed è integrato direttamente in Maven e Gradle.
Scorri verso il basso nel file pom.xml e aggiorna la sezione Build per includere il plug-in Jib. Una volta completata, la sezione di creazione dovrebbe corrispondere a quella riportata di seguito.
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>
Scegli Always se ti viene chiesto di modificare il file di build.

Genera manifest
Skaffold fornisce strumenti integrati per semplificare lo sviluppo dei container. In questo passaggio inizializzerai Skaffold, che creerà automaticamente i file YAML Kubernetes di base. Il processo tenta di identificare le directory con definizioni di immagini container, come un Dockerfile, e poi crea un manifest di deployment e servizio per ciascuna.
Esegui il comando riportato di seguito per iniziare la procedura.
- Esegui questo comando nel terminale
skaffold init --generate-manifests
- Quando richiesto:
- Usa le frecce per spostare il cursore su
Jib Maven Plugin. - Premi la barra spaziatrice per selezionare l'opzione.
- Premi Invio per continuare
- Inserisci 8080 per la porta.
- Inserisci y per salvare la configurazione.
Allo spazio di lavoro vengono aggiunti due file: skaffold.yaml e deployment.yaml
Aggiorna il nome dell'app
I valori predefiniti inclusi nella configurazione non corrispondono attualmente al nome dell'applicazione. Aggiorna i file in modo che facciano riferimento al nome dell'applicazione anziché ai valori predefiniti.
- Modificare le voci nella configurazione di Skaffold
- Apri
skaffold.yaml - Seleziona il nome dell'immagine attualmente impostata come
pom-xml-image - Fai clic con il tasto destro del mouse e scegli Modifica tutte le occorrenze.
- Digita il nuovo nome come
demo-app
- Modificare le voci nella configurazione di Kubernetes
- Apri il file
deployment.yaml - Seleziona il nome dell'immagine attualmente impostata come
pom-xml-image - Fai clic con il tasto destro del mouse e scegli Modifica tutte le occorrenze.
- Digita il nuovo nome come
demo-app
Attivare la sincronizzazione rapida
Per facilitare un'esperienza di ricarica rapida ottimizzata, utilizzerai la funzionalità di sincronizzazione fornita da Jib. In questo passaggio configurerai Skaffold per utilizzare questa funzionalità nel processo di compilazione.
Tieni presente che il profilo "sync" che stai configurando nella configurazione di Skaffold utilizza il profilo "sync" di Spring che hai configurato nel passaggio precedente, in cui hai attivato il supporto per spring-dev-tools.
- Aggiorna configurazione Skaffold
Nel file skaffold.yaml, sostituisci l'intera sezione di build del file con la seguente specifica. Non modificare altre sezioni del file.
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
Aggiungere una route predefinita
Crea un file denominato HelloController.java in /src/main/java/com/example/springboot/.
Incolla i seguenti contenuti nel file per creare una route HTTP predefinita
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. Esaminare il processo di sviluppo
In questa sezione, seguirai alcuni passaggi utilizzando il plug-in Cloud Code per apprendere le procedure di base e convalidare la configurazione e la configurazione dell'applicazione iniziale.
Cloud Code si integra con Skaffold per semplificare il processo di sviluppo. Quando esegui il deployment in GKE nei passaggi successivi, Cloud Code e Skaffold creeranno automaticamente l'immagine container, eseguiranno il push in Container Registry e poi eseguiranno il deployment dell'applicazione in GKE. Questa operazione avviene dietro le quinte, astraendo i dettagli dal flusso dello sviluppatore. Cloud Code migliora anche il processo di sviluppo fornendo funzionalità di debug e sincronizzazione rapida tradizionali allo sviluppo basato su container.
Eseguire il deployment in Kubernetes
- Nel riquadro in fondo all'editor di Cloud Shell, seleziona Cloud Code. 

- Nel riquadro visualizzato in alto, seleziona Debug su Kubernetes. Se richiesto, seleziona Sì per utilizzare il contesto Kubernetes attuale.

- La prima volta che esegui il comando, nella parte superiore dello schermo viene visualizzato un prompt che ti chiede se vuoi utilizzare il contesto Kubernetes corrente. Seleziona "Sì" per accettare e utilizzare il contesto corrente.

- Successivamente, verrà visualizzato un prompt che chiede quale registro dei container utilizzare. Premi Invio per accettare il valore predefinito fornito.

- Seleziona la scheda Output nel riquadro inferiore per visualizzare l'avanzamento e le notifiche.

- Seleziona "Kubernetes: Run/Debug - Detailed" (Kubernetes: esecuzione/debug - dettagliato) nel menu a discesa del canale a destra per visualizzare ulteriori dettagli e i log in streaming live dai container.

- Torna alla visualizzazione semplificata selezionando "Kubernetes: Run/Debug" (Kubernetes: esegui/debug) dal menu a discesa.
- Al termine della build e dei test, nella scheda Output viene visualizzato il messaggio
Resource deployment/demo-app status completed successfullye viene elencato un URL: "Forwarded URL from service demo-app: http://localhost:8080" - Nel terminale Cloud Code, passa il mouse sopra l'URL nell'output (http://localhost:8080) e poi seleziona Apri anteprima web nel suggerimento visualizzato.
La risposta sarà:
Hello from your local environment!
Utilizzare i punti di interruzione
- Apri l'applicazione HelloController.java che si trova in /src/main/java/com/example/springboot/HelloController.java
- Individua la dichiarazione return per il percorso principale che indica
return String.format("Hello from your %s environment!", target); - Aggiungi un punto di interruzione a quella riga facendo clic sullo spazio vuoto a sinistra del numero di riga. Viene visualizzato un indicatore rosso per indicare che il punto di interruzione è impostato
- Ricarica il browser e nota che il debugger interrompe il processo nel punto di interruzione e ti consente di esaminare lo stato delle variabili e dell'applicazione in esecuzione da remoto in GKE
- Fai clic nella sezione delle variabili fino a trovare la variabile "Target".
- Osserva il valore corrente "local"
- Fai doppio clic sul nome della variabile "target" e, nel popup, modifica il valore in qualcosa di diverso, ad esempio "Cloud".
- Fai clic sul pulsante Continua nel pannello di controllo del debug
- Controlla la risposta nel browser, che ora mostra il valore aggiornato appena inserito.
Ricaricamento rapido
- Modifica l'istruzione in modo che restituisca un valore diverso, ad esempio "Hello from %s Code"
- Il file viene salvato e sincronizzato automaticamente nei container remoti in GKE
- Aggiorna il browser per visualizzare i risultati aggiornati.
- Interrompi la sessione di debug facendo clic sul quadrato rosso nella barra degli strumenti di debug

5. Sviluppo di un semplice servizio REST CRUD
A questo punto, l'applicazione è completamente configurata per lo sviluppo containerizzato e hai esaminato il flusso di lavoro di sviluppo di base con Cloud Code. Nelle sezioni seguenti metterai in pratica ciò che hai imparato aggiungendo endpoint del servizio REST che si connettono a un database gestito in Google Cloud.
Configura le dipendenze
Il codice dell'applicazione utilizza un database per archiviare i dati del servizio REST. Assicurati che le dipendenze siano disponibili aggiungendo quanto segue in pom.xl
- Apri il file
pom.xmle aggiungi quanto segue alla sezione delle dipendenze della configurazione
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>
Codifica il servizio REST
Quote.java
Crea un file denominato Quote.java in /src/main/java/com/example/springboot/ e copia il codice riportato di seguito. Definisce il modello di entità per l'oggetto Quote utilizzato nell'applicazione.
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
Crea un file denominato QuoteRepository.java in src/main/java/com/example/springboot e copia il seguente codice
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();
}
Questo codice utilizza JPA per rendere persistenti i dati. La classe estende l'interfaccia Spring JPARepository e consente la creazione di codice personalizzato. Nel codice che hai aggiunto, è presente un metodo personalizzato findRandomQuote.
QuoteController.java
Per esporre l'endpoint per il servizio, una classe QuoteController fornirà questa funzionalità.
Crea un file denominato QuoteController.java in src/main/java/com/example/springboot e copia i seguenti contenuti
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);
}
}
}
Aggiungere configurazioni del database
application.yaml
Aggiungi la configurazione per il database di backend a cui accede il servizio. Modifica (o crea se non presente) il file application.yaml in src/main/resources e aggiungi una configurazione Spring parametrizzata per il 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
Aggiungi migrazione del database
Crea una cartella in src/main/resources/db/migration/
Crea un file SQL: V1__create_quotes_table.sql
Incolla i seguenti contenuti nel file
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 Config
Le seguenti aggiunte al file deployment.yaml consentono all'applicazione di connettersi alle istanze Cloud SQL.
- TARGET: configura la variabile per indicare l'ambiente in cui viene eseguita l'app
- SPRING_PROFILES_ACTIVE: mostra il profilo Spring attivo, che verrà configurato su
cloud-dev - DB_HOST: l'IP privato per il database, annotato durante la creazione dell'istanza del database o facendo clic su
SQLnel menu di navigazione di console Google Cloud. Modifica il valore. - DB_USER e DB_PASS, come impostati nella configurazione dell'istanza Cloud SQL, memorizzati come secret in GCP
Aggiorna il file deployment.yaml con i contenuti riportati di seguito.
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
Sostituisci il valore DB_HOST con l'indirizzo del tuo database.
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
Esegui il deployment e convalida l'applicazione
- Nel riquadro in basso dell'editor di Cloud Shell, seleziona Cloud Code, quindi seleziona Debug su Kubernetes nella parte superiore dello schermo.
- Al termine della build e dei test, nella scheda Output viene visualizzato il messaggio
Resource deployment/demo-app status completed successfullye viene elencato un URL: "Forwarded URL from service demo-app: http://localhost:8080" - Visualizzare citazioni casuali
Dal terminale Cloud Shell, esegui più volte il comando riportato di seguito sull'endpoint random-quote. Osserva le chiamate ripetute che restituiscono preventivi diversi
curl -v 127.0.0.1:8080/random-quote
- Aggiungere una citazione
Crea una nuova citazione con id=6 utilizzando il comando elencato di seguito e osserva la richiesta che viene restituita
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
- Eliminare un preventivo
Ora elimina la citazione che hai appena aggiunto con il metodo di eliminazione e osserva un codice di risposta HTTP/1.1 204.
curl -v -X DELETE 127.0.0.1:8080/quotes/6
- Errore del server
Visualizzare uno stato di errore eseguendo di nuovo l'ultima richiesta dopo che la voce è già stata eliminata
curl -v -X DELETE 127.0.0.1:8080/quotes/6
Nota che la risposta restituisce un HTTP:500 Internal Server Error.
Esegui il debug dell'applicazione
Nella sezione precedente hai riscontrato uno stato di errore nell'applicazione quando hai tentato di eliminare una voce che non era presente nel database. In questa sezione imposterai un punto di interruzione per individuare il problema. L'errore si è verificato nell'operazione DELETE, quindi lavorerai con la classe QuoteController.
- Apri src.main.java.com.example.springboot.QuoteController.java
- Trova il metodo
deleteQuote() - Trova la riga in cui eliminare un elemento dal database:
quoteRepository.deleteById(id); - Imposta un punto di interruzione su quella riga facendo clic sullo spazio vuoto a sinistra del numero di riga.
- Verrà visualizzato un indicatore rosso che indica che il punto di interruzione è impostato.
- Esegui di nuovo il comando
delete.
curl -v -X DELETE 127.0.0.1:8080/quotes/6
- Torna alla visualizzazione di debug facendo clic sull'icona nella colonna a sinistra.
- Osserva la riga di debug interrotta nella classe QuoteController.
- Nel debugger, fai clic sull'icona
step over
e osserva che viene generata un'eccezione. - Tieni presente che viene restituito un errore interno del server HTTP 500 al client, il che non è l'ideale.
RuntimeException was caught.
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
Aggiorna il codice
Il codice non è corretto e il blocco di eccezione deve essere sottoposto a refactoring per rilevare l'eccezione EmptyResultDataAccessException e restituire un codice di stato HTTP 404 pagina non trovata.
Correggi l'errore.
- Con la sessione di debug ancora in esecuzione, completa la richiesta premendo il pulsante "Continua" nel pannello di controllo del debug.
- Poi aggiungi il seguente blocco al codice:
} catch (EmptyResultDataAccessException e){
return new ResponseEntity<HttpStatus>(HttpStatus.NOT_FOUND);
}
Il metodo dovrebbe avere il seguente aspetto
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);
}
}
- Esegui di nuovo il comando di eliminazione
curl -v -X DELETE 127.0.0.1:8080/quotes/6
- Esegui il debug e osserva l'eccezione
EmptyResultDataAccessExceptionintercettata e la risposta HTTP 404 Not Found restituita al chiamante.
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
- Interrompi la sessione di debug facendo clic sul quadrato rosso nella barra degli strumenti di debug

6. Esegui la pulizia
Complimenti! In questo lab hai creato una nuova applicazione Java da zero e l'hai configurata per funzionare in modo efficace con i container. Successivamente, hai eseguito il deployment e il debug dell'applicazione in un cluster GKE remoto seguendo lo stesso flusso di sviluppo presente negli stack di applicazioni tradizionali.
Per liberare spazio dopo aver completato il lab:
- Eliminare i file utilizzati nel lab
cd ~ && rm -rf container-developer-workshop
- Elimina il progetto per rimuovere tutte le infrastrutture e le risorse correlate