1. Обзор
В этой лабораторной работе демонстрируются функции и возможности, разработанные для оптимизации рабочего процесса разработки для инженеров-программистов, занимающихся разработкой Java-приложений в контейнеризированной среде. Типичная разработка в контейнерах требует от пользователя понимания деталей контейнеров и процесса сборки контейнеров. Кроме того, разработчикам обычно приходится прерывать свой рабочий процесс, выходя из IDE для тестирования и отладки своих приложений в удаленных средах. С помощью инструментов и технологий, упомянутых в этом руководстве, разработчики могут эффективно работать с контейнеризированными приложениями, не покидая свою IDE.
Что вы узнаете
В этой лабораторной работе вы изучите методы разработки с использованием контейнеров в GCP, в том числе:
- Настройка и требования
- Создание нового стартового Java-приложения
- Обзор процесса разработки
- Разработка простого REST-сервиса с операциями CRUD.
- Уборка
2. Настройка и требования
Настройка среды для самостоятельного обучения
- Войдите в консоль Google Cloud и создайте новый проект или используйте существующий. Если у вас еще нет учетной записи Gmail или Google Workspace, вам необходимо ее создать .



- Название проекта — это отображаемое имя участников данного проекта. Это строка символов, не используемая API Google, и вы можете изменить её в любое время.
- Идентификатор проекта должен быть уникальным для всех проектов Google Cloud и неизменяемым (его нельзя изменить после установки). Консоль Cloud автоматически генерирует уникальную строку; обычно вам неважно, какая она. В большинстве практических заданий вам потребуется указать идентификатор проекта (обычно он обозначается как
PROJECT_ID), поэтому, если он вам не нравится, сгенерируйте другой случайный идентификатор или попробуйте свой собственный и посмотрите, доступен ли он. Затем он "замораживается" после создания проекта. - Существует третье значение — номер проекта , который используется некоторыми API. Подробнее обо всех трех значениях можно узнать в документации .
- Далее вам потребуется включить оплату в консоли Cloud, чтобы использовать ресурсы/API Cloud. Выполнение этого практического задания не должно стоить дорого, если вообще что-либо. Чтобы отключить ресурсы и избежать дополнительных расходов после завершения этого урока, следуйте инструкциям по «очистке», приведенным в конце практического задания. Новые пользователи Google Cloud имеют право на бесплатную пробную версию стоимостью 300 долларов США .
Запустить редактор Cloudshell
Данная лабораторная работа разработана и протестирована для использования с редактором Google Cloud Shell. Для доступа к редактору,
- Получите доступ к своему проекту Google по адресу https://console.cloud.google.com .
- В правом верхнем углу нажмите на значок редактора облачной оболочки.

- В нижней части вашего окна откроется новое окно.
- Нажмите кнопку «Открыть редактор».

- В начале редактора справа будет изображен исследователь, а в центре — сам редактор.
- В нижней части экрана также должна быть доступна панель терминала.
- Если терминал НЕ открыт, используйте комбинацию клавиш `Ctrl+`, чтобы открыть новое окно терминала.
Настройте gcloud
В Cloud Shell укажите идентификатор проекта и регион, в который вы хотите развернуть приложение. Сохраните их как переменные PROJECT_ID и REGION .
export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
Получите исходный код
Исходный код для этой лабораторной работы находится в репозитории container-developer-workshop на GitHub в GoogleCloudPlatform. Клонируйте его с помощью приведенной ниже команды, а затем перейдите в нужную директорию.
git clone https://github.com/GoogleCloudPlatform/container-developer-workshop.git
cd container-developer-workshop/labs/spring-boot
Обеспечьте наличие инфраструктуры, используемой в этой лаборатории.
В этой лабораторной работе вы развернете код в GKE и получите доступ к данным, хранящимся в базе данных CloudSQL. Приведенный ниже скрипт настройки подготовит для вас эту инфраструктуру. Процесс развертывания займет более 10 минут. Вы можете продолжить выполнение следующих шагов, пока идет процесс настройки.
./setup.sh
3. Создание нового стартового Java-приложения
В этом разделе вы создадите новое Java-приложение Spring Boot с нуля, используя пример приложения, предоставленный spring.io.
Клонируйте демонстрационное приложение
- Создайте стартовое приложение
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 и Jib.
Чтобы включить инструменты разработчика Spring Boot, найдите и откройте файл pom.xml в обозревателе вашего редактора. Затем вставьте следующий код после строки описания, которая выглядит так: <description>Демонстрационный проект для Spring Boot</description>
- Добавьте spring-boot-devtools в pom.xml
Откройте файл pom.xml в корне проекта. Добавьте следующую конфигурацию после записи Description .
pom.xml
<!-- Spring profiles-->
<profiles>
<profile>
<id>sync</id>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
</profile>
</profiles>
- Включите jib-maven-plugin в pom.xml
Jib — это инструмент контейнеризации Java с открытым исходным кодом от Google, который позволяет Java-разработчикам создавать контейнеры, используя знакомые им инструменты Java. Jib — это быстрый и простой конструктор образов контейнеров, который обрабатывает все этапы упаковки вашего приложения в образ контейнера. Он не требует написания Dockerfile или установки Docker и напрямую интегрирован в Maven и Gradle.
Прокрутите вниз файл pom.xml и обновите раздел Build , добавив плагин Jib. После завершения раздел Build должен соответствовать следующему содержимому.
pom.xml
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- Jib Plugin-->
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>3.2.0</version>
</plugin>
<!-- Maven Resources Plugin-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.1.0</version>
</plugin>
</plugins>
</build>
При изменении файла сборки выберите Always .

Создать манифесты
Skaffold предоставляет интегрированные инструменты для упрощения разработки контейнеров. На этом этапе вы инициализируете Skaffold, который автоматически создаст базовые YAML-файлы Kubernetes. Процесс пытается идентифицировать каталоги с определениями образов контейнеров, подобно 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 для использования этой функции в процессе сборки.
Обратите внимание, что профиль "sync", который вы настраиваете в конфигурации skaffold, использует профиль "sync" Spring, который вы настроили на предыдущем шаге, где вы включили поддержку spring-dev-tools.
- Обновить конфигурацию Skaffold
В файле skaffold.yaml замените весь раздел build следующим описанием. Не изменяйте другие разделы файла.
skaffold.yaml
build:
artifacts:
- image: demo-app
jib:
project: com.example:demo
type: maven
args:
- --no-transfer-progress
- -Psync
fromImage: gcr.io/distroless/java:debug
sync:
auto: true
Добавить маршрут по умолчанию
Создайте файл с именем HelloController.java в папке /src/main/java/com/example/springboot/
Вставьте следующее содержимое в файл, чтобы создать HTTP-маршрут по умолчанию.
HelloController.java
package com.example.springboot;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Value;
@RestController
public class HelloController {
@Value("${target:local}")
String target;
@GetMapping("/")
public String hello()
{
return String.format("Hello from your %s environment!", target);
}
}
4. Обзор процесса разработки.
В этом разделе вы шаг за шагом изучите основные процессы с помощью плагина Cloud Code, а также проверите конфигурацию и настройку вашего стартового приложения.
Cloud Code интегрируется со Skaffold для оптимизации процесса разработки. При развертывании в GKE на следующих этапах Cloud Code и Skaffold автоматически создадут образ контейнера, загрузят его в реестр контейнеров, а затем развернут ваше приложение в GKE. Это происходит в фоновом режиме, абстрагируя детали от процесса разработки. Cloud Code также улучшает процесс разработки, предоставляя традиционные возможности отладки и синхронизации для разработки на основе контейнеров.
Развертывание в Kubernetes
- В нижней части окна редактора Cloud Shell выберите Cloud Code.

- В появившейся вверху панели выберите «Отладка в Kubernetes». Если появится запрос, выберите «Да», чтобы использовать текущий контекст Kubernetes.

- При первом запуске команды в верхней части экрана появится запрос, спрашивающий, хотите ли вы использовать текущий контекст Kubernetes. Выберите «Да», чтобы принять запрос и использовать текущий контекст.

- Далее появится запрос о том, какой реестр контейнеров использовать. Нажмите Enter, чтобы принять предоставленное значение по умолчанию.

- Чтобы просмотреть ход выполнения и уведомления, выберите вкладку «Вывод» в нижней панели.

- Выберите «Kubernetes: Запуск/Отладка — Подробная информация» в раскрывающемся списке каналов справа, чтобы просмотреть дополнительные сведения и журналы, транслируемые в режиме реального времени из контейнеров.

- Вернитесь к упрощенному представлению, выбрав в раскрывающемся списке «Kubernetes: Run/Debug».
- После завершения сборки и тестирования на вкладке «Вывод» отображается сообщение:
Resource deployment/demo-app status completed successfully, а также указан URL-адрес: «Перенаправленный URL-адрес из службы демонстрационного приложения: http://localhost:8080». - В терминале Cloud Code наведите курсор на URL-адрес в выводе (http://localhost:8080), а затем во всплывающей подсказке выберите «Открыть предварительный просмотр веб-страницы».
Ответ будет следующим:
Hello from your local environment!
Используйте точки останова
- Откройте приложение HelloController.java, расположенное по адресу /src/main/java/com/example/springboot/HelloController.java
- Найдите оператор return для корневого пути, который выглядит следующим образом:
return String.format("Hello from your %s environment!", target); - Установите точку останова на этой строке, щелкнув по пустому месту слева от номера строки. Красный индикатор покажет, что точка останова установлена.
- Перезагрузите браузер и обратите внимание, что отладчик останавливает процесс в точке останова и позволяет исследовать переменные и состояние приложения, работающего удаленно в GKE.
- Прокрутите вниз в раздел переменных, пока не найдете переменную "Цель".
- Текущее значение следует рассматривать как "локальное".
- Дважды щелкните по имени переменной "target", и во всплывающем окне измените значение на другое, например, "Cloud".
- Нажмите кнопку «Продолжить» на панели управления отладкой.
- Проверьте ответ в браузере, где теперь отображается обновленное значение, которое вы только что ввели.
Горячая перезарядка
- Измените оператор так, чтобы он возвращал другое значение, например, "Привет от %s Code".
- Файл автоматически сохраняется и синхронизируется с удалёнными контейнерами в GKE.
- Обновите страницу в браузере, чтобы увидеть обновленные результаты.
- Остановите сеанс отладки, нажав на красный квадрат на панели инструментов отладки.

5. Разработка простого REST-сервиса с операциями CRUD.
На этом этапе ваше приложение полностью настроено для контейнерной разработки, и вы прошли базовый рабочий процесс разработки с помощью Cloud Code. В следующих разделах вы попрактикуетесь в применении полученных знаний, добавив конечные точки REST-сервиса, подключающиеся к управляемой базе данных в Google Cloud.
Настройка зависимостей
Код приложения использует базу данных для сохранения данных REST-сервиса. Убедитесь в наличии зависимостей, добавив следующее в файл pom.xl.
- Откройте файл
pom.xmlи добавьте следующее в раздел dependencies файла config.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>
Напишите код для REST-сервиса
Quote.java
Создайте файл с именем Quote.java в папке /src/main/java/com/example/springboot/ и скопируйте в него приведенный ниже код. Он определяет модель Entity для объекта Quote, используемого в приложении.
package com.example.springboot;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Objects;
@Entity
@Table(name = "quotes")
public class Quote
{
@Id
@Column(name = "id")
private Integer id;
@Column(name="quote")
private String quote;
@Column(name="author")
private String author;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getQuote() {
return quote;
}
public void setQuote(String quote) {
this.quote = quote;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Quote quote1 = (Quote) o;
return Objects.equals(id, quote1.id) &&
Objects.equals(quote, quote1.quote) &&
Objects.equals(author, quote1.author);
}
@Override
public int hashCode() {
return Objects.hash(id, quote, author);
}
}
QuoteRepository.java
Создайте файл с именем QuoteRepository.java в папке src/main/java/com/example/springboot и скопируйте в него следующий код.
package com.example.springboot;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
public interface QuoteRepository extends JpaRepository<Quote,Integer> {
@Query( nativeQuery = true, value =
"SELECT id,quote,author FROM quotes ORDER BY RANDOM() LIMIT 1")
Quote findRandomQuote();
}
Этот код использует JPA для сохранения данных. Класс расширяет интерфейс Spring JPARepository и позволяет создавать собственный код. В коде вы добавили пользовательский метод findRandomQuote .
QuoteController.java
Для предоставления доступа к конечной точке сервиса этот функционал будет реализован в классе QuoteController .
Создайте файл с именем QuoteController.java в папке src/main/java/com/example/springboot и скопируйте в него следующее содержимое.
package com.example.springboot;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class QuoteController {
private final QuoteRepository quoteRepository;
public QuoteController(QuoteRepository quoteRepository) {
this.quoteRepository = quoteRepository;
}
@GetMapping("/random-quote")
public Quote randomQuote()
{
return quoteRepository.findRandomQuote();
}
@GetMapping("/quotes")
public ResponseEntity<List<Quote>> allQuotes()
{
try {
List<Quote> quotes = new ArrayList<Quote>();
quoteRepository.findAll().forEach(quotes::add);
if (quotes.size()==0 || quotes.isEmpty())
return new ResponseEntity<List<Quote>>(HttpStatus.NO_CONTENT);
return new ResponseEntity<List<Quote>>(quotes, HttpStatus.OK);
} catch (Exception e) {
System.out.println(e.getMessage());
return new ResponseEntity<List<Quote>>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@PostMapping("/quotes")
public ResponseEntity<Quote> createQuote(@RequestBody Quote quote) {
try {
Quote saved = quoteRepository.save(quote);
return new ResponseEntity<Quote>(saved, HttpStatus.CREATED);
} catch (Exception e) {
System.out.println(e.getMessage());
return new ResponseEntity<Quote>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@PutMapping("/quotes/{id}")
public ResponseEntity<Quote> updateQuote(@PathVariable("id") Integer id, @RequestBody Quote quote) {
try {
Optional<Quote> existingQuote = quoteRepository.findById(id);
if(existingQuote.isPresent()){
Quote updatedQuote = existingQuote.get();
updatedQuote.setAuthor(quote.getAuthor());
updatedQuote.setQuote(quote.getQuote());
return new ResponseEntity<Quote>(updatedQuote, HttpStatus.OK);
} else {
return new ResponseEntity<Quote>(HttpStatus.NOT_FOUND);
}
} catch (Exception e) {
System.out.println(e.getMessage());
return new ResponseEntity<Quote>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@DeleteMapping("/quotes/{id}")
public ResponseEntity<HttpStatus> deleteQuote(@PathVariable("id") Integer id) {
try {
quoteRepository.deleteById(id);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} catch (RuntimeException e) {
System.out.println(e.getMessage());
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
Добавить настройки базы данных
application.yaml
Добавьте конфигурацию для базы данных бэкэнда, к которой обращается сервис. Отредактируйте (или создайте, если она отсутствует) файл application.yaml в папке src/main/resources и добавьте параметризованную конфигурацию 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 позволяют приложению подключаться к экземплярам CloudSQL.
- TARGET — задает переменную, указывающую среду, в которой выполняется приложение.
- SPRING_PROFILES_ACTIVE — показывает активный профиль Spring, который будет настроен для
cloud-dev - DB_HOST — частный IP-адрес базы данных, который был указан при создании экземпляра базы данных или при нажатии на
SQLв навигационном меню консоли Google Cloud — пожалуйста, измените это значение! - 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.
- После завершения сборки и тестирования на вкладке «Вывод» отображается сообщение:
Resource deployment/demo-app status completed successfully, а также указан URL-адрес: «Перенаправленный URL-адрес из службы демонстрационного приложения: http://localhost:8080». - Просмотреть случайные цитаты
В терминале CloudShell выполните приведенную ниже команду несколько раз, обращаясь к конечной точке random-quote. Обратите внимание, что при повторном вызове возвращаются разные котировки.
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
- Удалить цитату
Теперь удалите только что добавленную цитату с помощью метода delete и посмотрите на код ответа 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.Оно возвращает клиенту ошибку Internal Server Error 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 "Не найдено".
Исправьте ошибку.
- Не отключая сеанс отладки, завершите запрос, нажав кнопку «Продолжить» на панели управления отладкой.
- Далее добавьте в код следующий блок:
} 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 Not Found.
Trying 127.0.0.1:8080... * Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0) > DELETE /quotes/6 HTTP/1.1 > Host: 127.0.0.1:8080 > User-Agent: curl/7.74.0 > Accept: */* > * Mark bundle as not supporting multiuse < HTTP/1.1 404 < Content-Length: 0 < Date: < * Connection #0 to host 127.0.0.1 left intact
- Остановите сеанс отладки, нажав на красный квадрат на панели инструментов отладки.

6. Уборка
Поздравляем! В этой лабораторной работе вы создали с нуля новое Java-приложение и настроили его для эффективной работы с контейнерами. Затем вы развернули и отладили свое приложение в удаленном кластере GKE, следуя тому же процессу разработки, что и в традиционных стеках приложений.
После завершения лабораторной работы необходимо провести уборку:
- Удалите файлы, использованные в лабораторной работе.
cd ~ && rm -rf container-developer-workshop
- Удалите проект, чтобы удалить всю связанную с ним инфраструктуру и ресурсы.