منذ أقل من 24 ساعة، كتبت منشورًا حول كيفية تسريع موقع الويب الخاص بك باستخدام ذاكرة التخزين المؤقت Cloudflare. ومع ذلك، فقد قمت منذ ذلك الحين بنقل معظم المنطق إلى برنامج وسيط Fastify باستخدام Redis. إليك السبب وكيف يمكنك القيام بذلك بنفسك.
لقد واجهت مشكلتين مع ذاكرة التخزين المؤقت لـ Cloudflare:
كانت هناك بعض المشكلات الأخرى التي واجهتها (مثل عدم القدرة على مسح ذاكرة التخزين المؤقت باستخدام مطابقة الأنماط)، ولكن هذه لم تكن حاسمة بالنسبة لحالة الاستخدام الخاصة بي.
لذلك، قررت نقل المنطق إلى برنامج وسيط Fastify باستخدام Redis.
[!ملحوظة]
لقد تركت ذاكرة التخزين المؤقت Cloudflare للتخزين المؤقت للصور. في هذه الحالة، تعمل ذاكرة التخزين المؤقت Cloudflare بشكل فعال كشبكة CDN.
ما يلي هو نسخة مشروحة من البرنامج الوسيط الذي كتبته لتخزين الاستجابات مؤقتًا باستخدام 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; 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 => { 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 ; 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; });
تتنقل التعليقات عبر الكود، ولكن إليك بعض النقاط الأساسية:
لقد أجريت اختبارات زمن الوصول من عدة مواقع وحصلت على أبطأ وقت استجابة لكل عنوان URL. النتائج أدناه:
عنوان URL | دولة | وقت استجابة المنشأ | زمن الاستجابة المخزن مؤقتًا على Cloudflare | تسريع وقت الاستجابة المخزنة مؤقتًا |
---|---|---|---|---|
https://pillser.com/vitamins/vitamin-b1 | الولايات المتحدة والغرب1 | 240 مللي ثانية | 16 مللي ثانية | 40 مللي ثانية |
https://pillser.com/vitamins/vitamin-b1 | أوروبا الغربية3 | 320 مللي ثانية | 10 مللي ثانية | 110 مللي ثانية |
https://pillser.com/vitamins/vitamin-b1 | أستراليا-جنوب شرق 1 | 362 مللي ثانية | 16 مللي ثانية | 192 مللي ثانية |
https://pillser.com/supplements/vitamin-b1-3254 | الولايات المتحدة والغرب1 | 280 مللي ثانية | 10 مللي ثانية | 38 مللي ثانية |
https://pillser.com/supplements/vitamin-b1-3254 | أوروبا الغربية3 | 340 مللي ثانية | 12 مللي ثانية | 141 مللي ثانية |
https://pillser.com/supplements/vitamin-b1-3254 | أستراليا-جنوب شرق 1 | 362 مللي ثانية | 14 مللي ثانية | 183 مللي ثانية |
بالمقارنة مع ذاكرة التخزين المؤقت Cloudflare، فإن ذاكرة التخزين المؤقت Fastify أبطأ. وذلك لأن المحتوى المخزن مؤقتًا لا يزال يتم تقديمه من الأصل، بينما يتم تقديم ذاكرة التخزين المؤقت لـ Cloudflare من مواقع الحافة الإقليمية. ومع ذلك، وجدت أن أوقات الاستجابة هذه كافية لتحقيق تجربة مستخدم جيدة.
تنصل: جميع الموارد المقدمة هي جزئيًا من الإنترنت. إذا كان هناك أي انتهاك لحقوق الطبع والنشر الخاصة بك أو الحقوق والمصالح الأخرى، فيرجى توضيح الأسباب التفصيلية وتقديم دليل على حقوق الطبع والنشر أو الحقوق والمصالح ثم إرسالها إلى البريد الإلكتروني: [email protected]. سوف نتعامل مع الأمر لك في أقرب وقت ممكن.
Copyright© 2022 湘ICP备2022001581号-3