# Основы масштабируемости ## Навигация - [🏠 Главная страница курса](../../../README.md) - [📚 Модуль 4](../README.md) --- ## Что такое масштабируемость **Масштабируемость** — способность системы обрабатывать увеличение нагрузки путем добавления ресурсов. ## Типы масштабирования ### 1. Вертикальное масштабирование (Scale Up) **Определение:** Увеличение мощности существующих серверов. **Преимущества:** - Простота реализации - Нет изменений в архитектуре - Быстрое внедрение **Недостатки:** - Ограниченность ресурсов - Высокая стоимость - Точка отказа **Пример:** ``` Сервер: 4 CPU, 8GB RAM → 8 CPU, 16GB RAM ``` ### 2. Горизонтальное масштабирование (Scale Out) **Определение:** Добавление новых серверов в систему. **Преимущества:** - Теоретически неограниченное масштабирование - Отказоустойчивость - Экономическая эффективность **Недостатки:** - Сложность архитектуры - Проблемы с данными - Сетевые задержки **Пример:** ``` 1 сервер → 2 сервера → 4 сервера → 8 серверов ``` ## Метрики масштабируемости ### 1. Throughput (Пропускная способность) - Запросов в секунду (RPS) - Транзакций в секунду (TPS) - Байт в секунду ### 2. Latency (Задержка) - Время отклика - Время обработки - Время ожидания ### 3. Resource Utilization (Использование ресурсов) - CPU utilization - Memory usage - Disk I/O - Network bandwidth ## Закон Амдала **Формула:** `Speedup = 1 / (P + (1-P)/N)` Где: - P — доля последовательного кода - N — количество процессоров **Вывод:** Параллелизация эффективна только при малой доле последовательного кода. ## Принципы проектирования масштабируемых систем ### 1. Stateless Services (Бессостоятельные сервисы) **Принцип:** Сервисы не должны хранить состояние между запросами. **Преимущества:** - Легкое горизонтальное масштабирование - Отказоустойчивость - Простота балансировки нагрузки **Реализация:** ```java // Плохо: Stateful сервис @Service public class StatefulUserService { private Map userCache = new HashMap<>(); public User getUser(String id) { // Состояние хранится в памяти сервиса return userCache.get(id); } } // Хорошо: Stateless сервис @Service public class StatelessUserService { @Autowired private UserRepository userRepository; @Autowired private RedisTemplate redisTemplate; public User getUser(String id) { // Состояние хранится во внешнем хранилище User cached = redisTemplate.opsForValue().get("user:" + id); if (cached != null) { return cached; } User user = userRepository.findById(id); redisTemplate.opsForValue().set("user:" + id, user, Duration.ofMinutes(30)); return user; } } ``` ### 2. Database Sharding (Шардирование базы данных) **Принцип:** Разделение данных на несколько независимых баз данных. **Стратегии шардирования:** ```java // Стратегия шардирования по пользователю @Service public class ShardedUserService { @Autowired private List shardDataSources; public DataSource getShardDataSource(String userId) { int shardIndex = Math.abs(userId.hashCode()) % shardDataSources.size(); return shardDataSources.get(shardIndex); } public User saveUser(User user) { DataSource shard = getShardDataSource(user.getId()); JdbcTemplate template = new JdbcTemplate(shard); String sql = "INSERT INTO users (id, name, email) VALUES (?, ?, ?)"; template.update(sql, user.getId(), user.getName(), user.getEmail()); return user; } public User getUser(String userId) { DataSource shard = getShardDataSource(userId); JdbcTemplate template = new JdbcTemplate(shard); String sql = "SELECT * FROM users WHERE id = ?"; return template.queryForObject(sql, new Object[]{userId}, new UserRowMapper()); } } ``` ### 3. Read Replicas (Реплики для чтения) **Принцип:** Использование отдельных реплик для операций чтения. ```java @Configuration public class DatabaseConfig { @Bean @Primary public DataSource masterDataSource() { HikariConfig config = new HikariConfig(); config.setJdbcUrl("jdbc:postgresql://master-db:5432/mydb"); config.setUsername("master_user"); config.setPassword("master_password"); return new HikariDataSource(config); } @Bean public DataSource readOnlyDataSource() { HikariConfig config = new HikariConfig(); config.setJdbcUrl("jdbc:postgresql://replica-db:5432/mydb"); config.setUsername("readonly_user"); config.setPassword("readonly_password"); return new HikariDataSource(config); } } @Service public class ReadWriteUserService { @Autowired @Qualifier("masterDataSource") private DataSource masterDataSource; @Autowired @Qualifier("readOnlyDataSource") private DataSource readOnlyDataSource; public User saveUser(User user) { // Запись в master JdbcTemplate masterTemplate = new JdbcTemplate(masterDataSource); String sql = "INSERT INTO users (id, name, email) VALUES (?, ?, ?)"; masterTemplate.update(sql, user.getId(), user.getName(), user.getEmail()); return user; } public User getUser(String userId) { // Чтение из replica JdbcTemplate replicaTemplate = new JdbcTemplate(readOnlyDataSource); String sql = "SELECT * FROM users WHERE id = ?"; return replicaTemplate.queryForObject(sql, new Object[]{userId}, new UserRowMapper()); } } ``` ### 4. Caching Strategies (Стратегии кэширования) **Cache-Aside Pattern:** ```java @Service public class CacheAsideUserService { @Autowired private UserRepository userRepository; @Autowired private RedisTemplate redisTemplate; public User getUser(String userId) { // 1. Проверка кэша User cached = redisTemplate.opsForValue().get("user:" + userId); if (cached != null) { return cached; } // 2. Загрузка из БД User user = userRepository.findById(userId); // 3. Сохранение в кэш if (user != null) { redisTemplate.opsForValue().set("user:" + userId, user, Duration.ofMinutes(30)); } return user; } public User updateUser(User user) { // 1. Обновление в БД User updated = userRepository.save(user); // 2. Обновление кэша redisTemplate.opsForValue().set("user:" + user.getId(), updated, Duration.ofMinutes(30)); return updated; } public void deleteUser(String userId) { // 1. Удаление из БД userRepository.deleteById(userId); // 2. Удаление из кэша redisTemplate.delete("user:" + userId); } } ``` ## Архитектурные принципы масштабируемости ### 1. Microservices Architecture **Принципы:** - Разделение по бизнес-доменам - Независимое развертывание - Слабая связанность - Высокая когезия ```java // Пример микросервиса пользователей @RestController @RequestMapping("/api/users") public class UserController { @Autowired private UserService userService; @GetMapping("/{id}") public ResponseEntity getUser(@PathVariable String id) { User user = userService.getUser(id); return ResponseEntity.ok(user); } @PostMapping public ResponseEntity createUser(@RequestBody CreateUserRequest request) { User user = userService.createUser(request); return ResponseEntity.status(HttpStatus.CREATED).body(user); } } ``` ### 2. Event-Driven Architecture **Принципы:** - События как основной способ коммуникации - Асинхронная обработка - Слабая связанность сервисов ```java // Event Publisher @Component public class UserEventPublisher { @Autowired private ApplicationEventPublisher eventPublisher; public void publishUserCreated(User user) { UserCreatedEvent event = new UserCreatedEvent(user); eventPublisher.publishEvent(event); } } // Event Listener @Component public class UserEventListener { @Autowired private NotificationService notificationService; @EventListener @Async public void handleUserCreated(UserCreatedEvent event) { User user = event.getUser(); // Отправка приветственного email notificationService.sendWelcomeEmail(user); } } ``` ### 3. API Gateway Pattern **Функции API Gateway:** - Маршрутизация запросов - Аутентификация и авторизация - Rate limiting - Мониторинг и логирование - Кэширование ответов ```java // Spring Cloud Gateway конфигурация @Configuration public class GatewayConfig { @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { return builder.routes() .route("user-service", r -> r.path("/api/users/**") .filters(f -> f .stripPrefix(1) .addRequestHeader("X-Service", "user-service") .circuitBreaker(config -> config .setName("user-service-cb") .setFallbackUri("forward:/fallback/user-service"))) .uri("lb://user-service")) .build(); } } ``` ## Метрики и мониторинг масштабируемости ### 1. Ключевые метрики ```java @Component public class ScalabilityMetrics { private final MeterRegistry meterRegistry; private final Counter requestCounter; private final Timer responseTimer; public ScalabilityMetrics(MeterRegistry meterRegistry) { this.meterRegistry = meterRegistry; this.requestCounter = Counter.builder("requests.total") .description("Total number of requests") .register(meterRegistry); this.responseTimer = Timer.builder("response.time") .description("Response time") .register(meterRegistry); } public void recordRequest(String service, String method) { requestCounter.increment( Tags.of("service", service, "method", method) ); } public void recordResponseTime(String service, Duration duration) { responseTimer.record(duration, TimeUnit.MILLISECONDS); } } ``` ### 2. Автоматическое масштабирование ```yaml # Kubernetes HPA конфигурация apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: user-service-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: user-service minReplicas: 2 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 - type: Resource resource: name: memory target: type: Utilization averageUtilization: 80 ```