461 lines
12 KiB
Markdown
461 lines
12 KiB
Markdown
---
|
||
created: 2024-12-19
|
||
updated: 2024-12-19
|
||
tags:
|
||
- производительность
|
||
- оптимизация
|
||
- профилирование
|
||
- веб-перформанс
|
||
parent: "[[Second Mind Pipeline/index|Second Mind Pipeline]]"
|
||
status: "планирование"
|
||
priority: "высокий"
|
||
---
|
||
|
||
# ⚡ Оптимизация производительности Second Mind Pipeline
|
||
|
||
## 🎯 Цели оптимизации
|
||
|
||
### Текущие показатели
|
||
- **Время сборки Quartz**: ~45 секунд
|
||
- **Время отклика сайта**: ~200-300ms
|
||
- **Размер сайта**: ~150MB
|
||
- **Time to First Byte**: ~150ms
|
||
- **Lighthouse Score**: 85/100
|
||
|
||
### Целевые показатели
|
||
- **Время сборки Quartz**: <15 секунд
|
||
- **Время отклика сайта**: <100ms
|
||
- **Размер сайта**: <100MB
|
||
- **Time to First Byte**: <50ms
|
||
- **Lighthouse Score**: >95/100
|
||
|
||
## 📊 Анализ узких мест
|
||
|
||
### 1. Процесс сборки Quartz
|
||
```mermaid
|
||
graph LR
|
||
A[Git Pull] --> B[Parse Markdown]
|
||
B --> C[Generate HTML]
|
||
C --> D[Process Assets]
|
||
D --> E[Build Search Index]
|
||
E --> F[Copy to Nginx]
|
||
|
||
B -.-> G[30% времени]
|
||
C -.-> H[40% времени]
|
||
D -.-> I[20% времени]
|
||
E -.-> J[10% времени]
|
||
```
|
||
|
||
**Проблемы:**
|
||
- Parsing больших markdown файлов
|
||
- Генерация графа связей
|
||
- Обработка изображений
|
||
- Создание search index
|
||
|
||
### 2. Веб-производительность
|
||
- **Большие bundle размеры**: JavaScript ~500KB
|
||
- **Неоптимизированные изображения**: PNG без compression
|
||
- **Отсутствие кеширования**: статические ресурсы
|
||
- **Блокирующие ресурсы**: CSS и JS loading
|
||
|
||
### 3. Серверная производительность
|
||
- **CPU Usage**: высокие пики во время сборки
|
||
- **Memory Usage**: до 2GB во время сборки
|
||
- **Disk I/O**: много операций чтения/записи
|
||
- **Network**: неоптимизированная отдача статики
|
||
|
||
## 🔧 План оптимизации
|
||
|
||
### Фаза 1: Оптимизация сборки Quartz
|
||
|
||
#### Кеширование промежуточных результатов
|
||
```javascript
|
||
// quartz.config.ts - добавить кеширование
|
||
const config = {
|
||
configuration: {
|
||
cachePath: "./cache",
|
||
incrementalBuild: true,
|
||
parallelProcessing: true,
|
||
}
|
||
}
|
||
```
|
||
|
||
#### Инкрементальная сборка
|
||
```bash
|
||
# Сборка только измененных файлов
|
||
npx quartz build --incremental --changed-only
|
||
```
|
||
|
||
#### Многопоточная обработка
|
||
```javascript
|
||
// Параллельная обработка файлов
|
||
import { Worker } from 'worker_threads';
|
||
|
||
const processFiles = async (files) => {
|
||
const workers = [];
|
||
const chunkSize = Math.ceil(files.length / os.cpus().length);
|
||
|
||
for (let i = 0; i < files.length; i += chunkSize) {
|
||
const chunk = files.slice(i, i + chunkSize);
|
||
workers.push(processChunk(chunk));
|
||
}
|
||
|
||
await Promise.all(workers);
|
||
};
|
||
```
|
||
|
||
### Фаза 2: Оптимизация веб-производительности
|
||
|
||
#### Code Splitting
|
||
```javascript
|
||
// Разделение bundle на чанки
|
||
const optimization = {
|
||
splitChunks: {
|
||
chunks: 'all',
|
||
cacheGroups: {
|
||
vendor: {
|
||
test: /[\\/]node_modules[\\/]/,
|
||
name: 'vendors',
|
||
chunks: 'all',
|
||
},
|
||
common: {
|
||
minChunks: 2,
|
||
chunks: 'all',
|
||
enforce: true
|
||
}
|
||
}
|
||
}
|
||
};
|
||
```
|
||
|
||
#### Lazy Loading изображений
|
||
```html
|
||
<!-- Использование native lazy loading -->
|
||
<img src="image.jpg" loading="lazy" decoding="async" />
|
||
|
||
<!-- Intersection Observer для старых браузеров -->
|
||
<script>
|
||
if ('IntersectionObserver' in window) {
|
||
const imageObserver = new IntersectionObserver((entries) => {
|
||
entries.forEach(entry => {
|
||
if (entry.isIntersecting) {
|
||
const img = entry.target;
|
||
img.src = img.dataset.src;
|
||
imageObserver.unobserve(img);
|
||
}
|
||
});
|
||
});
|
||
}
|
||
</script>
|
||
```
|
||
|
||
#### Оптимизация шрифтов
|
||
```css
|
||
/* Preload критических шрифтов */
|
||
@font-face {
|
||
font-family: 'Inter';
|
||
font-weight: 400;
|
||
font-display: swap;
|
||
src: url('/fonts/inter-400.woff2') format('woff2');
|
||
}
|
||
|
||
/* Subset шрифтов для кириллицы */
|
||
@font-face {
|
||
font-family: 'Inter';
|
||
src: url('/fonts/inter-cyrillic.woff2') format('woff2');
|
||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||
}
|
||
```
|
||
|
||
### Фаза 3: Серверная оптимизация
|
||
|
||
#### Nginx конфигурация
|
||
```nginx
|
||
# Улучшенная конфигурация nginx.conf
|
||
http {
|
||
# Gzip сжатие
|
||
gzip on;
|
||
gzip_vary on;
|
||
gzip_min_length 1024;
|
||
gzip_comp_level 6;
|
||
gzip_types
|
||
text/plain
|
||
text/css
|
||
text/xml
|
||
text/javascript
|
||
application/javascript
|
||
application/json
|
||
application/xml+rss;
|
||
|
||
# Brotli сжатие (если доступно)
|
||
brotli on;
|
||
brotli_comp_level 6;
|
||
brotli_types text/plain text/css application/javascript;
|
||
|
||
# Кеширование статических файлов
|
||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
|
||
expires 1y;
|
||
add_header Cache-Control "public, immutable";
|
||
add_header Vary "Accept-Encoding";
|
||
}
|
||
|
||
# HTTP/2 Server Push
|
||
location = /index.html {
|
||
http2_push /css/main.css;
|
||
http2_push /js/app.js;
|
||
}
|
||
|
||
# Security headers
|
||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||
add_header X-Content-Type-Options "nosniff" always;
|
||
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
||
}
|
||
```
|
||
|
||
#### Docker оптимизация
|
||
```dockerfile
|
||
# Multi-stage build для уменьшения размера образа
|
||
FROM node:22-alpine as builder
|
||
WORKDIR /app
|
||
COPY package*.json ./
|
||
RUN npm ci --only=production
|
||
|
||
FROM node:22-alpine as runner
|
||
RUN addgroup --system --gid 1001 nodejs
|
||
RUN adduser --system --uid 1001 nextjs
|
||
WORKDIR /app
|
||
COPY --from=builder /app/node_modules ./node_modules
|
||
COPY . .
|
||
USER nextjs
|
||
EXPOSE 3000
|
||
CMD ["node", "server.js"]
|
||
```
|
||
|
||
#### Resource limits
|
||
```yaml
|
||
# docker-compose.yml - ограничения ресурсов
|
||
services:
|
||
quartz-webhook:
|
||
deploy:
|
||
resources:
|
||
limits:
|
||
memory: 1G
|
||
cpus: '0.5'
|
||
reservations:
|
||
memory: 512M
|
||
cpus: '0.25'
|
||
```
|
||
|
||
## 📱 CDN и кеширование
|
||
|
||
### CloudFlare интеграция
|
||
```yaml
|
||
# cloudflare-config.yml
|
||
zones:
|
||
- zone: notes.aepif.ru
|
||
settings:
|
||
caching_level: aggressive
|
||
browser_cache_ttl: 31536000 # 1 год
|
||
edge_cache_ttl: 2592000 # 30 дней
|
||
always_online: true
|
||
minify:
|
||
css: true
|
||
js: true
|
||
html: true
|
||
```
|
||
|
||
### Cache strategies
|
||
```javascript
|
||
// Service Worker для агрессивного кеширования
|
||
const CACHE_NAME = 'second-mind-v1';
|
||
const urlsToCache = [
|
||
'/',
|
||
'/css/main.css',
|
||
'/js/app.js',
|
||
'/manifest.json'
|
||
];
|
||
|
||
self.addEventListener('install', event => {
|
||
event.waitUntil(
|
||
caches.open(CACHE_NAME)
|
||
.then(cache => cache.addAll(urlsToCache))
|
||
);
|
||
});
|
||
|
||
self.addEventListener('fetch', event => {
|
||
event.respondWith(
|
||
caches.match(event.request)
|
||
.then(response => {
|
||
// Cache hit - return response
|
||
if (response) {
|
||
return response;
|
||
}
|
||
return fetch(event.request);
|
||
}
|
||
)
|
||
);
|
||
});
|
||
```
|
||
|
||
## 🔍 Мониторинг производительности
|
||
|
||
### Metrics collection
|
||
```javascript
|
||
// Performance monitoring
|
||
const observer = new PerformanceObserver((list) => {
|
||
list.getEntries().forEach((entry) => {
|
||
console.log({
|
||
name: entry.name,
|
||
duration: entry.duration,
|
||
type: entry.entryType
|
||
});
|
||
|
||
// Отправка метрик в analytics
|
||
analytics.track('performance', {
|
||
metric: entry.name,
|
||
value: entry.duration,
|
||
timestamp: Date.now()
|
||
});
|
||
});
|
||
});
|
||
|
||
observer.observe({entryTypes: ['measure', 'navigation']});
|
||
```
|
||
|
||
### Core Web Vitals
|
||
```javascript
|
||
// Измерение Core Web Vitals
|
||
import {getCLS, getFID, getFCP, getLCP, getTTFB} from 'web-vitals';
|
||
|
||
function sendToAnalytics(metric) {
|
||
const body = JSON.stringify(metric);
|
||
|
||
// Использование Beacon API если доступно
|
||
if (navigator.sendBeacon) {
|
||
navigator.sendBeacon('/analytics', body);
|
||
} else {
|
||
fetch('/analytics', {method: 'POST', body, keepalive: true});
|
||
}
|
||
}
|
||
|
||
getCLS(sendToAnalytics);
|
||
getFID(sendToAnalytics);
|
||
getFCP(sendToAnalytics);
|
||
getLCP(sendToAnalytics);
|
||
getTTFB(sendToAnalytics);
|
||
```
|
||
|
||
### Automated testing
|
||
```bash
|
||
#!/bin/bash
|
||
# performance-test.sh
|
||
|
||
# Lighthouse CI
|
||
npx lhci autorun --config=.lighthouserc.json
|
||
|
||
# WebPageTest
|
||
curl -X POST "https://www.webpagetest.org/runtest.php" \
|
||
-d "url=https://notes.aepif.ru" \
|
||
-d "key=$WPT_API_KEY" \
|
||
-d "location=eu-west-1" \
|
||
-d "runs=3"
|
||
|
||
# Load testing с Artillery
|
||
artillery run load-test.yml
|
||
```
|
||
|
||
## 📊 A/B тестирование оптимизаций
|
||
|
||
### Experimental features
|
||
```javascript
|
||
// Feature flags для экспериментов
|
||
const featureFlags = {
|
||
enableServiceWorker: process.env.NODE_ENV === 'production',
|
||
enableImageOptimization: true,
|
||
enableCodeSplitting: true,
|
||
enablePrefetch: Math.random() > 0.5 // A/B test
|
||
};
|
||
|
||
if (featureFlags.enablePrefetch) {
|
||
// Prefetch следующих страниц
|
||
document.querySelectorAll('a[href^="/"]').forEach(link => {
|
||
link.addEventListener('mouseenter', () => {
|
||
const prefetchLink = document.createElement('link');
|
||
prefetchLink.rel = 'prefetch';
|
||
prefetchLink.href = link.href;
|
||
document.head.appendChild(prefetchLink);
|
||
});
|
||
});
|
||
}
|
||
```
|
||
|
||
## 🎯 Измерение результатов
|
||
|
||
### KPI до и после
|
||
| Метрика | До оптимизации | После оптимизации | Улучшение |
|
||
|---------|---------------|-------------------|-----------|
|
||
| Build time | 45s | 15s | 66% |
|
||
| TTFB | 150ms | 50ms | 66% |
|
||
| LCP | 2.5s | 1.2s | 52% |
|
||
| FID | 100ms | 20ms | 80% |
|
||
| CLS | 0.15 | 0.05 | 66% |
|
||
| Bundle size | 500KB | 200KB | 60% |
|
||
| Lighthouse | 85 | 95+ | 12% |
|
||
|
||
### Continuous monitoring
|
||
```yaml
|
||
# Grafana dashboard queries
|
||
- name: "Average Build Time"
|
||
query: "avg(build_duration_seconds)"
|
||
target: 15
|
||
|
||
- name: "95th Percentile Response Time"
|
||
query: "histogram_quantile(0.95, response_time_seconds_bucket)"
|
||
target: 0.1
|
||
|
||
- name: "Error Rate"
|
||
query: "rate(http_requests_total{status=~'5..'}[5m])"
|
||
target: 0.01
|
||
```
|
||
|
||
## 📋 Checklist реализации
|
||
|
||
### Фаза 1: Quick wins (1-2 недели)
|
||
- [ ] Включить Gzip/Brotli сжатие в Nginx
|
||
- [ ] Добавить правильные Cache-Control headers
|
||
- [ ] Оптимизировать изображения (WebP формат)
|
||
- [ ] Минифицировать CSS/JS
|
||
- [ ] Использовать CDN для статических ресурсов
|
||
|
||
### Фаза 2: Build optimization (2-3 недели)
|
||
- [ ] Внедрить инкрементальную сборку Quartz
|
||
- [ ] Добавить кеширование промежуточных результатов
|
||
- [ ] Оптимизировать Docker build процесс
|
||
- [ ] Параллелизовать обработку файлов
|
||
|
||
### Фаза 3: Advanced optimization (1 месяц)
|
||
- [ ] Внедрить Service Worker
|
||
- [ ] Code splitting и lazy loading
|
||
- [ ] HTTP/2 Server Push
|
||
- [ ] Performance monitoring
|
||
- [ ] A/B тестирование оптимизаций
|
||
|
||
## 🔬 Дальнейшие исследования
|
||
|
||
### Альтернативные подходы
|
||
1. **Статическая генерация с ISR** (Incremental Static Regeneration)
|
||
2. **Edge computing** для персонализации
|
||
3. **Client-side routing** для SPA experience
|
||
4. **Streaming SSR** для быстрого TTFB
|
||
|
||
### Экспериментальные технологии
|
||
- **WebAssembly** для тяжелых вычислений
|
||
- **HTTP/3** для улучшения сетевой производительности
|
||
- **Origin Private File System API** для локального кеширования
|
||
- **Web Streams API** для streaming обработки
|
||
|
||
---
|
||
|
||
*Связано с: [[Second Mind Pipeline/index|Главная страница проекта]]*
|
||
*Приоритет: Высокий | Срок: 4 недели*
|