- 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
13 KiB
13 KiB
Основы масштабируемости
Навигация
Что такое масштабируемость
Масштабируемость — способность системы обрабатывать увеличение нагрузки путем добавления ресурсов.
Типы масштабирования
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