Мультитенантність з Hystrix у Spring Boot

Мультитенантність — це здатність однієї інстанції програмного забезпечення обслуговувати кілька клієнтів. У цьому контексті клієнт (tenant) може бути окремим замовником, компанією або групою користувачів, які використовують одну інфраструктуру, але мають ізольовані дані та конфігурації. Для механізму захисту від збоїв (circuit-breaking) це означає, що кожен клієнт має свої окремі налаштування для переривання з'єднання та незалежну логіку відновлення.

Мети

  • Реалізувати переривання з'єднання Hystrix в мікросервісі Spring Boot для кожного клієнта.
  • Налаштувати Hystrix для кожного клієнта, щоб забезпечити ізоляцію.
  • Використовувати динамічну зміну клієнта в додатку на основі поточного контексту запиту.

Що таке мультитенантність у Hystrix?

У Hystrix переривання з'єднання визначається через команду, яка обгортає виклики до зовнішнього сервісу чи ресурсу. У мультитенантному додатку кожен клієнт може мати різні сервіси та різні патерни відмов, тому кожен клієнт може потребувати власну конфігурацію переривання з'єднання (наприклад, різні пороги, тайм-аути та резервні варіанти).

Hystrix дозволяє використовувати кастомізовані налаштування для кожної команди, які можна розширити для забезпечення ізоляції для кожного клієнта. Мета — гарантувати, що відмова одного з сервісів клієнта не впливатиме на інших.

Крок за кроком: реалізація

Ми реалізуємо мультитенантність у додатку Spring Boot за допомогою Hystrix, виконавши наступні кроки:

  1. Налаштування проекту Spring Boot
  2. Налаштування мультитенантності
  3. Імплементація Hystrix з ізоляцією для клієнтів
  4. Обробка переривання з'єднання для кількох клієнтів

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

Leave a Reply

Your email address will not be published. Required fields are marked *