Redis
 Computer >> コンピューター >  >> プログラミング >> Redis

Next.jsアプリにEdge Functionsで待合室を実装する

この記事では、Vercel Edge FunctionsとUpstash Redisを使用して、Next.jsアプリケーションに待合室(ウェイティングルーム)機能を実装する方法を解説します。

ソースコードデモアプリも公開されています。

待合室とは?

待合室は、Webサイトへの同時アクセス数を制限してリソースの過負荷を防ぎたい場合に有効です。

この実装では、最大アクティブ訪問者数を設定でき、トラフィックを制御する2つのパラメータがあります:

  • 最大サイト容量(Max website capacity):同時にサイトに滞在できる最大訪問者数
  • 最大セッションタイムアウト(Max session timeout):訪問者がアイドル状態でいられる最大秒数

ステップ1:プロジェクトのセットアップ

Next.jsアプリの作成

npx create-next-app@latest --typescript
# プロジェクト名: nextjs-waiting-room

upstash-redisのインストール

npm install @upstash/redis

ステップ2:実装

VercelはNext.jsミドルウェアを通じてEdge Functionsをサポートしています。pages/api/_middleware.tsを作成し、/apiへのすべてのリクエストをインターセプトします。詳細な設定については公式ドキュメントを参照してください。

pages/api/_middleware.tsを以下のように更新します:

import { Redis } from "@upstash/redis";
import { NextFetchEvent, NextRequest, NextResponse } from "next/server";

const COOKIE_NAME_ID = "__waiting_room_id";
const COOKIE_NAME_TIME = "__waiting_room_last_update_time";
const UPSTASH_REDIS_REST_TOKEN = "YOUR_TOKEN_HERE";
const UPSTASH_REDIS_REST_URL = "YOUR_URL_HERE";
const TOTAL_ACTIVE_USERS = 10;
const SESSION_DURATION_SECONDS = 30;

const redis = new Redis({
  url: UPSTASH_REDIS_REST_URL,
  token: UPSTASH_REDIS_REST_TOKEN,
});

export async function middleware(req: NextRequest, ev: NextFetchEvent) {
  const cookies = req.cookies;
  let userId;
  
  if (cookies[COOKIE_NAME_ID] != null) {
    userId = cookies[COOKIE_NAME_ID];
  } else {
    userId = makeid(8);
  }

  const size = await redis.dbsize();
  console.log("current capacity:" + size);
  
  // 容量に余裕がある場合
  if (size < TOTAL_ACTIVE_USERS) {
    return getDefaultResponse(req, userId);
  } else {
    // サイト容量が満杯の場合
    const user = await redis.get(userId);
    if (user === "1") {
      // 既にアクティブなセッションを持っている
      return getDefaultResponse(req, userId);
    } else {
      // 容量オーバーなので待合室へ転送
      return getWaitingRoomResponse();
    }
  }
}

function makeid(length: number) {
  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;
}

async function getDefaultResponse(request: NextRequest, userId: string) {
  // 静的HTMLでテストする場合は以下をアンコメント
  let newResponse = new NextResponse(default_html);
  newResponse.headers.set("content-type", "text/html;charset=UTF-8");

  // 実際のリクエストをプロキシする場合は以下を使用
  // const response = await fetch(request)
  // const newResponse = new Response(response.body, response)

  const cookies = request.cookies;
  const now = Date.now();
  let lastUpdate = cookies[COOKIE_NAME_TIME];
  let lastUpdateTime = 0;
  if (lastUpdate) lastUpdateTime = parseInt(lastUpdate);
  
  const diff = now - lastUpdateTime;
  const updateInterval = (SESSION_DURATION_SECONDS * 1000) / 2;
  
  if (diff > updateInterval) {
    await redis.setex(userId, SESSION_DURATION_SECONDS, "1");
    newResponse.cookies.set(COOKIE_NAME_TIME, now.toString());
  }
  newResponse.cookies.set(COOKIE_NAME_ID, userId);
  return newResponse;
}

async function getWaitingRoomResponse() {
  const newResponse = new NextResponse(waiting_room_html);
  newResponse.headers.set("content-type", "text/html;charset=UTF-8");
  return newResponse;
}

const waiting_room_html = `
Waiting Room




只今混み合っております
お待ちいただきありがとうございます

アクセスが集中しています。そのままお待ちください。まもなくご案内いたします。

このページは自動的に更新されます。ブラウザを閉じないでください。

`; const default_html = ` Waiting Room Demo

Waiting Room Demo

別のブラウザからアクセスすると、容量が満杯のときに待合室へ転送されます。

このプロジェクトのセットアップ方法は こちら をご覧ください。

`;

ステートストアとしてUpstash Redisを使用し、アクティブなユーザーセッションを管理します。REST APIを持つUpstash Redisは、Vercel Edge Functionsと互換性があります。

Upstashコンソールでグローバルデータベースを作成し、RESTトークンとREST URLをコピーしてください。Redisデータベースは空の状態で、このアプリケーション専用として使用します。

要件に合わせてTOTAL_ACTIVE_USERSSESSION_DURATION_SECONDSを調整してください。

新規訪問者には一意のIDを生成してCookieとして設定し、Redisに登録します。再訪問時はRedisでセッションの有無を確認します。Redisへの登録時にはセッションアイドルタイムアウトを有効期限として設定します。セッション数が最大容量を超えた場合、新規ユーザーは待合室ページへ転送されます。

waiting_room_htmlを編集して待合室ページをカスタマイズできます。

getDefaultResponse()メソッドを変更して、NextResponseを使用して独自のページへ転送することも可能です。

ステップ3:実行とデプロイ

npm run devでローカル実行できます。テストしやすくするため、TOTAL_ACTIVE_USERS1に設定し、異なるブラウザでhttps://localhost:3000/api/helloを開いて動作確認してください。

Vercelへのデプロイは以下のコマンドで行えます:

vercel deploy --prod

Vercelは_middleware.tsを世界中のエッジロケーションで実行し、グローバルにレイテンシを最小化します。

まとめ

このチュートリアルでは、VercelとUpstashのおかげで、エッジでの動的アプリケーション構築がいかに容易かを示しました。その他の例はExamplesリポジトリで確認できます。

フィードバックはTwitterまたはDiscordでお待ちしています。

  1. iOS 13ダークモードにアプリを対応させる方法:開発者向け実装ガイド

    Appleは2019年9月19日、過去4年以内に発売されたすべてのiPhone(iPhone 6s以降)を対象に、待望のiOS 13アップデートを世界中でリリースしました。このアップデートの最大の目玉機能のひとつが、システム全体に適用されるiOS 13のダークモードです。スマートフォンのディスプレイが発する白い光による目の疲れを軽減できることが期待されています。この機能はAppleデバイスのエンドユーザーにとって嬉しいものですが、iOS開発者にとっては、iOS 13ダークモードに対応したアプリを準備するという課題でもあります。iOS 13ダークモードにアプリを対応させるには?開発者の皆さんの参

  2. CCleaner for Android 徹底レビュー:スマホの動作が重いと感じたら試したい無料最適化アプリ

    スマートフォンは、私たちが所有する最も重要で価値のある道具です。人とのつながりだけでなく、財布、ナビゲーション、エンターテインメント、さらには仕事のツールとしても欠かせない存在になっています。新しいスマホを手にしたときのワクワク感は格別ですが、数ヶ月使用するとその感覚は薄れていきます。かつてのキビキビした操作感、瞬時の反応、一瞬で起動するアプリの速さは消え去り、代わりに動作のもっさり感やアプリ起動までの待ち時間の増加などに悩まされるようになるのです。 しかし、それはスマホを買い替えるタイミングではなく、最適化すべきサインです。あらゆる機械にはメンテナンスが必要であり、スマートフォンも例外ではあ