Files
Alexander Abakumov 15e744da49 Initial commit: Complete Software Architecture course with 11 modules
- Added comprehensive course structure with 11 modules
- Included theoretical content, practical examples, and tasks
- Multi-language code examples (Java, Rust, Node.js, Python)
- Detailed analysis of architectural approaches and trade-offs
- Complete caching strategies documentation
- Architecture examples from real-world systems (Twitter, Netflix, Uber)
- Structured navigation and learning path
- Ready for educational use
2025-09-09 22:08:07 +03:00

13 KiB
Raw Permalink Blame History

Основы масштабируемости

Навигация


Что такое масштабируемость

Масштабируемость — способность системы обрабатывать увеличение нагрузки путем добавления ресурсов.

Типы масштабирования

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 (Бессостоятельные сервисы)

Принцип: Сервисы не должны хранить состояние между запросами.

Преимущества:

  • Легкое горизонтальное масштабирование
  • Отказоустойчивость
  • Простота балансировки нагрузки

Реализация:

// Плохо: Stateful сервис
@Service
public class StatefulUserService {
    private Map<String, User> userCache = new HashMap<>();
    
    public User getUser(String id) {
        // Состояние хранится в памяти сервиса
        return userCache.get(id);
    }
}

// Хорошо: Stateless сервис
@Service
public class StatelessUserService {
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private RedisTemplate<String, User> 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 (Шардирование базы данных)

Принцип: Разделение данных на несколько независимых баз данных.

Стратегии шардирования:

// Стратегия шардирования по пользователю
@Service
public class ShardedUserService {
    
    @Autowired
    private List<DataSource> 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 (Реплики для чтения)

Принцип: Использование отдельных реплик для операций чтения.

@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:

@Service
public class CacheAsideUserService {
    
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private RedisTemplate<String, User> 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

Принципы:

  • Разделение по бизнес-доменам
  • Независимое развертывание
  • Слабая связанность
  • Высокая когезия
// Пример микросервиса пользователей
@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    @GetMapping("/{id}")
    public ResponseEntity<User> getUser(@PathVariable String id) {
        User user = userService.getUser(id);
        return ResponseEntity.ok(user);
    }
    
    @PostMapping
    public ResponseEntity<User> createUser(@RequestBody CreateUserRequest request) {
        User user = userService.createUser(request);
        return ResponseEntity.status(HttpStatus.CREATED).body(user);
    }
}

2. Event-Driven Architecture

Принципы:

  • События как основной способ коммуникации
  • Асинхронная обработка
  • Слабая связанность сервисов
// 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
  • Мониторинг и логирование
  • Кэширование ответов
// 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. Ключевые метрики

@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. Автоматическое масштабирование

# 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