- 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
2142 lines
64 KiB
Markdown
2142 lines
64 KiB
Markdown
# Модуль 10: Производительность и оптимизация
|
||
|
||
## Содержание модуля
|
||
1. [Основы производительности](#основы-производительности)
|
||
2. [Профилирование и анализ](#профилирование-и-анализ)
|
||
3. [Оптимизация базы данных](#оптимизация-базы-данных)
|
||
4. [Кэширование](#кэширование)
|
||
5. [Оптимизация кода и минификация](#оптимизация-кода-и-минификация)
|
||
6. [Оптимизация сети](#оптимизация-сети)
|
||
7. [Capacity Planning](#capacity-planning)
|
||
8. [Performance Testing](#performance-testing)
|
||
|
||
---
|
||
|
||
## Основы производительности
|
||
|
||
### Что такое производительность
|
||
|
||
**Производительность** — это способность системы выполнять задачи за определенное время с использованием доступных ресурсов.
|
||
|
||
### Метрики производительности
|
||
|
||
#### 1. Throughput (Пропускная способность)
|
||
**Определение:** Количество операций, выполняемых за единицу времени.
|
||
|
||
**Технический пример - Advanced Performance Monitoring System:**
|
||
|
||
```java
|
||
// Advanced Performance Metrics Collector
|
||
@Component
|
||
public class AdvancedPerformanceMetrics {
|
||
|
||
private final MeterRegistry meterRegistry;
|
||
private final Counter requestCounter;
|
||
private final Timer requestTimer;
|
||
private final Gauge activeConnections;
|
||
private final DistributionSummary responseSize;
|
||
private final Counter errorCounter;
|
||
|
||
public AdvancedPerformanceMetrics(MeterRegistry meterRegistry) {
|
||
this.meterRegistry = meterRegistry;
|
||
|
||
// Request metrics
|
||
this.requestCounter = Counter.builder("requests.total")
|
||
.description("Total number of requests")
|
||
.tag("service", "user-service")
|
||
.register(meterRegistry);
|
||
|
||
this.requestTimer = Timer.builder("request.duration")
|
||
.description("Request processing time")
|
||
.publishPercentiles(0.5, 0.95, 0.99)
|
||
.publishPercentileHistogram()
|
||
.register(meterRegistry);
|
||
|
||
// Connection metrics
|
||
this.activeConnections = Gauge.builder("connections.active")
|
||
.description("Number of active connections")
|
||
.register(meterRegistry, this, AdvancedPerformanceMetrics::getActiveConnections);
|
||
|
||
// Response size metrics
|
||
this.responseSize = DistributionSummary.builder("response.size")
|
||
.description("Response size in bytes")
|
||
.publishPercentiles(0.5, 0.95, 0.99)
|
||
.register(meterRegistry);
|
||
|
||
// Error metrics
|
||
this.errorCounter = Counter.builder("errors.total")
|
||
.description("Total number of errors")
|
||
.register(meterRegistry);
|
||
}
|
||
|
||
public void recordRequest(Duration duration, int responseSizeBytes, boolean isError) {
|
||
requestCounter.increment();
|
||
requestTimer.record(duration);
|
||
this.responseSize.record(responseSizeBytes);
|
||
|
||
if (isError) {
|
||
errorCounter.increment();
|
||
}
|
||
}
|
||
|
||
public PerformanceReport generateReport() {
|
||
return PerformanceReport.builder()
|
||
.throughput(getThroughput())
|
||
.averageLatency(getAverageLatency())
|
||
.p95Latency(getP95Latency())
|
||
.p99Latency(getP99Latency())
|
||
.errorRate(getErrorRate())
|
||
.activeConnections(getActiveConnections())
|
||
.averageResponseSize(getAverageResponseSize())
|
||
.timestamp(LocalDateTime.now())
|
||
.build();
|
||
}
|
||
|
||
public double getThroughput() {
|
||
double totalTime = requestTimer.totalTime(TimeUnit.SECONDS);
|
||
return totalTime > 0 ? requestCounter.count() / totalTime : 0;
|
||
}
|
||
|
||
public double getAverageLatency() {
|
||
return requestTimer.mean(TimeUnit.MILLISECONDS);
|
||
}
|
||
|
||
public double getP95Latency() {
|
||
return requestTimer.percentile(0.95, TimeUnit.MILLISECONDS);
|
||
}
|
||
|
||
public double getP99Latency() {
|
||
return requestTimer.percentile(0.99, TimeUnit.MILLISECONDS);
|
||
}
|
||
|
||
public double getErrorRate() {
|
||
double totalRequests = requestCounter.count();
|
||
return totalRequests > 0 ? (errorCounter.count() / totalRequests) * 100 : 0;
|
||
}
|
||
|
||
private double getActiveConnections() {
|
||
return 0; // Placeholder
|
||
}
|
||
|
||
public double getAverageResponseSize() {
|
||
return responseSize.mean();
|
||
}
|
||
}
|
||
|
||
// Performance Optimization Service
|
||
@Service
|
||
public class PerformanceOptimizationService {
|
||
|
||
@Autowired
|
||
private AdvancedPerformanceMetrics metrics;
|
||
|
||
@Autowired
|
||
private DatabaseOptimizationService dbOptimizationService;
|
||
|
||
// Automatic performance optimization
|
||
@Scheduled(fixedRate = 60000) // Every minute
|
||
public void optimizePerformance() {
|
||
PerformanceReport report = metrics.generateReport();
|
||
|
||
if (needsOptimization(report)) {
|
||
log.warn("Performance optimization triggered: {}", report);
|
||
|
||
if (report.getAverageLatency() > 1000) {
|
||
optimizeLatency();
|
||
}
|
||
|
||
if (report.getErrorRate() > 5) {
|
||
optimizeErrorHandling();
|
||
}
|
||
|
||
if (report.getThroughput() < 100) {
|
||
optimizeThroughput();
|
||
}
|
||
}
|
||
}
|
||
|
||
private boolean needsOptimization(PerformanceReport report) {
|
||
return report.getAverageLatency() > 1000 ||
|
||
report.getErrorRate() > 5 ||
|
||
report.getThroughput() < 100 ||
|
||
report.getP95Latency() > 2000;
|
||
}
|
||
|
||
private void optimizeLatency() {
|
||
log.info("Optimizing latency...");
|
||
increaseConnectionPoolSize();
|
||
enableCompression();
|
||
optimizeJVMSettings();
|
||
}
|
||
|
||
private void optimizeErrorHandling() {
|
||
log.info("Optimizing error handling...");
|
||
enableCircuitBreaker();
|
||
enableRetryLogic();
|
||
improveErrorLogging();
|
||
}
|
||
|
||
private void optimizeThroughput() {
|
||
log.info("Optimizing throughput...");
|
||
increaseThreadPoolSize();
|
||
enableAsyncProcessing();
|
||
optimizeBatchProcessing();
|
||
}
|
||
|
||
private void increaseConnectionPoolSize() {
|
||
log.info("Increasing connection pool size");
|
||
}
|
||
|
||
private void enableCompression() {
|
||
log.info("Enabling compression");
|
||
}
|
||
|
||
private void optimizeJVMSettings() {
|
||
log.info("Optimizing JVM settings");
|
||
}
|
||
|
||
private void enableCircuitBreaker() {
|
||
log.info("Enabling circuit breaker");
|
||
}
|
||
|
||
private void enableRetryLogic() {
|
||
log.info("Enabling retry logic");
|
||
}
|
||
|
||
private void improveErrorLogging() {
|
||
log.info("Improving error logging");
|
||
}
|
||
|
||
private void increaseThreadPoolSize() {
|
||
log.info("Increasing thread pool size");
|
||
}
|
||
|
||
private void enableAsyncProcessing() {
|
||
log.info("Enabling async processing");
|
||
}
|
||
|
||
private void optimizeBatchProcessing() {
|
||
log.info("Optimizing batch processing");
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 2. Latency (Задержка)
|
||
**Определение:** Время выполнения одной операции.
|
||
|
||
```java
|
||
// Измерение latency
|
||
@Service
|
||
public class UserService {
|
||
private final Timer requestTimer;
|
||
|
||
public UserService(MeterRegistry meterRegistry) {
|
||
this.requestTimer = Timer.builder("user.service.duration")
|
||
.register(meterRegistry);
|
||
}
|
||
|
||
public User getUser(Long id) {
|
||
return requestTimer.recordCallable(() -> {
|
||
// бизнес-логика
|
||
return userRepository.findById(id);
|
||
});
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 3. Resource Utilization (Использование ресурсов)
|
||
**Определение:** Процент использования доступных ресурсов.
|
||
|
||
```java
|
||
// Мониторинг ресурсов
|
||
@Component
|
||
public class ResourceMonitor {
|
||
private final Gauge cpuUsage;
|
||
private final Gauge memoryUsage;
|
||
private final Gauge diskUsage;
|
||
|
||
public ResourceMonitor(MeterRegistry meterRegistry) {
|
||
this.cpuUsage = Gauge.builder("system.cpu.usage")
|
||
.register(meterRegistry, this, ResourceMonitor::getCpuUsage);
|
||
this.memoryUsage = Gauge.builder("system.memory.usage")
|
||
.register(meterRegistry, this, ResourceMonitor::getMemoryUsage);
|
||
this.diskUsage = Gauge.builder("system.disk.usage")
|
||
.register(meterRegistry, this, ResourceMonitor::getDiskUsage);
|
||
}
|
||
|
||
private double getCpuUsage() {
|
||
OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean();
|
||
return osBean.getProcessCpuLoad();
|
||
}
|
||
|
||
private double getMemoryUsage() {
|
||
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
|
||
MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
|
||
return (double) heapUsage.getUsed() / heapUsage.getMax();
|
||
}
|
||
|
||
private double getDiskUsage() {
|
||
File root = new File("/");
|
||
long totalSpace = root.getTotalSpace();
|
||
long freeSpace = root.getFreeSpace();
|
||
return (double) (totalSpace - freeSpace) / totalSpace;
|
||
}
|
||
}
|
||
```
|
||
|
||
### Производительность по уровням
|
||
|
||
#### 1. Application Level
|
||
```java
|
||
// Оптимизация на уровне приложения
|
||
@Service
|
||
public class OptimizedUserService {
|
||
@Autowired
|
||
private UserRepository userRepository;
|
||
|
||
@Autowired
|
||
private UserCache userCache;
|
||
|
||
// Оптимизация: кэширование
|
||
@Cacheable("users")
|
||
public User getUser(Long id) {
|
||
return userRepository.findById(id);
|
||
}
|
||
|
||
// Оптимизация: batch операции
|
||
public List<User> getUsers(List<Long> ids) {
|
||
return userRepository.findByIdIn(ids);
|
||
}
|
||
|
||
// Оптимизация: асинхронная обработка
|
||
@Async
|
||
public CompletableFuture<Void> processUserAsync(User user) {
|
||
// асинхронная обработка
|
||
return CompletableFuture.completedFuture(null);
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 2. Database Level
|
||
```sql
|
||
-- Оптимизация запросов
|
||
-- Плохо: N+1 проблема
|
||
SELECT * FROM users;
|
||
SELECT * FROM orders WHERE user_id = 1;
|
||
SELECT * FROM orders WHERE user_id = 2;
|
||
-- ... для каждого пользователя
|
||
|
||
-- Хорошо: JOIN запрос
|
||
SELECT u.*, o.*
|
||
FROM users u
|
||
LEFT JOIN orders o ON u.id = o.user_id;
|
||
|
||
-- Хорошо: индексы
|
||
CREATE INDEX idx_user_email ON users(email);
|
||
CREATE INDEX idx_order_user_id ON orders(user_id);
|
||
CREATE INDEX idx_order_status ON orders(status);
|
||
```
|
||
|
||
#### 3. Infrastructure Level
|
||
```yaml
|
||
# Оптимизация инфраструктуры
|
||
apiVersion: apps/v1
|
||
kind: Deployment
|
||
metadata:
|
||
name: user-service
|
||
spec:
|
||
replicas: 3
|
||
template:
|
||
spec:
|
||
containers:
|
||
- name: user-service
|
||
image: user-service:latest
|
||
resources:
|
||
requests:
|
||
memory: "256Mi"
|
||
cpu: "250m"
|
||
limits:
|
||
memory: "512Mi"
|
||
cpu: "500m"
|
||
env:
|
||
- name: JAVA_OPTS
|
||
value: "-Xms256m -Xmx512m -XX:+UseG1GC"
|
||
```
|
||
|
||
---
|
||
|
||
## Профилирование и анализ
|
||
|
||
### Инструменты профилирования
|
||
|
||
#### 1. JProfiler
|
||
```java
|
||
// Профилирование с JProfiler
|
||
@Service
|
||
public class ProfiledUserService {
|
||
@Autowired
|
||
private UserRepository userRepository;
|
||
|
||
public User getUser(Long id) {
|
||
// JProfiler покажет время выполнения этого метода
|
||
return userRepository.findById(id);
|
||
}
|
||
|
||
public List<User> searchUsers(String query) {
|
||
// Профилирование покажет узкие места
|
||
List<User> users = userRepository.findByNameContaining(query);
|
||
|
||
// Профилирование покажет время обработки
|
||
return users.stream()
|
||
.filter(user -> user.isActive())
|
||
.collect(Collectors.toList());
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 2. VisualVM
|
||
```java
|
||
// Мониторинг с VisualVM
|
||
@Component
|
||
public class PerformanceMonitor {
|
||
private final MemoryMXBean memoryBean;
|
||
private final ThreadMXBean threadBean;
|
||
|
||
public PerformanceMonitor() {
|
||
this.memoryBean = ManagementFactory.getMemoryMXBean();
|
||
this.threadBean = ManagementFactory.getThreadMXBean();
|
||
}
|
||
|
||
@Scheduled(fixedRate = 5000)
|
||
public void logPerformanceMetrics() {
|
||
// Мониторинг памяти
|
||
MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
|
||
log.info("Heap usage: {} / {}", heapUsage.getUsed(), heapUsage.getMax());
|
||
|
||
// Мониторинг потоков
|
||
int threadCount = threadBean.getThreadCount();
|
||
log.info("Thread count: {}", threadCount);
|
||
|
||
// Мониторинг CPU
|
||
OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean();
|
||
double cpuUsage = osBean.getProcessCpuLoad();
|
||
log.info("CPU usage: {}%", cpuUsage * 100);
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 3. Application Performance Monitoring (APM)
|
||
```java
|
||
// APM интеграция
|
||
@Service
|
||
public class APMUserService {
|
||
@Autowired
|
||
private NewRelic newRelic;
|
||
|
||
public User getUser(Long id) {
|
||
newRelic.addCustomParameter("user.id", id);
|
||
|
||
try {
|
||
User user = userRepository.findById(id);
|
||
newRelic.recordMetric("User/Found", 1);
|
||
return user;
|
||
} catch (Exception e) {
|
||
newRelic.recordMetric("User/Error", 1);
|
||
newRelic.recordException(e);
|
||
throw e;
|
||
}
|
||
}
|
||
|
||
public List<User> searchUsers(String query) {
|
||
newRelic.addCustomParameter("search.query", query);
|
||
|
||
List<User> users = userRepository.findByNameContaining(query);
|
||
newRelic.recordMetric("User/SearchResults", users.size());
|
||
|
||
return users;
|
||
}
|
||
}
|
||
```
|
||
|
||
### Анализ производительности
|
||
|
||
#### 1. Bottleneck Analysis
|
||
```java
|
||
// Анализ узких мест
|
||
@Service
|
||
public class BottleneckAnalyzer {
|
||
private final Timer databaseTimer;
|
||
private final Timer cacheTimer;
|
||
private final Timer networkTimer;
|
||
|
||
public BottleneckAnalyzer(MeterRegistry meterRegistry) {
|
||
this.databaseTimer = Timer.builder("database.operation.duration")
|
||
.register(meterRegistry);
|
||
this.cacheTimer = Timer.builder("cache.operation.duration")
|
||
.register(meterRegistry);
|
||
this.networkTimer = Timer.builder("network.operation.duration")
|
||
.register(meterRegistry);
|
||
}
|
||
|
||
public User getUser(Long id) {
|
||
// Анализ времени выполнения каждой операции
|
||
User user = databaseTimer.recordCallable(() -> {
|
||
return userRepository.findById(id);
|
||
});
|
||
|
||
if (user != null) {
|
||
cacheTimer.recordCallable(() -> {
|
||
userCache.put(id, user);
|
||
});
|
||
}
|
||
|
||
return user;
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 2. Memory Analysis
|
||
```java
|
||
// Анализ памяти
|
||
@Component
|
||
public class MemoryAnalyzer {
|
||
private final MemoryMXBean memoryBean;
|
||
|
||
public MemoryAnalyzer() {
|
||
this.memoryBean = ManagementFactory.getMemoryMXBean();
|
||
}
|
||
|
||
@Scheduled(fixedRate = 10000)
|
||
public void analyzeMemory() {
|
||
MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
|
||
MemoryUsage nonHeapUsage = memoryBean.getNonHeapMemoryUsage();
|
||
|
||
log.info("Heap: {} / {} ({}%)",
|
||
heapUsage.getUsed(),
|
||
heapUsage.getMax(),
|
||
(heapUsage.getUsed() * 100) / heapUsage.getMax());
|
||
|
||
log.info("Non-Heap: {} / {} ({}%)",
|
||
nonHeapUsage.getUsed(),
|
||
nonHeapUsage.getMax(),
|
||
(nonHeapUsage.getUsed() * 100) / nonHeapUsage.getMax());
|
||
|
||
// Анализ утечек памяти
|
||
if (heapUsage.getUsed() > heapUsage.getMax() * 0.8) {
|
||
log.warn("High memory usage detected!");
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Оптимизация базы данных
|
||
|
||
### Оптимизация запросов
|
||
|
||
#### 1. Query Optimization
|
||
```sql
|
||
-- Оптимизация запросов
|
||
-- Плохо: медленный запрос
|
||
SELECT * FROM users u
|
||
JOIN orders o ON u.id = o.user_id
|
||
WHERE u.created_at > '2023-01-01'
|
||
AND o.status = 'COMPLETED';
|
||
|
||
-- Хорошо: оптимизированный запрос
|
||
SELECT u.id, u.name, u.email, COUNT(o.id) as order_count
|
||
FROM users u
|
||
JOIN orders o ON u.id = o.user_id
|
||
WHERE u.created_at > '2023-01-01'
|
||
AND o.status = 'COMPLETED'
|
||
GROUP BY u.id, u.name, u.email;
|
||
|
||
-- Хорошо: использование индексов
|
||
CREATE INDEX idx_user_created_at ON users(created_at);
|
||
CREATE INDEX idx_order_status ON orders(status);
|
||
CREATE INDEX idx_order_user_status ON orders(user_id, status);
|
||
```
|
||
|
||
#### 2. Connection Pooling
|
||
```java
|
||
// Настройка пула соединений
|
||
@Configuration
|
||
public class DatabaseConfig {
|
||
@Bean
|
||
public DataSource dataSource() {
|
||
HikariConfig config = new HikariConfig();
|
||
config.setJdbcUrl("jdbc:postgresql://localhost:5432/mydb");
|
||
config.setUsername("user");
|
||
config.setPassword("password");
|
||
|
||
// Оптимизация пула соединений
|
||
config.setMaximumPoolSize(20);
|
||
config.setMinimumIdle(5);
|
||
config.setConnectionTimeout(30000);
|
||
config.setIdleTimeout(600000);
|
||
config.setMaxLifetime(1800000);
|
||
config.setLeakDetectionThreshold(60000);
|
||
|
||
return new HikariDataSource(config);
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 3. Batch Operations
|
||
```java
|
||
// Batch операции
|
||
@Repository
|
||
public class OptimizedUserRepository {
|
||
@Autowired
|
||
private JdbcTemplate jdbcTemplate;
|
||
|
||
// Batch вставка
|
||
public void saveUsers(List<User> users) {
|
||
String sql = "INSERT INTO users (name, email, created_at) VALUES (?, ?, ?)";
|
||
|
||
jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
|
||
@Override
|
||
public void setValues(PreparedStatement ps, int i) throws SQLException {
|
||
User user = users.get(i);
|
||
ps.setString(1, user.getName());
|
||
ps.setString(2, user.getEmail());
|
||
ps.setTimestamp(3, Timestamp.valueOf(user.getCreatedAt()));
|
||
}
|
||
|
||
@Override
|
||
public int getBatchSize() {
|
||
return users.size();
|
||
}
|
||
});
|
||
}
|
||
|
||
// Batch обновление
|
||
public void updateUsers(List<User> users) {
|
||
String sql = "UPDATE users SET name = ?, email = ? WHERE id = ?";
|
||
|
||
jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
|
||
@Override
|
||
public void setValues(PreparedStatement ps, int i) throws SQLException {
|
||
User user = users.get(i);
|
||
ps.setString(1, user.getName());
|
||
ps.setString(2, user.getEmail());
|
||
ps.setLong(3, user.getId());
|
||
}
|
||
|
||
@Override
|
||
public int getBatchSize() {
|
||
return users.size();
|
||
}
|
||
});
|
||
}
|
||
}
|
||
```
|
||
|
||
### Настройка базы данных
|
||
|
||
#### 1. Index Optimization
|
||
```sql
|
||
-- Оптимизация индексов
|
||
-- Анализ использования индексов
|
||
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'user@example.com';
|
||
|
||
-- Создание составных индексов
|
||
CREATE INDEX idx_user_email_status ON users(email, status);
|
||
|
||
-- Создание частичных индексов
|
||
CREATE INDEX idx_active_users ON users(email) WHERE status = 'ACTIVE';
|
||
|
||
-- Создание функциональных индексов
|
||
CREATE INDEX idx_user_lower_email ON users(LOWER(email));
|
||
```
|
||
|
||
#### 2. Query Plan Analysis
|
||
```sql
|
||
-- Анализ планов выполнения
|
||
EXPLAIN (ANALYZE, BUFFERS)
|
||
SELECT u.name, COUNT(o.id) as order_count
|
||
FROM users u
|
||
LEFT JOIN orders o ON u.id = o.user_id
|
||
WHERE u.created_at > '2023-01-01'
|
||
GROUP BY u.id, u.name;
|
||
|
||
-- Анализ статистики
|
||
ANALYZE users;
|
||
ANALYZE orders;
|
||
|
||
-- Обновление статистики
|
||
UPDATE pg_stat_user_tables
|
||
SET n_tup_ins = 0, n_tup_upd = 0, n_tup_del = 0
|
||
WHERE relname = 'users';
|
||
```
|
||
|
||
---
|
||
|
||
## Кэширование
|
||
|
||
### Стратегии кэширования
|
||
|
||
#### 1. Cache-Aside Pattern
|
||
**Технический пример - Advanced Multi-Level Caching System:**
|
||
|
||
```java
|
||
// Advanced Cache Management Service
|
||
@Service
|
||
public class AdvancedCacheService {
|
||
|
||
@Autowired
|
||
private RedisTemplate<String, Object> redisTemplate;
|
||
|
||
@Autowired
|
||
private CacheMetricsService cacheMetricsService;
|
||
|
||
// Multi-level caching
|
||
private final Map<String, Object> localCache = new ConcurrentHashMap<>();
|
||
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
|
||
|
||
public <T> T get(String cacheKey, Class<T> type, Supplier<T> dataLoader) {
|
||
// Level 1: Local cache
|
||
T value = getFromLocalCache(cacheKey, type);
|
||
if (value != null) {
|
||
cacheMetricsService.recordCacheHit(cacheKey, CacheLevel.LOCAL);
|
||
return value;
|
||
}
|
||
|
||
// Level 2: Redis cache
|
||
value = getFromRedisCache(cacheKey, type);
|
||
if (value != null) {
|
||
putToLocalCache(cacheKey, value, Duration.ofMinutes(5));
|
||
cacheMetricsService.recordCacheHit(cacheKey, CacheLevel.REDIS);
|
||
return value;
|
||
}
|
||
|
||
// Level 3: Data source
|
||
cacheMetricsService.recordCacheMiss(cacheKey);
|
||
value = dataLoader.get();
|
||
|
||
if (value != null) {
|
||
putToLocalCache(cacheKey, value, Duration.ofMinutes(5));
|
||
putToRedisCache(cacheKey, value, Duration.ofMinutes(30));
|
||
}
|
||
|
||
return value;
|
||
}
|
||
|
||
public <T> CompletableFuture<T> getAsync(String cacheKey, Class<T> type, Supplier<T> dataLoader) {
|
||
return CompletableFuture.supplyAsync(() -> get(cacheKey, type, dataLoader), scheduler);
|
||
}
|
||
|
||
public <T> List<T> getBatch(List<String> cacheKeys, Class<T> type, Function<List<String>, List<T>> dataLoader) {
|
||
List<T> results = new ArrayList<>();
|
||
List<String> missingKeys = new ArrayList<>();
|
||
|
||
// Check local cache first
|
||
for (String key : cacheKeys) {
|
||
T value = getFromLocalCache(key, type);
|
||
if (value != null) {
|
||
results.add(value);
|
||
cacheMetricsService.recordCacheHit(key, CacheLevel.LOCAL);
|
||
} else {
|
||
missingKeys.add(key);
|
||
}
|
||
}
|
||
|
||
if (missingKeys.isEmpty()) {
|
||
return results;
|
||
}
|
||
|
||
// Check Redis cache
|
||
List<T> redisValues = getFromRedisCacheBatch(missingKeys, type);
|
||
List<String> stillMissingKeys = new ArrayList<>();
|
||
|
||
for (int i = 0; i < missingKeys.size(); i++) {
|
||
String key = missingKeys.get(i);
|
||
T value = redisValues.get(i);
|
||
|
||
if (value != null) {
|
||
results.add(value);
|
||
putToLocalCache(key, value, Duration.ofMinutes(5));
|
||
cacheMetricsService.recordCacheHit(key, CacheLevel.REDIS);
|
||
} else {
|
||
stillMissingKeys.add(key);
|
||
}
|
||
}
|
||
|
||
// Load missing data
|
||
if (!stillMissingKeys.isEmpty()) {
|
||
List<T> loadedData = dataLoader.apply(stillMissingKeys);
|
||
|
||
for (int i = 0; i < stillMissingKeys.size(); i++) {
|
||
String key = stillMissingKeys.get(i);
|
||
T value = loadedData.get(i);
|
||
|
||
if (value != null) {
|
||
results.add(value);
|
||
putToLocalCache(key, value, Duration.ofMinutes(5));
|
||
putToRedisCache(key, value, Duration.ofMinutes(30));
|
||
}
|
||
|
||
cacheMetricsService.recordCacheMiss(key);
|
||
}
|
||
}
|
||
|
||
return results;
|
||
}
|
||
|
||
public void put(String cacheKey, Object value) {
|
||
putToLocalCache(cacheKey, value, Duration.ofMinutes(5));
|
||
putToRedisCache(cacheKey, value, Duration.ofMinutes(30));
|
||
cacheMetricsService.recordCachePut(cacheKey);
|
||
}
|
||
|
||
public void evict(String cacheKey) {
|
||
localCache.remove(cacheKey);
|
||
redisTemplate.delete(cacheKey);
|
||
cacheMetricsService.recordCacheEvict(cacheKey);
|
||
}
|
||
|
||
public void evictPattern(String pattern) {
|
||
localCache.entrySet().removeIf(entry -> entry.getKey().matches(pattern));
|
||
|
||
Set<String> keys = redisTemplate.keys(pattern);
|
||
if (keys != null && !keys.isEmpty()) {
|
||
redisTemplate.delete(keys);
|
||
}
|
||
|
||
cacheMetricsService.recordCacheEvictPattern(pattern);
|
||
}
|
||
|
||
// Cache warming
|
||
public void warmCache(List<String> keys, Supplier<List<Object>> dataLoader) {
|
||
log.info("Warming cache with {} keys", keys.size());
|
||
|
||
List<Object> data = dataLoader.get();
|
||
|
||
for (int i = 0; i < keys.size() && i < data.size(); i++) {
|
||
String key = keys.get(i);
|
||
Object value = data.get(i);
|
||
|
||
if (value != null) {
|
||
putToLocalCache(key, value, Duration.ofMinutes(5));
|
||
putToRedisCache(key, value, Duration.ofMinutes(30));
|
||
}
|
||
}
|
||
|
||
log.info("Cache warming completed");
|
||
}
|
||
|
||
private <T> T getFromLocalCache(String key, Class<T> type) {
|
||
Object value = localCache.get(key);
|
||
return type.cast(value);
|
||
}
|
||
|
||
private <T> T getFromRedisCache(String key, Class<T> type) {
|
||
try {
|
||
Object value = redisTemplate.opsForValue().get(key);
|
||
return type.cast(value);
|
||
} catch (Exception e) {
|
||
log.warn("Error getting from Redis cache: {}", e.getMessage());
|
||
return null;
|
||
}
|
||
}
|
||
|
||
private <T> List<T> getFromRedisCacheBatch(List<String> keys, Class<T> type) {
|
||
try {
|
||
List<Object> values = redisTemplate.opsForValue().multiGet(keys);
|
||
return values.stream()
|
||
.map(type::cast)
|
||
.collect(Collectors.toList());
|
||
} catch (Exception e) {
|
||
log.warn("Error getting batch from Redis cache: {}", e.getMessage());
|
||
return keys.stream().map(k -> (T) null).collect(Collectors.toList());
|
||
}
|
||
}
|
||
|
||
private void putToLocalCache(String key, Object value, Duration ttl) {
|
||
localCache.put(key, value);
|
||
|
||
scheduler.schedule(() -> localCache.remove(key), ttl.toMillis(), TimeUnit.MILLISECONDS);
|
||
}
|
||
|
||
private void putToRedisCache(String key, Object value, Duration ttl) {
|
||
try {
|
||
redisTemplate.opsForValue().set(key, value, ttl);
|
||
} catch (Exception e) {
|
||
log.warn("Error putting to Redis cache: {}", e.getMessage());
|
||
}
|
||
}
|
||
}
|
||
|
||
// Cache Metrics Service
|
||
@Service
|
||
public class CacheMetricsService {
|
||
|
||
private final MeterRegistry meterRegistry;
|
||
private final Map<String, CacheMetrics> metrics = new ConcurrentHashMap<>();
|
||
|
||
public CacheMetricsService(MeterRegistry meterRegistry) {
|
||
this.meterRegistry = meterRegistry;
|
||
}
|
||
|
||
public void recordCacheHit(String cacheKey, CacheLevel level) {
|
||
String cacheName = extractCacheName(cacheKey);
|
||
CacheMetrics cacheMetrics = getOrCreateMetrics(cacheName);
|
||
cacheMetrics.recordHit(level);
|
||
|
||
Counter.builder("cache.hits")
|
||
.tag("cache", cacheName)
|
||
.tag("level", level.name())
|
||
.register(meterRegistry)
|
||
.increment();
|
||
}
|
||
|
||
public void recordCacheMiss(String cacheKey) {
|
||
String cacheName = extractCacheName(cacheKey);
|
||
CacheMetrics cacheMetrics = getOrCreateMetrics(cacheName);
|
||
cacheMetrics.recordMiss();
|
||
|
||
Counter.builder("cache.misses")
|
||
.tag("cache", cacheName)
|
||
.register(meterRegistry)
|
||
.increment();
|
||
}
|
||
|
||
public void recordCachePut(String cacheKey) {
|
||
String cacheName = extractCacheName(cacheKey);
|
||
|
||
Counter.builder("cache.puts")
|
||
.tag("cache", cacheName)
|
||
.register(meterRegistry)
|
||
.increment();
|
||
}
|
||
|
||
public void recordCacheEvict(String cacheKey) {
|
||
String cacheName = extractCacheName(cacheKey);
|
||
|
||
Counter.builder("cache.evicts")
|
||
.tag("cache", cacheName)
|
||
.register(meterRegistry)
|
||
.increment();
|
||
}
|
||
|
||
public void recordCacheEvictPattern(String pattern) {
|
||
Counter.builder("cache.evicts.pattern")
|
||
.tag("pattern", pattern)
|
||
.register(meterRegistry)
|
||
.increment();
|
||
}
|
||
|
||
public double getHitRate(String cacheName) {
|
||
CacheMetrics cacheMetrics = metrics.get(cacheName);
|
||
return cacheMetrics != null ? cacheMetrics.getHitRate() : 0.0;
|
||
}
|
||
|
||
public double getMissRate(String cacheName) {
|
||
CacheMetrics cacheMetrics = metrics.get(cacheName);
|
||
return cacheMetrics != null ? cacheMetrics.getMissRate() : 0.0;
|
||
}
|
||
|
||
public double getAverageAccessTime(String cacheName) {
|
||
CacheMetrics cacheMetrics = metrics.get(cacheName);
|
||
return cacheMetrics != null ? cacheMetrics.getAverageAccessTime() : 0.0;
|
||
}
|
||
|
||
private CacheMetrics getOrCreateMetrics(String cacheName) {
|
||
return metrics.computeIfAbsent(cacheName, k -> new CacheMetrics());
|
||
}
|
||
|
||
private String extractCacheName(String cacheKey) {
|
||
if (cacheKey.contains(":")) {
|
||
return cacheKey.split(":")[0];
|
||
}
|
||
return "default";
|
||
}
|
||
}
|
||
|
||
// Cache Level Enum
|
||
public enum CacheLevel {
|
||
LOCAL, REDIS, DATABASE
|
||
}
|
||
|
||
// Cache Metrics
|
||
public class CacheMetrics {
|
||
private final AtomicLong hits = new AtomicLong(0);
|
||
private final AtomicLong misses = new AtomicLong(0);
|
||
private final AtomicLong puts = new AtomicLong(0);
|
||
private final AtomicLong evicts = new AtomicLong(0);
|
||
private final AtomicLong totalAccessTime = new AtomicLong(0);
|
||
private final AtomicLong accessCount = new AtomicLong(0);
|
||
|
||
public void recordHit(CacheLevel level) {
|
||
hits.incrementAndGet();
|
||
recordAccessTime();
|
||
}
|
||
|
||
public void recordMiss() {
|
||
misses.incrementAndGet();
|
||
recordAccessTime();
|
||
}
|
||
|
||
public void recordPut() {
|
||
puts.incrementAndGet();
|
||
}
|
||
|
||
public void recordEvict() {
|
||
evicts.incrementAndGet();
|
||
}
|
||
|
||
private void recordAccessTime() {
|
||
long startTime = System.nanoTime();
|
||
accessCount.incrementAndGet();
|
||
totalAccessTime.addAndGet(System.nanoTime() - startTime);
|
||
}
|
||
|
||
public double getHitRate() {
|
||
long total = hits.get() + misses.get();
|
||
return total > 0 ? (double) hits.get() / total : 0.0;
|
||
}
|
||
|
||
public double getMissRate() {
|
||
long total = hits.get() + misses.get();
|
||
return total > 0 ? (double) misses.get() / total : 0.0;
|
||
}
|
||
|
||
public double getAverageAccessTime() {
|
||
long count = accessCount.get();
|
||
return count > 0 ? (double) totalAccessTime.get() / count : 0.0;
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 2. Write-Through Pattern
|
||
```java
|
||
// Write-Through реализация
|
||
@Service
|
||
public class WriteThroughUserService {
|
||
@Autowired
|
||
private UserRepository userRepository;
|
||
|
||
@Autowired
|
||
private RedisTemplate<String, User> redisTemplate;
|
||
|
||
public void createUser(User user) {
|
||
// Сохраняем в БД
|
||
user = userRepository.save(user);
|
||
|
||
// Сохраняем в кэш
|
||
String cacheKey = "user:" + user.getId();
|
||
redisTemplate.opsForValue().set(cacheKey, user, Duration.ofMinutes(30));
|
||
}
|
||
|
||
public void updateUser(User user) {
|
||
// Обновляем БД
|
||
user = userRepository.save(user);
|
||
|
||
// Обновляем кэш
|
||
String cacheKey = "user:" + user.getId();
|
||
redisTemplate.opsForValue().set(cacheKey, user, Duration.ofMinutes(30));
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 3. Write-Behind Pattern
|
||
```java
|
||
// Write-Behind реализация
|
||
@Service
|
||
public class WriteBehindUserService {
|
||
@Autowired
|
||
private UserRepository userRepository;
|
||
|
||
@Autowired
|
||
private RedisTemplate<String, User> redisTemplate;
|
||
|
||
@Async
|
||
public void createUser(User user) {
|
||
// Сохраняем в кэш сразу
|
||
String cacheKey = "user:" + user.getId();
|
||
redisTemplate.opsForValue().set(cacheKey, user, Duration.ofMinutes(30));
|
||
|
||
// Сохраняем в БД асинхронно
|
||
CompletableFuture.runAsync(() -> {
|
||
userRepository.save(user);
|
||
});
|
||
}
|
||
}
|
||
```
|
||
|
||
### Многоуровневое кэширование
|
||
|
||
#### 1. L1 Cache (Application Level)
|
||
```java
|
||
// L1 кэш (в памяти приложения)
|
||
@Service
|
||
public class L1CacheUserService {
|
||
private final Map<Long, User> localCache = new ConcurrentHashMap<>();
|
||
private final Timer cacheTimer;
|
||
|
||
public L1CacheUserService(MeterRegistry meterRegistry) {
|
||
this.cacheTimer = Timer.builder("l1.cache.duration")
|
||
.register(meterRegistry);
|
||
}
|
||
|
||
public User getUser(Long id) {
|
||
return cacheTimer.recordCallable(() -> {
|
||
// Проверяем L1 кэш
|
||
User user = localCache.get(id);
|
||
if (user != null) {
|
||
return user;
|
||
}
|
||
|
||
// Загружаем из L2 кэша или БД
|
||
user = loadUserFromL2OrDatabase(id);
|
||
if (user != null) {
|
||
localCache.put(id, user);
|
||
}
|
||
|
||
return user;
|
||
});
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 2. L2 Cache (Distributed Cache)
|
||
```java
|
||
// L2 кэш (распределенный)
|
||
@Service
|
||
public class L2CacheUserService {
|
||
@Autowired
|
||
private RedisTemplate<String, User> redisTemplate;
|
||
|
||
@Autowired
|
||
private UserRepository userRepository;
|
||
|
||
public User getUser(Long id) {
|
||
String cacheKey = "user:" + id;
|
||
|
||
// Проверяем L2 кэш
|
||
User user = redisTemplate.opsForValue().get(cacheKey);
|
||
if (user != null) {
|
||
return user;
|
||
}
|
||
|
||
// Загружаем из БД
|
||
user = userRepository.findById(id);
|
||
if (user != null) {
|
||
// Сохраняем в L2 кэш
|
||
redisTemplate.opsForValue().set(cacheKey, user, Duration.ofMinutes(30));
|
||
}
|
||
|
||
return user;
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Оптимизация кода и минификация
|
||
|
||
### Принципы оптимизации кода
|
||
|
||
#### 1. Минификация JavaScript/TypeScript
|
||
|
||
**Цели минификации:**
|
||
- Уменьшение размера файлов
|
||
- Ускорение загрузки страниц
|
||
- Снижение трафика
|
||
- Улучшение производительности
|
||
|
||
**Инструменты минификации:**
|
||
|
||
```javascript
|
||
// webpack.config.js - Конфигурация минификации
|
||
const TerserPlugin = require('terser-webpack-plugin');
|
||
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
|
||
|
||
module.exports = {
|
||
mode: 'production',
|
||
optimization: {
|
||
minimize: true,
|
||
minimizer: [
|
||
new TerserPlugin({
|
||
terserOptions: {
|
||
compress: {
|
||
drop_console: true, // Удаление console.log
|
||
drop_debugger: true, // Удаление debugger
|
||
pure_funcs: ['console.log'], // Удаление чистых функций
|
||
dead_code: true, // Удаление мертвого кода
|
||
unused: true, // Удаление неиспользуемых переменных
|
||
},
|
||
mangle: {
|
||
toplevel: true, // Обфускация глобальных переменных
|
||
properties: {
|
||
regex: /^_/ // Обфускация приватных свойств
|
||
}
|
||
},
|
||
format: {
|
||
comments: false // Удаление комментариев
|
||
}
|
||
},
|
||
extractComments: false
|
||
}),
|
||
new CssMinimizerPlugin({
|
||
minimizerOptions: {
|
||
preset: [
|
||
'default',
|
||
{
|
||
discardComments: { removeAll: true },
|
||
normalizeWhitespace: true,
|
||
colormin: true,
|
||
minifySelectors: true
|
||
}
|
||
]
|
||
}
|
||
})
|
||
],
|
||
splitChunks: {
|
||
chunks: 'all',
|
||
cacheGroups: {
|
||
vendor: {
|
||
test: /[\\/]node_modules[\\/]/,
|
||
name: 'vendors',
|
||
chunks: 'all',
|
||
},
|
||
common: {
|
||
name: 'common',
|
||
minChunks: 2,
|
||
chunks: 'all',
|
||
enforce: true
|
||
}
|
||
}
|
||
}
|
||
}
|
||
};
|
||
```
|
||
|
||
**Java примеры - Оптимизация сборки:**
|
||
|
||
```gradle
|
||
// build.gradle - Оптимизация Java приложения
|
||
plugins {
|
||
id 'java'
|
||
id 'org.springframework.boot' version '3.0.0'
|
||
}
|
||
|
||
java {
|
||
toolchain {
|
||
languageVersion = JavaLanguageVersion.of(17)
|
||
}
|
||
}
|
||
|
||
tasks.withType(JavaCompile) {
|
||
options.compilerArgs += [
|
||
'-parameters', // Сохранение имен параметров
|
||
'-Xlint:unchecked', // Предупреждения о небезопасных операциях
|
||
'-Xlint:deprecation' // Предупреждения об устаревших API
|
||
]
|
||
}
|
||
|
||
// Оптимизация JAR файла
|
||
jar {
|
||
enabled = false
|
||
archiveClassifier = ''
|
||
}
|
||
|
||
bootJar {
|
||
enabled = true
|
||
archiveClassifier = ''
|
||
|
||
// Исключение ненужных файлов
|
||
exclude 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA'
|
||
|
||
// Оптимизация манифеста
|
||
manifest {
|
||
attributes(
|
||
'Main-Class': 'com.example.Application',
|
||
'Implementation-Version': project.version,
|
||
'Built-By': System.getProperty('user.name'),
|
||
'Built-Date': new Date().toString()
|
||
)
|
||
}
|
||
}
|
||
|
||
// ProGuard для обфускации и минификации
|
||
buildscript {
|
||
dependencies {
|
||
classpath 'com.guardsquare:proguard-gradle:7.2.2'
|
||
}
|
||
}
|
||
|
||
apply plugin: 'com.guardsquare.proguard'
|
||
|
||
proguard {
|
||
configurations {
|
||
release {
|
||
defaultConfiguration 'proguard-android-optimize.txt'
|
||
|
||
// Правила для Spring Boot
|
||
configuration '''
|
||
-keep class org.springframework.** { *; }
|
||
-keep class com.example.** { *; }
|
||
-dontwarn org.springframework.**
|
||
-dontwarn javax.annotation.**
|
||
|
||
# Оптимизация
|
||
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
|
||
-optimizationpasses 5
|
||
-allowaccessmodification
|
||
-repackageclasses ''
|
||
'''
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**Rust примеры - Оптимизация компиляции:**
|
||
|
||
```toml
|
||
# Cargo.toml - Оптимизация Rust приложения
|
||
[package]
|
||
name = "my-app"
|
||
version = "0.1.0"
|
||
edition = "2021"
|
||
|
||
[profile.release]
|
||
# Максимальная оптимизация
|
||
opt-level = 3
|
||
# Оптимизация размера
|
||
lto = true
|
||
# Удаление отладочной информации
|
||
debug = false
|
||
# Оптимизация кода
|
||
codegen-units = 1
|
||
# Оптимизация паники
|
||
panic = "abort"
|
||
# Оптимизация размера
|
||
strip = true
|
||
|
||
[profile.release.package."*"]
|
||
# Оптимизация всех зависимостей
|
||
opt-level = 3
|
||
|
||
# Дополнительные флаги компиляции
|
||
[build]
|
||
rustflags = [
|
||
"-C", "target-cpu=native", # Оптимизация под текущий CPU
|
||
"-C", "link-arg=-s", # Удаление символов
|
||
"-C", "opt-level=3", # Максимальная оптимизация
|
||
"-C", "lto=fat", # Link Time Optimization
|
||
]
|
||
```
|
||
|
||
```rust
|
||
// main.rs - Оптимизированный код
|
||
#![allow(dead_code)]
|
||
|
||
use std::sync::Arc;
|
||
use tokio::sync::RwLock;
|
||
|
||
// Оптимизированная структура данных
|
||
#[derive(Debug, Clone)]
|
||
pub struct OptimizedUser {
|
||
id: u64,
|
||
name: String,
|
||
email: String,
|
||
// Использование более компактных типов
|
||
status: u8, // вместо enum
|
||
created_at: u32, // timestamp как u32
|
||
}
|
||
|
||
impl OptimizedUser {
|
||
pub fn new(id: u64, name: String, email: String) -> Self {
|
||
Self {
|
||
id,
|
||
name,
|
||
email,
|
||
status: 1, // ACTIVE
|
||
created_at: chrono::Utc::now().timestamp() as u32,
|
||
}
|
||
}
|
||
|
||
// Inline функции для критического пути
|
||
#[inline(always)]
|
||
pub fn is_active(&self) -> bool {
|
||
self.status == 1
|
||
}
|
||
|
||
#[inline(always)]
|
||
pub fn get_id(&self) -> u64 {
|
||
self.id
|
||
}
|
||
}
|
||
|
||
// Оптимизированный сервис с пулом соединений
|
||
pub struct OptimizedUserService {
|
||
users: Arc<RwLock<Vec<OptimizedUser>>>,
|
||
// Предварительно выделенная память
|
||
capacity: usize,
|
||
}
|
||
|
||
impl OptimizedUserService {
|
||
pub fn new(capacity: usize) -> Self {
|
||
Self {
|
||
users: Arc::new(RwLock::new(Vec::with_capacity(capacity))),
|
||
capacity,
|
||
}
|
||
}
|
||
|
||
// Batch операции для оптимизации
|
||
pub async fn add_users_batch(&self, users: Vec<OptimizedUser>) {
|
||
let mut user_list = self.users.write().await;
|
||
user_list.reserve(users.len());
|
||
user_list.extend(users);
|
||
}
|
||
|
||
// Оптимизированный поиск
|
||
pub async fn find_user_by_id(&self, id: u64) -> Option<OptimizedUser> {
|
||
let user_list = self.users.read().await;
|
||
user_list.iter().find(|u| u.id == id).cloned()
|
||
}
|
||
}
|
||
```
|
||
|
||
**Python примеры - Оптимизация:**
|
||
|
||
```python
|
||
# Оптимизация Python приложения
|
||
import sys
|
||
from typing import List, Dict, Optional
|
||
from dataclasses import dataclass
|
||
from functools import lru_cache
|
||
import asyncio
|
||
import aiohttp
|
||
|
||
# Оптимизированная структура данных
|
||
@dataclass(frozen=True, slots=True) # slots для экономии памяти
|
||
class OptimizedUser:
|
||
id: int
|
||
name: str
|
||
email: str
|
||
status: int # 1 = active, 0 = inactive
|
||
|
||
def is_active(self) -> bool:
|
||
return self.status == 1
|
||
|
||
# Кэширование для оптимизации
|
||
class OptimizedUserService:
|
||
def __init__(self):
|
||
self._users_cache: Dict[int, OptimizedUser] = {}
|
||
self._session: Optional[aiohttp.ClientSession] = None
|
||
|
||
async def __aenter__(self):
|
||
self._session = aiohttp.ClientSession(
|
||
connector=aiohttp.TCPConnector(
|
||
limit=100, # Пул соединений
|
||
limit_per_host=30,
|
||
ttl_dns_cache=300,
|
||
use_dns_cache=True,
|
||
),
|
||
timeout=aiohttp.ClientTimeout(total=30)
|
||
)
|
||
return self
|
||
|
||
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
||
if self._session:
|
||
await self._session.close()
|
||
|
||
# LRU кэш для часто используемых методов
|
||
@lru_cache(maxsize=1000)
|
||
def get_user_by_id_cached(self, user_id: int) -> Optional[OptimizedUser]:
|
||
return self._users_cache.get(user_id)
|
||
|
||
# Batch операции
|
||
async def get_users_batch(self, user_ids: List[int]) -> List[OptimizedUser]:
|
||
tasks = [self._fetch_user_async(user_id) for user_id in user_ids]
|
||
results = await asyncio.gather(*tasks, return_exceptions=True)
|
||
|
||
users = []
|
||
for result in results:
|
||
if isinstance(result, OptimizedUser):
|
||
users.append(result)
|
||
|
||
return users
|
||
|
||
async def _fetch_user_async(self, user_id: int) -> Optional[OptimizedUser]:
|
||
if user_id in self._users_cache:
|
||
return self._users_cache[user_id]
|
||
|
||
# Асинхронный запрос к API
|
||
if self._session:
|
||
async with self._session.get(f'/api/users/{user_id}') as response:
|
||
if response.status == 200:
|
||
data = await response.json()
|
||
user = OptimizedUser(
|
||
id=data['id'],
|
||
name=data['name'],
|
||
email=data['email'],
|
||
status=data['status']
|
||
)
|
||
self._users_cache[user_id] = user
|
||
return user
|
||
|
||
return None
|
||
|
||
# Оптимизация с помощью Cython
|
||
# user_service.pyx
|
||
cdef class FastUserService:
|
||
cdef dict _users_cache
|
||
cdef int _max_cache_size
|
||
|
||
def __init__(self, max_cache_size: int = 1000):
|
||
self._users_cache = {}
|
||
self._max_cache_size = max_cache_size
|
||
|
||
cdef OptimizedUser _get_user_fast(self, int user_id):
|
||
return self._users_cache.get(user_id)
|
||
|
||
def get_user(self, user_id: int) -> Optional[OptimizedUser]:
|
||
return self._get_user_fast(user_id)
|
||
```
|
||
|
||
#### 2. Оптимизация изображений
|
||
|
||
```javascript
|
||
// webpack.config.js - Оптимизация изображений
|
||
const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin');
|
||
|
||
module.exports = {
|
||
module: {
|
||
rules: [
|
||
{
|
||
test: /\.(jpe?g|png|gif|svg)$/i,
|
||
type: 'asset',
|
||
parser: {
|
||
dataUrlCondition: {
|
||
maxSize: 8 * 1024 // 8kb
|
||
}
|
||
},
|
||
generator: {
|
||
filename: 'images/[name].[hash:8][ext]'
|
||
}
|
||
}
|
||
]
|
||
},
|
||
optimization: {
|
||
minimizer: [
|
||
new ImageMinimizerPlugin({
|
||
minimizer: {
|
||
implementation: ImageMinimizerPlugin.imageminMinify,
|
||
options: {
|
||
plugins: [
|
||
['gifsicle', { interlaced: true }],
|
||
['jpegtran', { progressive: true }],
|
||
['optipng', { optimizationLevel: 5 }],
|
||
[
|
||
'svgo',
|
||
{
|
||
plugins: [
|
||
{
|
||
name: 'preset-default',
|
||
params: {
|
||
overrides: {
|
||
removeViewBox: false,
|
||
},
|
||
},
|
||
},
|
||
],
|
||
},
|
||
],
|
||
],
|
||
},
|
||
},
|
||
}),
|
||
],
|
||
},
|
||
};
|
||
```
|
||
|
||
#### 3. Оптимизация CSS
|
||
|
||
```css
|
||
/* Оптимизированный CSS */
|
||
/* Использование CSS переменных */
|
||
:root {
|
||
--primary-color: #007bff;
|
||
--secondary-color: #6c757d;
|
||
--font-size-base: 1rem;
|
||
--line-height-base: 1.5;
|
||
}
|
||
|
||
/* Минимизация селекторов */
|
||
.btn {
|
||
display: inline-block;
|
||
padding: 0.375rem 0.75rem;
|
||
font-size: var(--font-size-base);
|
||
line-height: var(--line-height-base);
|
||
border: 1px solid transparent;
|
||
border-radius: 0.25rem;
|
||
cursor: pointer;
|
||
transition: all 0.15s ease-in-out;
|
||
}
|
||
|
||
.btn-primary {
|
||
color: #fff;
|
||
background-color: var(--primary-color);
|
||
border-color: var(--primary-color);
|
||
}
|
||
|
||
.btn-primary:hover {
|
||
background-color: #0056b3;
|
||
border-color: #004085;
|
||
}
|
||
|
||
/* Критический CSS */
|
||
.critical {
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||
font-size: 16px;
|
||
line-height: 1.5;
|
||
color: #333;
|
||
}
|
||
```
|
||
|
||
#### 4. Оптимизация загрузки ресурсов
|
||
|
||
```html
|
||
<!-- Оптимизированная загрузка ресурсов -->
|
||
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Optimized App</title>
|
||
|
||
<!-- Preload критических ресурсов -->
|
||
<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>
|
||
<link rel="preload" href="/css/critical.css" as="style">
|
||
<link rel="preload" href="/js/main.js" as="script">
|
||
|
||
<!-- DNS prefetch для внешних ресурсов -->
|
||
<link rel="dns-prefetch" href="//fonts.googleapis.com">
|
||
<link rel="dns-prefetch" href="//api.example.com">
|
||
|
||
<!-- Preconnect для важных доменов -->
|
||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||
<link rel="preconnect" href="https://api.example.com">
|
||
|
||
<!-- Критический CSS inline -->
|
||
<style>
|
||
/* Critical CSS */
|
||
body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, sans-serif; }
|
||
.header { background: #007bff; color: white; padding: 1rem; }
|
||
.main { padding: 2rem; }
|
||
</style>
|
||
|
||
<!-- Lazy load остального CSS -->
|
||
<link rel="stylesheet" href="/css/main.css" media="print" onload="this.media='all'">
|
||
<noscript><link rel="stylesheet" href="/css/main.css"></noscript>
|
||
</head>
|
||
<body>
|
||
<header class="header">
|
||
<h1>Optimized App</h1>
|
||
</header>
|
||
|
||
<main class="main">
|
||
<div id="app"></div>
|
||
</main>
|
||
|
||
<!-- Lazy load JavaScript -->
|
||
<script>
|
||
// Lazy load non-critical scripts
|
||
function loadScript(src) {
|
||
const script = document.createElement('script');
|
||
script.src = src;
|
||
script.async = true;
|
||
document.head.appendChild(script);
|
||
}
|
||
|
||
// Load after page load
|
||
window.addEventListener('load', function() {
|
||
loadScript('/js/main.js');
|
||
loadScript('/js/analytics.js');
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|
||
```
|
||
|
||
#### 5. Оптимизация базы данных
|
||
|
||
```java
|
||
// Оптимизация запросов к БД
|
||
@Repository
|
||
public class OptimizedUserRepository {
|
||
|
||
@Autowired
|
||
private JdbcTemplate jdbcTemplate;
|
||
|
||
// Batch операции для оптимизации
|
||
public void saveUsersBatch(List<User> users) {
|
||
String sql = "INSERT INTO users (name, email, created_at) VALUES (?, ?, ?)";
|
||
|
||
List<Object[]> batchArgs = users.stream()
|
||
.map(user -> new Object[]{user.getName(), user.getEmail(), user.getCreatedAt()})
|
||
.collect(Collectors.toList());
|
||
|
||
jdbcTemplate.batchUpdate(sql, batchArgs);
|
||
}
|
||
|
||
// Оптимизированный поиск с индексами
|
||
public List<User> findUsersByEmailDomain(String domain) {
|
||
String sql = "SELECT * FROM users WHERE email LIKE ? ORDER BY created_at DESC LIMIT 100";
|
||
return jdbcTemplate.query(sql, new Object[]{"%" + domain}, new UserRowMapper());
|
||
}
|
||
|
||
// Использование prepared statements
|
||
public Optional<User> findUserById(Long id) {
|
||
String sql = "SELECT * FROM users WHERE id = ?";
|
||
try {
|
||
User user = jdbcTemplate.queryForObject(sql, new Object[]{id}, new UserRowMapper());
|
||
return Optional.ofNullable(user);
|
||
} catch (EmptyResultDataAccessException e) {
|
||
return Optional.empty();
|
||
}
|
||
}
|
||
|
||
// Оптимизация с помощью кэширования
|
||
@Cacheable(value = "users", key = "#id")
|
||
public User findUserByIdCached(Long id) {
|
||
return findUserById(id).orElse(null);
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Оптимизация сети
|
||
|
||
### Оптимизация сети
|
||
|
||
#### 1. Connection Pooling
|
||
```java
|
||
// Оптимизация HTTP соединений
|
||
@Configuration
|
||
public class HttpClientConfig {
|
||
@Bean
|
||
public RestTemplate restTemplate() {
|
||
HttpComponentsClientHttpRequestFactory factory =
|
||
new HttpComponentsClientHttpRequestFactory();
|
||
|
||
// Настройка пула соединений
|
||
PoolingHttpClientConnectionManager connectionManager =
|
||
new PoolingHttpClientConnectionManager();
|
||
connectionManager.setMaxTotal(100);
|
||
connectionManager.setDefaultMaxPerRoute(20);
|
||
|
||
CloseableHttpClient httpClient = HttpClients.custom()
|
||
.setConnectionManager(connectionManager)
|
||
.setDefaultRequestConfig(RequestConfig.custom()
|
||
.setConnectTimeout(5000)
|
||
.setSocketTimeout(10000)
|
||
.build())
|
||
.build();
|
||
|
||
factory.setHttpClient(httpClient);
|
||
return new RestTemplate(factory);
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 2. Compression
|
||
```java
|
||
// Сжатие HTTP ответов
|
||
@Configuration
|
||
public class CompressionConfig {
|
||
@Bean
|
||
public FilterRegistrationBean<CompressionFilter> compressionFilter() {
|
||
FilterRegistrationBean<CompressionFilter> registration =
|
||
new FilterRegistrationBean<>();
|
||
registration.setFilter(new CompressionFilter());
|
||
registration.addUrlPatterns("/*");
|
||
registration.setName("compressionFilter");
|
||
registration.setOrder(1);
|
||
return registration;
|
||
}
|
||
}
|
||
|
||
@Component
|
||
public class CompressionFilter implements Filter {
|
||
@Override
|
||
public void doFilter(ServletRequest request, ServletResponse response,
|
||
FilterChain chain) throws IOException, ServletException {
|
||
|
||
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
||
HttpServletResponse httpResponse = (HttpServletResponse) response;
|
||
|
||
String acceptEncoding = httpRequest.getHeader("Accept-Encoding");
|
||
if (acceptEncoding != null && acceptEncoding.contains("gzip")) {
|
||
httpResponse.setHeader("Content-Encoding", "gzip");
|
||
GzipResponseWrapper gzipResponse = new GzipResponseWrapper(httpResponse);
|
||
chain.doFilter(request, gzipResponse);
|
||
gzipResponse.finish();
|
||
} else {
|
||
chain.doFilter(request, response);
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 3. CDN Integration
|
||
```java
|
||
// Интеграция с CDN
|
||
@Service
|
||
public class CDNService {
|
||
@Value("${cdn.base-url}")
|
||
private String cdnBaseUrl;
|
||
|
||
public String getCDNUrl(String path) {
|
||
return cdnBaseUrl + "/" + path;
|
||
}
|
||
|
||
public void uploadToCDN(String path, byte[] content) {
|
||
// Загрузка контента в CDN
|
||
// Реализация зависит от выбранного CDN провайдера
|
||
}
|
||
|
||
public void invalidateCDNCache(String path) {
|
||
// Инвалидация кэша CDN
|
||
// Реализация зависит от выбранного CDN провайдера
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Capacity Planning
|
||
|
||
### Процесс планирования мощности
|
||
|
||
#### 1. Current Capacity Analysis
|
||
```java
|
||
// Анализ текущей производительности
|
||
@Component
|
||
public class CapacityAnalyzer {
|
||
private final MeterRegistry meterRegistry;
|
||
|
||
public CapacityAnalyzer(MeterRegistry meterRegistry) {
|
||
this.meterRegistry = meterRegistry;
|
||
}
|
||
|
||
@Scheduled(fixedRate = 60000) // каждую минуту
|
||
public void analyzeCapacity() {
|
||
// Анализ CPU
|
||
double cpuUsage = getCpuUsage();
|
||
log.info("CPU usage: {}%", cpuUsage * 100);
|
||
|
||
// Анализ памяти
|
||
double memoryUsage = getMemoryUsage();
|
||
log.info("Memory usage: {}%", memoryUsage * 100);
|
||
|
||
// Анализ пропускной способности
|
||
double throughput = getThroughput();
|
||
log.info("Throughput: {} RPS", throughput);
|
||
|
||
// Анализ задержки
|
||
double latency = getLatency();
|
||
log.info("Latency: {} ms", latency);
|
||
|
||
// Предупреждения
|
||
if (cpuUsage > 0.8) {
|
||
log.warn("High CPU usage detected!");
|
||
}
|
||
if (memoryUsage > 0.8) {
|
||
log.warn("High memory usage detected!");
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 2. Load Forecasting
|
||
```java
|
||
// Прогнозирование нагрузки
|
||
@Service
|
||
public class LoadForecaster {
|
||
@Autowired
|
||
private MetricsRepository metricsRepository;
|
||
|
||
public LoadForecast forecastLoad(int days) {
|
||
// Получаем исторические данные
|
||
List<MetricData> historicalData = metricsRepository
|
||
.findByDateRange(LocalDateTime.now().minusDays(30), LocalDateTime.now());
|
||
|
||
// Анализируем тренды
|
||
double growthRate = calculateGrowthRate(historicalData);
|
||
|
||
// Прогнозируем нагрузку
|
||
double currentLoad = getCurrentLoad();
|
||
double forecastedLoad = currentLoad * Math.pow(1 + growthRate, days);
|
||
|
||
return new LoadForecast(forecastedLoad, growthRate, days);
|
||
}
|
||
|
||
private double calculateGrowthRate(List<MetricData> data) {
|
||
// Простой расчет темпа роста
|
||
if (data.size() < 2) return 0.0;
|
||
|
||
double firstValue = data.get(0).getValue();
|
||
double lastValue = data.get(data.size() - 1).getValue();
|
||
|
||
return (lastValue - firstValue) / firstValue / data.size();
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 3. Resource Planning
|
||
```yaml
|
||
# Планирование ресурсов
|
||
capacity_planning:
|
||
current_capacity:
|
||
cpu: "4 cores"
|
||
memory: "8 GB"
|
||
storage: "100 GB"
|
||
network: "1 Gbps"
|
||
|
||
forecasted_load:
|
||
peak_rps: 1000
|
||
average_rps: 500
|
||
growth_rate: "20% per month"
|
||
|
||
resource_requirements:
|
||
cpu:
|
||
current: "80% utilization"
|
||
target: "70% utilization"
|
||
required: "6 cores"
|
||
|
||
memory:
|
||
current: "75% utilization"
|
||
target: "65% utilization"
|
||
required: "12 GB"
|
||
|
||
storage:
|
||
current: "60% utilization"
|
||
target: "50% utilization"
|
||
required: "150 GB"
|
||
```
|
||
|
||
---
|
||
|
||
## Performance Testing
|
||
|
||
### Типы тестов производительности
|
||
|
||
#### 1. Load Testing
|
||
```java
|
||
// Load тестирование с JMeter
|
||
@Component
|
||
public class LoadTestRunner {
|
||
public void runLoadTest() {
|
||
// Настройка JMeter теста
|
||
JMeterEngine engine = new StandardJMeterEngine();
|
||
|
||
// Загрузка тестового плана
|
||
HashTree testPlanTree = new HashTree();
|
||
TestPlan testPlan = new TestPlan();
|
||
testPlan.setName("User Service Load Test");
|
||
|
||
// Настройка потока
|
||
ThreadGroup threadGroup = new ThreadGroup();
|
||
threadGroup.setName("Users");
|
||
threadGroup.setNumThreads(100);
|
||
threadGroup.setRampUp(60);
|
||
threadGroup.setDuration(300); // 5 минут
|
||
|
||
// Настройка HTTP запроса
|
||
HTTPSamplerProxy httpSampler = new HTTPSamplerProxy();
|
||
httpSampler.setDomain("localhost");
|
||
httpSampler.setPort(8080);
|
||
httpSampler.setPath("/api/users");
|
||
httpSampler.setMethod("GET");
|
||
|
||
// Добавление элементов в тест
|
||
testPlanTree.add(testPlan);
|
||
testPlanTree.add(testPlan, threadGroup);
|
||
testPlanTree.add(threadGroup, httpSampler);
|
||
|
||
// Запуск теста
|
||
engine.configure(testPlanTree);
|
||
engine.run();
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 2. Stress Testing
|
||
```java
|
||
// Stress тестирование
|
||
@Service
|
||
public class StressTestService {
|
||
@Autowired
|
||
private UserService userService;
|
||
|
||
public void runStressTest() {
|
||
int numberOfThreads = 200;
|
||
int requestsPerThread = 1000;
|
||
|
||
ExecutorService executor = Executors.newFixedThreadPool(numberOfThreads);
|
||
CountDownLatch latch = new CountDownLatch(numberOfThreads);
|
||
|
||
for (int i = 0; i < numberOfThreads; i++) {
|
||
executor.submit(() -> {
|
||
try {
|
||
for (int j = 0; j < requestsPerThread; j++) {
|
||
// Выполняем запрос
|
||
userService.getUser((long) (Math.random() * 1000));
|
||
|
||
// Небольшая задержка
|
||
Thread.sleep(10);
|
||
}
|
||
} catch (Exception e) {
|
||
log.error("Stress test error", e);
|
||
} finally {
|
||
latch.countDown();
|
||
}
|
||
});
|
||
}
|
||
|
||
try {
|
||
latch.await();
|
||
} catch (InterruptedException e) {
|
||
Thread.currentThread().interrupt();
|
||
}
|
||
|
||
executor.shutdown();
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 3. Endurance Testing
|
||
```java
|
||
// Endurance тестирование
|
||
@Component
|
||
public class EnduranceTestRunner {
|
||
@Autowired
|
||
private UserService userService;
|
||
|
||
@Scheduled(fixedRate = 1000) // каждую секунду
|
||
public void runEnduranceTest() {
|
||
try {
|
||
// Выполняем запрос
|
||
userService.getUser(1L);
|
||
|
||
// Логируем метрики
|
||
log.info("Endurance test - request completed");
|
||
|
||
} catch (Exception e) {
|
||
log.error("Endurance test error", e);
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### Автоматизация тестов производительности
|
||
|
||
#### 1. Automated Performance Tests
|
||
```java
|
||
// Автоматизированные performance тесты
|
||
@SpringBootTest
|
||
@TestPropertySource(properties = {
|
||
"spring.datasource.url=jdbc:h2:mem:testdb",
|
||
"spring.jpa.hibernate.ddl-auto=create-drop"
|
||
})
|
||
public class PerformanceTestSuite {
|
||
|
||
@Autowired
|
||
private UserService userService;
|
||
|
||
@Test
|
||
public void testUserServicePerformance() {
|
||
// Подготовка данных
|
||
List<User> users = createTestUsers(1000);
|
||
|
||
// Измерение времени выполнения
|
||
long startTime = System.currentTimeMillis();
|
||
|
||
for (User user : users) {
|
||
userService.createUser(user);
|
||
}
|
||
|
||
long endTime = System.currentTimeMillis();
|
||
long duration = endTime - startTime;
|
||
|
||
// Проверка производительности
|
||
assertThat(duration).isLessThan(5000); // менее 5 секунд
|
||
|
||
// Проверка пропускной способности
|
||
double throughput = (double) users.size() / duration * 1000;
|
||
assertThat(throughput).isGreaterThan(100); // более 100 RPS
|
||
}
|
||
|
||
@Test
|
||
public void testDatabaseQueryPerformance() {
|
||
// Подготовка данных
|
||
createTestUsers(10000);
|
||
|
||
// Измерение времени выполнения запроса
|
||
long startTime = System.currentTimeMillis();
|
||
|
||
List<User> users = userService.findUsersByName("Test");
|
||
|
||
long endTime = System.currentTimeMillis();
|
||
long duration = endTime - startTime;
|
||
|
||
// Проверка производительности
|
||
assertThat(duration).isLessThan(1000); // менее 1 секунды
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Практические задания
|
||
|
||
### Задание 1: Профилирование
|
||
1. Настройте профилирование приложения
|
||
2. Проведите анализ производительности
|
||
3. Выявите узкие места
|
||
|
||
### Задание 2: Оптимизация базы данных
|
||
1. Проанализируйте медленные запросы
|
||
2. Оптимизируйте индексы
|
||
3. Настройте пул соединений
|
||
|
||
### Задание 3: Performance Testing
|
||
1. Создайте load тесты
|
||
2. Проведите stress тестирование
|
||
3. Проанализируйте результаты
|
||
|
||
---
|
||
|
||
## Дополнительные ресурсы
|
||
|
||
### Инструменты
|
||
- **Profiling**: JProfiler, VisualVM, YourKit
|
||
- **APM**: New Relic, AppDynamics, Datadog
|
||
- **Load Testing**: JMeter, Gatling, K6
|
||
- **Database**: pgAdmin, MySQL Workbench, MongoDB Compass
|
||
|
||
### Книги
|
||
- "Java Performance" by Scott Oaks
|
||
- "High Performance MySQL" by Baron Schwartz
|
||
- "Systems Performance" by Brendan Gregg
|
||
|
||
---
|
||
|
||
## Контрольные вопросы
|
||
|
||
1. Какие метрики производительности вы знаете?
|
||
2. Как проводить профилирование приложения?
|
||
3. Как оптимизировать базу данных?
|
||
4. Какие стратегии кэширования существуют?
|
||
5. Как оптимизировать сетевые запросы?
|
||
6. Что такое Capacity Planning?
|
||
7. Какие типы performance тестов вы знаете?
|
||
8. Как автоматизировать performance тестирование?
|