Cloudflare Çalışanlarında Önbellek API'si ve Geçersiz Kılma Stratejileri
Workers'taki Cache API, TTL'nin ayrıntılı kontrolüne olanak tanır. anahtarla yeniden doğrulama ve geçersiz kılma: nasıl oluşturulacağını öğrenin yüksek performanslı küresel dağıtılmış önbellekleme katmanı.
Uçta Önbelleğe Alma: Geleneksel CDN'nin Ötesinde
Geleneksel bir CDN, standart HTTP başlıklarını takip ederek statik varlıkları önbelleğe alır. Cloudflare Çalışanları konsepti çok daha ileri götürüyor: Önbellek API'si, kodunuz TypeScript, neyin ne kadar süreyle önbelleğe alınacağını cerrahi hassasiyetle kontrol eder. nasıl geçersiz kılındığı ve önbellek eskidiğinde hangi geri dönüş mantığının uygulanacağı.
Sonuç bir Programlanabilir CDN: kuralları yapılandırmak yerine Bir kontrol panelinde statik, bir şeyin olup olmadığına anında karar veren iş mantığını yazın. yanıt, hangi TTL ve hangi önbellek anahtarıyla önbelleğe alınabilir. Bu esneklik REST API'leri, özel sayfalar ve yarı dinamik içerik için özellikle değerlidir.
Ne Öğreneceksiniz
- Cloudflare Workers Cache API'si nasıl çalışır ve Tarayıcı Önbelleği API'sinden farkı
- Önbelleğe alma stratejileri: Önce Önbellek, Önce Ağ, Eskimişken Yeniden Doğrulama
- Özel önbellek anahtarları: önbelleği kullanıcıya, dile ve sürüme göre ayırın
- Cloudflare Zone Purge API ile URL'ler, etiketler ve önekler için geçersiz kılma
- Başlıkları değiştirin: Accept-Encoding, Accept-Language için farklılaştırılmış önbellek
- Gelişmiş modeller: önbellek ısınması, ödemesiz süre ve KV ile devre kesici
- Yaygın hatalar ve üretimde bunlardan nasıl kaçınılacağı
Önbellek API'si: Temel Bilgiler ve Tarayıcıdan Farklılıklar
Workers'ta kullanıma sunulan Cache API, API ile aynı arayüzü izler. Hizmet Çalışanı Önbellek API'si tarayıcı, ancak önemli farklar var. İşçilerde önbellek dağıtılmış tüm Cloudflare PoP'larında: Frankfurt'taki bir İşçi bir yanıtı önbelleğe aldığında, bu yanıt Londra veya Amsterdam'da otomatik olarak mevcut değildir. Her PoP'ta kendi yerel önbelleği.
// 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
Önbellek API'si: Önemli Kısıtlamalar
- Yalnızca HTTP istekleri (tarayıcıdaki gibi rastgele URL'ler değil)
- Yanıtları önbelleğe almak mümkün değildir
Vary: * - Önbellek PoP başınadır: otomatik olarak veri merkezleri arası geçersiz kılma yoktur.
cache.delete() - Durumu 206 (Kısmi İçerik) olan yanıtlar önbelleğe alınamaz
- Uyulan maksimum TTL 31 gündür
Strateji 1: Geri Dönüş Ağı ile Önbellek Öncelikli
Nadiren değişen verilere sahip API'ler için en yaygın strateji: önbellekten sunma mümkün olduğunda kaynağa gidin ve önbelleği doldurun.
// 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 {}
Strateji 2: Eskimişken Yeniden Doğrulama
Strateji yeniden doğrulama sırasında eskimiş en iyisini veren odur veri güncelliği ile algılanan hız arasındaki uzlaşma: müşteri alır Her zaman arka planda çalışıyor olsa bile önbellekten anında yanıt alınması Çalışan bir sonraki istek için önbelleği günceller.
// 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 {}
Özel Önbellek Anahtarları: Önbelleği Bağlama Göre Ayırın
Varsayılan olarak önbellek anahtarı isteğin tam URL'sidir. Ama çoğu zaman ihtiyacın var
dile, API sürümüne, kullanıcı veya cihaz katmanına göre ayrılmış önbelleklerin sayısı. Çözüm şu:
bir tane inşa et özel önbellek anahtarı bir nesne olarak Request
kısa bir URL ile.
// 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 {}
Önbellek Başlıkları: s-maxage, yeniden doğrulama sırasında eskime, hata durumunda eskime
Cloudflare saygı duyuyor Standart Önbellek Kontrolü başlıkları ve onu uzatıyor anlamı. Bu yönergeleri anlamak önemlidir:
| Direktif | Anlam | Örnek |
|---|---|---|
max-age=N |
Tarayıcı ve CDN için TTL (N saniye) | max-age=300 |
s-maxage=N |
Yalnızca CDN/proxy için TTL (Cloudflare için maksimum yaşı geçersiz kılar) | s-maxage=3600, max-age=60 |
stale-while-revalidate=N |
Yeniden doğrulanırken bayat yayınlanacak ek saniyeler | s-maxage=60, stale-while-revalidate=300 |
stale-if-error=N |
Kaynak hata döndürürse, bayat yayınlanacak saniyeler | stale-if-error=86400 |
no-store |
Hiçbir koşulda önbelleğe almayın | Hassas veriler için |
private |
Yalnızca önbellek tarayıcısı, CDN değil | Kimliği doğrulanmış yanıtlar için |
// 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 {}
Geçersiz Kılma: URL, Etiket ve Önek için Temizleme
cache.delete(request) önbelleği yalnızca Worker'ın çalıştığı yerel PoP'ta silin.
Önbelleği geçersiz kılmak için küresel olarak tüm PoP'lardaAPI'yi kullanmanız gerekir
Cloudflare Bölgesini Temizleme REST. Bu içerik yönetimi için doğru mekanizmadır
ve konuşlandırın.
// 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;
}
Önbellek Etiketleri: Semantik Geçersiz Kılma
I Önbellek Etiketleri geçersiz kılmanın en güçlü mekanizmasıdırlar
seçici. Bir başlık ekleyerek çalışırlar Cache-Tag cevaplara:
her yanıtın birden fazla etiketi olabilir ve tüm URL'leri geçersiz kılabilirsiniz
tek bir API çağrısına sahip bir etiketle ilişkilendirilir.
// 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 {}
Gelişmiş Model: L2 olarak KV ile Önbellek
Önbellek API'sinin önemli bir sınırlaması vardır: program aracılığıyla erişilemez isteğe bağlı okuma/yazma için, yalnızca HTTP istekleri. Daha fazla desen için karmaşık (koordineli geçersiz kılma, devre kesici veya nesne önbelleği gibi) HTTP olmayan), kullanım L2 önbelleği olarak İşçi KV'si.
// 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;
}
Önbellek Isıtma: Önbelleği Önceden Doldurun
Il önbellek ısınması önce önbelleği önceden doldurma uygulamasıdır gerçek isteklerin gelmesini sağlayarak dağıtımdan sonra soğuk önbellek sorununu ortadan kaldırır büyük geçersizlikler. Cron Trigger aracılığıyla programlanan bir İşçi ile uygulanır.
// 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;
}
Önbellek Hata Ayıklama ve İzleme
Cloudflare, önbellek durumunu anlamak için yanıtlarda teşhis başlıklarını gösterir.
En önemlisi CF-Cache-Status:
| CF-Önbellek Durumu | Anlam |
|---|---|
HIT |
Cloudflare önbelleğinden sunulur |
MISS |
Önbelleğe alınmadı, kaynakta istendi |
EXPIRED |
Önbelleğe alındı ancak TTL'nin süresi doldu, kaynak isteği |
STALE |
Bayat olarak servis edildi (yeniden doğrulama sırasında bayat) |
BYPASS |
Önbellek atlandı (Çerez, Kimlik Doğrulama başlığı vb.) |
DYNAMIC |
Önbelleğe alınamaz (dinamik yanıt) |
REVALIDATED |
Önbellek, kaynakla doğrulandı (304 Değiştirilmedi) |
// 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;
}
Sonuçlar ve Sonraki Adımlar
Cloudflare Workers Cache API, CDN'yi pasif bir araçtan bir bileşene dönüştürür Mimarlıkta aktif. Bu makalede açıklanan stratejilerle şunları yapabilirsiniz: yükü azaltan ayrıntılı, anlamsal, geçersiz bir önbellekleme katmanı Tipik genel API iş yükleri için kaynakta %70-90.
Hatırlanması gereken önemli noktalar: kullanın ctx.waitUntil() yanıtı engellememek için
istemciye, farklı bağlamları izole etmek için özel önbellek anahtarları oluşturun, Önbellek Etiketlerini kullanın
anlamsal geçersizleştirme için ve daha karmaşık modeller için Cache API'yi KV ile birleştirin.
Serideki Sonraki Yazılar
- Madde 9: Yerelde İşçilerin Test Edilmesi — Miniflare, Vitest e Wrangler Dev: Çalışanlar için dağıtım olmadan birim testleri ve entegrasyon testleri nasıl yazılır?
- Madde 10: Uçta Tam Yığın Mimariler — Örnek Olay İncelemesi Sıfırdan Üretime: Workers + D1 + R2 ve GitHub Eylemlerinde CI/CD içeren eksiksiz bir REST API.







