Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Redis

ห้องรอสำหรับแอป Next.js ของคุณโดยใช้ฟังก์ชันขอบ

ในโพสต์นี้ เราจะสร้างห้องรอสำหรับแอปพลิเคชัน Next.js ของคุณโดยใช้ฟังก์ชัน Vercel Edge และ Upstash Redis

คุณสามารถตรวจสอบซอร์สโค้ดและแอปสาธิตได้

ห้องรอ?

ห้องรอมีประโยชน์เมื่อคุณต้องการจำกัดจำนวนผู้เข้าชมเว็บไซต์ของคุณเพื่อไม่ให้ใช้ทรัพยากรมากเกินไป

ในการใช้งานของเรา คุณจะสามารถกำหนดจำนวนผู้เยี่ยมชมที่ใช้งานได้สูงสุด จะมีพารามิเตอร์สองตัวเพื่อควบคุมการรับส่งข้อมูล:

  • ความจุเว็บไซต์สูงสุด:จำนวนผู้เข้าชมเว็บไซต์สูงสุดพร้อมกันหรือไม่
  • หมดเวลาเซสชันสูงสุด:จำนวนวินาทีสูงสุดที่ผู้เยี่ยมชมสามารถอยู่นิ่งได้

ขั้นตอนที่ 1:การตั้งค่าโครงการ

สร้างแอป Next.js:

examples git:(master) ✗ npx create-next-app@latest --typescript

✔ What is your project named? … nextjs-waiting-room

Creating a new Next.js app in /Users/enes/dev/examples/nextjs-waiting-room.

ติดตั้ง upstash-redis:

npm install @upstash/redis

ขั้นตอนที่ 2:การนำไปใช้

Vercel รองรับฟังก์ชัน Edge ผ่านมิดเดิลแวร์ Next.js ดังนั้นเราจะเพิ่ม _middleware.ts ภายใต้ pages/api/ . รหัสมิดเดิลแวร์สกัดกั้นคำขอทั้งหมดที่ส่งไปยัง /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 = "REPLACE_HERE";
const UPSTASH_REDIS_REST_URL = "REPLACE_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);
  // there is enough capacity
  if (size < TOTAL_ACTIVE_USERS) {
    return getDefaultResponse(req, userId);
  } else {
    // site capacity is full
    const user = await redis.get(userId);
    if (user === "1") {
      // the user has already active session
      return getDefaultResponse(req, userId);
    } else {
      // capacity is full so the user is forwarded to waiting room
      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) {
  // uncomment below to test the function with a static html content
  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.cookie(COOKIE_NAME_TIME, now.toString());
  }
  newResponse.cookie(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 = `
<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='//github.com/upstash/redis-examples/tree/master/nextjs-waiting-room' style={{"color": "blue"}}>this project </a> to set up a waiting room for your website.</p>
</div>
`;

เราใช้ Upstash Redis เป็นที่เก็บสถานะเพื่อเก็บเซสชันผู้ใช้ที่ใช้งานอยู่ ด้วย REST API ทำให้ upstash-redis เข้ากันได้กับฟังก์ชัน Vercel Edge

คุณต้องสร้างฐานข้อมูลส่วนกลางจากคอนโซล Upstash คัดลอกและวางโทเค็น REST และ REST URL จากคอนโซล ฐานข้อมูล Redis ควรว่างเปล่าและใช้โดยแอปพลิเคชันนี้เท่านั้น

ตั้งค่า TOTAL_ACTIVE_USERS และ SESSION_DURATION_SECONDS ด้วย ขึ้นอยู่กับความต้องการของคุณเอง

แอปพลิเคชันสร้างรหัสเฉพาะสำหรับผู้เยี่ยมชมใหม่และตั้งเป็นคุกกี้และพุชไปที่ Redis ดังนั้นในครั้งต่อไป แอปพลิเคชันจะตรวจสอบว่าผู้เยี่ยมชมได้ตรวจสอบเซสชัน Redis แล้วหรือยัง ขณะแทรกลงใน Redis จะตั้งเวลาหมดอายุเป็นการหมดเวลาของเซสชันที่ไม่ได้ใช้งาน หากจำนวนเซสชันเกินความจุสูงสุด ผู้ใช้ใหม่จะถูกส่งต่อไปยังหน้าห้องรอ

คุณสามารถอัปเดต waiting_room_html เพื่อปรับแต่งหน้าห้องรอ

คุณสามารถอัปเดต getDefaultResponse() วิธีการส่งต่อไปยังหน้าของคุณเองโดยใช้ NextResponse

ขั้นตอนที่ 3:เรียกใช้และปรับใช้

เรียกใช้แอปพลิเคชันในเครื่องโดย npm run dev . คุณอาจต้องการตั้งค่า 1 เป็น TOTAL_ACTIVE_USERS และเปิดหน้า (https://localhost:3000/api/hello) ในเบราว์เซอร์ต่างๆ เพื่อทดสอบห้องรออย่างง่ายดาย

คุณสามารถปรับใช้แอปพลิเคชันของคุณกับ Vercel โดย

vercel deploy –prod

Vercel จะเรียกใช้ _middleware.ts ในตำแหน่งขอบเพื่อลดเวลาในการตอบสนองทั่วโลก

บทสรุป

บทช่วยสอนนี้แสดงให้เห็นว่าการสร้างแอปพลิเคชันแบบไดนามิกที่ Edge นั้นง่ายเพียงใดด้วย Vercel และ Upstash ตรวจสอบตัวอย่างของเราสำหรับตัวอย่างเพิ่มเติม

รอความคิดเห็นของคุณใน Twitter หรือ Discord