Cache API i strategie unieważniania w procesach roboczych Cloudflare
Cache API w Workers umożliwia szczegółową kontrolę TTL, nieaktualne podczas ponownego sprawdzania poprawności i unieważniania według klucza: naucz się budować wysokowydajna globalna rozproszona warstwa pamięci podręcznej.
Buforowanie na krawędzi: poza tradycyjnym CDN
Tradycyjna sieć CDN buforuje zasoby statyczne zgodnie ze standardowymi nagłówkami HTTP. Pracownicy Cloudflare posuwa tę koncepcję znacznie dalej: za pomocą Pamięć podręczna API, Twój kod TypeScript kontroluje z chirurgiczną precyzją, co i jak długo jest buforowane, w jaki sposób jest ona unieważniana i jaką logikę zastępczą zastosować, gdy pamięć podręczna jest nieaktualna.
Wynik jest jeden Programowalny CDN: zamiast konfigurować reguły statyczny w dashboardzie, napisz logikę biznesową, która na bieżąco będzie decydować, czy a odpowiedź jest buforowana, z jakim TTL i jakim kluczem pamięci podręcznej. Ta elastyczność jest to szczególnie cenne w przypadku interfejsów API REST, stron niestandardowych i treści półdynamicznych.
Czego się nauczysz
- Jak działa API Cloudflare Workers Cache i czym różni się od API Cache przeglądarki
- Strategie buforowania: Cache-First, Network-First, Stale-While-Revalidate
- Niestandardowe klucze pamięci podręcznej: izoluj pamięć podręczną według użytkownika, języka i wersji
- Unieważnianie adresów URL, tagów i prefiksów za pomocą interfejsu API Cloudflare Zone Purge
- Zmieniaj nagłówki: zróżnicowana pamięć podręczna dla kodowania akceptacji i języka akceptacji
- Zaawansowane wzorce: podgrzewanie pamięci podręcznej, okres karencji i wyłącznik automatyczny z KV
- Typowe błędy i jak ich uniknąć na produkcji
Interfejs API pamięci podręcznej: podstawy i różnice w stosunku do przeglądarki
Interfejs API pamięci podręcznej udostępniony w programie Workers ma ten sam interfejs, co interfejs Interfejs API pamięci podręcznej Service Worker przeglądarkę, ale z istotnymi różnicami. W Workers pamięć podręczna jest dystrybuowane we wszystkich punktach PoP Cloudflare: Kiedy Robotnik we Frankfurcie buforuje odpowiedź, ta odpowiedź nie jest automatycznie dostępna w Londynie i Amsterdamie. Każdy PoP ma własną lokalną pamięć podręczną.
// Accesso alla cache nel Worker
// In Workers esiste un unico "default" cache namespace
const cache = caches.default;
// Oppure cache named (isolata per nome, utile per namespace logici)
const apiCache = await caches.open('api-v2');
// Le operazioni fondamentali:
// cache.match(request) -> Response | undefined
// cache.put(request, response) -> void
// cache.delete(request) -> boolean
Cache API: ważne ograniczenia
- Tylko żądania HTTP (nie dowolne adresy URL, jak w przeglądarce)
- Nie można buforować odpowiedzi w
Vary: * - Pamięć podręczna jest obsługiwana przez PoP: nie ma automatycznego unieważniania między centrami danych
cache.delete() - Odpowiedzi o statusie 206 (Częściowa treść) nie są buforowane
- Maksymalny przestrzegany TTL wynosi 31 dni
Strategia 1: Najpierw pamięć podręczna z siecią awaryjną
Najpopularniejsza strategia dla interfejsów API z rzadko zmieniającymi się danymi: udostępniaj z pamięci podręcznej jeśli jest dostępny, w przeciwnym razie przejdź do źródła i zapełnij pamięć podręczną.
// worker.ts - Cache-First Strategy
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
// Solo richieste GET sono cacheable
if (request.method !== 'GET') {
return fetch(request);
}
const cache = caches.default;
// 1. Controlla la cache
let response = await cache.match(request);
if (response) {
// Cache HIT: aggiungi header diagnostico e restituisci
const headers = new Headers(response.headers);
headers.set('X-Cache-Status', 'HIT');
return new Response(response.body, {
status: response.status,
headers,
});
}
// 2. Cache MISS: fetch dall'origin
response = await fetch(request);
// 3. Clona la risposta (il body e un ReadableStream, consumabile una sola volta)
const responseToCache = response.clone();
// 4. Metti in cache con ctx.waitUntil() per non bloccare la risposta al client
ctx.waitUntil(
cache.put(request, responseToCache)
);
// 5. Restituisci la risposta originale con header diagnostico
const headers = new Headers(response.headers);
headers.set('X-Cache-Status', 'MISS');
return new Response(response.body, {
status: response.status,
headers,
});
},
};
interface Env {}
Strategia 2: Ponowna weryfikacja na bieżąco
Strategia nieaktualne podczas ponownej weryfikacji to ten, który daje najlepsze kompromis pomiędzy świeżością danych a postrzeganą szybkością: klient otrzymuje Zawsze natychmiastową odpowiedź z pamięci podręcznej, nawet jeśli działa ona w tle Worker aktualizuje pamięć podręczną dla następnego żądania.
// Stale-While-Revalidate implementato manualmente
// (Cloudflare supporta anche il header standard, ma questa versione offre più controllo)
const CACHE_TTL = 60; // Secondi prima che la cache sia "fresh"
const STALE_TTL = 300; // Secondi aggiuntivi in cui la cache e "stale but usable"
interface CacheMetadata {
cachedAt: number;
ttl: number;
}
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
if (request.method !== 'GET') return fetch(request);
const cache = caches.default;
// Crea una Request con una custom cache key che include i metadata
const cacheKey = new Request(request.url, {
headers: request.headers,
});
const cached = await cache.match(cacheKey);
if (cached) {
const cachedAt = parseInt(cached.headers.get('X-Cached-At') ?? '0');
const age = (Date.now() - cachedAt) / 1000;
if (age < CACHE_TTL) {
// FRESH: servi dalla cache senza revalidazione
return addCacheHeaders(cached, 'FRESH', age);
}
if (age < CACHE_TTL + STALE_TTL) {
// STALE: servi dalla cache ma revalida in background
ctx.waitUntil(revalidate(cacheKey, cache));
return addCacheHeaders(cached, 'STALE', age);
}
}
// MISS o troppo vecchio: fetch sincrono
return fetchAndCache(cacheKey, cache, ctx);
},
};
async function revalidate(cacheKey: Request, cache: Cache): Promise<void> {
const fresh = await fetch(cacheKey.url);
if (fresh.ok) {
const toCache = addTimestamp(fresh);
await cache.put(cacheKey, toCache);
}
}
async function fetchAndCache(
cacheKey: Request,
cache: Cache,
ctx: ExecutionContext
): Promise<Response> {
const response = await fetch(cacheKey.url);
if (response.ok) {
const toCache = addTimestamp(response.clone());
ctx.waitUntil(cache.put(cacheKey, toCache));
}
const headers = new Headers(response.headers);
headers.set('X-Cache-Status', 'MISS');
return new Response(response.body, { status: response.status, headers });
}
function addTimestamp(response: Response): Response {
const headers = new Headers(response.headers);
headers.set('X-Cached-At', String(Date.now()));
// Cache-Control: max-age elevato per far "sopravvivere" la risposta in cache
headers.set('Cache-Control', 'public, max-age=86400');
return new Response(response.body, { status: response.status, headers });
}
function addCacheHeaders(response: Response, status: string, age: number): Response {
const headers = new Headers(response.headers);
headers.set('X-Cache-Status', status);
headers.set('Age', String(Math.floor(age)));
return new Response(response.body, { status: response.status, headers });
}
interface Env {}
Niestandardowe klucze pamięci podręcznej: izoluj pamięć podręczną według kontekstu
Domyślnie kluczem pamięci podręcznej jest pełny adres URL żądania. Ale często potrzebujesz
pamięci podręcznych rozdzielonych według języka, wersji API, poziomu użytkownika lub urządzenia. Rozwiązaniem jest
zbuduj jeden niestandardowy klucz pamięci podręcznej jako przedmiot Request
z krótkim adresem URL.
// Cache differenziata per lingua e versione API
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const cache = caches.default;
const url = new URL(request.url);
// Estrai parametri rilevanti per la cache key
const lang = request.headers.get('Accept-Language')?.split(',')[0]?.split('-')[0] ?? 'en';
const apiVersion = url.searchParams.get('v') ?? 'v1';
const tier = request.headers.get('X-User-Tier') ?? 'free';
// Costruisci una URL sintetica come cache key
// Non deve essere una URL reale, solo identificativa
const cacheKeyUrl = new URL(request.url);
cacheKeyUrl.searchParams.set('_ck_lang', lang);
cacheKeyUrl.searchParams.set('_ck_v', apiVersion);
// Non includiamo 'tier' nella key se vuoi condividere la cache tra tier
const cacheKey = new Request(cacheKeyUrl.toString(), {
method: 'GET',
// Importante: non copiare headers di autenticazione nella cache key
// altrimenti ogni utente avrebbe la propria cache entry
});
// Cerca nella cache con la custom key
let response = await cache.match(cacheKey);
if (response) {
return response;
}
// Fetch dall'origin passando la richiesta originale (con auth headers)
const originResponse = await fetch(request);
if (originResponse.ok && isCacheable(originResponse)) {
const toCache = setCacheHeaders(originResponse.clone(), 300);
ctx.waitUntil(cache.put(cacheKey, toCache));
}
return originResponse;
},
};
function isCacheable(response: Response): boolean {
// Non mettere in cache risposte con dati personali o Set-Cookie
if (response.headers.has('Set-Cookie')) return false;
if (response.headers.get('Cache-Control')?.includes('private')) return false;
if (response.headers.get('Cache-Control')?.includes('no-store')) return false;
return true;
}
function setCacheHeaders(response: Response, maxAge: number): Response {
const headers = new Headers(response.headers);
headers.set('Cache-Control', `public, max-age=${maxAge}, s-maxage=${maxAge}`);
// Rimuovi header che potrebbero impedire il caching
headers.delete('Set-Cookie');
return new Response(response.body, { status: response.status, headers });
}
interface Env {}
Nagłówki pamięci podręcznej: s-maxage, nieaktualne podczas ponownego sprawdzania poprawności, nieaktualne w przypadku błędu
Cloudflare szanuje Standardowe nagłówki Cache-Control i przedłuża go znaczenie. Zrozumienie tych wytycznych jest niezbędne:
| Dyrektywa | Oznaczający | Przykład |
|---|---|---|
max-age=N |
TTL dla przeglądarki i CDN (N sekund) | max-age=300 |
s-maxage=N |
TTL tylko dla CDN/proxy (zastępuje maksymalny wiek dla Cloudflare) | s-maxage=3600, max-age=60 |
stale-while-revalidate=N |
Dodatkowe sekundy, w ciągu których można podać nieaktualne podczas ponownej walidacji | s-maxage=60, stale-while-revalidate=300 |
stale-if-error=N |
Sekundy, w których ma zostać wyświetlony nieaktualny, jeśli źródło zwróci błąd | stale-if-error=86400 |
no-store |
W żadnym wypadku nie buforuj | Dla wrażliwych danych |
private |
Tylko przeglądarka pamięci podręcznej, a nie CDN | Dla uwierzytelnionych odpowiedzi |
// Esempio: API con s-maxage e stale-while-revalidate via header
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
// Routing con TTL differenziati per tipo di risorsa
if (url.pathname.startsWith('/api/products')) {
return fetchWithCacheHeaders(request, {
sMaxAge: 300, // 5 minuti fresh in CDN
staleWhileRevalidate: 3600, // 1 ora stale accettabile
staleIfError: 86400, // 1 giorno stale in caso di errore origin
});
}
if (url.pathname.startsWith('/api/user')) {
// Dati utente: non cacheare in CDN
return fetchWithCacheHeaders(request, {
sMaxAge: 0,
private: true,
});
}
if (url.pathname.startsWith('/static')) {
// Asset statici: cache aggressiva
return fetchWithCacheHeaders(request, {
sMaxAge: 31536000, // 1 anno
immutable: true,
});
}
return fetch(request);
},
};
interface CacheOptions {
sMaxAge?: number;
staleWhileRevalidate?: number;
staleIfError?: number;
private?: boolean;
immutable?: boolean;
}
async function fetchWithCacheHeaders(
request: Request,
options: CacheOptions
): Promise<Response> {
const response = await fetch(request);
const headers = new Headers(response.headers);
let cacheControl = '';
if (options.private) {
cacheControl = 'private, no-store';
} else {
const parts: string[] = ['public'];
if (options.sMaxAge !== undefined) parts.push(`s-maxage=${options.sMaxAge}`);
if (options.staleWhileRevalidate) parts.push(`stale-while-revalidate=${options.staleWhileRevalidate}`);
if (options.staleIfError) parts.push(`stale-if-error=${options.staleIfError}`);
if (options.immutable) parts.push('immutable');
cacheControl = parts.join(', ');
}
headers.set('Cache-Control', cacheControl);
return new Response(response.body, { status: response.status, headers });
}
interface Env {}
Unieważnienie: Usuń adres URL, tag i prefiks
cache.delete(request) usuń pamięć podręczną tylko w lokalnym punkcie PoP, w którym działa Worker.
Aby unieważnić pamięć podręczną we wszystkich punktach PoP na całym świecie, musisz skorzystać z API
REST usuwania strefy Cloudflare. Jest to właściwy mechanizm zarządzania treścią
i wdrożyć.
// Invalidation globale tramite Cloudflare API
// Da usare tipicamente da un webhook CMS o da un Worker admin
interface PurgeOptions {
files?: string[]; // URL specifici
tags?: string[]; // Cache-Tag headers
prefixes?: string[]; // URL prefix
hosts?: string[]; // Tutti gli URL di un host
}
async function purgeCloudflareCache(
zoneId: string,
apiToken: string,
options: PurgeOptions
): Promise<void> {
const response = await fetch(
`https://api.cloudflare.com/client/v4/zones/${zoneId}/purge_cache`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${apiToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(options),
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(`Purge failed: ${JSON.stringify(error)}`);
}
}
// Worker che funge da webhook per invalidazione CMS
export default {
async fetch(request: Request, env: Env): Promise<Response> {
if (request.method !== 'POST') {
return new Response('Method Not Allowed', { status: 405 });
}
// Verifica il secret del webhook
const secret = request.headers.get('X-Webhook-Secret');
if (secret !== env.WEBHOOK_SECRET) {
return new Response('Unauthorized', { status: 401 });
}
const body = await request.json() as WebhookPayload;
// Invalida le URL specifiche aggiornate dal CMS
if (body.type === 'post.updated') {
await purgeCloudflareCache(env.ZONE_ID, env.CF_API_TOKEN, {
files: [
`https://example.com/blog/${body.slug}`,
`https://example.com/api/posts/${body.id}`,
`https://example.com/sitemap.xml`,
],
});
}
// Invalida per tag (richiede Cache-Tag header sulle risposte origin)
if (body.type === 'category.updated') {
await purgeCloudflareCache(env.ZONE_ID, env.CF_API_TOKEN, {
tags: [`category-${body.categorySlug}`],
});
}
return new Response(JSON.stringify({ purged: true }), {
headers: { 'Content-Type': 'application/json' },
});
},
};
interface WebhookPayload {
type: string;
id?: string;
slug?: string;
categorySlug?: string;
}
interface Env {
ZONE_ID: string;
CF_API_TOKEN: string;
WEBHOOK_SECRET: string;
}
Tagi pamięci podręcznej: unieważnienie semantyczne
I Tagi pamięci podręcznej są najpotężniejszym mechanizmem unieważniania
selektywny. Działają poprzez dodanie nagłówka Cache-Tag do odpowiedzi:
każda odpowiedź może mieć wiele tagów i możesz unieważnić wszystkie adresy URL
powiązany z tagiem za pomocą pojedynczego wywołania API.
// Origin server o Worker che aggiunge Cache-Tag alle risposte
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
const response = await fetch(request);
const headers = new Headers(response.headers);
// Aggiungi tag semantici basati sul contenuto
const tags: string[] = [];
// Tag per tipo di risorsa
if (url.pathname.startsWith('/api/products')) {
const productId = url.pathname.split('/')[3];
tags.push('products'); // Invalida tutti i prodotti
if (productId) tags.push(`product-${productId}`); // Invalida questo prodotto specifico
}
if (url.pathname.startsWith('/api/categories')) {
const catId = url.pathname.split('/')[3];
tags.push('categories');
if (catId) tags.push(`category-${catId}`);
}
// Tag per versione dell'API
const apiVersion = url.pathname.split('/')[2];
if (apiVersion?.startsWith('v')) {
tags.push(`api-${apiVersion}`);
}
if (tags.length > 0) {
// Cache-Tag: lista separata da virgole, max 16KB
headers.set('Cache-Tag', tags.join(','));
}
return new Response(response.body, { status: response.status, headers });
},
};
// Esempio di invalidazione per tag dopo un aggiornamento:
// POST /api/admin/purge
// { "tags": ["product-123", "categories"] }
// Invalida tutte le URL che hanno Cache-Tag: product-123 o categories
interface Env {}
Zaawansowany wzorzec: pamięć podręczna z KV jako L2
Interfejs API pamięci podręcznej ma ważne ograniczenie: nie jest dostępny programowo dla dowolnego odczytu/zapisu, tylko żądania HTTP. Więcej wzorów złożone (takie jak skoordynowane unieważnianie, wyłącznik automatyczny lub pamięć podręczna obiektów). inne niż HTTP), użyj Workers KV jako pamięć podręczna L2.
// Cache a due livelli: Cache API (L1, HTTP) + KV (L2, programmabile)
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const url = new URL(request.url);
const cacheKey = buildCacheKey(url);
// L1: Cache API (piu veloce, locale al PoP)
const l1Cache = caches.default;
const l1Hit = await l1Cache.match(request);
if (l1Hit) {
return addHeader(l1Hit, 'X-Cache', 'L1-HIT');
}
// L2: KV Store (globale, programmabile, con TTL gestito da KV)
const kvCached = await env.API_CACHE.getWithMetadata<CacheMetadata>(cacheKey);
if (kvCached.value !== null) {
const { value, metadata } = kvCached;
// Ricostruisci una Response dalla stringa KV
const cachedResponse = new Response(value, {
headers: {
'Content-Type': metadata?.contentType ?? 'application/json',
'Cache-Control': 'public, max-age=60',
'X-Cache': 'L2-HIT',
'X-Cached-At': String(metadata?.cachedAt ?? 0),
},
});
// Popola anche L1 per richieste successive nello stesso PoP
ctx.waitUntil(l1Cache.put(request, cachedResponse.clone()));
return cachedResponse;
}
// MISS su entrambi i livelli: fetch dall'origin
const originResponse = await fetch(request);
if (originResponse.ok) {
const body = await originResponse.text();
const contentType = originResponse.headers.get('Content-Type') ?? 'application/json';
const metadata: CacheMetadata = {
cachedAt: Date.now(),
contentType,
url: url.toString(),
};
// Salva in KV con TTL di 5 minuti
ctx.waitUntil(
env.API_CACHE.put(cacheKey, body, {
expirationTtl: 300,
metadata,
})
);
// Salva anche in L1
const toL1 = new Response(body, {
headers: {
'Content-Type': contentType,
'Cache-Control': 'public, max-age=60',
'X-Cache': 'MISS',
},
});
ctx.waitUntil(l1Cache.put(request, toL1));
return new Response(body, {
headers: {
'Content-Type': contentType,
'X-Cache': 'MISS',
},
});
}
return addHeader(originResponse, 'X-Cache', 'BYPASS-ERROR');
},
};
function buildCacheKey(url: URL): string {
// Normalizza l'URL per la cache key (rimuovi query params non semantici)
const params = new URLSearchParams(url.searchParams);
params.delete('utm_source');
params.delete('utm_medium');
params.delete('_t'); // timestamp di cache-busting
params.sort(); // ordine deterministico
return `${url.pathname}?${params.toString()}`;
}
function addHeader(response: Response, key: string, value: string): Response {
const headers = new Headers(response.headers);
headers.set(key, value);
return new Response(response.body, { status: response.status, headers });
}
interface CacheMetadata {
cachedAt: number;
contentType: string;
url: string;
}
interface Env {
API_CACHE: KVNamespace;
}
Ogrzewanie pamięci podręcznej: Wstępnie wypełnij pamięć podręczną
Il podgrzewanie pamięci podręcznej jest to praktyka polegająca na wstępnym wypełnieniu pamięci podręcznej że przychodzą prawdziwe żądania, eliminując problem zimnej pamięci podręcznej po wdrożeniu masowe unieważnienia. Jest implementowany za pomocą pracownika zaplanowanego za pomocą Cron Trigger.
// wrangler.toml - Cron Trigger per cache warming
// [triggers]
// crons = ["*/15 * * * *"] # Ogni 15 minuti
// worker.ts - Cache Warming Worker
const URLS_TO_WARM = [
'https://api.example.com/products?featured=true',
'https://api.example.com/categories',
'https://api.example.com/homepage',
'https://api.example.com/navigation',
];
export default {
// Scheduled handler per Cron Trigger
async scheduled(event: ScheduledEvent, env: Env, ctx: ExecutionContext): Promise<void> {
console.log(`Cache warming started at ${new Date(event.scheduledTime).toISOString()}`);
const results = await Promise.allSettled(
URLS_TO_WARM.map(url => warmUrl(url))
);
const succeeded = results.filter(r => r.status === 'fulfilled').length;
const failed = results.filter(r => r.status === 'rejected').length;
console.log(`Cache warming complete: ${succeeded} success, ${failed} failed`);
},
async fetch(request: Request): Promise<Response> {
return new Response('Cache Warmer Worker', { status: 200 });
},
};
async function warmUrl(url: string): Promise<void> {
// Forza bypass della cache aggiungendo header speciale
// (da gestire lato Worker principale con whitelist IP o secret)
const response = await fetch(url, {
headers: {
'Cache-Control': 'no-cache', // Forza revalidazione
'X-Cache-Warm': 'true',
},
});
if (!response.ok) {
throw new Error(`Failed to warm ${url}: ${response.status}`);
}
}
interface Env {}
interface ScheduledEvent {
scheduledTime: number;
cron: string;
}
Debugowanie i monitorowanie pamięci podręcznej
Cloudflare udostępnia nagłówki diagnostyczne w odpowiedziach, aby zrozumieć stan pamięci podręcznej.
Najważniejsze jest CF-Cache-Status:
| Stan pamięci podręcznej CF | Oznaczający |
|---|---|
HIT |
Obsługiwane z pamięci podręcznej Cloudflare |
MISS |
Nie buforowane, wymagane w miejscu pochodzenia |
EXPIRED |
Zapisano w pamięci podręcznej, ale wygasł czas TTL, żądanie do źródła |
STALE |
Podawane jako nieświeże (nieaktualne do ponownego sprawdzenia) |
BYPASS |
Pominięto pamięć podręczną (plik cookie, nagłówek uwierzytelniania itp.) |
DYNAMIC |
Niebuforowalne (odpowiedź dynamiczna) |
REVALIDATED |
Pamięć podręczna sprawdzona w Origin (304 niemodyfikowana) |
// Worker che logga le metriche di cache su KV Analytics
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const response = await fetch(request);
const cacheStatus = response.headers.get('CF-Cache-Status') ?? 'UNKNOWN';
// Logga la metrica in background
ctx.waitUntil(
logCacheMetric(env, {
url: request.url,
status: cacheStatus,
timestamp: Date.now(),
country: request.cf?.country ?? 'unknown',
datacenter: request.cf?.colo ?? 'unknown',
})
);
return response;
},
};
async function logCacheMetric(env: Env, metric: CacheMetric): Promise<void> {
// Aggrega per finestre di 1 minuto
const minuteKey = `metrics:${Math.floor(metric.timestamp / 60000)}:${metric.status}`;
const current = parseInt(await env.METRICS_KV.get(minuteKey) ?? '0');
await env.METRICS_KV.put(minuteKey, String(current + 1), { expirationTtl: 3600 });
}
interface CacheMetric {
url: string;
status: string;
timestamp: number;
country: string;
datacenter: string;
}
interface Env {
METRICS_KV: KVNamespace;
}
Wnioski i dalsze kroki
API Cloudflare Workers Cache API przekształca CDN z narzędzia pasywnego w komponent aktywny w architekturze. Dzięki strategiom opisanym w tym artykule możesz budować ziarnista, semantyczna, nieprawidłowa warstwa pamięci podręcznej, która zmniejsza obciążenie 70–90% pochodzenia w przypadku typowych obciążeń publicznych interfejsów API.
Kluczowe punkty do zapamiętania: użyj ctx.waitUntil() aby nie blokować odpowiedzi
do klienta, zbuduj niestandardowe klucze pamięci podręcznej, aby odizolować różne konteksty, użyj znaczników pamięci podręcznej
dla unieważnienia semantycznego i połącz Cache API z KV, aby uzyskać bardziej złożone wzorce.
Następne artykuły z serii
- Artykuł 9: Testowanie pracowników lokalnych — Miniflare, Vitest e Wrangler Dev: jak pisać testy jednostkowe i testy integracyjne dla pracowników bez wdrażania.
- Artykuł 10: Architektury Full-Stack na krawędzi — studium przypadku autorstwa Od zera do produkcji: kompletny interfejs API REST z modułami Workers + D1 + R2 i CI/CD w akcjach GitHub.







