1. Panoramica
Questo lab illustra caratteristiche e funzionalità progettate per semplificare il flusso di lavoro di sviluppo per i tecnici del software incaricati di sviluppare applicazioni Java in un ambiente containerizzato. Lo sviluppo tipico dei container richiede che l'utente comprenda i dettagli dei container e del processo di compilazione dei container. Inoltre, gli sviluppatori in genere devono interrompere il flusso, uscendo dal loro IDE per testare ed eseguire il debug delle loro applicazioni in ambienti remoti. Con gli strumenti e le tecnologie menzionati in questo tutorial, gli sviluppatori possono lavorare in modo efficace con le applicazioni containerizzate senza uscire dall'IDE.
Cosa imparerai a fare
In questo lab imparerai i metodi per lo sviluppo con container in Google Cloud, tra cui:
- Configurazione e requisiti
- Creazione di una nuova applicazione iniziale Java
- Panoramica del processo di sviluppo
- Sviluppo di un servizio REST CRUD semplice
- Esegui la pulizia
2. Configurazione e requisiti
Configurazione dell'ambiente autogestito
- 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 progetto è il nome visualizzato dei partecipanti del progetto. Si tratta di una stringa di caratteri non utilizzata dalle API di Google e può essere aggiornata 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). La console Cloud genera automaticamente una stringa univoca. di solito non ti importa cosa sia. Nella maggior parte dei codelab, devi fare riferimento all'ID progetto (che solitamente è identificato come
PROJECT_ID
), quindi, se non ti piace, generane un altro a caso oppure puoi fare un tentativo personalizzato e controllare se è disponibile. Poi c'è "congelato" dopo la creazione del progetto. - C'è un terzo valore, il numero di progetto, utilizzato da alcune API. Scopri di più su tutti e tre questi valori nella documentazione.
- Successivamente, dovrai abilitare la fatturazione nella console Cloud per utilizzare le risorse/le API Cloud. Eseguire questo codelab non dovrebbe costare molto. Per arrestare le risorse in modo da non incorrere in fatturazione oltre questo tutorial, segui eventuali "pulizie" istruzioni riportate alla fine del codelab. I nuovi utenti di Google Cloud sono idonei al programma prova senza costi di 300$.
Avvia editor Cloudshell
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 di Cloud Shell
- Si aprirà un nuovo riquadro nella parte inferiore della finestra
- Fai clic sul pulsante Apri editor
- L'editor si apre 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. Salvale 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 nel container-developer-workshop in GoogleCloudPlatform su GitHub. Clonalo con il comando seguente, 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 su GKE e potrai accedere ai dati archiviati in un database Cloud SQL. Lo script di configurazione riportato di seguito prepara l'infrastruttura per te. Il processo di provisioning richiederà più di 10 minuti. Puoi continuare con i passaggi successivi durante l'elaborazione della configurazione.
./setup.sh
3. Creazione di una nuova applicazione iniziale Java
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
- Crea 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 nell'area di lavoro IDE di Cloud Shell
cd sample-app && cloudshell workspace .
Aggiungi Spring-boot-devtools e Fiocco
Per abilitare i DevTools Spring Boot, individua e apri il file pom.xml dall'Explorer nel tuo editor. Incolla il codice seguente dopo la riga descrittiva con la dicitura <description>Progetto demo per Spring Boot</description>.
- Aggiungi spring-boot-devtools in pom.xml
Apri il file pom.xml nella directory principale 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>
- Attiva jib-maven-plugin in pom.xml
Jib è uno strumento di containerizzazione Java open source di Google che consente agli sviluppatori Java di creare container utilizzando gli strumenti Java che conoscono. Jib è uno strumento per la creazione di immagini container semplice e veloce che gestisce tutti i passaggi per pacchettizzare la tua applicazione in un'immagine container. Non richiede la scrittura di un Dockerfile o l'installazione di un Dockerfile ed è integrato direttamente in Maven e Gradle.
Scorri verso il basso il file pom.xml e aggiorna la sezione Build
per includere il plug-in Jib. Una volta completata, la sezione relativa alla build dovrebbe corrispondere alla seguente.
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 richiesto della modifica del file di build.
Genera manifest
Skaffold fornisce strumenti integrati per semplificare lo sviluppo dei container. In questo passaggio inizializzarai 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, quindi crea un manifest di deployment e di servizio per ciascuna.
Esegui il comando seguente per iniziare il processo.
- Esegui questo comando nel terminale
skaffold init --generate-manifests
- Quando richiesto:
- Utilizza 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
Alla visualizzazione dell'area di lavoro vengono aggiunti due file, skaffold.yaml
e deployment.yaml
Aggiorna il nome dell'app
I valori predefiniti inclusi nella configurazione al momento non corrispondono al nome dell'applicazione. Aggiorna i file in modo che facciano riferimento al nome dell'applicazione anziché ai valori predefiniti.
- Modifica le voci nella configurazione Skaffold
- Apri
skaffold.yaml
- Seleziona il nome dell'immagine attualmente impostato su
pom-xml-image
- Fai clic con il tasto destro del mouse e scegli Modifica tutte le occorrenze
- Digita il nuovo nome come
demo-app
.
- Modifica le voci nella configurazione di Kubernetes
- Apri file
deployment.yaml
- Seleziona il nome dell'immagine attualmente impostato su
pom-xml-image
- Fai clic con il tasto destro del mouse e scegli Modifica tutte le occorrenze
- Digita il nuovo nome come
demo-app
.
Attiva sincronizzazione a caldo
Per facilitare un'esperienza di ricarica rapida ottimizzata utilizzerai la funzionalità di sincronizzazione fornita da Jib. In questo passaggio configurerai Skaffold in modo che utilizzi questa funzionalità nel processo di creazione.
Tieni presente che la procedura di sincronizzazione il profilo che stai configurando nella configurazione skaffold utilizza la funzione Profilo che hai configurato nel passaggio precedente, in cui hai abilitato il supporto per spring-dev-tools.
- Aggiorna configurazione skaffold
Nel file skaffold.yaml, sostituisci l'intera sezione di compilazione del file con la seguente specifica. Non alterare 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
Aggiungi una route predefinita
Crea un file denominato HelloController.java all'indirizzo /src/main/java/com/example/springboot/
Incolla i contenuti seguenti 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. Panoramica del processo di sviluppo
In questa sezione analizzerai alcuni passaggi utilizzando il plug-in Cloud Code per apprendere i processi di base e convalidare la configurazione della tua applicazione iniziale.
Cloud Code si integra con skaffold per semplificare il processo di sviluppo. Quando esegui il deployment su GKE nei passaggi seguenti, Cloud Code e Skaffold creano automaticamente l'immagine container, ne eseguono il push a un Container Registry e quindi il deployment dell'applicazione su GKE. Ciò avviene dietro le quinte, astraendo i dettagli dal flusso degli sviluppatori. Cloud Code migliora inoltre il processo di sviluppo fornendo funzionalità tradizionali di debug e sincronizzazione a caldo allo sviluppo basato su container.
Eseguire il deployment in Kubernetes
- Nel riquadro nella parte inferiore dell'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 il contesto attuale di Kubernetes, seleziona "Sì" per accettare e utilizzare il contesto corrente.
- A seguire, viene visualizzato un prompt in cui si chiede quale Container Registry 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: Esegui/Debug - Dettagliato" nel menu a discesa del canale a destra per visualizzare ulteriori dettagli e i log trasmessi in live streaming dai container
- Torna alla visualizzazione semplificata selezionando "Kubernetes: Run/Debug" dal menu a discesa
- Una volta completati la build e i test, la scheda Output riporta il seguente messaggio:
Resource deployment/demo-app status completed successfully
e 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), quindi nella descrizione comando visualizzata seleziona Apri anteprima web.
La risposta sarà:
Hello from your local environment!
Utilizza i punti di interruzione
- Apri l'applicazione HelloController.java che si trova in /src/main/java/com/example/springboot/HelloController.java
- Individua l'istruzione di ritorno per il percorso principale che indica
return String.format("Hello from your %s environment!", target);
. - Aggiungi un punto di interruzione alla 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 in corrispondenza del punto di interruzione e ti consente di esaminare lo stato di sabbia variabile dell'applicazione in esecuzione da remoto in GKE
- Fai clic nella sezione Variabili fino a trovare la colonna "Target" .
- Osserva il valore attuale come "local"
- Fai doppio clic sul nome della variabile "target" e nel popup cambia il valore, ad esempio "Cloud".
- Fai clic sul pulsante Continua nel pannello di controllo del debug
- Esamina la risposta nel browser, che ora mostra il valore aggiornato che hai appena inserito.
Ricarica a caldo
- Modifica l'istruzione in modo che restituisca un valore diverso, ad esempio "Ciao da codice %s"
- 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 servizio REST CRUD semplice
A questo punto la tua applicazione è completamente configurata per lo sviluppo containerizzato e hai seguito il flusso di lavoro di sviluppo di base con Cloud Code. Nelle sezioni seguenti metterai in pratica ciò che hai imparato aggiungendo endpoint di servizio REST che si connettono a un database gestito in Google Cloud.
Configura le dipendenze
Il codice dell'applicazione utilizza un database per rendere persistenti i dati del servizio rimanenti. Assicurati che le dipendenze siano disponibili aggiungendo quanto segue nel file pom.xl
- Apri il file
pom.xml
e aggiungi quanto segue nella 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 rimanente
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 all'indirizzo src/main/java/com/example/springboot e copia il codice seguente
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 hai aggiunto 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 all'indirizzo src/main/java/com/example/springboot e copia il contenuto seguente
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);
}
}
}
Aggiungi configurazioni di 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 con parametri 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 di database
Crea una cartella in src/main/resources/db/migration/
Crea un file SQL: V1__create_quotes_table.sql
Incolla i contenuti seguenti 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');
Configurazione di Kubernetes
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 al momento della creazione dell'istanza del database o facendo clic su
SQL
nel menu di navigazione della console Google Cloud. Modifica il valore. - DB_USER e DB_PASS: come impostato nella configurazione dell'istanza Cloud SQL, archiviati come secret in Google Cloud
Aggiorna il file deployment.yaml con il contenuto seguente.
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 nella parte inferiore dell'editor di Cloud Shell, seleziona Cloud Code, quindi Debug su Kubernetes nella parte superiore dello schermo.
- Una volta completati la build e i test, la scheda Output riporta il seguente messaggio:
Resource deployment/demo-app status completed successfully
e un URL: "Forwarded URL from service demo-app: http://localhost:8080" - Visualizza citazioni casuali
Dal terminale Cloudshell, esegui il comando seguente più volte sull'endpoint con virgolette casuali. Osservare una chiamata ripetuta con citazioni diverse
curl -v 127.0.0.1:8080/random-quote
- Aggiungi un preventivo
Crea una nuova citazione con id=6 utilizzando il comando elencato di seguito e osserva la richiesta ripetuta
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 delete e osserva un codice di risposta HTTP/1.1 204
.
curl -v -X DELETE 127.0.0.1:8080/quotes/6
- Errore del server
Si verifica 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 rilevato 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 utilizzerai la classe QuoteController.
- Apri src.main.java.com.example.springboot.QuoteController.java
- Trova il metodo
deleteQuote()
- Trova la riga in cui elimina un elemento dal database:
quoteRepository.deleteById(id);
- Imposta un punto di interruzione sulla riga in questione facendo clic sullo spazio vuoto a sinistra del numero di riga.
- Viene 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 - Osserva che un valore
RuntimeException was caught.
molto generico restituisce al client un errore interno del server HTTP 500, che non è l'ideale.
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 delle eccezioni deve essere sottoposto a refactoring per rilevare l'eccezione EmptyResultDataAccessException
e inviare un codice di stato HTTP 404 Non trovato.
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 essere simile al seguente
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 debugger e osserva il messaggio
EmptyResultDataAccessException
rilevato e il messaggio HTTP 404 - Non trovato restituito 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 da zero una nuova applicazione Java e l'hai configurata per funzionare in modo efficace con i container. Hai quindi eseguito il deployment dell'applicazione ed eseguito il debug in un cluster GKE remoto seguendo lo stesso flusso di sviluppatori che si trova negli stack di applicazioni tradizionali.
Per eseguire la pulizia dopo aver completato il lab:
- Elimina i file utilizzati nel lab
cd ~ && rm -rf container-developer-workshop
- Elimina il progetto per rimuovere tutte le infrastrutture e le risorse correlate