Resilience4J — это легкая библиотека отказоустойчивости и отказоустойчивости для приложений Java 8. Он предназначен для обеспечения устойчивости и отказоустойчивости между службами в распределенной системе. Название Resilience4J происходит от его основной функциональности, которая заключается в том, чтобы сделать системы (сервисы) «устойчивыми» к различным ситуациям сбоев, включая проблемы с сетью, сбои сторонних служб и т. д.
Resilience4J предоставляет следующие возможности:
Основной особенностью Resilience4J является его легковесность. Он использует только библиотеку Vavr (библиотеку функционального программирования) и не имеет других зависимостей от внешних библиотек. Это делает очень удобной интеграцию в существующие системы с небольшими затратами на производительность.
Resilience4J спроектирован так, чтобы его можно было легко настроить с помощью кода, файлов конфигурации или параметров времени выполнения. Он также поддерживает интеграцию с функциями мониторинга и управления Spring Boot через модуль привода.
Благодаря этим функциям и преимуществам Resilience4J он широко используется в современных распределенных системах и архитектурах микросервисов, особенно в средах, требующих высокой доступности и отказоустойчивости.
https://resilience4j.readme.io/
https://github.com/resilience4j/resilience4j
CircuitBreaker в Resilience4j — это основной инструмент для защиты распределенных систем от сбоев.
Принцип его работы в основном заключается вконечный автоматвыполнить,В том числе CLOSED (закрыт), OPEN (открыт) и HALF-OPEN (полуоткрыт).
Resilience4j — это легкая отказоустойчивая библиотека, разработанная для Java 8 и функционального программирования. Его основная цель — помочь разработчикам добиться устойчивости и отказоустойчивости в распределенных системах.
Resilience4j предоставляет различные механизмы отказоустойчивости, включая автоматический выключатель (CircuitBreaker), ограничитель тока (RateLimiter), повторную попытку (Retry), стратегию изоляции (Bulkhead) и управление тайм-аутом (TimeLimiter). Среди них CircuitBreaker является важной частью Resilience4j.
Автоматический выключатель, то есть автоматический выключатель, принцип его конструкции основан на автоматическом выключателе в цепи. Когда ток превышает установленное значение, автоматический выключатель автоматически размыкается, чтобы защитить цепь от повреждения чрезмерным током. В программных системах автоматические выключатели используются для защиты системы от сбоя компонента или службы.
Принцип реализации CircuitBreaker Resilience4j заключается в следующем:
Благодаря вышеуказанным принципам CircuitBreaker Resilience4j может эффективно защитить распределенные системы от сбоев и повысить доступность и надежность системы.
Служба A вызывает службу B, но, к сожалению, служба B недоступна или не отвечает. Таким образом, служба A может дождаться ответа службы B или обработать возникшее исключение. Последующие запросы к Сервису B столкнутся с аналогичными проблемами, что приведет к ухудшению пользовательского опыта.
В этом случае автоматический выключатель может проверить правильность работы службы B, останавливая отправку запросов на определенный период времени, ожидая истечения тайм-аута и разрешая ограниченное количество запросов. Если эти запросы будут успешными, микросервис сможет продолжать работать в обычном режиме. В противном случае время ожидания снова начнется.
Автоматические выключатели имеют три состояния: замкнуто, разомкнуто и полуоткрыто.
Существует 2 службы: адресная служба и служба заказов.
Сначала создайте адресную службу, поскольку это зависимая служба.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.edu</groupId>
<artifactId>address-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>address-service</name>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
package com.edu.addressservice.model;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Builder
@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "addresses")
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String postalCode;
private String state;
private String city;
}
package com.edu.addressservice.repository;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.edu.addressservice.model.Address;
@Repository
public interface AddressRepository extends JpaRepository<Address, Integer> {
Optional<Address> findByPostalCode(String postalCode);
}
public interface AddressService {
Address getAddressByPostalCode(String postalCode);
}
@Service
public class AddressServiceImpl implements AddressService {
@Autowired
private AddressRepository addressRepository;
public Address getAddressByPostalCode(String postalCode) {
return addressRepository.findByPostalCode(postalCode)
.orElseThrow(() -> new RuntimeException("Address Not Found: " + postalCode));
}
}
package com.edu.addressservice.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.edu.addressservice.model.Address;
import com.edu.addressservice.service.AddressService;
@RestController
@RequestMapping("addresses")
public class AddressController {
@Autowired
private AddressService addressService;
@GetMapping("/{postalCode}")
public Address getAddressByPostalCode(@PathVariable("postalCode") String postalCode) {
return addressService.getAddressByPostalCode(postalCode);
}
}
использовать@PostConstruct
аннотацияизметод。 Spring будет инициализирован bean Этот метод вызывается после заполнения свойства данными.
package com.edu.addressservice.config;
import java.util.Arrays;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import com.edu.addressservice.model.Address;
import com.edu.addressservice.repository.AddressRepository;
import jakarta.annotation.PostConstruct;
@Configuration
public class DataSetup {
@Autowired
private AddressRepository addressRepository;
@PostConstruct
public void setupData() {
addressRepository.saveAll(Arrays.asList(
Address.builder().id(1).postalCode("1000001").state("SD").city("JN")
.build(),
Address.builder().id(2).postalCode("1100000").state("JS").city("NJ").build(),
Address.builder().id(3).postalCode("2100001").state("ZJ").city("HZ")
.build()));
}
}
server:
port: 9090
spring:
application:
name: address-service
jpa:
database-platform: org.hibernate.dialect.H2Dialect
hibernate:
ddl-auto: create-drop
datasource:
url: jdbc:h2:mem:address-db
username: admin
password: 1111
driverClassName: org.h2.Driver
h2:
console:
enabled: true
Запустите и перейдите по ссылкеhttp://localhost:9090/addresses/1000001
,Ожидаемый ответ должен выглядеть так
На этом построение адресной службы завершено.
Основное внимание уделяется тому, как сконфигурирован автоматический выключатель и как его состояние контролируется с помощью исполнительного механизма.
public interface Type {
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Entity
@Table(name = "orders")
public class Order implements Type {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Integer id;
private String orderNumber;
private String postalCode;
private String shippingState;
private String shippingCity;
}
@Data
public class Failure implements Type {
private final String msg;
}
package com.edu.orderservice.repository;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.edu.orderservice.model.Order;
@Repository
public interface OrderRepository extends JpaRepository<Order, Integer> {
Optional<Order> findByOrderNumber(String orderNumber);
}
Вся бизнес-логика здесь.
нравиться何вызов外部API---------------------> Spring предоставил RestTemplate
package com.edu.orderservice.service;
import com.edu.orderservice.model.Type;
public interface OrderService {
Type getOrderByPostCode(String orderNumber);
}
package com.edu.orderservice.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import com.edu.orderservice.dto.AddressDTO;
import com.edu.orderservice.model.Failure;
import com.edu.orderservice.model.Order;
import com.edu.orderservice.model.Type;
import com.edu.orderservice.repository.OrderRepository;
import com.edu.orderservice.service.OrderService;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private RestTemplate restTemplate;
private static final String SERVICE_NAME = "order-service";
private static final String ADDRESS_SERVICE_URL = "http://localhost:9090/addresses/";
@Override
@CircuitBreaker(name = SERVICE_NAME, fallbackMethod = "fallbackMethod")
public Type getOrderByPostCode(String orderNumber) {
Order order = orderRepository.findByOrderNumber(orderNumber)
.orElseThrow(() -> new RuntimeException("Order Not Found: " + orderNumber));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<AddressDTO> entity = new HttpEntity<>(null, headers);
ResponseEntity<AddressDTO> response = restTemplate.exchange(
(ADDRESS_SERVICE_URL + order.getPostalCode()), HttpMethod.GET, entity,
AddressDTO.class);
AddressDTO addressDTO = response.getBody();
if (addressDTO != null) {
order.setShippingState(addressDTO.getState());
order.setShippingCity(addressDTO.getCity());
}
return order;
}
private Type fallbackMethod(Exception e) {
return new Failure("Address service is not responding properly");
}
}
@CircuitBreaker
свойство
name
”被分配для“order-service”,Имя таблицы «заказ-услуга», экземпляр каждого Конфигурация подходит для этого метода.fallbackMethod
”свойство,Цель вызывает метод понижения версии, когда зависимость Служить (адрес Служить) не отвечает правильно.@Configuration
public class RestConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
package com.edu.orderservice.config;
import java.util.Arrays;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import com.edu.orderservice.model.Order;
import com.edu.orderservice.repository.OrderRepository;
import jakarta.annotation.PostConstruct;
@Configuration
public class DataSetup {
@Autowired
private OrderRepository orderRepository;
@PostConstruct
public void setupData() {
orderRepository.saveAll(Arrays.asList(
Order.builder().id(1).orderNumber("0c70c0c2").postalCode("1000001").build(),
Order.builder().id(2).orderNumber("7f8f9f15").postalCode("1100000").build(),
Order.builder().id(3).orderNumber("394627b2").postalCode("2100001").build()));
}
}
server:
port: 1010
spring:
application:
name: order-service
jpa:
database-platform: org.hibernate.dialect.H2Dialect
hibernate:
ddl-auto: create-drop
datasource:
url: jdbc:h2:mem:order-db
username: root
password: 123
driverClassName: org.h2.Driver
h2:
console:
enabled: true
management:
endpoint:
health:
show-details: always
endpoints:
web:
exposure:
include: health
health:
circuitbreakers:
enabled: true
resilience4j:
circuitbreaker:
instances:
order-service:
sliding-window-type: COUNT_BASED
failure-rate-threshold: 50
minimum-number-of-calls: 5
automatic-transition-from-open-to-half-open-enabled: true
wait-duration-in-open-state: 5s
permitted-number-of-calls-in-half-open-state: 3
sliding-window-size: 10
register-health-indicator: true
Конфигурация предназначена для Resilience4j
Конфигурация библиотеки
Далее идет пара order-service
Пояснения к каждому элементу конфигурации экземпляра автоматического выключателя:
COUNT_BASED
(на основе Второсортныйчисло)и TIME_BASED
(на основе时间)。В совокупности эти конфигурации определяют order-service
Поведение автоматического выключателя. Автоматический выключатель будет отслеживать частоту отказов и количество вызовов, чтобы определить, когда следует размыкать, а когда переходить в полуоткрытое состояние, обеспечивая систему механизмом самозащиты для предотвращения каскадных сбоев.
Последовательно запустите адресную службу и службу заказов и перейдите по ссылке http://localhost:1010/orders?orderNumber=0c70c0c2.
Если служба адресов отвечает неправильно (служба не работает), мы получим следующий ответ
На этом построение сервиса заказов завершено.
Убедитесь, что обе службы запущены, перейдите по ссылке http://localhost:1010/actuator/health
Посмотреть подробную информацию о автоматическом выключателе
Мы видим следующие ключевые конфигурации:
bufferedCalls
(缓冲вызов Второсортныйчисло)для0,Это означает, что вызовы API от службы заказа к службе адреса не выполняются.failedCalls
(неудачавызов Второсортныйчисло)也для0,Это означает, что все вызовы прошли успешно.,Нет возможности неудачного звонка.failureRate
(неудача率)для-1.0%,Это аномалия значения,Потому что процент отказов не может быть отрицательным. в целом,Процент неудач должен быть частью процента успеха.,То есть 0% означает 100% вероятности успеха. Это может быть так, потому что 0 используется в качестве знаменателя при расчете частоты отказов.,В результате получаются отрицательные числа из.state
(состояние)для"CLOSED"(закрытие),Это указывает на то, что автоматический выключатель в данный момент замкнут.,Механизм автоматического выключателя не срабатывает.Вызов API сервиса заказов 2 разаhttp://localhost:1010/orders?orderNumber=0c70c0c2
,
Затем обновите ссылку на привод.
Закройте адресную службу и вызовите API службы заказа 3.Второсортныйhttp://localhost:1010/orders?orderNumber=0c70c0c2
,Затем обновите ссылку исполнителя.
Мы заметили, что сработал автоматический выключатель. Причина в том, что «failureRate» теперь больше, чем «failure-rate-threshold».
В состоянии HALF_OPEN разрешаем запросы «разрешенного количества вызовов в полуоткрытом состоянии» (мы настраиваем его значение равным 3), а затем снова вычисляем частоту отказов, если частота отказов все еще превышает «порог интенсивности отказов», автоматический выключатель сработает снова.
Продолжайте вызывать API службы заказов 3 раза. http://localhost:1010/orders?orderNumber=0c70c0c2
,Затем обновите ссылку исполнителя.
Запустите адресную службу, а затем продолжайте вызывать API службы заказов 3 раза.http://localhost:1010/orders?orderNumber=0c70c0c2
,Обновить ссылку исполнителя
Автоматический выключатель замкнут.
Схема устойчивости4j breakerспециальный режимиметьиспользовать,Может не вызвать Служить после достижения определенного количества Второсортный,Автоматический разрыв цепи,Избегайте дальнейших звонков,Это защищает приложение от сбоев. Когда Служить выздоровеет,Цепь снова замкнется,Разрешить выполнение обычных вызовов.