CloudflareワーカーとサーバーレスRedisを使用して、Webサイト用の独自の待合室を構築します
このブログ投稿では、あなたのウェブサイトの待合室ページを実装します。
あなたのウェブサイトの訪問者の数が多いことは一般的に良いことですが、常にではありません。突然の高トラフィックは、アプリケーションを簡単に圧倒し、サービスを完全に中断させる可能性があります。待合室は、トラフィックを制御し、トラフィックの急増時にリソースを保護するのに役立つソリューションです。 Cloudflareの待合室は優れたソリューションですが、ビジネスアカウントとエンタープライズアカウントでのみ利用できます。心配しないでください。このブログでは、CloudflareWorkersとUpstashRedisを使用して、あらゆるタイプのWebサイトの待合室を構築します。
待合室を制御するための2つのパラメータがあります。
- 最大セッション期間 :訪問者はWebサイトでどのくらいアイドル状態を維持できますか?
- ウェブサイトの最大容量 :ウェブサイトは同時に何人の訪問者を許可できますか?
訪問者が当社のWebサイトにアクセスすると、一意のキー(セッションキーなど)が生成され、Redisに保持されます。 最大セッション期間である有効期限を使用して、このキーをRedisに配置します 。訪問者をWebサイトにアクセスさせる前に、Redisのdbsizeを確認します。サイズが**ウェブサイトの最大容量よりも大きい場合**、訪問者を待合室に転送します。待合室は静的なHTMLですが、30秒ごとに更新されます。つまり、30秒に1回、空きがあれば訪問者はWebサイトにアクセスできます。
生成した一意のキーも、Cookieとして訪問者のリクエストに書き込まれます。したがって、同じ訪問者のリクエストには同じキーがあります。これは、定員がいっぱいになったときに訪問者がWebサイト内ですでにセッションを行っているかどうかを確認するために必要です。そのため、訪問者は、キーがRedisキースペースに存在する限り、容量がいっぱいであってもWebサイトにアクセスできます。最大セッション期間より長くアイドル状態を維持すると、キーがRedisから削除されます。また、リクエストを送信するときに容量がいっぱいになると、待合室に転送されます。
このロジックはCloudflareワーカーで実装します。 UpstashをRedisストアとして使用します。それでは、これらのテクノロジーに関する決定の背景について説明しましょう。
待合室の実装は、Webサイトへのすべての要求をインターセプトします。したがって、パフォーマンスのオーバーヘッドは最小限に抑える必要があります。 Cloudflare WorkersはCloudflareエッジインフラストラクチャを活用しているため、グローバルに最小のレイテンシーを提供します。また、AWS Lambdaとは異なり、コールドスタートの問題はありません。サーバーレステクノロジーであるため、スケーラビリティも問題になりません。
UpstashRedisを使用する理由
新しい訪問者を許可する前に、Webサイトの現在のサイズを確認する必要があります。 Cloudflareワーカーはステートレスであるため、この情報を外部に保持する必要があります。 Redisは、レイテンシが低いため、最良の選択です。ただし、Redisオファリングには、CloudflareワーカーでサポートされていないTCPベースの接続が必要です。 Upstashは、RESTAPIが組み込まれた唯一のRedisオファリングです。また、グローバルレプリケーションのおかげで、世界中で低遅延が実現します。
以下のステップバイステップでプロジェクトを実装します。プロジェクトのクローンを作成し、Webサイトの待合室を直接設定する場合は、ソースコードのreadmeの手順に従うことができます。
1
プロジェクトの設定
ラングラーを使用してプロジェクトを作成します。
wrangler generate waiting-room
次に、依存関係をインストールします:
npm install cookie upstash@redis
2
wrangler.tomlを更新する
タイプを更新します:
type = "webpack"
CloudflareアカウントIDを設定します。これをチェックして、アカウントIDを見つけてください。
account_id = "REPLACE_HERE"
これらの変数を追加します:
[vars]
UPSTASH_REDIS_REST_TOKEN = "REPLACE_HERE"
UPSTASH_REDIS_REST_URL = "REPLACE_HERE"
TOTAL_ACTIVE_USERS = 10
SESSION_DURATION_SECONDS = 30
Upstashコンソールからグローバルデータベースを作成する必要があります。コンソールからRESTトークンとURLをコピーして貼り付けるだけです。 Redisデータベースは最初は空で、このアプリケーションでのみ使用する必要があります。
独自の要件に応じて、TOTAL_ACTIVE_USERSとSESSION_DURATION_SECONDSを設定する必要があります。
3
index.js
Index.jsはCloudflareWorkersの実装ファイルです。したがって、すべてのロジックをその中に配置します。以下のコードをコピーして貼り付けます:
import { parse } from "cookie";
import { Redis } from "@upstash/redis/cloudflare";
const redis = Redis.fromEnv();
addEventListener("fetch", (event) => {
event.respondWith(
handleRequest(event.request).catch(
(err) => new Response(err.stack, { status: 500 })
)
);
});
const COOKIE_NAME_ID = "__waiting_room_id";
const COOKIE_NAME_TIME = "__waiting_room_last_update_time";
const init = {
headers: {
Authorization: "Bearer " + UPSTASH_REDIS_REST_TOKEN,
},
};
async function handleRequest(request) {
const { pathname } = new URL(request.url);
if (!pathname.startsWith("/favicon")) {
const cookie = parse(request.headers.get("Cookie") || "");
let userId;
if (cookie[COOKIE_NAME_ID] != null) {
userId = cookie[COOKIE_NAME_ID];
} else {
userId = makeid(8);
}
const size = await redis.dbsize();
console.log("current capacity:" + size);
// there is enough capacity
if (size < TOTAL_ACTIVE_USERS) {
return getDefaultResponse(request, cookie, userId);
} else {
// site capacity is full
const user = await redis.get(userId);
if (user === "1") {
// the user has already active session
return getDefaultResponse(request, cookie, userId);
} else {
// capacity is full so the user is forwarded to waiting room
return getWaitingRoomResponse(userId);
}
}
} else {
return fetch(request);
}
}
async function getDefaultResponse(request, cookie, userId) {
// uncomment below to test the function with a static html content
// const newResponse = new Response(default_html)
// newResponse.headers.append('content-type', 'text/html;charset=UTF-8')
const response = await fetch(request);
const newResponse = new Response(response.body, response);
const now = Date.now();
let lastUpdate = cookie[COOKIE_NAME_TIME];
if (!lastUpdate) lastUpdate = 0;
const diff = now - lastUpdate;
const updateInterval = (SESSION_DURATION_SECONDS * 1000) / 2;
if (diff > updateInterval) {
await redis.setex(userId, SESSION_DURATION_SECONDS, 1);
newResponse.headers.append(
"Set-Cookie",
`${COOKIE_NAME_TIME}=${now}; path=/`
);
}
newResponse.headers.append(
"Set-Cookie",
`${COOKIE_NAME_ID}=${userId}; path=/`
);
return newResponse;
}
async function getWaitingRoomResponse(userId) {
const newResponse = new Response(waiting_room_html);
newResponse.headers.set("content-type", "text/html;charset=UTF-8");
return newResponse;
}
function makeid(length) {
let result = "";
const characters =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
const charactersLength = characters.length;
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
const waiting_room_html = `
<title>Waiting Room</title>
<meta http-equiv='refresh' content='30' />
<style>*{box-sizing:border-box;margin:0;padding:0}body{line-height:1.4;font-size:1rem;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif;padding:2rem;display:grid;place-items:center;min-height:100vh}.container{width:100%;max-width:800px}p{margin-top:.5rem}</style>
<div class='container'>
<h1>
<div>You are now in line.</div>
<div>Thanks for your patience.</div>
</h1>
<p>We are experiencing a high volume of traffic. Please sit tight and we will let you in soon. </p>
<p><b>This page will automatically refresh, please do not close your browser.</b></p>
</div>
`;
const default_html = `
<title>Waiting Room Demo</title>
<style>*{box-sizing:border-box;margin:0;padding:0}body{line-height:1.4;font-size:1rem;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif;padding:2rem;display:grid;place-items:center;min-height:100vh}.container{width:100%;max-width:800px}p{margin-top:.5rem}</style>
<div class="container">
<h1>
<div>Waiting Room Demo</div>
</h1>
<p>
Visit this site from a different browser, you will be forwarded to the waiting room when the capacity is full.
</p>
<p> Check <a href={"https://github.com/upstash/waiting-room"} style={{"color": "blue"}}>this project </a> to set up a waiting room for your website.</p>
</div>
`;
上記のコードでは、waiting_room_html
を編集できます。 変数。待合室ページの静的HTMLです。
handleRequestメソッドは、Webサイトの可用性に応じて、getDefaultResponseまたはgetWaitingRoomResponseのいずれかを返します。
4
ローカルで実行
待合室をローカルでテストするには、容量を1に設定し、セッション期間を30秒に設定するのが理にかなっている場合があります。次に実行します
wrangler dev
Chromeでhttps://127.0.0.1:8787/を開くと、次のように表示されます。
次に、Safari(またはChrome incognito)で同じURLを開くと、次のように表示されます。
30秒以上待ちます。待合室のページが更新され、Webサイトに入るのが表示されます。
私たちのローカルは実際のウェブサイトに転送しないので、Cloudflareの404ページが表示されます。それで問題ありません。
5
公開
CFワーカー機能を次のようにデプロイします:
wrangler publish
https://waiting-room.upsdev.workers.dev/
のようなURLが表示されますそれでは、ワーカーをWebサイトにルーティングしましょう。まず、ドメインのネームサーバーがCloudflareを指している必要があります。これをチェックして。次に、ドメインをルートとして追加し、CFワーカーダッシュボードでワーカー機能を選択する必要があります。
CloudflareWorkersとUpstashRedisのおかげで、アプリケーションコードに触れることなく待合室を構築することに成功しました。これは、強力なエッジ機能がUpstashとどのように連携するかを示すもう1つの指標であると考えています。
改善すべき点があります:
- 推定待機時間:平均待機時間を計算して表示できます。
- 公平で順序付けられたキュー:現在待機中の訪問者は、空きがあるときにランダムにサイトに入ります。待ち行列を維持し、訪問者を順番に許可しようとする場合があります。
上記の両方の改善には、より多くの状態を維持し、より多くのリモート呼び出しを行う必要があります。そのため、それらをスキップすることを選択しました。ユースケースで厳密にそれらが必要な場合は、このブログをチェックして、Cloudflareチームのエンタープライズソリューションに関する作業から着想を得てください。
ソースコードを確認してください。
TwitterまたはDiscordであなたの考えを教えてください。
-
CloudflareワーカーとサーバーレスRedisを使用して、Webサイト用の独自の待合室を構築します
このブログ投稿では、あなたのウェブサイトの待合室ページを実装します。 なぜですか? あなたのウェブサイトの訪問者の数が多いことは一般的に良いことですが、常にではありません。突然の高トラフィックは、アプリケーションを簡単に圧倒し、サービスを完全に中断させる可能性があります。待合室は、トラフィックを制御し、トラフィックの急増時にリソースを保護するのに役立つソリューションです。 Cloudflareの待合室は優れたソリューションですが、ビジネスアカウントとエンタープライズアカウントでのみ利用できます。心配しないでください。このブログでは、CloudflareWorkersとUpstashRedis
-
CloudflareワーカーとのRedis@Edge
エッジでのコンピューティングは、近年最もエキサイティングな機能の1つです。 CDNを使用すると、ファイルをユーザーに近づけることができます。エッジコンピューティングを使用すると、アプリケーションをユーザーの近くで実行できます。これは、開発者がグローバルに分散されたパフォーマンスの高いアプリケーションを構築するのに役立ちます。 Cloudflare Workersは、現在この分野の主要製品です。コールドスタートのないサーバーレス処理環境を提供します。 Cloudflareのグローバルネットワークを活用して、アプリケーションのレイテンシーを最小限に抑えます。関数はJavascript、Rust、