Home SOFTWAREPomoć i savetiArhitektura modernih web aplikacija – Deo 7: Keširanje, performanse i skaliranje

Arhitektura modernih web aplikacija – Deo 7: Keširanje, performanse i skaliranje

od itn
skaliranje i keširanje

Dobrodošli u sedmi deo serijala na ITNetwork.rs! Do sada smo prošli kroz monolit i mikroservise, frontend arhitekturu, API-je, baze, mikroservise i serverless. Danas ulazimo u srž brzine i otpornostikeširanje, optimizaciju performansi i skaliranje. Naučićemo kako smanjiti latenciju sa <50 ms, kako izbeći „thundering herd“ i kako skalirati sa milionima korisnika.

Zašto su performanse ključne?

Metrika Utjecaj
1s kašnjenja –16% konverzije (Amazon)
100ms brže +1% prihoda (Google)
LCP > 2.5s 30% bounce rate

Brzina nije luksuz – to je osnovna funkcionalnost.

1. Keširanje – više nivoa, više brzine

Keširanje na 4 nivoa

Keširanje na 4 nivoa1.1 HTTP Keširanje (CDN + Browser)

Ključni zaglavlji:

Cache-Control: public, max-age=31536000, immutable
ETag: "abc123"
Last-Modified: Wed, 21 Oct 2025 07:28:00 GMT

Primer: Statički asseti (Next.js)

// next.config.js
module.exports = {
  async headers() {
    return [
      {
        source: '/_next/static/:path*',
        headers: [
          { key: 'Cache-Control', value: 'public, max-age=31536000, immutable' }
        ]
      }
    ];
  }
};

Rezultat: CSS/JS se kešira godinu dana u browseru i CDN-u.

1.2 Aplikativno keširanje (Redis)

Kada keširati?

Podaci TTL
Lista proizvoda 5–15 min
Korisnički profil 1 min
Konfiguracija 1h (sa invalidacijom)
Primer: Redis keš za skupi DB upit
// lib/cache.ts
import Redis from 'ioredis';
const redis = new Redis(process.env.REDIS_URL);

const CACHE_TTL = 300; // 5 min

export async function getExpensiveData(userId: string) {
  const cacheKey = `user:${userId}:dashboard`;
  
  // 1. Proveri keš
  const cached = await redis.get(cacheKey);
  if (cached) {
    return JSON.parse(cached);
  }

  // 2. Ako nema – DB
  const data = await db.query('SELECT ... FROM analytics WHERE user_id = $1', [userId]);

  // 3. Sačuvaj u keš
  await redis.set(cacheKey, JSON.stringify(data), 'EX', CACHE_TTL);

  return data;
}
1.3 Baza keširanje (Query + Materialized Views)

PostgreSQL trikovi:

-- Materialized View (osveži svakih 5 min)
CREATE MATERIALIZED VIEW daily_sales AS
SELECT date, SUM(amount) FROM orders GROUP BY date;

-- Osveži asinhrono
REFRESH MATERIALIZED VIEW CONCURRENTLY daily_sales;

2. Rate Limiting & Throttling

Zaštita od:

  • DDoS napada
  • „Scraping“ botova
  • „Thundering herd“ (1000 korisnika odjednom)

Alati:

Alat Gde
Redis + Lua API Gateway
Cloudflare Rate Limiting CDN nivo
NGINX limit_req Reverse proxy
Primer: Redis Rate Limit (10 req/s po IP)
-- rate-limiter.lua
local key = KEYS[1]  -- IP adresa
local limit = tonumber(ARGV[1])  -- 10
local window = tonumber(ARGV[2]) -- 1s

local current = redis.call('INCR', key)
if current == 1 then
  redis.call('EXPIRE', key, window)
end

if current > limit then
  return 0
else
  return 1
end
// middleware.ts
const script = redis.defineScript({ /* Lua script */ });

app.use(async (req, res, next) => {
  const ip = req.ip;
  const allowed = await redis.evalsha(script.sha, 1, `rate:${ip}`, 10, 1);
  if (!allowed) {
    return res.status(429).json({ error: 'Too many requests' });
  }
  next();
});
3. Skaliranje – horizontalno i vertikalno
Tip Kada Primer
Vertikalno Mali tim, jednostavnost Veći VM (16 → 32 GB RAM)
Horizontalno Veliko opterećenje Dodaj nodove iza Load Balancer-a
Automatsko skaliranje (Kubernetes HPA)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: api-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: api
  minReplicas: 3
  maxReplicas: 50
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
4. Load Balancing & Health Checks

Strategije:

Strategija Opis
Round Robin Ravnomerno
Least Connections Najmanje aktivnih
IP Hash Isti korisnik → isti server
Health check (Node.js)
// health.ts
app.get('/health', (req, res) => {
  const checks = {
    database: db.isConnected,
    redis: redis.status === 'ready',
    uptime: process.uptime()
  };
  const healthy = Object.values(checks).every(Boolean);
  res.status(healthy ? 200 : 503).json(checks);
});
5. Performansne metrike (Golden Signals)
Metrika Alat Cilj
Latency Prometheus P95 < 200ms
Error Rate Grafana < 0.1%
Throughput Loki req/s
Saturation Node.js os.loadavg() CPU < 80%
Primer: Prometheus exporter
import { Registry, Counter, Histogram } from 'prom-client';
const register = new Registry();

const httpRequests = new Counter({
  name: 'http_requests_total',
  help: 'Broj HTTP zahteva',
  labelNames: ['method', 'route', 'status']
});

const requestDuration = new Histogram({
  name: 'http_request_duration_ms',
  help: 'Trajanje zahteva',
  buckets: [5, 10, 25, 50, 100, 250, 500, 1000]
});

app.use((req, res, next) => {
  const end = requestDuration.startTimer();
  res.on('finish', () => {
    httpRequests.inc({ method: req.method, route: req.route?.path || req.path, status: res.statusCode });
    end({ route: req.route?.path });
  });
  next();
});
6. Praktičan primer: Keširani dashboard

Arhitektura

Keširani dashboard

Prvi poziv: 800ms Sledeći (5 min): <50ms

Poređenje: Bez vs Sa keširanjem

Scenarij Latencija Trošak baze Skalabilnost
Bez keša 800ms 1000 req/s ★★
Sa Redis + CDN 50ms 10 req/s ★★★★★
Zaključak: Strategija keširanja
Nivo Kada koristiti
CDN Statički asseti, HTML (SSG)
Redis Dinamički podaci, sesije
Aplikacija Lokalni keš (LRU)
Baza Materialized views, indeksiranje

Zlatno pravilo: „Keširaj sve što možeš – ali uvek imaj plan za invalidaciju.“

Šta dalje?

U osmom delu ulazimo u bezbednost:

  • JWT vs OAuth2
  • RBAC/ABAC
  • Passkeys, Magic Links

Tvoj izazov

  1. Dodaj Redis keš u svoj API
  2. Postavi Cloudflare sa Cache Everything za /blog/*
  3. Uradi load test sa k6:
    bash
    k6 run --vus 100 --duration 30s script.js
  4. Komentariši ispod: „Koliko je tvoj P95 latencija trenutno?“

Autor: Dušan Antonijević – saradnik ITNetwork.rs
Hvala na čitanju! Podeli tekst sa kolegama.

Banner

Banner

Možda će vam se svideti i