- 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
440 lines
13 KiB
Markdown
440 lines
13 KiB
Markdown
# Основы масштабируемости
|
|
|
|
## Навигация
|
|
- [🏠 Главная страница курса](../../../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<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 (Шардирование базы данных)
|
|
|
|
**Принцип:** Разделение данных на несколько независимых баз данных.
|
|
|
|
**Стратегии шардирования:**
|
|
|
|
```java
|
|
// Стратегия шардирования по пользователю
|
|
@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 (Реплики для чтения)
|
|
|
|
**Принцип:** Использование отдельных реплик для операций чтения.
|
|
|
|
```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<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
|
|
|
|
**Принципы:**
|
|
- Разделение по бизнес-доменам
|
|
- Независимое развертывание
|
|
- Слабая связанность
|
|
- Высокая когезия
|
|
|
|
```java
|
|
// Пример микросервиса пользователей
|
|
@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
|
|
|
|
**Принципы:**
|
|
- События как основной способ коммуникации
|
|
- Асинхронная обработка
|
|
- Слабая связанность сервисов
|
|
|
|
```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
|
|
```
|