24 時間も前に、Cloudflare キャッシュを使用して Web サイトを高速化する方法についての投稿を書きました。ただし、その後、ロジックの大部分を Redis を使用する Fastify ミドルウェアに移行しました。その理由と、自分で行う方法は次のとおりです。
Cloudflare キャッシュで 2 つの問題が発生しました:
他にもいくつか問題が発生しました (パターン マッチングを使用してキャッシュをパージできないなど) が、それらは私の使用例にとって重大なものではありませんでした。
そこで、Redis を使用してロジックを Fastify ミドルウェアに移動することにしました。
[!注記]
画像キャッシュのために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 | us-west1 | 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 | us-west1 | 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