ในบล็อกโพสต์นี้ เราจะใช้หน้าห้องรอสำหรับเว็บไซต์ของคุณ
ทำไม?
ผู้เข้าชมเว็บไซต์ของคุณเป็นจำนวนมากเป็นสิ่งที่ดีโดยทั่วไปแต่ไม่เสมอไป การรับส่งข้อมูลสูงอย่างกะทันหันอาจทำให้แอปพลิเคชันของคุณล้นหลามได้ง่าย ซึ่งอาจขัดขวางบริการของคุณโดยสิ้นเชิง ห้องรอเป็นโซลูชันที่ช่วยให้คุณควบคุมการรับส่งข้อมูลและปกป้องทรัพยากรของคุณในช่วงที่มีการจราจรหนาแน่น Cloudflare Waiting Room เป็นโซลูชันที่ดี แต่ใช้ได้เฉพาะกับบัญชีธุรกิจและบัญชีองค์กรเท่านั้น ไม่ต้องกังวล ในบล็อกนี้ เราจะสร้างห้องรอสำหรับเว็บไซต์ทุกประเภทโดยใช้ Cloudflare Workers และ Upstash Redis
อย่างไร
มีสองพารามิเตอร์ในการควบคุมห้องรอ
- ระยะเวลาเซสชันสูงสุด :ผู้เยี่ยมชมสามารถอยู่เฉยๆบนเว็บไซต์ได้นานแค่ไหน?
- ความจุเว็บไซต์สูงสุด :เว็บไซต์สามารถให้ผู้เข้าชมได้กี่คนพร้อมกัน?
เมื่อผู้เยี่ยมชมเข้าสู่เว็บไซต์ของเรา เราจะสร้างคีย์เฉพาะ (เช่น คีย์เซสชัน) และเก็บไว้ใน Redis เราจะใส่คีย์นี้ใน Redis โดยมีเวลาหมดอายุซึ่งก็คือ ระยะเวลาเซสชันสูงสุด . ก่อนให้ผู้เยี่ยมชมเข้าสู่เว็บไซต์ เราจะตรวจสอบ dbsize ของ Redis หากขนาดสูงกว่า **ความจุสูงสุดของเว็บไซต์ **เราจะส่งต่อผู้เยี่ยมชมไปยังห้องรอ ห้องรอเป็น html แบบคงที่ แต่จะรีเฟรชทุกๆ 30 วินาที นั่นคือทุกๆ 30 วินาที ผู้เข้าชมสามารถเข้าสู่เว็บไซต์ได้หากมีห้องว่าง
คีย์เฉพาะที่เราสร้างขึ้นยังถูกเขียนไปยังคำขอของผู้เข้าชมเป็นคุกกี้ ดังนั้นคำขอของผู้มาเยี่ยมคนเดียวกันจะมีคีย์เดียวกัน นี่เป็นสิ่งจำเป็นเพื่อตรวจสอบว่าผู้เข้าชมมีเซสชันภายในเว็บไซต์อยู่แล้วเมื่อความจุเต็มหรือไม่ ดังนั้นในฐานะผู้เยี่ยมชม คุณสามารถเข้าสู่เว็บไซต์ได้แม้ว่าความจุจะเต็มตราบเท่าที่คีย์ของคุณอยู่ในคีย์สเปซ Redis หากคุณไม่ได้ใช้งานนานกว่าระยะเวลาสูงสุดของเซสชัน คีย์ของคุณจะถูกลบออกจาก Redis และถ้าความจุเต็มเมื่อคุณส่งคำขอคุณจะถูกส่งต่อไปยังห้องรอ
เราจะนำตรรกะนี้ไปใช้ที่ Cloudflare Workers เราจะใช้ Upstash เป็นร้าน Redis ตอนนี้ เรามาอธิบายภูมิหลังของการตัดสินใจด้านเทคโนโลยีเหล่านี้กัน
ทำไมต้อง Cloudflare Workers
การใช้งานห้องรอจะสกัดกั้นคำขอทั้งหมดที่มายังเว็บไซต์ของคุณ ดังนั้นค่าใช้จ่ายด้านประสิทธิภาพจึงควรน้อยที่สุด Cloudflare Workers ใช้ประโยชน์จากโครงสร้างพื้นฐาน Edge ของ Cloudflare ดังนั้นจึงให้เวลาแฝงขั้นต่ำทั่วโลก ไม่เหมือนกับ AWS Lambda ตรงที่ไม่มีปัญหาการสตาร์ทแบบเย็น มันเป็นเทคโนโลยีแบบไร้เซิร์ฟเวอร์ ดังนั้นความสามารถในการปรับขนาดก็ไม่ใช่ปัญหาเช่นกัน
ทำไมต้อง Upstash Redis
คุณต้องตรวจสอบขนาดปัจจุบันของเว็บไซต์ก่อนที่จะอนุญาตให้ผู้เยี่ยมชมใหม่แต่ละคน Cloudflare Workers ไม่มีสัญชาติ ดังนั้นคุณต้องเก็บข้อมูลนี้ไว้ภายนอก Redis เป็นตัวเลือกที่ดีที่สุดโดยมีเวลาแฝงต่ำ แต่ข้อเสนอ Redis ต้องการการเชื่อมต่อแบบ TCP ซึ่ง Cloudflare Workers ไม่รองรับ Upstash เป็นข้อเสนอ Redis เดียวที่มี REST API ในตัว และต้องขอบคุณการจำลองแบบทั่วโลกที่ทำให้คุณมีเวลาแฝงต่ำไปทั่วโลก
การใช้งานทีละขั้นตอน
เราจะดำเนินโครงการทีละขั้นตอนด้านล่าง หากคุณต้องการโคลนโปรเจ็กต์และตั้งค่าห้องรอสำหรับเว็บไซต์ของคุณโดยตรง คุณสามารถทำตามขั้นตอนใน readme ของซอร์สโค้ดได้
1
ตั้งค่าโครงการ
สร้างโครงการโดยใช้ wrangler
wrangler generate waiting-room
จากนั้นติดตั้งการพึ่งพา:
npm install cookie upstash@redis
2
อัพเดท wrangler.toml
อัปเดตประเภท:
type = "webpack"
ตั้งค่า ID บัญชี Cloudflare ของคุณ เลือกตัวเลือกนี้เพื่อค้นหารหัสบัญชีของคุณ
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 เป็นไฟล์การใช้งาน Cloudflare Workers ดังนั้นเราจะใส่ตรรกะทั้งหมดไว้ข้างใน คัดลอกและวางโค้ดด้านล่างลงไป:
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 จะคืนค่า getDefaultResponse หรือ getWaitingRoomResponse ขึ้นอยู่กับความพร้อมใช้งานของเว็บไซต์ของคุณ
4
เรียกใช้ในเครื่อง
หากต้องการทดสอบห้องรอในเครื่อง คุณควรตั้งค่าความจุเป็น 1 ระยะเวลาเซสชันเป็น 30 วินาที จากนั้นเรียกใช้
wrangler dev
ตอนนี้เปิด https://127.0.0.1:8787/ ที่ Chrome คุณจะเห็น:
จากนั้นเปิด URL เดียวกันบน Safari (หรือ Chrome incognito) คุณจะเห็น:
รอนานกว่า 30 วินาที คุณควรเห็นหน้าในห้องรอจะรีเฟรชและเข้าสู่เว็บไซต์
ในพื้นที่ของเราไม่ส่งต่อไปยังเว็บไซต์จริง ดังนั้นเราจึงเห็นหน้า 404 ของ Cloudflare ไม่เป็นไร
5
เผยแพร่
ปรับใช้ฟังก์ชัน CF Workers ของคุณด้วย:
wrangler publish
มันจะให้ URL แก่คุณเช่น:https://waiting-room.upsdev.workers.dev/
ตอนนี้ ให้กำหนดเส้นทางพนักงานของเราไปยังเว็บไซต์ของคุณ อันดับแรก เนมเซิร์ฟเวอร์ของโดเมนควรชี้ไปที่ Cloudflare ตรวจสอบสิ่งนี้ จากนั้น คุณต้องเพิ่มโดเมนของคุณเป็นเส้นทางและเลือกฟังก์ชัน Workers ของคุณในแดชบอร์ด CF Workers
บทสรุป
เราสร้างห้องรอได้สำเร็จโดยไม่ต้องแตะรหัสแอปพลิเคชันของเราด้วย Cloudflare Workers และ Upstash Redis เราเชื่อว่านี่เป็นอีกตัวบ่งชี้ว่าฟังก์ชัน edge อันทรงพลังมารวมกับ Upstash ได้อย่างไร
มีสิ่งที่ต้องปรับปรุง:
- เวลารอโดยประมาณ:เราสามารถคำนวณและแสดงเวลารอเฉลี่ยได้
- คิวที่ยุติธรรมและเป็นระเบียบ:ขณะนี้ผู้เข้าชมที่รอจะสุ่มเข้าสู่ไซต์เมื่อมีห้องว่าง เราอาจเก็บคิวและพยายามให้ผู้มาเยี่ยมชมเป็นลำดับ
การปรับปรุงทั้งสองอย่างข้างต้นต้องการการรักษาสถานะให้มากขึ้นและโทรทางไกลมากขึ้น นั่นเป็นเหตุผลที่เราต้องการข้ามสิ่งเหล่านั้น หากกรณีการใช้งานของคุณจำเป็นต้องใช้อย่างเข้มงวด คุณสามารถตรวจสอบบล็อกนี้เพื่อรับแรงบันดาลใจจากงานของทีม Cloudflare ในโซลูชันระดับองค์กร
ตรวจสอบซอร์สโค้ด
แจ้งให้เราทราบความคิดเห็นของคุณบน Twitter หรือ Discord