Kurang daripada 24 jam yang lalu, saya menulis siaran tentang cara mempercepatkan tapak web anda menggunakan cache Cloudflare. Walau bagaimanapun, saya telah mengalihkan sebahagian besar logik ke perisian tengah Fastify menggunakan Redis. Inilah sebab dan cara anda boleh melakukannya sendiri.
Saya menghadapi dua isu dengan cache Cloudflare:
Terdapat beberapa isu lain yang saya hadapi (seperti tidak dapat membersihkan cache menggunakan padanan corak), tetapi itu tidak kritikal untuk kes penggunaan saya.
Oleh itu, saya memutuskan untuk mengalihkan logik ke perisian tengah Fastify menggunakan Redis.
[!NOTA]
Saya meninggalkan cache Cloudflare untuk cache imej. Dalam kes ini, cache Cloudflare berfungsi dengan berkesan sebagai CDN.
Yang berikut ialah versi perisian tengah beranotasi yang saya tulis untuk cache respons menggunakan Fastify.
const isCacheableRequest = (request: FastifyRequest): boolean => { // Do not attempt to use cache for authenticated visitors. if (request.visitor?.userAccount) { return false; } if (request.method !== 'GET') { return false; } // We only want to cache responses under /supplements/. if (!request.url.includes('/supplements/')) { return false; } // We provide a mechanism to bypass the cache. // This is necessary for implementing the "Serve Stale Content While Revalidating" feature. if (request.headers['cache-control'] === 'no-cache') { return false; } return true; }; const isCacheableResponse = (reply: FastifyReply): boolean => { if (reply.statusCode !== 200) { return false; } // We don't want to cache responses that are served from the cache. if (reply.getHeader('x-pillser-cache') === 'HIT') { return false; } // We only want to cache responses that are HTML. if (!reply.getHeader('content-type')?.toString().includes('text/html')) { return false; } return true; }; const generateRequestCacheKey = (request: FastifyRequest): string => { // We need to namespace the cache key to allow an easy purging of all the cache entries. return 'request:' + generateHash({ algorithm: 'sha256', buffer: stringifyJson({ method: request.method, url: request.url, // This is used to cache viewport specific responses. viewportWidth: request.viewportWidth, }), encoding: 'hex', }); }; type CachedResponse = { body: string; headers: Record<string, string>; statusCode: number; }; const refreshRequestCache = async (request: FastifyRequest) => { await got({ headers: { 'cache-control': 'no-cache', 'sec-ch-viewport-width': String(request.viewportWidth), 'user-agent': request.headers['user-agent'], }, method: 'GET', url: pathToAbsoluteUrl(request.originalUrl), }); }; app.addHook('onRequest', async (request, reply) => { if (!isCacheableRequest(request)) { return; } const cachedResponse = await redis.get(generateRequestCacheKey(request)); if (!cachedResponse) { return; } reply.header('x-pillser-cache', 'HIT'); const response: CachedResponse = parseJson(cachedResponse); reply.status(response.statusCode); reply.headers(response.headers); reply.send(response.body); reply.hijack(); setImmediate(() => { // After the response is sent, we send a request to refresh the cache in the background. // This effectively serves stale content while revalidating. // Therefore, this cache does not reduce the number of requests to the origin; // The goal is to reduce the response time for the user. refreshRequestCache(request); }); }); const readableToString = (readable: Readable): Promise<string> => { const chunks: Uint8Array[] = []; return new Promise((resolve, reject) => { readable.on('data', (chunk) => chunks.push(Buffer.from(chunk))); readable.on('error', (err) => reject(err)); readable.on('end', () => resolve(Buffer.concat(chunks).toString('utf8'))); }); }; app.addHook('onSend', async (request, reply, payload) => { if (reply.hasHeader('x-pillser-cache')) { return payload; } if (!isCacheableRequest(request) || !isCacheableResponse(reply) || !(payload instanceof Readable)) { // Indicate that the response is not cacheable. reply.header('x-pillser-cache', 'DYNAMIC'); return payload; } const content = await readableToString(payload); const headers = omit(reply.getHeaders(), [ 'content-length', 'set-cookie', 'x-pillser-cache', ]) as Record<string, string>; reply.header('x-pillser-cache', 'MISS'); await redis.setex( generateRequestCacheKey(request), getDuration('1 day', 'seconds'), stringifyJson({ body: content, headers, statusCode: reply.statusCode, } satisfies CachedResponse), ); return content; });
Komen berjalan melalui kod, tetapi berikut adalah beberapa perkara penting:
Saya menjalankan ujian kependaman dari beberapa lokasi dan menangkap masa respons yang paling perlahan untuk setiap URL. Keputusan adalah di bawah:
URL | Country | Origin Response Time | Cloudflare Cached Response Time | Fastify Cached Response Time |
---|---|---|---|---|
https://pillser.com/vitamins/vitamin-b1 | us-west1 | 240ms | 16ms | 40ms |
https://pillser.com/vitamins/vitamin-b1 | europe-west3 | 320ms | 10ms | 110ms |
https://pillser.com/vitamins/vitamin-b1 | australia-southeast1 | 362ms | 16ms | 192ms |
https://pillser.com/supplements/vitamin-b1-3254 | us-west1 | 280ms | 10ms | 38ms |
https://pillser.com/supplements/vitamin-b1-3254 | europe-west3 | 340ms | 12ms | 141ms |
https://pillser.com/supplements/vitamin-b1-3254 | australia-southeast1 | 362ms | 14ms | 183ms |
Berbanding dengan cache Cloudflare, cache Fastify adalah lebih perlahan. Ini kerana kandungan cache masih disampaikan dari asal, manakala cache Cloudflare dihidangkan dari lokasi pinggir wilayah. Walau bagaimanapun, saya mendapati bahawa masa respons ini adalah banyak untuk mencapai pengalaman pengguna yang baik.
Atas ialah kandungan terperinci Mempercepatkan Tapak Web Anda Menggunakan Fastify dan Cache Redis. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!