Мультитенантність — це здатність однієї інстанції програмного забезпечення обслуговувати кілька клієнтів. У цьому контексті клієнт (tenant) може бути окремим замовником, компанією або групою користувачів, які використовують одну інфраструктуру, але мають ізольовані дані та конфігурації. Для механізму захисту від збоїв (circuit-breaking) це означає, що кожен клієнт має свої окремі налаштування для переривання з'єднання та незалежну логіку відновлення.
Мети
- Реалізувати переривання з'єднання Hystrix в мікросервісі Spring Boot для кожного клієнта.
- Налаштувати Hystrix для кожного клієнта, щоб забезпечити ізоляцію.
- Використовувати динамічну зміну клієнта в додатку на основі поточного контексту запиту.
Що таке мультитенантність у Hystrix?
У Hystrix переривання з'єднання визначається через команду, яка обгортає виклики до зовнішнього сервісу чи ресурсу. У мультитенантному додатку кожен клієнт може мати різні сервіси та різні патерни відмов, тому кожен клієнт може потребувати власну конфігурацію переривання з'єднання (наприклад, різні пороги, тайм-аути та резервні варіанти).
Hystrix дозволяє використовувати кастомізовані налаштування для кожної команди, які можна розширити для забезпечення ізоляції для кожного клієнта. Мета — гарантувати, що відмова одного з сервісів клієнта не впливатиме на інших.
Крок за кроком: реалізація
Ми реалізуємо мультитенантність у додатку Spring Boot за допомогою Hystrix, виконавши наступні кроки:
- Налаштування проекту Spring Boot
- Налаштування мультитенантності
- Імплементація Hystrix з ізоляцією для клієнтів
- Обробка переривання з'єднання для кількох клієнтів
1. Налаштування проекту Spring Boot
Спочатку створіть додаток Spring Boot за допомогою Spring Initializr (https://start.spring.io/). Додайте залежності для:
- Spring Web
- Spring Cloud Netflix Hystrix
- Spring Boot DevTools (необов'язково для розробки)
- Spring Data JPA (необов'язково, якщо ви використовуєте базу даних)
Додайте наступні залежності до вашого файлу pom.xml
:
org.springframework.cloud
spring-cloud-starter-netflix-hystrix
org.springframework.boot
spring-boot-starter-web
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
2. Налаштування мультитенантності
Для мультитенантності найпоширенішим підходом є визначення клієнта на основі HTTP-запиту.
Це можна реалізувати за допомогою кастомного TenantContext
, який зберігає інформацію про поточного клієнта.
Створення TenantContext
для зберігання інформації про поточного клієнта
package com.example.microservice.tenant;
public class TenantContext {
private static final ThreadLocal currentTenant = new ThreadLocal<>();
public static void setCurrentTenant(String tenant) {
currentTenant.set(tenant);
}
public static String getCurrentTenant() {
return currentTenant.get();
}
public static void clear() {
currentTenant.remove();
}
}
Створення TenantInterceptor
для витягування інформації про клієнта з заголовка запиту
package com.example.microservice.interceptor;
import com.example.microservice.tenant.TenantContext;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class TenantInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// Витягування ID клієнта з заголовка запиту (наприклад)
String tenant = request.getHeader("X-Tenant-ID");
if (tenant != null) {
TenantContext.setCurrentTenant(tenant);
} else {
// Обробка клієнта за замовчуванням, якщо потрібно
TenantContext.setCurrentTenant("default");
}
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
TenantContext.clear();
}
}
Реєстрація TenantInterceptor
у WebConfig
package com.example.microservice.config;
import com.example.microservice.interceptor.TenantInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
private final TenantInterceptor tenantInterceptor;
public WebConfig(TenantInterceptor tenantInterceptor) {
this.tenantInterceptor = tenantInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(tenantInterceptor);
}
}
У цьому налаштуванні ми витягуємо ідентифікатор клієнта (наприклад, з заголовка X-Tenant-ID
) і зберігаємо його в TenantContext
. Це дозволяє ізолювати команди та конфігурації Hystrix на основі клієнта.
3. Реалізація Hystrix з ізоляцією для клієнтів
Тепер давайте реалізуємо Hystrix для застосування переривання з'єднань у мультитенантному середовищі. Ми налаштуємо конфігурацію Hystrix динамічно, залежно від поточного клієнта.
Створення сервісу з командою Hystrix
package com.example.microservice.service;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.stereotype.Service;
@Service
public class ExternalService {
@HystrixCommand(fallbackMethod = "fallbackMethod")
public String callExternalService() {
// Симуляція збоїв зовнішнього сервісу
if (Math.random() > 0.5) {
throw new RuntimeException("Сервіс не працює");
}
return "Сервіс працює";
}
public String fallbackMethod() {
return "Резервний відгук: Сервіс тимчасово недоступний";
}
}
Тут метод callExternalService
симулює зовнішній сервіс, який може вийти з ладу.
Анотація @HystrixCommand
гарантує, що якщо команда зазнає невдачі, буде викликано метод fallbackMethod()
.
Налаштування конфігурації Hystrix для кожного клієнта
Щоб налаштувати Hystrix для кожного клієнта, ми можемо створити бін HystrixCommandProperties
, який залежить від поточного клієнта.
package com.example.microservice.config;
import com.netflix.hystrix.HystrixCommandProperties;
import com.example.microservice.tenant.TenantContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class HystrixConfig {
@Bean
public HystrixCommandProperties.Setter hystrixCommandProperties() {
String currentTenant = TenantContext.getCurrentTenant();
// Налаштування конфігурації залежно від клієнта
if ("tenant1".equals(currentTenant)) {
return HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(2000);
} else if ("tenant2".equals(currentTenant)) {
return HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(5000);
} else {
return HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(3000);
}
}
}
У цій конфігурації, HystrixCommandProperties.Setter
налаштовується для кожного клієнта. Наприклад, tenant1 має тайм-аут 2 секунди, а tenant2 — 5 секунд. Ви можете додати більше налаштувань залежно від потреб вашого клієнта.
4. Обробка переривання з'єднання для кількох клієнтів
Щоб кожен клієнт мав свій набір переривань з'єднання та механізмів резервного копіювання, ви можете використовувати HystrixCommandGroupKey та HystrixCommandKey. Ці ключі можна динамічно змінювати залежно від поточного клієнта.
package com.example.microservice.service;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixCommandProperties;
import org.springframework.stereotype.Service;
@Service
public class TenantAwareService {
public String executeTenantCommand() {
String currentTenant = TenantContext.getCurrentTenant();
// Динамічне створення групи та ключа команди залежно від клієнта
HystrixCommand.Setter setter = HystrixCommand.Setter
.withGroupKey(HystrixCommandGroupKey.Factory.asKey("Group-" + currentTenant))
.andCommandKey(HystrixCommandKey.Factory.asKey("Command-" + currentTenant));
HystrixCommand command = new HystrixCommand(setter) {
@Override
protected String run() throws Exception {
// Симуляція виклику зовнішнього сервісу
return "Відповідь від " + currentTenant;
}
@Override
protected String getFallback() {
return "Резервна відповідь від " + currentTenant;
}
};
return command.execute();
}
}
Тут ми створюємо унікальні HystrixCommandGroupKey та HystrixCommandKey для кожного клієнта. Це гарантує, що переривання з'єднання для кожного клієнта працюватиме незалежно, на основі контексту клієнта.
Висновок
У цій статті ми розглянули, як реалізувати мультитенантність з Hystrix в додатку на Spring Boot. Ізолюючи конфігурації для переривання з'єднання для кожного клієнта, ми гарантуємо, що невдача одного клієнта не вплине на інших. Такий підхід дозволяє створювати гнучку та масштабовану архітектуру, де кожен клієнт отримує персоналізовану стійкість до збоїв залежно від своїх специфічних потреб.
Ми також продемонстрували, як:
- Витягувати та встановлювати контекст клієнта з вхідних запитів.
- Динамічно налаштовувати конфігурації Hystrix (тайм-аути, пороги помилок) залежно від клієнта.
- Ізолювати переривання з'єднання для кожного клієнта за допомогою ключів команд Hystrix.
Це налаштування можна легко розширити для складніших сценаріїв мультитенантності, що гарантує, що ваша система залишатиметься стійкою та оперативною, навіть у випадку збоїв.
Якщо ви хочете дізнатися більше про стійкі архітектури мікросервісів, «Release It!» Майкла Т. Нігард — це відмінний ресурс для створення надійних систем, готових до виробництва.
Перекладено з: Multitenancy with Hystrix in Spring Boot